diff options
Diffstat (limited to 'libpurple/protocols/qq/im.c')
-rw-r--r-- | libpurple/protocols/qq/im.c | 665 |
1 files changed, 268 insertions, 397 deletions
diff --git a/libpurple/protocols/qq/im.c b/libpurple/protocols/qq/im.c index 845bcc6bed..614d009e98 100644 --- a/libpurple/protocols/qq/im.c +++ b/libpurple/protocols/qq/im.c @@ -35,15 +35,13 @@ #include "buddy_list.h" #include "buddy_opt.h" #include "char_conv.h" -#include "group_im.h" -#include "header_info.h" +#include "qq_define.h" #include "im.h" #include "packet_parse.h" #include "qq_network.h" #include "send_file.h" #include "utils.h" -#define QQ_SEND_IM_REPLY_OK 0x00 #define DEFAULT_FONT_NAME_LEN 4 enum @@ -64,52 +62,15 @@ enum QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87 }; -enum { - QQ_RECV_SYS_IM_KICK_OUT = 0x01 -}; - -typedef struct _qq_recv_im_header qq_recv_im_header; -typedef struct _qq_recv_normal_im_text qq_recv_normal_im_text; -typedef struct _qq_recv_normal_im_common qq_recv_normal_im_common; -typedef struct _qq_recv_normal_im_unprocessed qq_recv_normal_im_unprocessed; +typedef struct _qq_im_header qq_im_header; +typedef struct _qq_recv_extended_im_text qq_recv_extended_im_text; -struct _qq_recv_normal_im_common { +struct _qq_im_header { /* this is the common part of normal_text */ - guint16 sender_ver; - guint32 sender_uid; - guint32 receiver_uid; + guint16 version_from; + guint32 uid_from; + guint32 uid_to; guint8 session_md5[QQ_KEY_LENGTH]; - guint16 normal_im_type; -}; - -struct _qq_recv_normal_im_text { - qq_recv_normal_im_common *common; - /* now comes the part for text only */ - guint16 msg_seq; - guint32 send_time; - guint16 sender_icon; - guint8 unknown2[3]; - guint8 is_there_font_attr; - guint8 unknown3[4]; - guint8 msg_type; - gchar *msg; /* no fixed length, ends with 0x00 */ - guint8 *font_attr; - gint font_attr_len; -}; - -struct _qq_recv_normal_im_unprocessed { - qq_recv_normal_im_common *common; - /* now comes the part of unprocessed */ - guint8 *unknown; /* no fixed length */ - gint length; -}; - -struct _qq_recv_im_header { - guint32 sender_uid; - guint32 receiver_uid; - guint32 server_im_seq; - struct in_addr sender_ip; - guint16 sender_port; guint16 im_type; }; @@ -182,327 +143,360 @@ guint8 *qq_get_send_im_tail(const gchar *font_color, return (guint8 *) send_im_tail; } -static const gchar *qq_get_recv_im_type_str(gint type) -{ - switch (type) { - case QQ_RECV_IM_TO_BUDDY: - return "QQ_RECV_IM_TO_BUDDY"; - case QQ_RECV_IM_TO_UNKNOWN: - return "QQ_RECV_IM_TO_UNKNOWN"; - case QQ_RECV_IM_UNKNOWN_QUN_IM: - return "QQ_RECV_IM_UNKNOWN_QUN_IM"; - case QQ_RECV_IM_ADD_TO_QUN: - return "QQ_RECV_IM_ADD_TO_QUN"; - case QQ_RECV_IM_DEL_FROM_QUN: - return "QQ_RECV_IM_DEL_FROM_QUN"; - case QQ_RECV_IM_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_CREATE_QUN: - return "QQ_RECV_IM_CREATE_QUN"; - case QQ_RECV_IM_SYS_NOTIFICATION: - return "QQ_RECV_IM_SYS_NOTIFICATION"; - case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_TEMP_QUN_IM: - return "QQ_RECV_IM_TEMP_QUN_IM"; - case QQ_RECV_IM_QUN_IM: - return "QQ_RECV_IM_QUN_IM"; - case QQ_RECV_IM_NEWS: - return "QQ_RECV_IM_NEWS"; - case QQ_RECV_IM_FROM_BUDDY_2006: - return "QQ_RECV_IM_FROM_BUDDY_2006"; - case QQ_RECV_IM_FROM_UNKNOWN_2006: - return "QQ_RECV_IM_FROM_UNKNOWN_2006"; - default: - return "QQ_RECV_IM_UNKNOWN"; - } -} - /* read the common parts of the normal_im, * returns the bytes read if succeed, or -1 if there is any error */ -static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common) +static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len) { gint bytes; - g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1); + g_return_val_if_fail(data != NULL && len > 0, -1); bytes = 0; - /* now push data into common header */ - bytes += qq_get16(&(common->sender_ver), data + bytes); - bytes += qq_get32(&(common->sender_uid), data + bytes); - bytes += qq_get32(&(common->receiver_uid), data + bytes); - bytes += qq_getdata(common->session_md5, QQ_KEY_LENGTH, data + bytes); - bytes += qq_get16(&(common->normal_im_type), data + bytes); - - if (bytes != 28) { /* read common place fail */ - purple_debug_error("QQ", "Expect 28 bytes, read %d bytes\n", bytes); - return -1; - } - + bytes += qq_get16(&(im_header->version_from), data + bytes); + bytes += qq_get32(&(im_header->uid_from), data + bytes); + bytes += qq_get32(&(im_header->uid_to), data + bytes); + bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes); + bytes += qq_get16(&(im_header->im_type), data + bytes); return bytes; } -static void _qq_process_recv_news(guint8 *data, gint data_len, PurpleConnection *gc) +void qq_got_attention(PurpleConnection *gc, const gchar *msg) { - qq_data *qd = (qq_data *) gc->proto_data; - gint bytes; - guint8 *temp; - guint8 temp_len; - gchar *title, *brief, *url; - gchar *title_utf8; - gchar *content, *content_utf8; - - g_return_if_fail(data != NULL && data_len != 0); - -#if 0 - qq_show_packet("Rcv news", data, data_len); -#endif - - temp = g_newa(guint8, data_len); - bytes = 4; /* ignore unknown 4 bytes */ - - bytes += qq_get8(&temp_len, data + bytes); - g_return_if_fail(bytes + temp_len <= data_len); - bytes += qq_getdata(temp, temp_len, data+bytes); - title = g_strndup((gchar *)temp, temp_len); - - bytes += qq_get8(&temp_len, data + bytes); - g_return_if_fail(bytes + temp_len <= data_len); - bytes += qq_getdata(temp, temp_len, data+bytes); - brief = g_strndup((gchar *)temp, temp_len); - - bytes += qq_get8(&temp_len, data + bytes); - g_return_if_fail(bytes + temp_len <= data_len); - bytes += qq_getdata(temp, temp_len, data+bytes); - url = g_strndup((gchar *)temp, temp_len); - - title_utf8 = qq_to_utf8(title, QQ_CHARSET_DEFAULT); - content = g_strdup_printf(_("%s\n\n%s"), brief, url); - content_utf8 = qq_to_utf8(content, QQ_CHARSET_DEFAULT); - - if (qd->is_show_news) { - purple_notify_info(gc, _("QQ Server News"), title_utf8, content_utf8); - } else { - purple_debug_info("QQ", "QQ Server news:\n%s\n%s", title_utf8, content_utf8); - } - g_free(title); - g_free(title_utf8); - g_free(brief); - g_free(url); - g_free(content); - g_free(content_utf8); + qq_data *qd; + gchar *from; + time_t now = time(NULL); + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = gc->proto_data; + + g_return_if_fail(qd->uid > 0); + + qq_buddy_find_or_new(gc, qd->uid); + + from = uid_to_purple_name(qd->uid); + serv_got_im(gc, from, msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY, now); + g_free(from); } /* process received normal text IM */ -static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc) +static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) { guint16 purple_msg_type; - gchar *name; + gchar *who; gchar *msg_with_purple_smiley; gchar *msg_utf8_encoded; qq_data *qd; - qq_recv_normal_im_text *im_text; gint bytes = 0; PurpleBuddy *b; - qq_buddy *qq_b; + qq_buddy_data *bd; + + struct { + /* now comes the part for text only */ + guint16 msg_seq; + guint32 send_time; + guint16 sender_icon; + guint8 unknown2[3]; + guint8 is_there_font_attr; + guint8 unknown3[4]; + guint8 msg_type; + gchar *msg; /* no fixed length, ends with 0x00 */ + guint8 *font_attr; + gint font_attr_len; + } im_text; + + g_return_if_fail (data != NULL && len > 0); + g_return_if_fail(im_header != NULL); - g_return_if_fail(common != NULL); qd = (qq_data *) gc->proto_data; - - /* now it is QQ_NORMAL_IM_TEXT */ - /* - if (*cursor >= (data + len - 1)) { - purple_debug_warning("QQ", "Received normal IM text is empty\n"); - return; - } else - */ - im_text = g_newa(qq_recv_normal_im_text, 1); - - im_text->common = common; + memset(&im_text, 0, sizeof(im_text)); /* push data into im_text */ - bytes += qq_get16(&(im_text->msg_seq), data + bytes); - bytes += qq_get32(&(im_text->send_time), data + bytes); - bytes += qq_get16(&(im_text->sender_icon), data + bytes); - bytes += qq_getdata((guint8 *) & (im_text->unknown2), 3, data + bytes); - bytes += qq_get8(&(im_text->is_there_font_attr), data + bytes); + bytes += qq_get16(&(im_text.msg_seq), data + bytes); + bytes += qq_get32(&(im_text.send_time), data + bytes); + bytes += qq_get16(&(im_text.sender_icon), data + bytes); + bytes += qq_getdata((guint8 *) & (im_text.unknown2), 3, data + bytes); + bytes += qq_get8(&(im_text.is_there_font_attr), data + bytes); /** * from lumaqq for unknown3 * totalFragments = buf.get() & 255; * fragmentSequence = buf.get() & 255; * messageId = buf.getChar(); */ - bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes); - bytes += qq_get8(&(im_text->msg_type), data + bytes); + bytes += qq_getdata((guint8 *) & (im_text.unknown3), 4, data + bytes); + bytes += qq_get8(&(im_text.msg_type), data + bytes); /* we need to check if this is auto-reply * QQ2003iii build 0304, returns the msg without font_attr * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */ - if (im_text->msg_type == QQ_IM_AUTO_REPLY) { - im_text->is_there_font_attr = 0x00; /* indeed there is no this flag */ - im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); + if (im_text.msg_type == QQ_IM_AUTO_REPLY) { + im_text.is_there_font_attr = 0x00; /* indeed there is no this flag */ + im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); } else { /* it is normal mesasge */ - if (im_text->is_there_font_attr) { - im_text->msg = g_strdup((gchar *)(data + bytes)); - bytes += strlen(im_text->msg) + 1; /* length decided by strlen! will it cause a crash? */ - im_text->font_attr_len = len - bytes; - im_text->font_attr = g_memdup(data + bytes, im_text->font_attr_len); - } else /* not im_text->is_there_font_attr */ - im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); - } /* if im_text->msg_type */ - - name = uid_to_purple_name(common->sender_uid); - b = purple_find_buddy(gc->account, name); + if (im_text.is_there_font_attr) { + im_text.msg = g_strdup((gchar *)(data + bytes)); + bytes += strlen(im_text.msg) + 1; /* length decided by strlen! will it cause a crash? */ + im_text.font_attr_len = len - bytes; + im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); + } else /* not im_text.is_there_font_attr */ + im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); + } /* if im_text.msg_type */ + + who = uid_to_purple_name(im_header->uid_from); + b = purple_find_buddy(gc->account, who); + if (b == NULL) { + /* create no-auth buddy */ + b = qq_buddy_new(gc, im_header->uid_from); + } + bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data; + if (bd != NULL) { + bd->client_tag = im_header->version_from; + } + + purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0; + + msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); + msg_utf8_encoded = im_text.is_there_font_attr ? + qq_encode_to_purple(im_text.font_attr, + im_text.font_attr_len, + msg_with_purple_smiley, qd->client_version) + : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); + + /* send encoded to purple, note that we use im_text.send_time, + * not the time we receive the message + * as it may have been delayed when I am not online. */ + serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); + + g_free(msg_utf8_encoded); + g_free(msg_with_purple_smiley); + g_free(who); + g_free(im_text.msg); + if (im_text.font_attr) g_free(im_text.font_attr); +} + +/* process received extended (2007) text IM */ +static void process_extend_im_text( + PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) +{ + guint16 purple_msg_type; + gchar *who; + gchar *msg_with_purple_smiley; + gchar *msg_utf8_encoded; + qq_data *qd; + PurpleBuddy *b; + qq_buddy_data *bd; + gint bytes, text_len; + + struct { + /* now comes the part for text only */ + guint16 sessionId; + guint32 send_time; + guint16 senderHead; + guint32 flag; + guint8 unknown2[8]; + guint8 fragmentCount; + guint8 fragmentIndex; + guint16 messageId; + guint8 replyType; + gchar *msg; /* no fixed length, ends with 0x00 */ + guint8 fromMobileQQ; + + guint8 is_there_font_attr; + guint8 *font_attr; + gint8 font_attr_len; + } im_text; + + g_return_if_fail (data != NULL && len > 0); + g_return_if_fail(im_header != NULL); + + qd = (qq_data *) gc->proto_data; + memset(&im_text, 0, sizeof(im_text)); + + /* push data into im_text */ + bytes = 0; + bytes += qq_get16(&(im_text.sessionId), data + bytes); + bytes += qq_get32(&(im_text.send_time), data + bytes); + bytes += qq_get16(&(im_text.senderHead), data + bytes); + bytes += qq_get32(&(im_text.flag), data + bytes); + + bytes += qq_getdata(im_text.unknown2, 8, data + bytes); + bytes += qq_get8(&(im_text.fragmentCount), data + bytes); + bytes += qq_get8(&(im_text.fragmentIndex), data + bytes); + + bytes += qq_get16(&(im_text.messageId), data + bytes); + bytes += qq_get8(&(im_text.replyType), data + bytes); + + im_text.font_attr_len = data[len-1] & 0xff; + + text_len = len - bytes - im_text.font_attr_len; + im_text.msg = g_strndup((gchar *)(data + bytes), text_len); + bytes += text_len; + if(im_text.font_attr_len >= 0) + im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len); + else + { + purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n", + im_text.font_attr_len); + return; + } + + if(im_text.fragmentCount == 0) + im_text.fragmentCount = 1; + + /* Filter tail space */ + if(im_text.fragmentIndex == im_text.fragmentCount -1) + { + gint real_len = text_len; + while(real_len > 0 && im_text.msg[real_len - 1] == 0x20) + real_len --; + + text_len = real_len; + /* Null string instead of space */ + im_text.msg[text_len] = 0; + } + + who = uid_to_purple_name(im_header->uid_from); + b = purple_find_buddy(gc->account, who); if (b == NULL) { - qq_add_buddy_by_recv_packet(gc, common->sender_uid, FALSE, TRUE); - b = purple_find_buddy(gc->account, name); + /* create no-auth buddy */ + b = qq_buddy_new(gc, im_header->uid_from); } - qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - if (qq_b != NULL) { - qq_b->client_version = common->sender_ver; + bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data; + if (bd != NULL) { + bd->client_tag = im_header->version_from; } - purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0; + purple_msg_type = 0; - msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg); - msg_utf8_encoded = im_text->is_there_font_attr ? - qq_encode_to_purple(im_text->font_attr, - im_text->font_attr_len, - msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); + msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg); + msg_utf8_encoded = im_text.font_attr ? + qq_encode_to_purple(im_text.font_attr, + im_text.font_attr_len, + msg_with_purple_smiley, qd->client_version) + : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); - /* send encoded to purple, note that we use im_text->send_time, + /* send encoded to purple, note that we use im_text.send_time, * not the time we receive the message * as it may have been delayed when I am not online. */ - serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text->send_time); + serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time); g_free(msg_utf8_encoded); g_free(msg_with_purple_smiley); - g_free(name); - g_free(im_text->msg); - if (im_text->is_there_font_attr) - g_free(im_text->font_attr); + g_free(who); + g_free(im_text.msg); + if (im_text.font_attr) g_free(im_text.font_attr); } /* it is a normal IM, maybe text or video request */ -static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc) +void qq_process_im(PurpleConnection *gc, guint8 *data, gint len) { gint bytes = 0; - qq_recv_normal_im_common *common; - qq_recv_normal_im_unprocessed *im_unprocessed; + qq_im_header im_header; - g_return_if_fail (data != NULL && len != 0); + g_return_if_fail (data != NULL && len > 0); - common = g_newa (qq_recv_normal_im_common, 1); - - bytes = _qq_normal_im_common_read(data, len, common); + bytes = get_im_header(&im_header, data, len); if (bytes < 0) { - purple_debug_error("QQ", "Fail read the common part of normal IM\n"); + purple_debug_error("QQ", "Fail read im header, len %d\n", len); + qq_show_packet ("IM Header", data, len); return; } + purple_debug_info("QQ", + "Got IM to %d, type: %02X from: %d ver: %s (%04X)\n", + im_header.uid_to, im_header.im_type, im_header.uid_from, + qq_get_ver_desc(im_header.version_from), im_header.version_from); - switch (common->normal_im_type) { + switch (im_header.im_type) { case QQ_NORMAL_IM_TEXT: - purple_debug_info("QQ", - "Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n", - common->sender_uid, common->receiver_uid, - qq_get_ver_desc (common->sender_ver), common->sender_ver); if (bytes >= len - 1) { purple_debug_warning("QQ", "Received normal IM text is empty\n"); return; } - _qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc); + process_im_text(gc, data + bytes, len - bytes, &im_header); break; case QQ_NORMAL_IM_FILE_REJECT_UDP: - qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc); + qq_process_recv_file_reject(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_APPROVE_UDP: - qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc); + qq_process_recv_file_accept(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_REQUEST_UDP: - qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc); + qq_process_recv_file_request(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_CANCEL: - qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc); + qq_process_recv_file_cancel(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_NOTIFY: - qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc); + qq_process_recv_file_notify(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_REQUEST_TCP: /* Check ReceivedFileIM::parseContents in eva*/ /* some client use this function for detect invisable buddy*/ - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n"); - qq_show_packet ("Not support", data, len); - break; case QQ_NORMAL_IM_FILE_APPROVE_TCP: - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_APPROVE_TCP\n"); - qq_show_packet ("Not support", data, len); - break; case QQ_NORMAL_IM_FILE_REJECT_TCP: - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REJECT_TCP\n"); - qq_show_packet ("Not support", data, len); - break; case QQ_NORMAL_IM_FILE_PASV: - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_PASV\n"); - qq_show_packet ("Not support", data, len); - break; case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n"); - qq_show_packet ("QQ", data, len); - break; case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT\n"); - qq_show_packet ("QQ", data, len); - break; case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL\n"); - qq_show_packet ("Not support", data, len); - break; case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: - purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_NOTIFY_IP\n"); qq_show_packet ("Not support", data, len); break; default: - im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1); - im_unprocessed->common = common; - im_unprocessed->unknown = data + bytes; - im_unprocessed->length = len - bytes; /* a simple process here, maybe more later */ - purple_debug_warning("QQ", - "Normal IM, unprocessed type [0x%04x], len %d\n", - common->normal_im_type, im_unprocessed->length); - qq_show_packet ("QQ", im_unprocessed->unknown, im_unprocessed->length); + qq_show_packet ("Unknow", data + bytes, len - bytes); return; } } -/* process im from system administrator */ -static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc) +/* it is a extended IM, maybe text or video request */ +void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len) { - gint len; - guint8 reply; - gchar **segments, *msg_utf8; - - g_return_if_fail(data != NULL && data_len != 0); + gint bytes; + qq_im_header im_header; - len = data_len; + g_return_if_fail (data != NULL && len > 0); - if (NULL == (segments = split_data(data, len, "\x2f", 2))) + bytes = get_im_header(&im_header, data, len); + if (bytes < 0) { + purple_debug_error("QQ", "Fail read im header, len %d\n", len); + qq_show_packet ("IM Header", data, len); return; - - reply = strtol(segments[0], NULL, 10); - if (reply == QQ_RECV_SYS_IM_KICK_OUT) - purple_debug_warning("QQ", "We are kicked out by QQ server\n"); - msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); - purple_notify_warning(gc, NULL, _("System Message"), msg_utf8); + } + purple_debug_info("QQ", + "Got Extend IM to %d, type: %02X from: %d ver: %s (%04X)\n", + im_header.uid_to, im_header.im_type, im_header.uid_from, + qq_get_ver_desc(im_header.version_from), im_header.version_from); + + switch (im_header.im_type) { + case QQ_NORMAL_IM_TEXT: + process_extend_im_text(gc, data + bytes, len - bytes, &im_header); + break; + case QQ_NORMAL_IM_FILE_REJECT_UDP: + qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_APPROVE_UDP: + qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_REQUEST_UDP: + qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_CANCEL: + qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc); + break; + case QQ_NORMAL_IM_FILE_NOTIFY: + qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc); + break; + default: + /* a simple process here, maybe more later */ + qq_show_packet ("Unknow", data + bytes, len - bytes); + break; + } } -/* send an IM to to_uid */ -void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type) +/* send an IM to uid_to */ +void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type) { qq_data *qd; guint8 *raw_data, *send_im_tail; - guint16 client_tag, normal_im_type; + guint16 im_type; gint msg_len, raw_len, font_name_len, tail_len, bytes; time_t now; gchar *msg_filtered; @@ -512,8 +506,7 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty const gchar *start, *end, *last; qd = (qq_data *) gc->proto_data; - client_tag = QQ_CLIENT; - normal_im_type = QQ_NORMAL_IM_TEXT; + im_type = QQ_NORMAL_IM_TEXT; last = msg; while (purple_markup_find_tag("font", last, &start, &end, &attribs)) { @@ -570,17 +563,17 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty /* 000-003: receiver uid */ bytes += qq_put32(raw_data + bytes, qd->uid); /* 004-007: sender uid */ - bytes += qq_put32(raw_data + bytes, to_uid); + bytes += qq_put32(raw_data + bytes, uid_to); /* 008-009: sender client version */ - bytes += qq_put16(raw_data + bytes, client_tag); + bytes += qq_put16(raw_data + bytes, qd->client_tag); /* 010-013: receiver uid */ bytes += qq_put32(raw_data + bytes, qd->uid); /* 014-017: sender uid */ - bytes += qq_put32(raw_data + bytes, to_uid); + bytes += qq_put32(raw_data + bytes, uid_to); /* 018-033: md5 of (uid+session_key) */ bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); /* 034-035: message type */ - bytes += qq_put16(raw_data + bytes, normal_im_type); + bytes += qq_put16(raw_data + bytes, im_type); /* 036-037: sequence number */ bytes += qq_put16(raw_data + bytes, qd->send_seq); /* 038-041: send time */ @@ -600,10 +593,10 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len); send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold, is_italic, is_underline, tail_len); - qq_show_packet("QQ_send_im_tail debug", send_im_tail, tail_len); + /* qq_show_packet("qq_get_send_im_tail", send_im_tail, tail_len); */ bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len); - qq_show_packet("QQ_raw_data debug", raw_data, bytes); + /* qq_show_packet("QQ_CMD_SEND_IM, raw_data, bytes); */ if (bytes == raw_len) /* create packet OK */ qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); @@ -619,127 +612,5 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty g_free(msg_filtered); } -/* parse the reply to send_im */ -void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(data != NULL && data_len != 0); - - qd = gc->proto_data; - - if (data[0] != QQ_SEND_IM_REPLY_OK) { - purple_debug_warning("QQ", "Send IM fail\n"); - purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL); - } else { - purple_debug_info("QQ", "IM ACK OK\n"); - } -} - -/* I receive a message, mainly it is text msg, - * but we need to proess other types (group etc) */ -void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes; - qq_recv_im_header *im_header; - g_return_if_fail(data != NULL && data_len != 0); - - qd = (qq_data *) gc->proto_data; - - if (data_len < 16) { /* we need to ack with the first 16 bytes */ - purple_debug_error("QQ", "MSG is too short\n"); - return; - } else { - /* when we receive a message, - * we send an ACK which is the first 16 bytes of incoming packet */ - qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16); - } - - /* check len first */ - if (data_len < 20) { /* length of im_header */ - purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len); - return; - } - - bytes = 0; - im_header = g_newa(qq_recv_im_header, 1); - bytes += qq_get32(&(im_header->sender_uid), data + bytes); - bytes += qq_get32(&(im_header->receiver_uid), data + bytes); - bytes += qq_get32(&(im_header->server_im_seq), data + bytes); - /* if the message is delivered via server, it is server IP/port */ - bytes += qq_getIP(&(im_header->sender_ip), data + bytes); - bytes += qq_get16(&(im_header->sender_port), data + bytes); - bytes += qq_get16(&(im_header->im_type), data + bytes); - /* im_header prepared */ - - if (im_header->receiver_uid != qd->uid) { /* should not happen */ - purple_debug_error("QQ", "MSG to [%d], NOT me\n", im_header->receiver_uid); - return; - } - - /* check bytes */ - if (bytes >= data_len - 1) { - purple_debug_warning("QQ", "Empty MSG\n"); - return; - } - - switch (im_header->im_type) { - case QQ_RECV_IM_NEWS: - _qq_process_recv_news(data + bytes, data_len - bytes, gc); - break; - case QQ_RECV_IM_FROM_BUDDY_2006: - case QQ_RECV_IM_FROM_UNKNOWN_2006: - case QQ_RECV_IM_TO_UNKNOWN: - case QQ_RECV_IM_TO_BUDDY: - purple_debug_info("QQ", "MSG from buddy [%d]\n", im_header->sender_uid); - _qq_process_recv_normal_im(data + bytes, data_len - bytes, gc); - break; - case QQ_RECV_IM_UNKNOWN_QUN_IM: - case QQ_RECV_IM_TEMP_QUN_IM: - case QQ_RECV_IM_QUN_IM: - purple_debug_info("QQ", "MSG from room [%d]\n", im_header->sender_uid); - /* sender_uid is in fact id */ - qq_process_room_msg_normal(data + bytes, data_len - bytes, im_header->sender_uid, gc, im_header->im_type); - break; - case QQ_RECV_IM_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from [%d], Added\n", im_header->sender_uid); - /* sender_uid is group id - * we need this to create a dummy group and add to blist */ - qq_process_room_msg_been_added(data + bytes, data_len - bytes, im_header->sender_uid, gc); - break; - case QQ_RECV_IM_DEL_FROM_QUN: - purple_debug_info("QQ", "Notice from room [%d], Removed\n", im_header->sender_uid); - /* sender_uid is group id */ - qq_process_room_msg_been_removed(data + bytes, data_len - bytes, im_header->sender_uid, gc); - break; - case QQ_RECV_IM_APPLY_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from room [%d], Joined\n", im_header->sender_uid); - /* sender_uid is group id */ - qq_process_room_msg_apply_join(data + bytes, data_len - bytes, im_header->sender_uid, gc); - break; - case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n", - im_header->sender_uid); - /* sender_uid is group id */ - qq_process_room_msg_been_approved(data + bytes, data_len - bytes, im_header->sender_uid, gc); - break; - case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: - purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n", - im_header->sender_uid); - /* sender_uid is group id */ - qq_process_room_msg_been_rejected(data + bytes, data_len - bytes, im_header->sender_uid, gc); - break; - case QQ_RECV_IM_SYS_NOTIFICATION: - purple_debug_info("QQ", "Admin notice from [%d]\n", im_header->sender_uid); - _qq_process_recv_sys_im(data + bytes, data_len - bytes, gc); - break; - default: - purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%02x]\n", - im_header->sender_uid, qq_get_recv_im_type_str(im_header->im_type), - im_header->im_type); - qq_show_packet("Unknown MSG type", data, data_len); - } -} |