Mercurial > pidgin.yaz
comparison libpurple/protocols/qq/im.c @ 24074:c2253c485728
2008.10.04 - ccpaging <ccpagint(at)gmail.com>
* Update protocol for 2007
* Code cleanup
author | SHiNE CsyFeK <csyfek@gmail.com> |
---|---|
date | Wed, 22 Oct 2008 14:47:39 +0000 |
parents | 619ac2303c46 |
children | ec3f7d3e0445 |
comparison
equal
deleted
inserted
replaced
24073:df699d739b8f | 24074:c2253c485728 |
---|---|
33 | 33 |
34 #include "buddy_info.h" | 34 #include "buddy_info.h" |
35 #include "buddy_list.h" | 35 #include "buddy_list.h" |
36 #include "buddy_opt.h" | 36 #include "buddy_opt.h" |
37 #include "char_conv.h" | 37 #include "char_conv.h" |
38 #include "group_im.h" | |
39 #include "qq_define.h" | 38 #include "qq_define.h" |
40 #include "im.h" | 39 #include "im.h" |
41 #include "packet_parse.h" | 40 #include "packet_parse.h" |
42 #include "qq_network.h" | 41 #include "qq_network.h" |
43 #include "send_file.h" | 42 #include "send_file.h" |
44 #include "utils.h" | 43 #include "utils.h" |
45 | 44 |
46 #define QQ_SEND_IM_REPLY_OK 0x00 | |
47 #define DEFAULT_FONT_NAME_LEN 4 | 45 #define DEFAULT_FONT_NAME_LEN 4 |
48 | 46 |
49 enum | 47 enum |
50 { | 48 { |
51 QQ_NORMAL_IM_TEXT = 0x000b, | 49 QQ_NORMAL_IM_TEXT = 0x000b, |
62 QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT = 0x83, | 60 QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT = 0x83, |
63 QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL = 0x85, | 61 QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL = 0x85, |
64 QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87 | 62 QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87 |
65 }; | 63 }; |
66 | 64 |
67 enum { | 65 typedef struct _qq_im_header qq_im_header; |
68 QQ_RECV_SYS_IM_KICK_OUT = 0x01 | 66 typedef struct _qq_recv_extended_im_text qq_recv_extended_im_text; |
69 }; | 67 |
70 | 68 struct _qq_im_header { |
71 typedef struct _qq_recv_im_header qq_recv_im_header; | |
72 typedef struct _qq_recv_normal_im_text qq_recv_normal_im_text; | |
73 typedef struct _qq_recv_normal_im_common qq_recv_normal_im_common; | |
74 typedef struct _qq_recv_normal_im_unprocessed qq_recv_normal_im_unprocessed; | |
75 | |
76 struct _qq_recv_normal_im_common { | |
77 /* this is the common part of normal_text */ | 69 /* this is the common part of normal_text */ |
78 guint16 sender_ver; | 70 guint16 version_from; |
79 guint32 sender_uid; | 71 guint32 uid_from; |
80 guint32 receiver_uid; | 72 guint32 uid_to; |
81 guint8 session_md5[QQ_KEY_LENGTH]; | 73 guint8 session_md5[QQ_KEY_LENGTH]; |
82 guint16 normal_im_type; | |
83 }; | |
84 | |
85 struct _qq_recv_normal_im_text { | |
86 qq_recv_normal_im_common *common; | |
87 /* now comes the part for text only */ | |
88 guint16 msg_seq; | |
89 guint32 send_time; | |
90 guint16 sender_icon; | |
91 guint8 unknown2[3]; | |
92 guint8 is_there_font_attr; | |
93 guint8 unknown3[4]; | |
94 guint8 msg_type; | |
95 gchar *msg; /* no fixed length, ends with 0x00 */ | |
96 guint8 *font_attr; | |
97 gint font_attr_len; | |
98 }; | |
99 | |
100 struct _qq_recv_normal_im_unprocessed { | |
101 qq_recv_normal_im_common *common; | |
102 /* now comes the part of unprocessed */ | |
103 guint8 *unknown; /* no fixed length */ | |
104 gint length; | |
105 }; | |
106 | |
107 struct _qq_recv_im_header { | |
108 guint32 sender_uid; | |
109 guint32 receiver_uid; | |
110 guint32 server_im_seq; | |
111 struct in_addr sender_ip; | |
112 guint16 sender_port; | |
113 guint16 im_type; | 74 guint16 im_type; |
114 }; | 75 }; |
115 | 76 |
116 #define QQ_SEND_IM_AFTER_MSG_HEADER_LEN 8 | 77 #define QQ_SEND_IM_AFTER_MSG_HEADER_LEN 8 |
117 #define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5" | 78 #define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5" |
180 send_im_tail[7] = 0x22; /* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */ | 141 send_im_tail[7] = 0x22; /* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */ |
181 /* qq_show_packet("QQ_MESG", send_im_tail, tail_len); */ | 142 /* qq_show_packet("QQ_MESG", send_im_tail, tail_len); */ |
182 return (guint8 *) send_im_tail; | 143 return (guint8 *) send_im_tail; |
183 } | 144 } |
184 | 145 |
185 static const gchar *qq_get_recv_im_type_str(gint type) | |
186 { | |
187 switch (type) { | |
188 case QQ_RECV_IM_TO_BUDDY: | |
189 return "QQ_RECV_IM_TO_BUDDY"; | |
190 case QQ_RECV_IM_TO_UNKNOWN: | |
191 return "QQ_RECV_IM_TO_UNKNOWN"; | |
192 case QQ_RECV_IM_UNKNOWN_QUN_IM: | |
193 return "QQ_RECV_IM_UNKNOWN_QUN_IM"; | |
194 case QQ_RECV_IM_ADD_TO_QUN: | |
195 return "QQ_RECV_IM_ADD_TO_QUN"; | |
196 case QQ_RECV_IM_DEL_FROM_QUN: | |
197 return "QQ_RECV_IM_DEL_FROM_QUN"; | |
198 case QQ_RECV_IM_APPLY_ADD_TO_QUN: | |
199 return "QQ_RECV_IM_APPLY_ADD_TO_QUN"; | |
200 case QQ_RECV_IM_CREATE_QUN: | |
201 return "QQ_RECV_IM_CREATE_QUN"; | |
202 case QQ_RECV_IM_SYS_NOTIFICATION: | |
203 return "QQ_RECV_IM_SYS_NOTIFICATION"; | |
204 case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: | |
205 return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN"; | |
206 case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: | |
207 return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN"; | |
208 case QQ_RECV_IM_TEMP_QUN_IM: | |
209 return "QQ_RECV_IM_TEMP_QUN_IM"; | |
210 case QQ_RECV_IM_QUN_IM: | |
211 return "QQ_RECV_IM_QUN_IM"; | |
212 case QQ_RECV_IM_NEWS: | |
213 return "QQ_RECV_IM_NEWS"; | |
214 case QQ_RECV_IM_FROM_BUDDY_2006: | |
215 return "QQ_RECV_IM_FROM_BUDDY_2006"; | |
216 case QQ_RECV_IM_FROM_UNKNOWN_2006: | |
217 return "QQ_RECV_IM_FROM_UNKNOWN_2006"; | |
218 default: | |
219 return "QQ_RECV_IM_UNKNOWN"; | |
220 } | |
221 } | |
222 | |
223 /* read the common parts of the normal_im, | 146 /* read the common parts of the normal_im, |
224 * returns the bytes read if succeed, or -1 if there is any error */ | 147 * returns the bytes read if succeed, or -1 if there is any error */ |
225 static gint normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common) | 148 static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len) |
226 { | 149 { |
227 gint bytes; | 150 gint bytes; |
228 g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1); | 151 g_return_val_if_fail(data != NULL && len > 0, -1); |
229 | 152 |
230 bytes = 0; | 153 bytes = 0; |
231 /* now push data into common header */ | 154 bytes += qq_get16(&(im_header->version_from), data + bytes); |
232 bytes += qq_get16(&(common->sender_ver), data + bytes); | 155 bytes += qq_get32(&(im_header->uid_from), data + bytes); |
233 bytes += qq_get32(&(common->sender_uid), data + bytes); | 156 bytes += qq_get32(&(im_header->uid_to), data + bytes); |
234 bytes += qq_get32(&(common->receiver_uid), data + bytes); | 157 bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes); |
235 bytes += qq_getdata(common->session_md5, QQ_KEY_LENGTH, data + bytes); | 158 bytes += qq_get16(&(im_header->im_type), data + bytes); |
236 bytes += qq_get16(&(common->normal_im_type), data + bytes); | |
237 | |
238 if (bytes != 28) { /* read common place fail */ | |
239 purple_debug_error("QQ", "Expect 28 bytes, read %d bytes\n", bytes); | |
240 return -1; | |
241 } | |
242 | |
243 return bytes; | 159 return bytes; |
244 } | |
245 | |
246 static void process_recv_news(guint8 *data, gint data_len, PurpleConnection *gc) | |
247 { | |
248 qq_data *qd = (qq_data *) gc->proto_data; | |
249 gint bytes; | |
250 guint8 *temp; | |
251 guint8 temp_len; | |
252 gchar *title, *brief, *url; | |
253 gchar *title_utf8; | |
254 gchar *content, *content_utf8; | |
255 | |
256 g_return_if_fail(data != NULL && data_len != 0); | |
257 | |
258 #if 0 | |
259 qq_show_packet("Rcv news", data, data_len); | |
260 #endif | |
261 | |
262 temp = g_newa(guint8, data_len); | |
263 bytes = 4; /* ignore unknown 4 bytes */ | |
264 | |
265 bytes += qq_get8(&temp_len, data + bytes); | |
266 g_return_if_fail(bytes + temp_len <= data_len); | |
267 bytes += qq_getdata(temp, temp_len, data+bytes); | |
268 title = g_strndup((gchar *)temp, temp_len); | |
269 | |
270 bytes += qq_get8(&temp_len, data + bytes); | |
271 g_return_if_fail(bytes + temp_len <= data_len); | |
272 bytes += qq_getdata(temp, temp_len, data+bytes); | |
273 brief = g_strndup((gchar *)temp, temp_len); | |
274 | |
275 bytes += qq_get8(&temp_len, data + bytes); | |
276 g_return_if_fail(bytes + temp_len <= data_len); | |
277 bytes += qq_getdata(temp, temp_len, data+bytes); | |
278 url = g_strndup((gchar *)temp, temp_len); | |
279 | |
280 title_utf8 = qq_to_utf8(title, QQ_CHARSET_DEFAULT); | |
281 content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url); | |
282 content_utf8 = qq_to_utf8(content, QQ_CHARSET_DEFAULT); | |
283 | |
284 if (qd->is_show_news) { | |
285 qq_got_attention(gc, content_utf8); | |
286 } else { | |
287 purple_debug_info("QQ", "QQ Server news:\n%s\n%s", title_utf8, content_utf8); | |
288 } | |
289 g_free(title); | |
290 g_free(title_utf8); | |
291 g_free(brief); | |
292 g_free(url); | |
293 g_free(content); | |
294 g_free(content_utf8); | |
295 } | 160 } |
296 | 161 |
297 void qq_got_attention(PurpleConnection *gc, const gchar *msg) | 162 void qq_got_attention(PurpleConnection *gc, const gchar *msg) |
298 { | 163 { |
299 qq_data *qd; | 164 qq_data *qd; |
314 serv_got_im(gc, from, msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY, now); | 179 serv_got_im(gc, from, msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY, now); |
315 g_free(from); | 180 g_free(from); |
316 } | 181 } |
317 | 182 |
318 /* process received normal text IM */ | 183 /* process received normal text IM */ |
319 static void process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc) | 184 static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) |
320 { | 185 { |
321 guint16 purple_msg_type; | 186 guint16 purple_msg_type; |
322 gchar *name; | 187 gchar *name; |
323 gchar *msg_with_purple_smiley; | 188 gchar *msg_with_purple_smiley; |
324 gchar *msg_utf8_encoded; | 189 gchar *msg_utf8_encoded; |
325 qq_data *qd; | 190 qq_data *qd; |
326 qq_recv_normal_im_text *im_text; | |
327 gint bytes = 0; | 191 gint bytes = 0; |
328 PurpleBuddy *b; | 192 PurpleBuddy *b; |
329 qq_buddy *qq_b; | 193 qq_buddy *qq_b; |
330 | 194 |
331 g_return_if_fail(common != NULL); | 195 struct { |
196 /* now comes the part for text only */ | |
197 guint16 msg_seq; | |
198 guint32 send_time; | |
199 guint16 sender_icon; | |
200 guint8 unknown2[3]; | |
201 guint8 is_there_font_attr; | |
202 guint8 unknown3[4]; | |
203 guint8 msg_type; | |
204 gchar *msg; /* no fixed length, ends with 0x00 */ | |
205 guint8 *font_attr; | |
206 gint font_attr_len; | |
207 } im_text; | |
208 | |
209 g_return_if_fail (data != NULL && len > 0); | |
210 g_return_if_fail(im_header != NULL); | |
211 | |
332 qd = (qq_data *) gc->proto_data; | 212 qd = (qq_data *) gc->proto_data; |
333 | 213 memset(&im_text, 0, sizeof(im_text)); |
334 /* now it is QQ_NORMAL_IM_TEXT */ | 214 |
335 /* | |
336 if (*cursor >= (data + len - 1)) { | |
337 purple_debug_warning("QQ", "Received normal IM text is empty\n"); | |
338 return; | |
339 } else | |
340 */ | |
341 im_text = g_newa(qq_recv_normal_im_text, 1); | |
342 | |
343 im_text->common = common; | |
344 | |
345 /* push data into im_text */ | 215 /* push data into im_text */ |
346 bytes += qq_get16(&(im_text->msg_seq), data + bytes); | 216 bytes += qq_get16(&(im_text.msg_seq), data + bytes); |
347 bytes += qq_get32(&(im_text->send_time), data + bytes); | 217 bytes += qq_get32(&(im_text.send_time), data + bytes); |
348 bytes += qq_get16(&(im_text->sender_icon), data + bytes); | 218 bytes += qq_get16(&(im_text.sender_icon), data + bytes); |
349 bytes += qq_getdata((guint8 *) & (im_text->unknown2), 3, data + bytes); | 219 bytes += qq_getdata((guint8 *) & (im_text.unknown2), 3, data + bytes); |
350 bytes += qq_get8(&(im_text->is_there_font_attr), data + bytes); | 220 bytes += qq_get8(&(im_text.is_there_font_attr), data + bytes); |
351 /** | 221 /** |
352 * from lumaqq for unknown3 | 222 * from lumaqq for unknown3 |
353 * totalFragments = buf.get() & 255; | 223 * totalFragments = buf.get() & 255; |
354 * fragmentSequence = buf.get() & 255; | 224 * fragmentSequence = buf.get() & 255; |
355 * messageId = buf.getChar(); | 225 * messageId = buf.getChar(); |
356 */ | 226 */ |
357 bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes); | 227 bytes += qq_getdata((guint8 *) & (im_text.unknown3), 4, data + bytes); |
358 bytes += qq_get8(&(im_text->msg_type), data + bytes); | 228 bytes += qq_get8(&(im_text.msg_type), data + bytes); |
359 | 229 |
360 /* we need to check if this is auto-reply | 230 /* we need to check if this is auto-reply |
361 * QQ2003iii build 0304, returns the msg without font_attr | 231 * QQ2003iii build 0304, returns the msg without font_attr |
362 * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */ | 232 * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */ |
363 if (im_text->msg_type == QQ_IM_AUTO_REPLY) { | 233 if (im_text.msg_type == QQ_IM_AUTO_REPLY) { |
364 im_text->is_there_font_attr = 0x00; /* indeed there is no this flag */ | 234 im_text.is_there_font_attr = 0x00; /* indeed there is no this flag */ |
365 im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); | 235 im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); |
366 } else { /* it is normal mesasge */ | 236 } else { /* it is normal mesasge */ |
367 if (im_text->is_there_font_attr) { | 237 if (im_text.is_there_font_attr) { |
368 im_text->msg = g_strdup((gchar *)(data + bytes)); | 238 im_text.msg = g_strdup((gchar *)(data + bytes)); |
369 bytes += strlen(im_text->msg) + 1; /* length decided by strlen! will it cause a crash? */ | 239 bytes += strlen(im_text.msg) + 1; /* length decided by strlen! will it cause a crash? */ |
370 im_text->font_attr_len = len - bytes; | 240 im_text.font_attr_len = len - bytes; |
371 im_text->font_attr = g_memdup(data + bytes, im_text->font_attr_len); | 241 im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); |
372 } else /* not im_text->is_there_font_attr */ | 242 } else /* not im_text.is_there_font_attr */ |
373 im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); | 243 im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); |
374 } /* if im_text->msg_type */ | 244 } /* if im_text.msg_type */ |
375 | 245 |
376 name = uid_to_purple_name(common->sender_uid); | 246 name = uid_to_purple_name(im_header->uid_from); |
377 b = purple_find_buddy(gc->account, name); | 247 b = purple_find_buddy(gc->account, name); |
378 if (b == NULL) { | 248 if (b == NULL) { |
379 qq_create_buddy(gc, common->sender_uid, FALSE, TRUE); | 249 qq_create_buddy(gc, im_header->uid_from, FALSE, TRUE); |
380 b = purple_find_buddy(gc->account, name); | 250 b = purple_find_buddy(gc->account, name); |
381 } | 251 } |
382 qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; | 252 qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; |
383 if (qq_b != NULL) { | 253 if (qq_b != NULL) { |
384 qq_b->client_tag = common->sender_ver; | 254 qq_b->client_tag = im_header->version_from; |
385 } | 255 } |
386 | 256 |
387 purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0; | 257 purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0; |
388 | 258 |
389 msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg); | 259 msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); |
390 msg_utf8_encoded = im_text->is_there_font_attr ? | 260 msg_utf8_encoded = im_text.is_there_font_attr ? |
391 qq_encode_to_purple(im_text->font_attr, | 261 qq_encode_to_purple(im_text.font_attr, |
392 im_text->font_attr_len, | 262 im_text.font_attr_len, |
393 msg_with_purple_smiley, qd->client_version) | 263 msg_with_purple_smiley, qd->client_version) |
394 : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); | 264 : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); |
395 | 265 |
396 /* send encoded to purple, note that we use im_text->send_time, | 266 /* send encoded to purple, note that we use im_text.send_time, |
397 * not the time we receive the message | 267 * not the time we receive the message |
398 * as it may have been delayed when I am not online. */ | 268 * as it may have been delayed when I am not online. */ |
399 serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text->send_time); | 269 serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); |
400 | 270 |
401 g_free(msg_utf8_encoded); | 271 g_free(msg_utf8_encoded); |
402 g_free(msg_with_purple_smiley); | 272 g_free(msg_with_purple_smiley); |
403 g_free(name); | 273 g_free(name); |
404 g_free(im_text->msg); | 274 g_free(im_text.msg); |
405 if (im_text->is_there_font_attr) | 275 if (im_text.font_attr) g_free(im_text.font_attr); |
406 g_free(im_text->font_attr); | 276 } |
277 | |
278 /* process received extended (2007) text IM */ | |
279 static void process_extend_im_text( | |
280 PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) | |
281 { | |
282 guint16 purple_msg_type; | |
283 gchar *name; | |
284 gchar *msg_with_purple_smiley; | |
285 gchar *msg_utf8_encoded; | |
286 qq_data *qd; | |
287 gint bytes, text_len; | |
288 | |
289 struct { | |
290 /* now comes the part for text only */ | |
291 guint16 sessionId; | |
292 guint32 send_time; | |
293 guint16 senderHead; | |
294 guint32 flag; | |
295 guint8 unknown2[8]; | |
296 guint8 fragmentCount; | |
297 guint8 fragmentIndex; | |
298 guint16 messageId; | |
299 guint8 replyType; | |
300 gchar *msg; /* no fixed length, ends with 0x00 */ | |
301 guint8 fromMobileQQ; | |
302 | |
303 guint8 is_there_font_attr; | |
304 guint8 *font_attr; | |
305 gint8 font_attr_len; | |
306 } im_text; | |
307 | |
308 g_return_if_fail (data != NULL && len > 0); | |
309 g_return_if_fail(im_header != NULL); | |
310 | |
311 qd = (qq_data *) gc->proto_data; | |
312 memset(&im_text, 0, sizeof(im_text)); | |
313 | |
314 /* push data into im_text */ | |
315 bytes = 0; | |
316 bytes += qq_get16(&(im_text.sessionId), data + bytes); | |
317 bytes += qq_get32(&(im_text.send_time), data + bytes); | |
318 bytes += qq_get16(&(im_text.senderHead), data + bytes); | |
319 bytes += qq_get32(&(im_text.flag), data + bytes); | |
320 | |
321 bytes += qq_getdata(im_text.unknown2, 8, data + bytes); | |
322 bytes += qq_get8(&(im_text.fragmentCount), data + bytes); | |
323 bytes += qq_get8(&(im_text.fragmentIndex), data + bytes); | |
324 | |
325 bytes += qq_get16(&(im_text.messageId), data + bytes); | |
326 bytes += qq_get8(&(im_text.replyType), data + bytes); | |
327 | |
328 im_text.font_attr_len = data[len-1] & 0xff; | |
329 | |
330 text_len = len - bytes - im_text.font_attr_len; | |
331 im_text.msg = g_strndup((gchar *)(data + bytes), text_len); | |
332 bytes += text_len; | |
333 if(im_text.font_attr_len >= 0) | |
334 im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); | |
335 else | |
336 { | |
337 purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n", | |
338 im_text.font_attr_len); | |
339 return; | |
340 } | |
341 | |
342 if(im_text.fragmentCount == 0) | |
343 im_text.fragmentCount = 1; | |
344 | |
345 // Filter tail space | |
346 if(im_text.fragmentIndex == im_text.fragmentCount -1) | |
347 { | |
348 gint real_len = text_len; | |
349 while(real_len > 0 && im_text.msg[real_len - 1] == 0x20) | |
350 real_len --; | |
351 | |
352 text_len = real_len; | |
353 // Null string instaed of space | |
354 im_text.msg[text_len] = 0; | |
355 } | |
356 | |
357 name = uid_to_purple_name(im_header->uid_from); | |
358 if (purple_find_buddy(gc->account, name) == NULL) | |
359 qq_create_buddy(gc, im_header->uid_from, FALSE, TRUE); | |
360 | |
361 purple_msg_type = 0; | |
362 | |
363 msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); | |
364 msg_utf8_encoded = im_text.font_attr ? | |
365 qq_encode_to_purple(im_text.font_attr, | |
366 im_text.font_attr_len, | |
367 msg_with_purple_smiley, qd->client_version) | |
368 : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); | |
369 | |
370 /* send encoded to purple, note that we use im_text.send_time, | |
371 * not the time we receive the message | |
372 * as it may have been delayed when I am not online. */ | |
373 serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); | |
374 | |
375 g_free(msg_utf8_encoded); | |
376 g_free(msg_with_purple_smiley); | |
377 g_free(name); | |
378 g_free(im_text.msg); | |
379 if (im_text.font_attr) g_free(im_text.font_attr); | |
407 } | 380 } |
408 | 381 |
409 /* it is a normal IM, maybe text or video request */ | 382 /* it is a normal IM, maybe text or video request */ |
410 static void process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc) | 383 void qq_process_im(PurpleConnection *gc, guint8 *data, gint len) |
411 { | 384 { |
412 gint bytes = 0; | 385 gint bytes = 0; |
413 qq_recv_normal_im_common *common; | 386 qq_im_header im_header; |
414 qq_recv_normal_im_unprocessed *im_unprocessed; | 387 |
415 | 388 g_return_if_fail (data != NULL && len > 0); |
416 g_return_if_fail (data != NULL && len != 0); | 389 |
417 | 390 bytes = get_im_header(&im_header, data, len); |
418 common = g_newa (qq_recv_normal_im_common, 1); | |
419 | |
420 bytes = normal_im_common_read(data, len, common); | |
421 if (bytes < 0) { | 391 if (bytes < 0) { |
422 purple_debug_error("QQ", "Fail read the common part of normal IM\n"); | 392 purple_debug_error("QQ", "Fail read im header, len %d\n", len); |
393 qq_show_packet ("IM Header", data, len); | |
423 return; | 394 return; |
424 } | 395 } |
425 | 396 purple_debug_info("QQ", |
426 switch (common->normal_im_type) { | 397 "Got IM to %d, type: %02X from: %d ver: %s (%04X)\n", |
398 im_header.uid_to, im_header.im_type, im_header.uid_from, | |
399 qq_get_ver_desc(im_header.version_from), im_header.version_from); | |
400 | |
401 switch (im_header.im_type) { | |
427 case QQ_NORMAL_IM_TEXT: | 402 case QQ_NORMAL_IM_TEXT: |
428 purple_debug_info("QQ", | |
429 "Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n", | |
430 common->sender_uid, common->receiver_uid, | |
431 qq_get_ver_desc (common->sender_ver), common->sender_ver); | |
432 if (bytes >= len - 1) { | 403 if (bytes >= len - 1) { |
433 purple_debug_warning("QQ", "Received normal IM text is empty\n"); | 404 purple_debug_warning("QQ", "Received normal IM text is empty\n"); |
434 return; | 405 return; |
435 } | 406 } |
436 process_recv_normal_im_text(data + bytes, len - bytes, common, gc); | 407 process_im_text(gc, data + bytes, len - bytes, &im_header); |
437 break; | 408 break; |
438 case QQ_NORMAL_IM_FILE_REJECT_UDP: | 409 case QQ_NORMAL_IM_FILE_REJECT_UDP: |
439 qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc); | 410 qq_process_recv_file_reject(data + bytes, len - bytes, im_header.uid_from, gc); |
440 break; | 411 break; |
441 case QQ_NORMAL_IM_FILE_APPROVE_UDP: | 412 case QQ_NORMAL_IM_FILE_APPROVE_UDP: |
442 qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc); | 413 qq_process_recv_file_accept(data + bytes, len - bytes, im_header.uid_from, gc); |
443 break; | 414 break; |
444 case QQ_NORMAL_IM_FILE_REQUEST_UDP: | 415 case QQ_NORMAL_IM_FILE_REQUEST_UDP: |
445 qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc); | 416 qq_process_recv_file_request(data + bytes, len - bytes, im_header.uid_from, gc); |
446 break; | 417 break; |
447 case QQ_NORMAL_IM_FILE_CANCEL: | 418 case QQ_NORMAL_IM_FILE_CANCEL: |
448 qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc); | 419 qq_process_recv_file_cancel(data + bytes, len - bytes, im_header.uid_from, gc); |
449 break; | 420 break; |
450 case QQ_NORMAL_IM_FILE_NOTIFY: | 421 case QQ_NORMAL_IM_FILE_NOTIFY: |
451 qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc); | 422 qq_process_recv_file_notify(data + bytes, len - bytes, im_header.uid_from, gc); |
452 break; | 423 break; |
453 case QQ_NORMAL_IM_FILE_REQUEST_TCP: | 424 case QQ_NORMAL_IM_FILE_REQUEST_TCP: |
454 /* Check ReceivedFileIM::parseContents in eva*/ | 425 /* Check ReceivedFileIM::parseContents in eva*/ |
455 /* some client use this function for detect invisable buddy*/ | 426 /* some client use this function for detect invisable buddy*/ |
456 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n"); | |
457 qq_show_packet ("Not support", data, len); | |
458 break; | |
459 case QQ_NORMAL_IM_FILE_APPROVE_TCP: | 427 case QQ_NORMAL_IM_FILE_APPROVE_TCP: |
460 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_APPROVE_TCP\n"); | |
461 qq_show_packet ("Not support", data, len); | |
462 break; | |
463 case QQ_NORMAL_IM_FILE_REJECT_TCP: | 428 case QQ_NORMAL_IM_FILE_REJECT_TCP: |
464 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REJECT_TCP\n"); | |
465 qq_show_packet ("Not support", data, len); | |
466 break; | |
467 case QQ_NORMAL_IM_FILE_PASV: | 429 case QQ_NORMAL_IM_FILE_PASV: |
468 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_PASV\n"); | |
469 qq_show_packet ("Not support", data, len); | |
470 break; | |
471 case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: | 430 case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: |
472 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n"); | |
473 qq_show_packet ("QQ", data, len); | |
474 break; | |
475 case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: | 431 case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: |
476 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT\n"); | |
477 qq_show_packet ("QQ", data, len); | |
478 break; | |
479 case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: | 432 case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: |
480 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL\n"); | |
481 qq_show_packet ("Not support", data, len); | |
482 break; | |
483 case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: | 433 case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: |
484 purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_NOTIFY_IP\n"); | |
485 qq_show_packet ("Not support", data, len); | 434 qq_show_packet ("Not support", data, len); |
486 break; | 435 break; |
487 default: | 436 default: |
488 im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1); | |
489 im_unprocessed->common = common; | |
490 im_unprocessed->unknown = data + bytes; | |
491 im_unprocessed->length = len - bytes; | |
492 /* a simple process here, maybe more later */ | 437 /* a simple process here, maybe more later */ |
493 purple_debug_warning("QQ", | 438 qq_show_packet ("Unknow", data + bytes, len - bytes); |
494 "Normal IM, unprocessed type [0x%04x], len %d\n", | |
495 common->normal_im_type, im_unprocessed->length); | |
496 qq_show_packet ("QQ", im_unprocessed->unknown, im_unprocessed->length); | |
497 return; | 439 return; |
498 } | 440 } |
499 } | 441 } |
500 | 442 |
501 /* process im from system administrator */ | 443 /* it is a extended IM, maybe text or video request */ |
502 static void process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc) | 444 void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len) |
503 { | 445 { |
504 gint len; | 446 gint bytes; |
505 guint8 reply; | 447 qq_im_header im_header; |
506 gchar **segments, *msg_utf8; | 448 |
507 | 449 g_return_if_fail (data != NULL && len > 0); |
508 g_return_if_fail(data != NULL && data_len != 0); | 450 |
509 | 451 bytes = get_im_header(&im_header, data, len); |
510 len = data_len; | 452 if (bytes < 0) { |
511 | 453 purple_debug_error("QQ", "Fail read im header, len %d\n", len); |
512 if (NULL == (segments = split_data(data, len, "\x2f", 2))) | 454 qq_show_packet ("IM Header", data, len); |
513 return; | 455 return; |
514 | 456 } |
515 reply = strtol(segments[0], NULL, 10); | 457 purple_debug_info("QQ", |
516 if (reply == QQ_RECV_SYS_IM_KICK_OUT) | 458 "Got Extend IM to %d, type: %02X from: %d ver: %s (%04X)\n", |
517 purple_debug_warning("QQ", "We are kicked out by QQ server\n"); | 459 im_header.uid_to, im_header.im_type, im_header.uid_from, |
518 msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); | 460 qq_get_ver_desc(im_header.version_from), im_header.version_from); |
519 purple_notify_warning(gc, NULL, _("System Message"), msg_utf8); | 461 |
520 } | 462 switch (im_header.im_type) { |
521 | 463 case QQ_NORMAL_IM_TEXT: |
522 /* send an IM to to_uid */ | 464 process_extend_im_text(gc, data + bytes, len - bytes, &im_header); |
523 void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type) | 465 break; |
466 case QQ_NORMAL_IM_FILE_REJECT_UDP: | |
467 qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc); | |
468 break; | |
469 case QQ_NORMAL_IM_FILE_APPROVE_UDP: | |
470 qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc); | |
471 break; | |
472 case QQ_NORMAL_IM_FILE_REQUEST_UDP: | |
473 qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc); | |
474 break; | |
475 case QQ_NORMAL_IM_FILE_CANCEL: | |
476 qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc); | |
477 break; | |
478 case QQ_NORMAL_IM_FILE_NOTIFY: | |
479 qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc); | |
480 break; | |
481 default: | |
482 /* a simple process here, maybe more later */ | |
483 qq_show_packet ("Unknow", data + bytes, len - bytes); | |
484 break; | |
485 } | |
486 } | |
487 | |
488 /* send an IM to uid_to */ | |
489 void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type) | |
524 { | 490 { |
525 qq_data *qd; | 491 qq_data *qd; |
526 guint8 *raw_data, *send_im_tail; | 492 guint8 *raw_data, *send_im_tail; |
527 guint16 normal_im_type; | 493 guint16 im_type; |
528 gint msg_len, raw_len, font_name_len, tail_len, bytes; | 494 gint msg_len, raw_len, font_name_len, tail_len, bytes; |
529 time_t now; | 495 time_t now; |
530 gchar *msg_filtered; | 496 gchar *msg_filtered; |
531 GData *attribs; | 497 GData *attribs; |
532 gchar *font_size = NULL, *font_color = NULL, *font_name = NULL, *tmp; | 498 gchar *font_size = NULL, *font_color = NULL, *font_name = NULL, *tmp; |
533 gboolean is_bold = FALSE, is_italic = FALSE, is_underline = FALSE; | 499 gboolean is_bold = FALSE, is_italic = FALSE, is_underline = FALSE; |
534 const gchar *start, *end, *last; | 500 const gchar *start, *end, *last; |
535 | 501 |
536 qd = (qq_data *) gc->proto_data; | 502 qd = (qq_data *) gc->proto_data; |
537 normal_im_type = QQ_NORMAL_IM_TEXT; | 503 im_type = QQ_NORMAL_IM_TEXT; |
538 | 504 |
539 last = msg; | 505 last = msg; |
540 while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { | 506 while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { |
541 tmp = g_datalist_get_data(&attribs, "size"); | 507 tmp = g_datalist_get_data(&attribs, "size"); |
542 if (tmp) { | 508 if (tmp) { |
589 bytes = 0; | 555 bytes = 0; |
590 | 556 |
591 /* 000-003: receiver uid */ | 557 /* 000-003: receiver uid */ |
592 bytes += qq_put32(raw_data + bytes, qd->uid); | 558 bytes += qq_put32(raw_data + bytes, qd->uid); |
593 /* 004-007: sender uid */ | 559 /* 004-007: sender uid */ |
594 bytes += qq_put32(raw_data + bytes, to_uid); | 560 bytes += qq_put32(raw_data + bytes, uid_to); |
595 /* 008-009: sender client version */ | 561 /* 008-009: sender client version */ |
596 bytes += qq_put16(raw_data + bytes, qd->client_tag); | 562 bytes += qq_put16(raw_data + bytes, qd->client_tag); |
597 /* 010-013: receiver uid */ | 563 /* 010-013: receiver uid */ |
598 bytes += qq_put32(raw_data + bytes, qd->uid); | 564 bytes += qq_put32(raw_data + bytes, qd->uid); |
599 /* 014-017: sender uid */ | 565 /* 014-017: sender uid */ |
600 bytes += qq_put32(raw_data + bytes, to_uid); | 566 bytes += qq_put32(raw_data + bytes, uid_to); |
601 /* 018-033: md5 of (uid+session_key) */ | 567 /* 018-033: md5 of (uid+session_key) */ |
602 bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); | 568 bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); |
603 /* 034-035: message type */ | 569 /* 034-035: message type */ |
604 bytes += qq_put16(raw_data + bytes, normal_im_type); | 570 bytes += qq_put16(raw_data + bytes, im_type); |
605 /* 036-037: sequence number */ | 571 /* 036-037: sequence number */ |
606 bytes += qq_put16(raw_data + bytes, qd->send_seq); | 572 bytes += qq_put16(raw_data + bytes, qd->send_seq); |
607 /* 038-041: send time */ | 573 /* 038-041: send time */ |
608 bytes += qq_put32(raw_data + bytes, (guint32) now); | 574 bytes += qq_put32(raw_data + bytes, (guint32) now); |
609 /* 042-043: sender icon */ | 575 /* 042-043: sender icon */ |
638 g_free(font_size); | 604 g_free(font_size); |
639 g_free(send_im_tail); | 605 g_free(send_im_tail); |
640 g_free(msg_filtered); | 606 g_free(msg_filtered); |
641 } | 607 } |
642 | 608 |
643 /* parse the reply to send_im */ | 609 |
644 void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc) | 610 |
645 { | |
646 qq_data *qd; | |
647 | |
648 g_return_if_fail(data != NULL && data_len != 0); | |
649 | |
650 qd = gc->proto_data; | |
651 | |
652 if (data[0] != QQ_SEND_IM_REPLY_OK) { | |
653 purple_debug_warning("QQ", "Send IM fail\n"); | |
654 purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL); | |
655 } else { | |
656 purple_debug_info("QQ", "IM ACK OK\n"); | |
657 } | |
658 } | |
659 | |
660 /* I receive a message, mainly it is text msg, | |
661 * but we need to proess other types (group etc) */ | |
662 void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc) | |
663 { | |
664 qq_data *qd; | |
665 gint bytes; | |
666 qq_recv_im_header *im_header; | |
667 | |
668 g_return_if_fail(data != NULL && data_len != 0); | |
669 | |
670 qd = (qq_data *) gc->proto_data; | |
671 | |
672 if (data_len < 16) { /* we need to ack with the first 16 bytes */ | |
673 purple_debug_error("QQ", "MSG is too short\n"); | |
674 return; | |
675 } else { | |
676 /* when we receive a message, | |
677 * we send an ACK which is the first 16 bytes of incoming packet */ | |
678 qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16); | |
679 } | |
680 | |
681 /* check len first */ | |
682 if (data_len < 20) { /* length of im_header */ | |
683 purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len); | |
684 return; | |
685 } | |
686 | |
687 bytes = 0; | |
688 im_header = g_newa(qq_recv_im_header, 1); | |
689 bytes += qq_get32(&(im_header->sender_uid), data + bytes); | |
690 bytes += qq_get32(&(im_header->receiver_uid), data + bytes); | |
691 bytes += qq_get32(&(im_header->server_im_seq), data + bytes); | |
692 /* if the message is delivered via server, it is server IP/port */ | |
693 bytes += qq_getIP(&(im_header->sender_ip), data + bytes); | |
694 bytes += qq_get16(&(im_header->sender_port), data + bytes); | |
695 bytes += qq_get16(&(im_header->im_type), data + bytes); | |
696 /* im_header prepared */ | |
697 | |
698 if (im_header->receiver_uid != qd->uid) { /* should not happen */ | |
699 purple_debug_error("QQ", "MSG to [%d], NOT me\n", im_header->receiver_uid); | |
700 return; | |
701 } | |
702 | |
703 /* check bytes */ | |
704 if (bytes >= data_len - 1) { | |
705 purple_debug_warning("QQ", "Empty MSG\n"); | |
706 return; | |
707 } | |
708 | |
709 switch (im_header->im_type) { | |
710 case QQ_RECV_IM_NEWS: | |
711 process_recv_news(data + bytes, data_len - bytes, gc); | |
712 break; | |
713 case QQ_RECV_IM_FROM_BUDDY_2006: | |
714 case QQ_RECV_IM_FROM_UNKNOWN_2006: | |
715 case QQ_RECV_IM_TO_UNKNOWN: | |
716 case QQ_RECV_IM_TO_BUDDY: | |
717 purple_debug_info("QQ", "MSG from buddy [%d]\n", im_header->sender_uid); | |
718 process_recv_normal_im(data + bytes, data_len - bytes, gc); | |
719 break; | |
720 case QQ_RECV_IM_UNKNOWN_QUN_IM: | |
721 case QQ_RECV_IM_TEMP_QUN_IM: | |
722 case QQ_RECV_IM_QUN_IM: | |
723 purple_debug_info("QQ", "MSG from room [%d]\n", im_header->sender_uid); | |
724 /* sender_uid is in fact id */ | |
725 qq_process_room_msg_normal(data + bytes, data_len - bytes, im_header->sender_uid, gc, im_header->im_type); | |
726 break; | |
727 case QQ_RECV_IM_ADD_TO_QUN: | |
728 purple_debug_info("QQ", "Notice from [%d], Added\n", im_header->sender_uid); | |
729 /* sender_uid is group id | |
730 * we need this to create a dummy group and add to blist */ | |
731 qq_process_room_msg_been_added(data + bytes, data_len - bytes, im_header->sender_uid, gc); | |
732 break; | |
733 case QQ_RECV_IM_DEL_FROM_QUN: | |
734 purple_debug_info("QQ", "Notice from room [%d], Removed\n", im_header->sender_uid); | |
735 /* sender_uid is group id */ | |
736 qq_process_room_msg_been_removed(data + bytes, data_len - bytes, im_header->sender_uid, gc); | |
737 break; | |
738 case QQ_RECV_IM_APPLY_ADD_TO_QUN: | |
739 purple_debug_info("QQ", "Notice from room [%d], Joined\n", im_header->sender_uid); | |
740 /* sender_uid is group id */ | |
741 qq_process_room_msg_apply_join(data + bytes, data_len - bytes, im_header->sender_uid, gc); | |
742 break; | |
743 case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: | |
744 purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n", | |
745 im_header->sender_uid); | |
746 /* sender_uid is group id */ | |
747 qq_process_room_msg_been_approved(data + bytes, data_len - bytes, im_header->sender_uid, gc); | |
748 break; | |
749 case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: | |
750 purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n", | |
751 im_header->sender_uid); | |
752 /* sender_uid is group id */ | |
753 qq_process_room_msg_been_rejected(data + bytes, data_len - bytes, im_header->sender_uid, gc); | |
754 break; | |
755 case QQ_RECV_IM_SYS_NOTIFICATION: | |
756 purple_debug_info("QQ", "Admin notice from [%d]\n", im_header->sender_uid); | |
757 process_recv_sys_im(data + bytes, data_len - bytes, gc); | |
758 break; | |
759 default: | |
760 purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%02x]\n", | |
761 im_header->sender_uid, qq_get_recv_im_type_str(im_header->im_type), | |
762 im_header->im_type); | |
763 qq_show_packet("Unknown MSG type", data, data_len); | |
764 } | |
765 } | |
766 |