comparison libpurple/protocols/qq/im.c @ 24146:ce94189f15ad

Flos Lonicerae <lonicerae(at)gmail.com> * Merge lonicerae and ccpaging into trunk
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:52:26 +0000
parents ec3f7d3e0445
children 1bdf7b602684
comparison
equal deleted inserted replaced
24145:ec3f7d3e0445 24146:ce94189f15ad
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