| 1 /** |
|
| 2 * The QQ2003C protocol plugin |
|
| 3 * |
|
| 4 * for gaim |
|
| 5 * |
|
| 6 * Copyright (C) 2004 Puzzlebird |
|
| 7 * |
|
| 8 * This program is free software; you can redistribute it and/or modify |
|
| 9 * it under the terms of the GNU General Public License as published by |
|
| 10 * the Free Software Foundation; either version 2 of the License, or |
|
| 11 * (at your option) any later version. |
|
| 12 * |
|
| 13 * This program is distributed in the hope that it will be useful, |
|
| 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 * GNU General Public License for more details. |
|
| 17 * |
|
| 18 * You should have received a copy of the GNU General Public License |
|
| 19 * along with this program; if not, write to the Free Software |
|
| 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 21 */ |
|
| 22 |
|
| 23 #include "conversation.h" |
|
| 24 #include "debug.h" |
|
| 25 #include "notify.h" |
|
| 26 #include "prefs.h" |
|
| 27 #include "request.h" |
|
| 28 #include "util.h" |
|
| 29 |
|
| 30 #include "char_conv.h" |
|
| 31 #include "group_find.h" |
|
| 32 #include "group_hash.h" |
|
| 33 #include "group_info.h" |
|
| 34 #include "group_im.h" |
|
| 35 #include "group_network.h" |
|
| 36 #include "group_opt.h" |
|
| 37 #include "im.h" |
|
| 38 #include "packet_parse.h" |
|
| 39 #include "utils.h" |
|
| 40 |
|
| 41 typedef struct _qq_recv_group_im { |
|
| 42 guint32 external_group_id; |
|
| 43 guint8 group_type; |
|
| 44 guint32 member_uid; |
|
| 45 guint16 msg_seq; |
|
| 46 time_t send_time; |
|
| 47 guint16 msg_len; |
|
| 48 gchar *msg; |
|
| 49 guint8 *font_attr; |
|
| 50 gint font_attr_len; |
|
| 51 } qq_recv_group_im; |
|
| 52 |
|
| 53 /* send IM to a group */ |
|
| 54 void qq_send_packet_group_im(GaimConnection *gc, qq_group *group, const gchar *msg) |
|
| 55 { |
|
| 56 gint data_len, bytes; |
|
| 57 guint8 *raw_data, *cursor, *send_im_tail; |
|
| 58 guint16 msg_len; |
|
| 59 gchar *msg_filtered; |
|
| 60 |
|
| 61 g_return_if_fail(gc != NULL && group != NULL && msg != NULL); |
|
| 62 |
|
| 63 msg_filtered = gaim_markup_strip_html(msg); |
|
| 64 msg_len = strlen(msg_filtered); |
|
| 65 data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN; |
|
| 66 raw_data = g_newa(guint8, data_len); |
|
| 67 cursor = raw_data; |
|
| 68 |
|
| 69 bytes = 0; |
|
| 70 bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEND_MSG); |
|
| 71 bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); |
|
| 72 bytes += create_packet_w(raw_data, &cursor, msg_len + QQ_SEND_IM_AFTER_MSG_LEN); |
|
| 73 bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len); |
|
| 74 send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL, |
|
| 75 FALSE, FALSE, FALSE, |
|
| 76 QQ_SEND_IM_AFTER_MSG_LEN); |
|
| 77 bytes += create_packet_data(raw_data, &cursor, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN); |
|
| 78 g_free(send_im_tail); |
|
| 79 g_free(msg_filtered); |
|
| 80 |
|
| 81 if (bytes == data_len) /* create OK */ |
|
| 82 qq_send_group_cmd(gc, group, raw_data, data_len); |
|
| 83 else |
|
| 84 gaim_debug(GAIM_DEBUG_ERROR, "QQ", |
|
| 85 "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes); |
|
| 86 } |
|
| 87 |
|
| 88 /* this is the ACK */ |
|
| 89 void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, GaimConnection *gc) |
|
| 90 { |
|
| 91 /* return should be the internal group id |
|
| 92 * but we have nothing to do with it */ |
|
| 93 return; |
|
| 94 } |
|
| 95 |
|
| 96 /* receive an application to join the group */ |
|
| 97 void qq_process_recv_group_im_apply_join |
|
| 98 (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, GaimConnection *gc) |
|
| 99 { |
|
| 100 guint32 external_group_id, user_uid; |
|
| 101 guint8 group_type; |
|
| 102 gchar *reason_utf8, *msg, *reason; |
|
| 103 group_member_opt *g; |
|
| 104 |
|
| 105 g_return_if_fail(gc != NULL && internal_group_id > 0 && data != NULL && len > 0); |
|
| 106 |
|
| 107 if (*cursor >= (data + len - 1)) { |
|
| 108 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Received group msg apply_join is empty\n"); |
|
| 109 return; |
|
| 110 } |
|
| 111 |
|
| 112 read_packet_dw(data, cursor, len, &external_group_id); |
|
| 113 read_packet_b(data, cursor, len, &group_type); |
|
| 114 read_packet_dw(data, cursor, len, &user_uid); |
|
| 115 |
|
| 116 g_return_if_fail(external_group_id > 0 && user_uid > 0); |
|
| 117 |
|
| 118 convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); |
|
| 119 |
|
| 120 msg = g_strdup_printf(_("User %d applied to join group %d"), user_uid, external_group_id); |
|
| 121 reason = g_strdup_printf(_("Reason: %s"), reason_utf8); |
|
| 122 |
|
| 123 g = g_new0(group_member_opt, 1); |
|
| 124 g->gc = gc; |
|
| 125 g->internal_group_id = internal_group_id; |
|
| 126 g->member = user_uid; |
|
| 127 |
|
| 128 gaim_request_action(gc, _("QQ Qun Operation"), |
|
| 129 msg, reason, |
|
| 130 2, g, 3, |
|
| 131 _("Approve"), |
|
| 132 G_CALLBACK |
|
| 133 (qq_group_approve_application_with_struct), |
|
| 134 _("Reject"), |
|
| 135 G_CALLBACK |
|
| 136 (qq_group_reject_application_with_struct), |
|
| 137 _("Search"), G_CALLBACK(qq_group_search_application_with_struct)); |
|
| 138 |
|
| 139 g_free(reason); |
|
| 140 g_free(msg); |
|
| 141 g_free(reason_utf8); |
|
| 142 } |
|
| 143 |
|
| 144 /* the request to join a group is rejected */ |
|
| 145 void qq_process_recv_group_im_been_rejected |
|
| 146 (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, GaimConnection *gc) |
|
| 147 { |
|
| 148 guint32 external_group_id, admin_uid; |
|
| 149 guint8 group_type; |
|
| 150 gchar *reason_utf8, *msg, *reason; |
|
| 151 qq_group *group; |
|
| 152 |
|
| 153 g_return_if_fail(gc != NULL && data != NULL && len > 0); |
|
| 154 |
|
| 155 if (*cursor >= (data + len - 1)) { |
|
| 156 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Received group msg been_rejected is empty\n"); |
|
| 157 return; |
|
| 158 } |
|
| 159 |
|
| 160 read_packet_dw(data, cursor, len, &external_group_id); |
|
| 161 read_packet_b(data, cursor, len, &group_type); |
|
| 162 read_packet_dw(data, cursor, len, &admin_uid); |
|
| 163 |
|
| 164 g_return_if_fail(external_group_id > 0 && admin_uid > 0); |
|
| 165 |
|
| 166 convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); |
|
| 167 |
|
| 168 msg = g_strdup_printf |
|
| 169 (_("You request to join group %d has been rejected by admin %d"), external_group_id, admin_uid); |
|
| 170 reason = g_strdup_printf(_("Reason: %s"), reason_utf8); |
|
| 171 |
|
| 172 gaim_notify_warning(gc, _("QQ Qun Operation"), msg, reason); |
|
| 173 |
|
| 174 group = qq_group_find_by_internal_group_id(gc, internal_group_id); |
|
| 175 if (group != NULL) { |
|
| 176 group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; |
|
| 177 qq_group_refresh(gc, group); |
|
| 178 } |
|
| 179 |
|
| 180 g_free(reason); |
|
| 181 g_free(msg); |
|
| 182 g_free(reason_utf8); |
|
| 183 } |
|
| 184 |
|
| 185 /* the request to join a group is approved */ |
|
| 186 void qq_process_recv_group_im_been_approved |
|
| 187 (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, GaimConnection *gc) |
|
| 188 { |
|
| 189 guint32 external_group_id, admin_uid; |
|
| 190 guint8 group_type; |
|
| 191 gchar *reason_utf8, *msg; |
|
| 192 qq_group *group; |
|
| 193 |
|
| 194 g_return_if_fail(gc != NULL && data != NULL && len > 0); |
|
| 195 |
|
| 196 if (*cursor >= (data + len - 1)) { |
|
| 197 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Received group msg been_approved is empty\n"); |
|
| 198 return; |
|
| 199 } |
|
| 200 |
|
| 201 read_packet_dw(data, cursor, len, &external_group_id); |
|
| 202 read_packet_b(data, cursor, len, &group_type); |
|
| 203 read_packet_dw(data, cursor, len, &admin_uid); |
|
| 204 |
|
| 205 g_return_if_fail(external_group_id > 0 && admin_uid > 0); |
|
| 206 /* it is also a "无" here, so do not display */ |
|
| 207 convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); |
|
| 208 |
|
| 209 msg = g_strdup_printf |
|
| 210 (_("You request to join group %d has been approved by admin %d"), external_group_id, admin_uid); |
|
| 211 |
|
| 212 gaim_notify_warning(gc, _("QQ Qun Operation"), msg, NULL); |
|
| 213 |
|
| 214 group = qq_group_find_by_internal_group_id(gc, internal_group_id); |
|
| 215 if (group != NULL) { |
|
| 216 group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; |
|
| 217 qq_group_refresh(gc, group); |
|
| 218 } |
|
| 219 |
|
| 220 g_free(msg); |
|
| 221 g_free(reason_utf8); |
|
| 222 } |
|
| 223 |
|
| 224 /* process the packet when removed from a group */ |
|
| 225 void qq_process_recv_group_im_been_removed |
|
| 226 (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, GaimConnection *gc) |
|
| 227 { |
|
| 228 guint32 external_group_id, uid; |
|
| 229 guint8 group_type; |
|
| 230 gchar *msg; |
|
| 231 qq_group *group; |
|
| 232 |
|
| 233 g_return_if_fail(gc != NULL && data != NULL && len > 0); |
|
| 234 |
|
| 235 if (*cursor >= (data + len - 1)) { |
|
| 236 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Received group msg been_removed is empty\n"); |
|
| 237 return; |
|
| 238 } |
|
| 239 |
|
| 240 read_packet_dw(data, cursor, len, &external_group_id); |
|
| 241 read_packet_b(data, cursor, len, &group_type); |
|
| 242 read_packet_dw(data, cursor, len, &uid); |
|
| 243 |
|
| 244 g_return_if_fail(external_group_id > 0 && uid > 0); |
|
| 245 |
|
| 246 msg = g_strdup_printf(_("You [%d] has exit group \"%d\""), uid, external_group_id); |
|
| 247 gaim_notify_info(gc, _("QQ Qun Operation"), msg, NULL); |
|
| 248 |
|
| 249 group = qq_group_find_by_internal_group_id(gc, internal_group_id); |
|
| 250 if (group != NULL) { |
|
| 251 group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; |
|
| 252 qq_group_refresh(gc, group); |
|
| 253 } |
|
| 254 |
|
| 255 g_free(msg); |
|
| 256 } |
|
| 257 |
|
| 258 /* process the packet when added to a group */ |
|
| 259 void qq_process_recv_group_im_been_added |
|
| 260 (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, GaimConnection *gc) |
|
| 261 { |
|
| 262 guint32 external_group_id, uid; |
|
| 263 guint8 group_type; |
|
| 264 qq_group *group; |
|
| 265 gchar *msg; |
|
| 266 |
|
| 267 g_return_if_fail(gc != NULL && data != NULL && len > 0); |
|
| 268 |
|
| 269 if (*cursor >= (data + len - 1)) { |
|
| 270 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Received group msg been_added is empty\n"); |
|
| 271 return; |
|
| 272 } |
|
| 273 |
|
| 274 read_packet_dw(data, cursor, len, &external_group_id); |
|
| 275 read_packet_b(data, cursor, len, &group_type); |
|
| 276 read_packet_dw(data, cursor, len, &uid); |
|
| 277 |
|
| 278 g_return_if_fail(external_group_id > 0 && uid > 0); |
|
| 279 |
|
| 280 msg = g_strdup_printf(_("You [%d] has been added by group \"%d\""), uid, external_group_id); |
|
| 281 gaim_notify_info(gc, _("QQ Qun Operation"), msg, _("OpenQ has added this group to your buddy list")); |
|
| 282 |
|
| 283 group = qq_group_find_by_internal_group_id(gc, internal_group_id); |
|
| 284 if (group != NULL) { |
|
| 285 group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; |
|
| 286 qq_group_refresh(gc, group); |
|
| 287 } else { /* no such group, try to create a dummy first, and then update */ |
|
| 288 group = qq_group_create_by_id(gc, internal_group_id, external_group_id); |
|
| 289 group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; |
|
| 290 qq_group_refresh(gc, group); |
|
| 291 qq_send_cmd_group_get_group_info(gc, group); |
|
| 292 /* the return of this cmd will automatically update the group in blist */ |
|
| 293 } |
|
| 294 |
|
| 295 g_free(msg); |
|
| 296 } |
|
| 297 |
|
| 298 /* recv an IM from a group chat */ |
|
| 299 void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len, |
|
| 300 guint32 internal_group_id, GaimConnection *gc, guint16 im_type) |
|
| 301 { |
|
| 302 gchar *msg_with_gaim_smiley, *msg_utf8_encoded, *im_src_name; |
|
| 303 guint16 unknown; |
|
| 304 guint32 unknown4; |
|
| 305 GaimConversation *conv; |
|
| 306 qq_data *qd; |
|
| 307 qq_buddy *member; |
|
| 308 qq_group *group; |
|
| 309 qq_recv_group_im *im_group; |
|
| 310 gint skip_len; |
|
| 311 |
|
| 312 g_return_if_fail(gc != NULL && gc->proto_data != NULL && data != NULL && data_len > 0); |
|
| 313 qd = (qq_data *) gc->proto_data; |
|
| 314 |
|
| 315 gaim_debug(GAIM_DEBUG_INFO, "QQ", |
|
| 316 "group im hex dump\n%s\n", hex_dump_to_str(*cursor, data_len - (*cursor - data))); |
|
| 317 |
|
| 318 if (*cursor >= (data + data_len - 1)) { |
|
| 319 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Received group im_group is empty\n"); |
|
| 320 return; |
|
| 321 } |
|
| 322 |
|
| 323 im_group = g_newa(qq_recv_group_im, 1); |
|
| 324 |
|
| 325 read_packet_dw(data, cursor, data_len, &(im_group->external_group_id)); |
|
| 326 read_packet_b(data, cursor, data_len, &(im_group->group_type)); |
|
| 327 |
|
| 328 if(QQ_RECV_IM_TEMP_QUN_IM == im_type) { |
|
| 329 read_packet_dw(data, cursor, data_len, &(internal_group_id)); |
|
| 330 } |
|
| 331 |
|
| 332 read_packet_dw(data, cursor, data_len, &(im_group->member_uid)); |
|
| 333 read_packet_w(data, cursor, data_len, &unknown); /* 0x0001? */ |
|
| 334 read_packet_w(data, cursor, data_len, &(im_group->msg_seq)); |
|
| 335 read_packet_dw(data, cursor, data_len, (guint32 *) & (im_group->send_time)); |
|
| 336 read_packet_dw(data, cursor, data_len, &unknown4); /* versionID */ |
|
| 337 /* |
|
| 338 * length includes font_attr |
|
| 339 * this msg_len includes msg and font_attr |
|
| 340 **** the format is **** |
|
| 341 * length of all |
|
| 342 * 1. unknown 10 bytes |
|
| 343 * 2. 0-ended string |
|
| 344 * 3. font_attr |
|
| 345 */ |
|
| 346 |
|
| 347 read_packet_w(data, cursor, data_len, &(im_group->msg_len)); |
|
| 348 g_return_if_fail(im_group->msg_len > 0); |
|
| 349 |
|
| 350 /* |
|
| 351 * 10 bytes from lumaqq |
|
| 352 * contentType = buf.getChar(); |
|
| 353 * totalFragments = buf.get() & 255; |
|
| 354 * fragmentSequence = buf.get() & 255; |
|
| 355 * messageId = buf.getChar(); |
|
| 356 * buf.getInt(); |
|
| 357 */ |
|
| 358 |
|
| 359 if(im_type != QQ_RECV_IM_UNKNOWN_QUN_IM) |
|
| 360 skip_len = 10; |
|
| 361 else |
|
| 362 skip_len = 0; |
|
| 363 *cursor += skip_len; |
|
| 364 |
|
| 365 im_group->msg = g_strdup((gchar *) *cursor); |
|
| 366 *cursor += strlen(im_group->msg) + 1; |
|
| 367 /* there might not be any font_attr, check it */ |
|
| 368 im_group->font_attr_len = im_group->msg_len - strlen(im_group->msg) - 1 - skip_len; |
|
| 369 if (im_group->font_attr_len > 0) |
|
| 370 im_group->font_attr = g_memdup(*cursor, im_group->font_attr_len); |
|
| 371 else |
|
| 372 im_group->font_attr = NULL; |
|
| 373 |
|
| 374 /* group im_group has no flag to indicate whether it has font_attr or not */ |
|
| 375 msg_with_gaim_smiley = qq_smiley_to_gaim(im_group->msg); |
|
| 376 if (im_group->font_attr_len > 0) |
|
| 377 msg_utf8_encoded = qq_encode_to_gaim(im_group->font_attr, |
|
| 378 im_group->font_attr_len, msg_with_gaim_smiley); |
|
| 379 else |
|
| 380 msg_utf8_encoded = qq_to_utf8(msg_with_gaim_smiley, QQ_CHARSET_DEFAULT); |
|
| 381 |
|
| 382 group = qq_group_find_by_internal_group_id(gc, internal_group_id); |
|
| 383 g_return_if_fail(group != NULL); |
|
| 384 |
|
| 385 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT, group->group_name_utf8, gaim_connection_get_account(gc)); |
|
| 386 if (conv == NULL && gaim_prefs_get_bool("/plugins/prpl/qq/prompt_group_msg_on_recv")) { |
|
| 387 serv_got_joined_chat(gc, qd->channel++, group->group_name_utf8); |
|
| 388 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT, group->group_name_utf8, gaim_connection_get_account(gc)); |
|
| 389 } |
|
| 390 |
|
| 391 if (conv != NULL) { |
|
| 392 member = qq_group_find_member_by_uid(group, im_group->member_uid); |
|
| 393 if (member == NULL || member->nickname == NULL) |
|
| 394 im_src_name = uid_to_gaim_name(im_group->member_uid); |
|
| 395 else |
|
| 396 im_src_name = g_strdup(member->nickname); |
|
| 397 serv_got_chat_in(gc, |
|
| 398 gaim_conv_chat_get_id(GAIM_CONV_CHAT |
|
| 399 (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time); |
|
| 400 g_free(im_src_name); |
|
| 401 } |
|
| 402 g_free(msg_with_gaim_smiley); |
|
| 403 g_free(msg_utf8_encoded); |
|
| 404 g_free(im_group->msg); |
|
| 405 g_free(im_group->font_attr); |
|
| 406 } |
|