# HG changeset patch # User SHiNE CsyFeK # Date 1224686000 0 # Node ID bdfcfd71449cb40056c8bae112248debfa28119c # Parent a3cd7c3d9da13d4dec365821e26c0387cc0c30f8 patch 20080922 from ccpaging diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/Makefile.am --- a/libpurple/protocols/qq/Makefile.am Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.am Wed Oct 22 14:33:20 2008 +0000 @@ -36,8 +36,8 @@ group_opt.h \ group_search.c \ group_search.h \ - header_info.c \ - header_info.h \ + qq_define.c \ + qq_define.h \ im.c \ im.h \ qq_process.c \ diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/Makefile.mingw --- a/libpurple/protocols/qq/Makefile.mingw Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.mingw Wed Oct 22 14:33:20 2008 +0000 @@ -55,7 +55,7 @@ group_join.c \ group_opt.c \ group_search.c \ - header_info.c \ + qq_define.c \ im.c \ packet_parse.c \ qq.c \ diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/buddy_info.c --- a/libpurple/protocols/qq/buddy_info.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Wed Oct 22 14:33:20 2008 +0000 @@ -32,7 +32,7 @@ #include "buddy_list.h" #include "buddy_info.h" #include "char_conv.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_base.h" #include "qq_network.h" @@ -426,7 +426,11 @@ info_request->iclass = iclass; info_request->segments = segments; - purple_request_fields(gc, utf8_title, utf8_prim, NULL, fields, + purple_request_fields(gc, + utf8_title, + utf8_prim, + NULL, + fields, _("Update"), G_CALLBACK(info_modify_ok_cb), _("Cancel"), G_CALLBACK(info_modify_cancel_cb), purple_connection_get_account(gc), NULL, NULL, diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/buddy_list.c --- a/libpurple/protocols/qq/buddy_list.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/buddy_list.c Wed Oct 22 14:33:20 2008 +0000 @@ -34,7 +34,7 @@ #include "buddy_list.h" #include "buddy_opt.h" #include "char_conv.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_base.h" #include "group.h" #include "group_find.h" @@ -78,7 +78,6 @@ bytes += qq_put16(raw_data + bytes, 0x0000); qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5, update_class, 0); - qd->last_get_online = time(NULL); } /* position starts with 0x0000, diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/buddy_opt.c --- a/libpurple/protocols/qq/buddy_opt.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/buddy_opt.c Wed Oct 22 14:33:20 2008 +0000 @@ -32,7 +32,7 @@ #include "buddy_list.h" #include "buddy_opt.h" #include "char_conv.h" -#include "header_info.h" +#include "qq_define.h" #include "im.h" #include "qq_base.h" #include "packet_parse.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/file_trans.c --- a/libpurple/protocols/qq/file_trans.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/file_trans.c Wed Oct 22 14:33:20 2008 +0000 @@ -30,7 +30,7 @@ #include "qq_crypt.h" #include "file_trans.h" -#include "header_info.h" +#include "qq_define.h" #include "im.h" #include "packet_parse.h" #include "proxy.h" @@ -81,7 +81,7 @@ const gint QQ_MAX_FILE_MD5_LENGTH = 10002432; g_return_if_fail(filename != NULL && md5 != NULL); - if (filelen > QQ_MAX_FILE_MD5_LENGTH) + if (filelen > QQ_MAX_FILE_MD5_LENGTH) filelen = QQ_MAX_FILE_MD5_LENGTH; fp = fopen(filename, "rb"); @@ -161,7 +161,7 @@ fd = open(purple_xfer_get_local_filename(xfer), O_RDONLY); info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ, MAP_PRIVATE, fd, 0); } - else + else { fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644); info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0); @@ -248,7 +248,7 @@ file_key = _gen_file_key(); bytes += qq_put8(raw_data + bytes, packet_type); - bytes += qq_put16(raw_data + bytes, QQ_CLIENT); + bytes += qq_put16(raw_data + bytes, qd->client_version); bytes += qq_put8(raw_data + bytes, file_key & 0xff); bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key)); bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key)); @@ -364,7 +364,7 @@ } /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */ -static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, +static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, guint32 fragment_index, guint16 seq, guint8 *data, gint len) { guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH]; @@ -402,11 +402,11 @@ _fill_file_md5(purple_xfer_get_local_filename(qd->xfer), purple_xfer_get_size(qd->xfer), file_md5); - + info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1; info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN; - purple_debug_info("QQ", + purple_debug_info("QQ", "start transfering data, %d fragments with %d length each\n", info->fragment_num, info->fragment_len); /* Unknown */ @@ -431,7 +431,7 @@ filename_len); break; case QQ_FILE_DATA_INFO: - purple_debug_info("QQ", + purple_debug_info("QQ", "sending %dth fragment with length %d, offset %d\n", fragment_index, len, (fragment_index-1)*fragment_size); /* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */ @@ -532,7 +532,7 @@ decryped_bytes = 0; qq_get_conn_info(info, decrypted_data + decryped_bytes); /* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */ - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); break; case QQ_FILE_CMD_SENDER_SAY_HELLO: /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */ @@ -573,8 +573,8 @@ ft_info *info = (ft_info *) xfer->data; guint32 mask; - purple_debug_info("QQ", - "receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n", + purple_debug_info("QQ", + "receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n", index, len, info->window, info->max_fragment_index); if (info->window == 0 && info->max_fragment_index == 0) { if (_qq_xfer_open_file(purple_xfer_get_local_filename(xfer), "wb", xfer) == -1) { @@ -605,7 +605,7 @@ if (mask & 0x8000) mask = 0x0001; else mask = mask << 1; } - purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n", + purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n", index, info->window, info->max_fragment_index); } @@ -650,10 +650,10 @@ PurpleXfer *xfer = qd->xfer; ft_info *info = (ft_info *) xfer->data; - purple_debug_info("QQ", - "receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n", + purple_debug_info("QQ", + "receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n", fragment_index, info->window, info->max_fragment_index); - if (fragment_index < info->max_fragment_index || + if (fragment_index < info->max_fragment_index || fragment_index >= info->max_fragment_index + sizeof(info->window)) { purple_debug_info("QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1); return; @@ -681,7 +681,7 @@ info->window &= ~mask; buffer = g_newa(guint8, info->fragment_len); - readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window), + readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window), info->fragment_len, xfer); if (readbytes > 0) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO, @@ -692,8 +692,8 @@ else mask = mask << 1; } } - purple_debug_info("QQ", - "procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n", + purple_debug_info("QQ", + "procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n", fragment_index, info->window, info->max_fragment_index); } @@ -727,13 +727,13 @@ bytes += qq_get32(&info->fragment_num, data + bytes); bytes += qq_get32(&info->fragment_len, data + bytes); - /* FIXME: We must check the md5 here, - * if md5 doesn't match we will ignore + /* FIXME: We must check the md5 here, + * if md5 doesn't match we will ignore * the packet or send sth as error number */ info->max_fragment_index = 0; info->window = 0; - purple_debug_info("QQ", + purple_debug_info("QQ", "start receiving data, %d fragments with %d length each\n", info->fragment_num, info->fragment_len); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, @@ -743,7 +743,7 @@ bytes += qq_get32(&fragment_index, data + bytes); bytes += qq_get32(&fragment_offset, data + bytes); bytes += qq_get16(&fragment_len, data + bytes); - purple_debug_info("QQ", + purple_debug_info("QQ", "received %dth fragment with length %d, offset %d\n", fragment_index, fragment_len, fragment_offset); diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/group.c --- a/libpurple/protocols/qq/group.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/group.c Wed Oct 22 14:33:20 2008 +0000 @@ -33,7 +33,7 @@ #include "group_search.h" #include "utils.h" #include "qq_network.h" -#include "header_info.h" +#include "qq_define.h" #include "group_free.h" static void _qq_group_search_callback(PurpleConnection *gc, const gchar *input) diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/group_conv.c --- a/libpurple/protocols/qq/group_conv.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/group_conv.c Wed Oct 22 14:33:20 2008 +0000 @@ -27,7 +27,7 @@ #include "group_conv.h" #include "buddy_list.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_network.h" #include "qq_process.h" #include "utils.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/group_im.c --- a/libpurple/protocols/qq/group_im.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/group_im.c Wed Oct 22 14:33:20 2008 +0000 @@ -39,7 +39,7 @@ #include "group_opt.h" #include "group_conv.h" #include "im.h" -#include "header_info.h" +#include "qq_define.h" #include "packet_parse.h" #include "qq_network.h" #include "qq_process.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/group_info.c --- a/libpurple/protocols/qq/group_info.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/group_info.c Wed Oct 22 14:33:20 2008 +0000 @@ -32,7 +32,7 @@ #include "group_internal.h" #include "group_info.h" #include "buddy_list.h" -#include "header_info.h" +#include "qq_define.h" #include "packet_parse.h" #include "qq_network.h" #include "utils.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/group_join.c --- a/libpurple/protocols/qq/group_join.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/group_join.c Wed Oct 22 14:33:20 2008 +0000 @@ -38,7 +38,7 @@ #include "group_opt.h" #include "group_conv.h" #include "group_search.h" -#include "header_info.h" +#include "qq_define.h" #include "packet_parse.h" #include "qq_network.h" #include "qq_process.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/group_opt.c --- a/libpurple/protocols/qq/group_opt.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.c Wed Oct 22 14:33:20 2008 +0000 @@ -35,7 +35,7 @@ #include "group_info.h" #include "group_join.h" #include "group_opt.h" -#include "header_info.h" +#include "qq_define.h" #include "packet_parse.h" #include "qq_network.h" #include "qq_process.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/group_search.c --- a/libpurple/protocols/qq/group_search.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/group_search.c Wed Oct 22 14:33:20 2008 +0000 @@ -33,7 +33,7 @@ #include "group_join.h" #include "group_search.h" #include "utils.h" -#include "header_info.h" +#include "qq_define.h" #include "packet_parse.h" #include "qq_network.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/im.c --- a/libpurple/protocols/qq/im.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/im.c Wed Oct 22 14:33:20 2008 +0000 @@ -36,7 +36,7 @@ #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" @@ -502,7 +502,7 @@ { qq_data *qd; guint8 *raw_data, *send_im_tail; - guint16 client_tag, normal_im_type; + guint16 normal_im_type; gint msg_len, raw_len, font_name_len, tail_len, bytes; time_t now; gchar *msg_filtered; @@ -512,7 +512,6 @@ const gchar *start, *end, *last; qd = (qq_data *) gc->proto_data; - client_tag = QQ_CLIENT; normal_im_type = QQ_NORMAL_IM_TEXT; last = msg; @@ -572,7 +571,7 @@ /* 004-007: sender uid */ bytes += qq_put32(raw_data + bytes, to_uid); /* 008-009: sender client version */ - bytes += qq_put16(raw_data + bytes, client_tag); + bytes += qq_put16(raw_data + bytes, qd->client_version); /* 010-013: receiver uid */ bytes += qq_put32(raw_data + bytes, qd->uid); /* 014-017: sender uid */ diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq.c --- a/libpurple/protocols/qq/qq.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Wed Oct 22 14:33:20 2008 +0000 @@ -44,7 +44,7 @@ #include "group_info.h" #include "group_join.h" #include "group_opt.h" -#include "header_info.h" +#include "qq_define.h" #include "im.h" #include "qq_process.h" #include "qq_base.h" @@ -128,6 +128,7 @@ PurpleConnection *gc; qq_data *qd; PurplePresence *presence; + const gchar *version_str; g_return_if_fail(account != NULL); @@ -154,6 +155,16 @@ server_list_create(account); purple_debug_info("QQ", "Server list has %d\n", g_list_length(qd->servers)); + version_str = purple_account_get_string(account, "client_version", NULL); + qd->client_version = QQ_CLIENT_0D55; /* set default as QQ2005 */ + qd->is_above_2007 = FALSE; + if (version_str != NULL && strlen(version_str) != 0) { + if (strcmp(version_str, "qq2007") == 0) { + qd->client_version = QQ_CLIENT_111D; + qd->is_above_2007 = TRUE; + } + } + qd->is_show_notice = purple_account_get_bool(account, "show_notice", TRUE); qd->is_show_news = purple_account_get_bool(account, "show_news", TRUE); @@ -203,6 +214,11 @@ } qq_disconnect(gc); + + if (qd->ld.token) g_free(qd->ld.token); + if (qd->captcha.token) g_free(qd->captcha.token); + if (qd->captcha.data) g_free(qd->captcha.data); + server_list_remove_all(qd); g_free(qd); @@ -536,18 +552,20 @@ GString *info; qd = (qq_data *) gc->proto_data; - info = g_string_new("\n"); + info = g_string_new(""); - g_string_append_printf(info, _("Current Online: %d
\n"), qd->total_online); - g_string_append_printf(info, _("Last Refresh: %s
\n"), ctime(&qd->last_get_online)); + g_string_append_printf(info, _("This Login: %s
\n"), ctime(&qd->login_time)); + g_string_append_printf(info, _("Online Buddies: %d
\n"), qd->online_total); + g_string_append_printf(info, _("Last Refresh: %s
\n"), ctime(&qd->online_last_update)); - g_string_append(info, "
\n"); + g_string_append(info, "
"); g_string_append_printf(info, _("Server: %s
\n"), qd->curr_server); + g_string_append_printf(info, _("Client Version: %s
\n"), qq_get_ver_desc(qd->client_version)); g_string_append_printf(info, _("Connection Mode: %s
\n"), qd->use_tcp ? "TCP" : "UDP"); - g_string_append_printf(info, _("My Internet Address: %s
\n"), inet_ntoa(qd->my_ip)); + g_string_append_printf(info, _("My Internet IP: %s
\n"), inet_ntoa(qd->my_ip)); - g_string_append(info, "
\n"); + g_string_append(info, "
"); g_string_append(info, "Network Status
\n"); g_string_append_printf(info, _("Sent: %lu
\n"), qd->net_stat.sent); g_string_append_printf(info, _("Resend: %lu
\n"), qd->net_stat.resend); @@ -555,12 +573,11 @@ g_string_append_printf(info, _("Received: %lu
\n"), qd->net_stat.rcved); g_string_append_printf(info, _("Received Duplicate: %lu
\n"), qd->net_stat.rcved_dup); - g_string_append(info, "
\n"); + g_string_append(info, "
"); g_string_append(info, "Information below may not be accurate
\n"); - g_string_append_printf(info, _("Login Time: %s
\n"), ctime(&qd->login_time)); + g_string_append_printf(info, _("Last Login: %s\n"), ctime(&qd->last_login_time)); g_string_append_printf(info, _("Last Login IP: %s
\n"), qd->last_login_ip); - g_string_append_printf(info, _("Last Login Time: %s\n"), ctime(&qd->last_login_time)); g_string_append(info, ""); @@ -848,35 +865,53 @@ { PurpleAccountOption *option; PurpleKeyValuePair *kvp; - GList *list = NULL; - GList *kvlist = NULL; - GList *entry; + GList *server_list = NULL; + GList *server_kv_list = NULL; + GList *it; + GList *version_kv_list = NULL; - list = server_list_build('A'); + server_list = server_list_build('A'); - purple_prefs_add_string_list("/plugins/prpl/qq/serverlist", list); - list = purple_prefs_get_string_list("/plugins/prpl/qq/serverlist"); + purple_prefs_add_string_list("/plugins/prpl/qq/serverlist", server_list); + server_list = purple_prefs_get_string_list("/plugins/prpl/qq/serverlist"); - kvlist = NULL; + server_kv_list = NULL; kvp = g_new0(PurpleKeyValuePair, 1); kvp->key = g_strdup(_("Auto")); kvp->value = g_strdup("auto"); - kvlist = g_list_append(kvlist, kvp); + server_kv_list = g_list_append(server_kv_list, kvp); - entry = list; - while(entry) { - if (entry->data != NULL && strlen(entry->data) > 0) { + it = server_list; + while(it) { + if (it->data != NULL && strlen(it->data) > 0) { kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(entry->data); - kvp->value = g_strdup(entry->data); - kvlist = g_list_append(kvlist, kvp); + kvp->key = g_strdup(it->data); + kvp->value = g_strdup(it->data); + server_kv_list = g_list_append(server_kv_list, kvp); } - entry = entry->next; + it = it->next; } - option = purple_account_option_list_new(_("Server"), "server", kvlist); + g_list_free(server_list); + + option = purple_account_option_list_new(_("Select Server"), "server", server_kv_list); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); +#ifdef DEBUG + kvp = g_new0(PurpleKeyValuePair, 1); + kvp->key = g_strdup(_("QQ2005")); + kvp->value = g_strdup("qq2005"); + version_kv_list = g_list_append(version_kv_list, kvp); + + kvp = g_new0(PurpleKeyValuePair, 1); + kvp->key = g_strdup(_("QQ2007")); + kvp->value = g_strdup("qq2007"); + version_kv_list = g_list_append(version_kv_list, kvp); + + option = purple_account_option_list_new(_("Client Version"), "client_version", version_kv_list); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); +#endif + option = purple_account_option_bool_new(_("Connect by TCP"), "use_tcp", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq.h --- a/libpurple/protocols/qq/qq.h Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq.h Wed Oct 22 14:33:20 2008 +0000 @@ -41,6 +41,35 @@ typedef struct _qq_interval qq_interval; typedef struct _qq_net_stat qq_net_stat; typedef struct _qq_add_request qq_add_request; +typedef struct _qq_redirect_data qq_redirect_data; +typedef struct _qq_login_data qq_login_data; +typedef struct _qq_captcha_data qq_captcha_data; + +struct _qq_captcha_data { + guint8 *token; + guint16 token_len; + guint8 next_index; + guint8 *data; + guint16 data_len; +}; + +struct _qq_login_data { + guint8 init_key[QQ_KEY_LENGTH]; /* first encrypt key generated by client */ + guint8 *token; /* get from server*/ + guint8 token_len; + guint8 *token_ex; /* get from server*/ + guint16 token_ex_len; + guint8 captcha_key[QQ_KEY_LENGTH]; /* encrypt key used in captcha generated by client */ + guint8 pwd_twice_md5[QQ_KEY_LENGTH]; /* password in md5 (or md5' md5) */ +}; + +struct _qq_redirect_data { + guint16 ret; + guint8 b1; + guint32 w1; + guint32 w2; + guint32 ip; +}; struct _qq_add_request { guint32 uid; @@ -111,8 +140,13 @@ GList *servers; gchar *curr_server; /* point to servers->data, do not free*/ + guint16 client_version; + gboolean is_above_2007; + struct in_addr redirect_ip; guint16 redirect_port; + qq_redirect_data redirect_data; + guint check_watcher; guint connect_watcher; gint connect_retry; @@ -125,10 +159,10 @@ GList *transactions; /* check ack packet and resend */ guint32 uid; /* QQ number */ - guint8 *token; /* get from server*/ - int token_len; - guint8 inikey[QQ_KEY_LENGTH]; /* initial key to encrypt login packet */ - guint8 password_twice_md5[QQ_KEY_LENGTH]; /* password in md5 (or md5' md5) */ + + qq_login_data ld; + qq_captcha_data captcha; + guint8 session_key[QQ_KEY_LENGTH]; /* later use this as key in this session */ guint8 session_md5[QQ_KEY_LENGTH]; /* concatenate my uid with session_key and md5 it */ @@ -147,8 +181,8 @@ guint16 my_port; /* my port detected by server */ guint16 my_icon; /* my icon index */ guint16 my_level; /* my level */ - guint32 total_online; /* the number of online QQ users */ - time_t last_get_online; /* last time send get_friends_online packet */ + guint32 online_total; /* the number of online QQ users */ + time_t online_last_update; /* last time send get_friends_online packet */ PurpleRoomlist *roomlist; gint channel; /* the id for opened chat conversation */ diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_base.c --- a/libpurple/protocols/qq/qq_base.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_base.c Wed Oct 22 14:33:20 2008 +0000 @@ -26,13 +26,14 @@ #include "internal.h" #include "server.h" #include "cipher.h" +#include "request.h" #include "buddy_info.h" #include "buddy_list.h" #include "char_conv.h" #include "qq_crypt.h" #include "group.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_base.h" #include "packet_parse.h" #include "qq.h" @@ -101,39 +102,6 @@ */ -typedef struct _qq_login_reply_ok qq_login_reply_ok_packet; -typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet; - -struct _qq_login_reply_ok { - guint8 result; - guint8 session_key[QQ_KEY_LENGTH]; - guint32 uid; - struct in_addr client_ip; /* those detected by server */ - guint16 client_port; - struct in_addr server_ip; - guint16 server_port; - time_t login_time; - guint8 unknown1[26]; - struct in_addr unknown_server1_ip; - guint16 unknown_server1_port; - struct in_addr unknown_server2_ip; - guint16 unknown_server2_port; - guint16 unknown2; /* 0x0001 */ - guint16 unknown3; /* 0x0000 */ - guint8 unknown4[32]; - guint8 unknown5[12]; - struct in_addr last_client_ip; - time_t last_login_time; - guint8 unknown6[8]; -}; - -struct _qq_login_reply_redirect { - guint8 result; - guint32 uid; - struct in_addr new_server_ip; - guint16 new_server_port; -}; - /* generate a md5 key using uid and session_key */ static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key) { @@ -151,53 +119,75 @@ { gint bytes; qq_data *qd; - qq_login_reply_ok_packet lrop; + + struct { + guint8 result; + guint8 session_key[QQ_KEY_LENGTH]; + guint32 uid; + struct in_addr client_ip; /* those detected by server */ + guint16 client_port; + struct in_addr server_ip; + guint16 server_port; + time_t login_time; + guint8 unknown1[26]; + struct in_addr unknown_server1_ip; + guint16 unknown_server1_port; + struct in_addr unknown_server2_ip; + guint16 unknown_server2_port; + guint16 unknown2; /* 0x0001 */ + guint16 unknown3; /* 0x0000 */ + guint8 unknown4[32]; + guint8 unknown5[12]; + struct in_addr last_client_ip; + time_t last_login_time; + guint8 unknown6[8]; + } packet; qd = (qq_data *) gc->proto_data; /* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */ bytes = 0; /* 000-000: reply code */ - bytes += qq_get8(&lrop.result, data + bytes); + bytes += qq_get8(&packet.result, data + bytes); /* 001-016: session key */ - bytes += qq_getdata(lrop.session_key, sizeof(lrop.session_key), data + bytes); + bytes += qq_getdata(packet.session_key, sizeof(packet.session_key), data + bytes); purple_debug_info("QQ", "Got session_key\n"); /* 017-020: login uid */ - bytes += qq_get32(&lrop.uid, data + bytes); + bytes += qq_get32(&packet.uid, data + bytes); /* 021-024: server detected user public IP */ - bytes += qq_getIP(&lrop.client_ip, data + bytes); + bytes += qq_getIP(&packet.client_ip, data + bytes); /* 025-026: server detected user port */ - bytes += qq_get16(&lrop.client_port, data + bytes); + bytes += qq_get16(&packet.client_port, data + bytes); /* 027-030: server detected itself ip 127.0.0.1 ? */ - bytes += qq_getIP(&lrop.server_ip, data + bytes); + bytes += qq_getIP(&packet.server_ip, data + bytes); /* 031-032: server listening port */ - bytes += qq_get16(&lrop.server_port, data + bytes); + bytes += qq_get16(&packet.server_port, data + bytes); /* 033-036: login time for current session */ - bytes += qq_getime(&lrop.login_time, data + bytes); + bytes += qq_getime(&packet.login_time, data + bytes); /* 037-062: 26 bytes, unknown */ - bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes); + bytes += qq_getdata((guint8 *) &packet.unknown1, 26, data + bytes); /* 063-066: unknown server1 ip address */ - bytes += qq_getIP(&lrop.unknown_server1_ip, data + bytes); + bytes += qq_getIP(&packet.unknown_server1_ip, data + bytes); /* 067-068: unknown server1 port */ - bytes += qq_get16(&lrop.unknown_server1_port, data + bytes); + bytes += qq_get16(&packet.unknown_server1_port, data + bytes); /* 069-072: unknown server2 ip address */ - bytes += qq_getIP(&lrop.unknown_server2_ip, data + bytes); + bytes += qq_getIP(&packet.unknown_server2_ip, data + bytes); /* 073-074: unknown server2 port */ - bytes += qq_get16(&lrop.unknown_server2_port, data + bytes); + bytes += qq_get16(&packet.unknown_server2_port, data + bytes); /* 075-076: 2 bytes unknown */ - bytes += qq_get16(&lrop.unknown2, data + bytes); + bytes += qq_get16(&packet.unknown2, data + bytes); /* 077-078: 2 bytes unknown */ - bytes += qq_get16(&lrop.unknown3, data + bytes); + bytes += qq_get16(&packet.unknown3, data + bytes); /* 079-110: 32 bytes unknown */ - bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes); + bytes += qq_getdata((guint8 *) &packet.unknown4, 32, data + bytes); /* 111-122: 12 bytes unknown */ - bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes); + bytes += qq_getdata((guint8 *) &packet.unknown5, 12, data + bytes); /* 123-126: login IP of last session */ - bytes += qq_getIP(&lrop.last_client_ip, data + bytes); + bytes += qq_getIP(&packet.last_client_ip, data + bytes); /* 127-130: login time of last session */ - bytes += qq_getime(&lrop.last_login_time, data + bytes); + bytes += qq_getime(&packet.last_login_time, data + bytes); /* 131-138: 8 bytes unknown */ - bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes); + bytes += qq_getdata((guint8 *) &packet.unknown6, 8, data + bytes); if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) { /* fail parsing login info */ purple_debug_warning("QQ", @@ -205,15 +195,15 @@ QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes); } /* but we still go on as login OK */ - memcpy(qd->session_key, lrop.session_key, sizeof(qd->session_key)); + memcpy(qd->session_key, packet.session_key, sizeof(qd->session_key)); get_session_md5(qd->session_md5, qd->uid, qd->session_key); - qd->my_ip.s_addr = lrop.client_ip.s_addr; + qd->my_ip.s_addr = packet.client_ip.s_addr; - qd->my_port = lrop.client_port; - qd->login_time = lrop.login_time; - qd->last_login_time = lrop.last_login_time; - qd->last_login_ip = g_strdup( inet_ntoa(lrop.last_client_ip) ); + qd->my_port = packet.client_port; + qd->login_time = packet.login_time; + qd->last_login_time = packet.last_login_time; + qd->last_login_ip = g_strdup( inet_ntoa(packet.last_client_ip) ); return QQ_LOGIN_REPLY_OK; } @@ -223,35 +213,41 @@ { qq_data *qd; gint bytes; - qq_login_reply_redirect_packet lrrp; + struct { + guint8 result; + guint32 uid; + struct in_addr new_server_ip; + guint16 new_server_port; + } packet; + qd = (qq_data *) gc->proto_data; bytes = 0; /* 000-000: reply code */ - bytes += qq_get8(&lrrp.result, data + bytes); + bytes += qq_get8(&packet.result, data + bytes); /* 001-004: login uid */ - bytes += qq_get32(&lrrp.uid, data + bytes); + bytes += qq_get32(&packet.uid, data + bytes); /* 005-008: redirected new server IP */ - bytes += qq_getIP(&lrrp.new_server_ip, data + bytes); + bytes += qq_getIP(&packet.new_server_ip, data + bytes); /* 009-010: redirected new server port */ - bytes += qq_get16(&lrrp.new_server_port, data + bytes); + bytes += qq_get16(&packet.new_server_port, data + bytes); if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) { purple_debug_error("QQ", "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n", QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes); - return QQ_LOGIN_REPLY_ERR_MISC; + return QQ_LOGIN_REPLY_ERR; } /* redirect to new server, do not disconnect or connect here * those connect should be called at packet_process */ - qd->redirect_ip.s_addr = lrrp.new_server_ip.s_addr; - qd->redirect_port = lrrp.new_server_port; + qd->redirect_ip.s_addr = packet.new_server_ip.s_addr; + qd->redirect_port = packet.new_server_port; return QQ_LOGIN_REPLY_REDIRECT; } /* request before login */ -void qq_send_packet_token(PurpleConnection *gc) +void qq_request_token(PurpleConnection *gc) { qq_data *qd; guint8 buf[16] = {0}; @@ -267,7 +263,7 @@ } /* send login packet to QQ server */ -void qq_send_packet_login(PurpleConnection *gc) +void qq_request_login(PurpleConnection *gc) { qq_data *qd; guint8 *buf, *raw_data; @@ -278,15 +274,7 @@ g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - g_return_if_fail(qd->token != NULL && qd->token_len > 0); - -#ifdef DEBUG - memset(qd->inikey, 0x01, sizeof(qd->inikey)); -#else - for (bytes = 0; bytes < sizeof(qd->inikey); bytes++) { - qd->inikey[bytes] = (guint8) (rand() & 0xff); - } -#endif + g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH); memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH); @@ -296,7 +284,7 @@ bytes = 0; /* now generate the encrypted data * 000-015 use password_twice_md5 as key to encrypt empty string */ - encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->password_twice_md5); + encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->ld.pwd_twice_md5); g_return_if_fail(encrypted_len == 16); bytes += encrypted_len; @@ -313,26 +301,26 @@ /* 053-068, fixed value, maybe related to per machine */ bytes += qq_putdata(raw_data + bytes, login_53_68, 16); /* 069, login token length */ - bytes += qq_put8(raw_data + bytes, qd->token_len); + bytes += qq_put8(raw_data + bytes, qd->ld.token_len); /* 070-093, login token, normally 24 bytes */ - bytes += qq_putdata(raw_data + bytes, qd->token, qd->token_len); + bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); /* 100 bytes unknown */ bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100); /* all zero left */ - encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey); + encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key); buf = g_newa(guint8, MAX_PACKET_SIZE); memset(buf, 0, MAX_PACKET_SIZE); bytes = 0; - bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH); + bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH); bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); qd->send_seq++; qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE); } -guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len) +guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len) { qq_data *qd; guint8 ret; @@ -346,7 +334,7 @@ ret = buf[0]; - if (ret != QQ_TOKEN_REPLY_OK) { + if (ret != QQ_LOGIN_REPLY_OK) { purple_debug_error("QQ", "Failed to request token: %d\n", buf[0]); qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", buf, buf_len, @@ -357,7 +345,7 @@ } purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); g_free(error_msg); - return ret; + return QQ_LOGIN_REPLY_ERR; } token_len = buf_len-2; @@ -365,7 +353,7 @@ error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); g_free(error_msg); - return -1; + return QQ_LOGIN_REPLY_ERR; } if (buf[1] != token_len) { @@ -376,34 +364,39 @@ buf+2, token_len, "<<< got a token -> [default] decrypt and dump"); - qd->token = g_new0(guint8, token_len); - qd->token_len = token_len; - g_memmove(qd->token, buf + 2, qd->token_len); + if (qd->ld.token != NULL) { + g_free(qd->ld.token); + qd->ld.token = NULL; + qd->ld.token_len = 0; + } + qd->ld.token = g_new0(guint8, token_len); + qd->ld.token_len = token_len; + g_memmove(qd->ld.token, buf + 2, qd->ld.token_len); return ret; } /* send logout packets to QQ server */ -void qq_send_packet_logout(PurpleConnection *gc) +void qq_request_logout(PurpleConnection *gc) { gint i; qq_data *qd; qd = (qq_data *) gc->proto_data; for (i = 0; i < 4; i++) - qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->password_twice_md5, QQ_KEY_LENGTH); + qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_twice_md5, QQ_KEY_LENGTH); qd->is_login = FALSE; /* update login status AFTER sending logout packets */ } /* process the login reply packet */ -guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len) +guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len) { qq_data *qd; guint8 ret = data[0]; gchar *server_reply, *server_reply_utf8; gchar *error_msg; - g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR_MISC); + g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR); qd = (qq_data *) gc->proto_data; @@ -472,7 +465,7 @@ } /* send keep-alive packet to QQ server (it is a heart-beat) */ -void qq_send_packet_keep_alive(PurpleConnection *gc) +void qq_request_keep_alive(PurpleConnection *gc) { qq_data *qd; guint8 raw_data[16] = {0}; @@ -505,8 +498,8 @@ return TRUE; /* segments[0] and segment[1] are all 0x30 ("0") */ - qd->total_online = strtol(segments[2], NULL, 10); - if(0 == qd->total_online) { + qd->online_total = strtol(segments[2], NULL, 10); + if(0 == qd->online_total) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Keep alive error")); } @@ -519,3 +512,341 @@ g_strfreev(segments); return TRUE; } + +/* For QQ2007/2008 */ +void qq_request_get_server(PurpleConnection *gc) +{ + qq_data *qd; + guint8 *buf, *raw_data; + gint bytes; + guint8 *encrypted_data; + gint encrypted_len; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH); + memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH); + + encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */ + + bytes = 0; + bytes += qq_putdata(raw_data + bytes, (guint8 *)&qd->redirect_data, sizeof(qd->redirect_data)); + + encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + bytes = 0; + bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH); + bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); + + qd->send_seq++; + qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE); +} + +guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len) +{ + qq_data *qd; + guint8 *data; + gint data_len; + qq_redirect_data *redirect; + + g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); + qd = (qq_data *) gc->proto_data; + + data = g_newa(guint8, rcved_len); + /* May use password_twice_md5 in the past version like QQ2005*/ + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.init_key); + if (data_len < 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Can not decrypt get server reply")); + return QQ_LOGIN_REPLY_ERR; + } + + if (data_len < sizeof(qq_redirect_data)) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Can not decrypt get server reply")); + return QQ_LOGIN_REPLY_ERR; + } + + redirect = (qq_redirect_data *)data; + if (redirect->ret == 0) { + memset(&qd->redirect_data, 0, sizeof(qd->redirect_data)); + qd->redirect_ip.s_addr = 0; + return QQ_LOGIN_REPLY_OK; + } + + g_memmove(&qd->redirect_data, redirect, sizeof(qd->redirect_data)); + g_memmove(&qd->redirect_ip, &redirect->ip, sizeof(qd->redirect_ip)); + return QQ_LOGIN_REPLY_REDIRECT; +} + +void qq_request_token_ex(PurpleConnection *gc) +{ + qq_data *qd; + guint8 *buf, *raw_data; + gint bytes; + guint8 *encrypted_data; + gint encrypted_len; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); + + raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16); + memset(raw_data, 0, MAX_PACKET_SIZE - 16); + + encrypted_data = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */ + + bytes = 0; + bytes += qq_put8(raw_data + bytes, qd->ld.token_len); + bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); + bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */ + bytes += qq_put16(raw_data + bytes, 5); + bytes += qq_put32(raw_data + bytes, 0); + bytes += qq_put8(raw_data + bytes, 0); /* fragment index */ + bytes += qq_put16(raw_data + bytes, 0); /* captcha token */ + + encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + bytes = 0; + bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH); + bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); + + qd->send_seq++; + qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE); +} + +void qq_request_token_ex_next(PurpleConnection *gc) +{ + qq_data *qd; + guint8 *buf, *raw_data; + gint bytes; + guint8 *encrypted_data; + gint encrypted_len; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); + + raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16); + memset(raw_data, 0, MAX_PACKET_SIZE - 16); + + encrypted_data = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */ + + bytes = 0; + bytes += qq_put8(raw_data + bytes, qd->ld.token_len); + bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); + bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */ + bytes += qq_put16(raw_data + bytes, 5); + bytes += qq_put32(raw_data + bytes, 0); + bytes += qq_put8(raw_data + bytes, qd->captcha.next_index); /* fragment index */ + bytes += qq_put16(raw_data + bytes, qd->captcha.token_len); /* captcha token */ + bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_len); + + encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + bytes = 0; + bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH); + bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); + + qd->send_seq++; + qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE); + + purple_connection_update_progress(gc, _("Requesting captcha ..."), 3, QQ_CONNECT_STEPS); +} + +static void request_token_ex_code(PurpleConnection *gc, + guint8 *token, guint16 token_len, guint8 *code, guint16 code_len) +{ + qq_data *qd; + guint8 *buf, *raw_data; + gint bytes; + guint8 *encrypted_data; + gint encrypted_len; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0); + g_return_if_fail(code != NULL && code_len > 0); + + raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16); + memset(raw_data, 0, MAX_PACKET_SIZE - 16); + + encrypted_data = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */ + + bytes = 0; + bytes += qq_put8(raw_data + bytes, qd->ld.token_len); + bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len); + bytes += qq_put8(raw_data + bytes, 4); /* Subcommand */ + bytes += qq_put16(raw_data + bytes, 5); + bytes += qq_put32(raw_data + bytes, 0); + bytes += qq_put16(raw_data + bytes, code_len); + bytes += qq_putdata(raw_data + bytes, code, code_len); + bytes += qq_put16(raw_data + bytes, qd->captcha.token_len); /* captcha token */ + bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_len); + + encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->ld.init_key); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + bytes = 0; + bytes += qq_putdata(buf + bytes, qd->ld.init_key, QQ_KEY_LENGTH); + bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); + + qd->send_seq++; + qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE); + + purple_connection_update_progress(gc, _("Checking code of captcha ..."), 3, QQ_CONNECT_STEPS); +} + +typedef struct { + PurpleConnection *gc; + guint8 *token; + guint16 token_len; +} qq_captcha_request; + +static void captcha_request_destory(qq_captcha_request *captcha_req) +{ + g_return_if_fail(captcha_req != NULL); + if (captcha_req->token) g_free(captcha_req->token); + g_free(captcha_req); +} + +static void captcha_input_cancel_cb(qq_captcha_request *captcha_req, + PurpleRequestFields *fields) +{ + captcha_request_destory(captcha_req); + + purple_connection_error_reason(captcha_req->gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, + _("Failed captcha verify")); +} + +static void captcha_input_ok_cb(qq_captcha_request *captcha_req, + PurpleRequestFields *fields) +{ + gchar *code; + + g_return_if_fail(captcha_req != NULL && captcha_req->gc != NULL); + + code = utf8_to_qq( + purple_request_fields_get_string(fields, "captcha_code"), + QQ_CHARSET_DEFAULT); + + if (strlen(code) <= 0) { + captcha_input_cancel_cb(captcha_req, fields); + return; + } + + request_token_ex_code(captcha_req->gc, + captcha_req->token, captcha_req->token_len, + (guint8 *)code, strlen(code)); + + captcha_request_destory(captcha_req); +} + +void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha) +{ + PurpleAccount *account; + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + qq_captcha_request *captcha_req; + + g_return_if_fail(captcha->token != NULL && captcha->token_len > 0); + g_return_if_fail(captcha->data != NULL && captcha->data_len > 0); + + captcha_req = g_new0(qq_captcha_request, 1); + captcha_req->gc = gc; + captcha_req->token = g_new0(guint8, captcha->token_len); + g_memmove(captcha_req->token, captcha->token, captcha->token_len); + captcha_req->token_len = captcha->token_len; + + account = purple_connection_get_account(gc); + + fields = purple_request_fields_new(); + group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, group); + + field = purple_request_field_image_new("captcha_img", + _("Captcha Image"), (char *)captcha->data, captcha->data_len); + purple_request_field_group_add_field(group, field); + + field = purple_request_field_string_new("captcha_code", + _("Enter code"), "", FALSE); + purple_request_field_string_set_masked(field, FALSE); + purple_request_field_group_add_field(group, field); + + purple_request_fields(account, + _("QQ Captcha Verifing"), + _("QQ Captcha Verifing"), + _("Please fill code according to image"), + fields, + _("OK"), G_CALLBACK(captcha_input_ok_cb), + _("Cancel"), G_CALLBACK(captcha_input_cancel_cb), + purple_connection_get_account(gc), NULL, NULL, + captcha_req); +} + +guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len) +{ + qq_data *qd; + int bytes; + guint8 ret; + guint8 sub_cmd; + guint8 reply; + guint16 captcha_len; + guint8 curr_index; + + g_return_val_if_fail(buf != NULL && buf_len != 0, -1); + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *) gc->proto_data; + + ret = buf[0]; + + bytes = 0; + bytes += qq_get8(&sub_cmd, buf + bytes); + bytes += 2; + bytes += qq_get8(&reply, buf + bytes); + + bytes += qq_get16(&(qd->ld.token_ex_len), buf + bytes); + if (qd->ld.token_ex != NULL) g_free(qd->ld.token_ex); + qd->ld.token_ex = g_new0(guint8, qd->ld.token_ex_len); + bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len , buf + bytes); + + if(reply != 1) + { + purple_debug_info("QQ", "All captchaes is verified\n"); + return QQ_LOGIN_REPLY_OK; + } + + bytes += qq_get16(&captcha_len, buf + bytes); + + qd->captcha.data = g_realloc(qd->captcha.data, qd->captcha.data_len + captcha_len); + bytes += qq_getdata(qd->captcha.data + qd->captcha.data_len, captcha_len, buf + bytes); + qd->captcha.data_len += captcha_len; + + bytes += qq_get8(&curr_index, buf + bytes); + bytes += qq_get8(&qd->captcha.next_index, buf + bytes); + + bytes += qq_get16(&qd->captcha.token_len, buf + bytes); + qd->captcha.token = g_new0(guint8, qd->captcha.token_len); + bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, buf + bytes); + + if(qd->captcha.next_index > 0) + { + return QQ_LOGIN_REPLY_NEXT_TOKEN_EX; + } + + return QQ_LOGIN_REPLY_CAPTCHA_DLG; +} diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_base.h --- a/libpurple/protocols/qq/qq_base.h Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_base.h Wed Oct 22 14:33:20 2008 +0000 @@ -28,29 +28,40 @@ #include #include "connection.h" -#define QQ_TOKEN_REPLY_OK 0x00 - #define QQ_LOGIN_REPLY_OK 0x00 #define QQ_LOGIN_REPLY_REDIRECT 0x01 #define QQ_LOGIN_REPLY_ERR_PWD 0x05 #define QQ_LOGIN_REPLY_NEED_REACTIVE 0x06 #define QQ_LOGIN_REPLY_REDIRECT_EX 0x0A -#define QQ_LOGIN_REPLY_ERR_MISC 0xff /* defined by myself */ +/* defined by myself */ +#define QQ_LOGIN_REPLY_CAPTCHA_DLG 0xfc +#define QQ_LOGIN_REPLY_NEXT_TOKEN_EX 0xfd +#define QQ_LOGIN_REPLY_ERR_DECRYPT 0xfe +#define QQ_LOGIN_REPLY_ERR 0xff -#define QQ_LOGIN_MODE_NORMAL 0x0a -#define QQ_LOGIN_MODE_AWAY 0x1e -#define QQ_LOGIN_MODE_HIDDEN 0x28 +#define QQ_LOGIN_MODE_NORMAL 0x0a +#define QQ_LOGIN_MODE_AWAY 0x1e +#define QQ_LOGIN_MODE_HIDDEN 0x28 #define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */ -void qq_send_packet_token(PurpleConnection *gc); -guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len); +void qq_request_token(PurpleConnection *gc); +guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len); -void qq_send_packet_login(PurpleConnection *gc); -guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len); +void qq_request_token_ex(PurpleConnection *gc); +void qq_request_token_ex_next(PurpleConnection *gc); +guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len); +void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha); -void qq_send_packet_logout(PurpleConnection *gc); +void qq_request_login(PurpleConnection *gc); +guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len); + +void qq_request_logout(PurpleConnection *gc); -void qq_send_packet_keep_alive(PurpleConnection *gc); +void qq_request_keep_alive(PurpleConnection *gc); gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc); + +/* for QQ2007 */ +void qq_request_get_server(PurpleConnection *gc); +guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len); #endif diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_define.c --- a/libpurple/protocols/qq/qq_define.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_define.c Wed Oct 22 14:33:20 2008 +0000 @@ -1,5 +1,5 @@ /** - * @file header_info.c + * @file qq_define.c * * purple * @@ -24,7 +24,7 @@ #include "internal.h" -#include "header_info.h" +#include "qq_define.h" #define QQ_CLIENT_062E 0x062e /* GB QQ2000c build 0630 */ #define QQ_CLIENT_072E 0x072e /* EN QQ2000c build 0305 */ @@ -51,7 +51,6 @@ #define QQ_CLIENT_0F4B 0x0F4B /* QQ2006 Beta 3 */ #define QQ_CLIENT_1105 0x1105 /* QQ2007 beta4*/ -#define QQ_CLIENT_111D 0x111D /* QQ2007 */ #define QQ_CLIENT_115B 0x115B /* QQ2008 */ #define QQ_CLIENT_1203 0x1203 /* QQ2008 */ #define QQ_CLIENT_1205 0x1205 /* QQ2008 */ @@ -94,6 +93,7 @@ return "QQ2005 beta1"; case QQ_CLIENT_0D51: return "QQ2005 beta2"; + case QQ_CLIENT_0D55: case QQ_CLIENT_0D61: return "QQ2005"; case QQ_CLIENT_0E1B: diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_define.h --- a/libpurple/protocols/qq/qq_define.h Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_define.h Wed Oct 22 14:33:20 2008 +0000 @@ -1,5 +1,5 @@ /** - * @file header_info.h + * @file qq_define.h * * purple * @@ -30,10 +30,11 @@ #define QQ_UDP_HEADER_LENGTH 7 #define QQ_TCP_HEADER_LENGTH 9 -#define QQ_PACKET_TAG 0x02 /* all QQ text packets starts with it */ -#define QQ_PACKET_TAIL 0x03 /* all QQ text packets end with it */ +#define QQ_PACKET_TAG 0x02 /* all QQ text packets starts with it */ +#define QQ_PACKET_TAIL 0x03 /* all QQ text packets end with it */ -#define QQ_CLIENT 0x0d55 +#define QQ_CLIENT_0D55 0x0d55 /* QQ2005 used by openq before */ +#define QQ_CLIENT_111D 0x111D /* QQ2007 */ const gchar *qq_get_ver_desc(gint source); @@ -64,6 +65,13 @@ QQ_CMD_TOKEN = 0x0062, /* get login token */ QQ_CMD_RECV_MSG_SYS = 0x0080, /* receive a system message */ QQ_CMD_BUDDY_CHANGE_STATUS = 0x0081, /* buddy change status */ + /* for QQ2007*/ + QQ_CMD_GET_SERVER = 0x0091, /* select login server */ + QQ_CMD_TOKEN_EX = 0x00BA, /* get LOGIN token */ + QQ_CMD_CHECK_PWD = 0x00DD, /* Password verify */ + QQ_CMD_GET_CAPTCHA = 0x00AE, /* the request verification of information */ + QQ_CMD_BUDDY_ADD_NO_AUTH_EX = 0x00A7, /* add friend without auth */ + QQ_CMD_BUDDY_ADD_AUTH_EX = 0x00A8, /* add buddy with auth */ }; const gchar *qq_get_cmd_desc(gint type); diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_network.c --- a/libpurple/protocols/qq/qq_network.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_network.c Wed Oct 22 14:33:20 2008 +0000 @@ -30,7 +30,7 @@ #include "group_info.h" #include "group_free.h" #include "qq_crypt.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_base.h" #include "buddy_list.h" #include "packet_parse.h" @@ -160,7 +160,7 @@ qd->connect_watcher = 0; } - if (qd->fd >= 0 && qd->token != NULL && qd->token_len >= 0) { + if (qd->fd >= 0 && qd->ld.token != NULL && qd->ld.token_len > 0) { purple_debug_info("QQ", "Connect ok\n"); return FALSE; } @@ -228,6 +228,19 @@ return FALSE; /* timeout callback stops */ } +static void redirect_server(PurpleConnection *gc) +{ + qq_data *qd; + qd = (qq_data *) gc->proto_data; + + if (qd->check_watcher > 0) { + purple_timeout_remove(qd->check_watcher); + qd->check_watcher = 0; + } + if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); + qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); +} + /* process the incoming packet from qq_pending */ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) { @@ -242,6 +255,7 @@ guint32 room_id; gint update_class; guint32 ship32; + int ret; qq_transaction *trans; @@ -292,20 +306,13 @@ switch (cmd) { case QQ_CMD_TOKEN: - if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) { - qq_send_packet_login(gc); - } - break; + case QQ_CMD_GET_SERVER: case QQ_CMD_LOGIN: - qq_proc_login_cmd(gc, buf + bytes, bytes_not_read); - /* check is redirect or not, and do it now */ - if (qd->redirect_ip.s_addr != 0) { - if (qd->check_watcher > 0) { - purple_timeout_remove(qd->check_watcher); - qd->check_watcher = 0; + ret = qq_proc_login_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); + if (ret != QQ_LOGIN_REPLY_OK) { + if (ret == QQ_LOGIN_REPLY_REDIRECT) { + redirect_server(gc); } - if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); - qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); return FALSE; /* do nothing after this function and return now */ } break; @@ -316,10 +323,10 @@ purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n", qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); #endif - qq_proc_room_cmd(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); + qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); break; default: - qq_proc_client_cmd(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); + qq_proc_client_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); break; } @@ -645,7 +652,7 @@ qd->itv_count.keep_alive--; if (qd->itv_count.keep_alive <= 0) { qd->itv_count.keep_alive = qd->itv_config.keep_alive; - qq_send_packet_keep_alive(gc); + qq_request_keep_alive(gc); return TRUE; } @@ -663,10 +670,9 @@ return TRUE; /* if return FALSE, timeout callback stops */ } -static void do_request_token(PurpleConnection *gc) +static void set_all_keys(PurpleConnection *gc) { qq_data *qd; - gchar *conn_msg; const gchar *passwd; /* _qq_show_socket("Got login socket", source); */ @@ -682,24 +688,27 @@ qd->channel = 1; qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); +#ifdef DEBUG + memset(qd->ld.init_key, 0x01, sizeof(qd->ld.init_key)); + memset(qd->ld.captcha_key, 0x02, sizeof(qd->ld.captcha_key)); +#else + for (bytes = 0; bytes < sizeof(qd->ld.init_key); bytes++) { + qd->ld.init_key[bytes] = (guint8) (rand() & 0xff); + } + for (bytes = 0; bytes < sizeof(qd->captcha_key); bytes++) { + qd->captcha_key[bytes] = (guint8) (rand() & 0xff); + } +#endif + /* now generate md5 processed passwd */ passwd = purple_account_get_password(purple_connection_get_account(gc)); /* use twice-md5 of user password as session key since QQ 2003iii */ - qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), + qq_get_md5(qd->ld.pwd_twice_md5, sizeof(qd->ld.pwd_twice_md5), (guint8 *)passwd, strlen(passwd)); - qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), - qd->password_twice_md5, sizeof(qd->password_twice_md5)); + qq_get_md5(qd->ld.pwd_twice_md5, sizeof(qd->ld.pwd_twice_md5), + qd->ld.pwd_twice_md5, sizeof(qd->ld.pwd_twice_md5)); - g_return_if_fail(qd->network_watcher == 0); - qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc); - - /* Update the login progress status display */ - conn_msg = g_strdup_printf(_("Request token")); - purple_connection_update_progress(gc, conn_msg, 2, QQ_CONNECT_STEPS); - g_free(conn_msg); - - qq_send_packet_token(gc); } /* the callback function after socket is built @@ -744,7 +753,19 @@ conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc); } - do_request_token( gc ); + g_return_if_fail(qd->network_watcher == 0); + qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc); + + set_all_keys( gc ); + + if (qd->is_above_2007) { + purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS); + qq_request_get_server(gc); + return; + } + + purple_connection_update_progress(gc, _("Request token"), 2, QQ_CONNECT_STEPS); + qq_request_token(gc); } #ifndef purple_proxy_connect_udp @@ -888,7 +909,6 @@ { PurpleAccount *account ; qq_data *qd; - gchar *conn_msg; g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); account = purple_connection_get_account(gc); @@ -900,9 +920,7 @@ return FALSE; } - conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), server, port); - purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS); - g_free(conn_msg); + purple_connection_update_progress(gc, _("Connecting server ..."), 1, QQ_CONNECT_STEPS); purple_debug_info("QQ", "Connect to %s:%d\n", server, port); @@ -963,7 +981,7 @@ /* finish all I/O */ if (qd->fd >= 0 && qd->is_login) { - qq_send_packet_logout(gc); + qq_request_logout(gc); } /* not connected */ @@ -988,14 +1006,9 @@ qq_trans_remove_all(gc); - if (qd->token) { - purple_debug_info("QQ", "free token\n"); - g_free(qd->token); - qd->token = NULL; - qd->token_len = 0; - } - memset(qd->inikey, 0, sizeof(qd->inikey)); - memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5)); + memset(qd->ld.init_key, 0, sizeof(qd->ld.init_key)); + memset(qd->ld.captcha_key, 0, sizeof(qd->ld.captcha_key)); + memset(qd->ld.pwd_twice_md5, 0, sizeof(qd->ld.pwd_twice_md5)); memset(qd->session_key, 0, sizeof(qd->session_key)); memset(qd->session_md5, 0, sizeof(qd->session_md5)); @@ -1019,7 +1032,7 @@ } /* now comes the normal QQ packet as UDP */ bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); - bytes += qq_put16(buf + bytes, QQ_CLIENT); + bytes += qq_put16(buf + bytes, qd->client_version); bytes += qq_put16(buf + bytes, cmd); bytes += qq_put16(buf + bytes, seq); diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_process.c --- a/libpurple/protocols/qq/qq_process.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_process.c Wed Oct 22 14:33:20 2008 +0000 @@ -42,7 +42,7 @@ #include "group_join.h" #include "group_opt.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_base.h" #include "im.h" #include "qq_process.h" @@ -176,8 +176,7 @@ _qq_process_msg_sys_notice(gc, from, to, msg_utf8); break; case QQ_SERVER_NEW_CLIENT: - purple_debug_warning("QQ", - "QQ Server has newer client than %s\n", qq_get_ver_desc(QQ_CLIENT)); + purple_debug_warning("QQ", "QQ Server has newer client version\n"); break; default: purple_debug_warning("QQ", "Recv unknown sys msg code: %s\nMsg: %s\n", code, msg_utf8); @@ -407,6 +406,10 @@ void qq_update_online(PurpleConnection *gc, guint16 cmd) { + qq_data *qd; + g_return_if_fail (gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + switch (cmd) { case 0: qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ONLINE); @@ -414,13 +417,14 @@ case QQ_CMD_GET_BUDDIES_ONLINE: /* last command */ update_all_rooms_online(gc, 0, 0); + qd->online_last_update = time(NULL); break; default: break; } } -void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, +void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32) { @@ -567,55 +571,108 @@ } } -void qq_proc_login_cmd(PurpleConnection *gc, guint8 *rcved, gint rcved_len) +guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, + guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32) { qq_data *qd; - guint8 *data; - gint data_len; - guint ret_8; + guint8 *data = NULL; + gint data_len = 0; + guint ret_8 = QQ_LOGIN_REPLY_ERR; - g_return_if_fail (gc != NULL && gc->proto_data != NULL); + g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); qd = (qq_data *) gc->proto_data; + g_return_val_if_fail(rcved_len > 0, QQ_LOGIN_REPLY_ERR); data = g_newa(guint8, rcved_len); - /* May use password_twice_md5 in the past version like QQ2005*/ - data_len = qq_decrypt(data, rcved, rcved_len, qd->inikey); - if (data_len >= 0) { + + switch (cmd) { + case QQ_CMD_TOKEN: + if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) { + if (qd->is_above_2007) { + qq_request_token_ex(gc); + } else { + qq_request_login(gc); + } + return QQ_LOGIN_REPLY_OK; + } + return QQ_LOGIN_REPLY_ERR; + case QQ_CMD_GET_SERVER: + case QQ_CMD_TOKEN_EX: + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.init_key); + break; + default: + /* May use password_twice_md5 in the past version like QQ2005 */ + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.init_key); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by init_key, %d bytes\n", data_len); + } else { + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by password_twice_md5, %d bytes\n", data_len); + } + } + break; + } + + if (data_len < 0) { purple_debug_warning("QQ", - "Decrypt login reply packet with inikey, %d bytes\n", data_len); - } else { - data_len = qq_decrypt(data, rcved, rcved_len, qd->password_twice_md5); - if (data_len >= 0) { - purple_debug_warning("QQ", - "Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len); - } else { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + "Can not decrypt login cmd, [%05d], 0x%04X %s, len %d\n", + seq, cmd, qq_get_cmd_desc(cmd), rcved_len); + qq_show_packet("Can not decrypted", rcved, rcved_len); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Can not decrypt login reply")); - return; - } + return QQ_LOGIN_REPLY_ERR_DECRYPT; } - ret_8 = qq_process_login_reply(gc, data, data_len); - if (ret_8 != QQ_LOGIN_REPLY_OK) { - return; - } - - purple_debug_info("QQ", "Login repliess OK; everything is fine\n"); + switch (cmd) { + case QQ_CMD_GET_SERVER: + ret_8 = qq_process_get_server(gc, data, data_len); + if ( ret_8 == QQ_LOGIN_REPLY_OK) { + qq_request_token(gc); + } else if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { + return QQ_LOGIN_REPLY_REDIRECT; + } + break; + case QQ_CMD_TOKEN_EX: + ret_8 = qq_process_token_ex(gc, data, data_len); + if (ret_8 == QQ_LOGIN_REPLY_OK) { + //qq_request_check_password(gc); + } else if (ret_8 == QQ_LOGIN_REPLY_NEXT_TOKEN_EX) { + qq_request_token_ex_next(gc); + } else if (ret_8 == QQ_LOGIN_REPLY_CAPTCHA_DLG) { + qq_captcha_input_dialog(gc, &(qd->captcha)); + g_free(qd->captcha.token); + g_free(qd->captcha.data); + memset(&qd->captcha, 0, sizeof(qd->captcha)); + } + break; + case QQ_CMD_LOGIN: + ret_8 = qq_process_login(gc, data, data_len); + if (ret_8 != QQ_LOGIN_REPLY_OK) { + return ret_8; + } - purple_connection_set_state(gc, PURPLE_CONNECTED); - qd->is_login = TRUE; /* must be defined after sev_finish_login */ + purple_debug_info("QQ", "Login repliess OK; everything is fine\n"); + purple_connection_set_state(gc, PURPLE_CONNECTED); + qd->is_login = TRUE; /* must be defined after sev_finish_login */ - /* now initiate QQ Qun, do it first as it may take longer to finish */ - qq_group_init(gc); + /* now initiate QQ Qun, do it first as it may take longer to finish */ + qq_group_init(gc); - /* is_login, but we have packets before login */ - qq_trans_process_remained(gc); + /* is_login, but we have packets before login */ + qq_trans_process_remained(gc); - qq_update_all(gc, 0); - return; + qq_update_all(gc, 0); + break; + default: + process_cmd_unknow(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq); + return QQ_LOGIN_REPLY_ERR; + } + return QQ_LOGIN_REPLY_OK; } -void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, +void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32) { qq_data *qd; @@ -710,7 +767,7 @@ purple_debug_info("QQ", "All buddies and groups received\n"); break; default: - process_cmd_unknow(gc, _("Unknow reply CMD"), data, data_len, cmd, seq); + process_cmd_unknow(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq); is_unknow = TRUE; break; } diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_process.h --- a/libpurple/protocols/qq/qq_process.h Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_process.h Wed Oct 22 14:33:20 2008 +0000 @@ -38,10 +38,11 @@ QQ_CMD_CLASS_UPDATE_ROOM, }; -void qq_proc_login_cmd(PurpleConnection *gc, guint8 *rcved, gint rcved_len); -void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, +guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32); -void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, +void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, + guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32); +void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32); diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/qq_trans.c --- a/libpurple/protocols/qq/qq_trans.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/qq_trans.c Wed Oct 22 14:33:20 2008 +0000 @@ -30,7 +30,7 @@ #include "prefs.h" #include "request.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_network.h" #include "qq_process.h" #include "qq_trans.h" diff -r a3cd7c3d9da1 -r bdfcfd71449c libpurple/protocols/qq/send_file.c --- a/libpurple/protocols/qq/send_file.c Wed Oct 22 14:28:17 2008 +0000 +++ b/libpurple/protocols/qq/send_file.c Wed Oct 22 14:33:20 2008 +0000 @@ -31,7 +31,7 @@ #include "buddy_list.h" #include "file_trans.h" -#include "header_info.h" +#include "qq_define.h" #include "im.h" #include "qq_base.h" #include "packet_parse.h" @@ -54,8 +54,8 @@ static int _qq_in_same_lan(ft_info *info) { if (info->remote_internet_ip == info->local_internet_ip) return 1; - purple_debug_info("QQ", - "Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n", + purple_debug_info("QQ", + "Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n", info->remote_internet_ip , info->local_internet_ip); return 0; @@ -87,7 +87,7 @@ info = (ft_info *) xfer->data; sinlen = sizeof(sin); r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen); - purple_debug_info("QQ", + purple_debug_info("QQ", "==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n", r, inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); return r; @@ -301,7 +301,7 @@ /* 004-007: sender uid */ bytes += qq_put32 (raw_data + bytes, to_uid); /* 008-009: sender client version */ - bytes += qq_put16 (raw_data + bytes, QQ_CLIENT); + bytes += qq_put16 (raw_data + bytes, qd->client_version); /* 010-013: receiver uid */ bytes += qq_put32 (raw_data + bytes, qd->uid); /* 014-017: sender uid */ @@ -380,7 +380,7 @@ static void _qq_xfer_init_socket(PurpleXfer *xfer) { - gint sockfd, listen_port = 0, i; + gint sockfd, listen_port = 0, i; socklen_t sin_len; struct sockaddr_in sin; ft_info *info; @@ -389,7 +389,7 @@ g_return_if_fail(xfer->data != NULL); info = (ft_info *) xfer->data; - /* debug + /* debug info->local_real_ip = 0x7f000001; */ info->local_real_ip = g_ntohl(inet_addr(purple_network_get_my_ip(-1))); @@ -460,7 +460,7 @@ raw_data = g_newa(guint8, packet_len); bytes = 0; - bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_REQ, qd, FALSE); bytes += qq_fill_conn_info(raw_data + bytes, info); /* 079: 0x20 */ @@ -682,7 +682,7 @@ } /* process reject im for file transfer request */ -void qq_process_recv_file_reject (guint8 *data, gint data_len, +void qq_process_recv_file_reject (guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { gchar *msg, *filename; @@ -711,7 +711,7 @@ } /* process cancel im for file transfer request */ -void qq_process_recv_file_cancel (guint8 *data, gint data_len, +void qq_process_recv_file_cancel (guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { gchar *msg, *filename; @@ -785,7 +785,7 @@ info->local_internet_port = qd->my_port; info->local_real_ip = 0x00000000; info->to_uid = sender_uid; - + if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) { purple_debug_warning("QQ", "Received file request message is empty\n"); return; @@ -822,18 +822,18 @@ q_bud->status = QQ_BUDDY_ONLINE_INVISIBLE; qq_update_buddy_contact(gc, q_bud); } - else + else purple_debug_info("QQ", "buddy %d is already online\n", sender_uid); } - else + else purple_debug_warning("QQ", "buddy %d is not in list\n", sender_uid); - g_free(sender_name); + g_free(sender_name); g_strfreev(fileinfo); return; } - + xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_RECEIVE, sender_name); @@ -875,7 +875,7 @@ */ } -void qq_process_recv_file_notify(guint8 *data, gint data_len, +void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { gint bytes; @@ -892,7 +892,7 @@ purple_debug_warning("QQ", "Received file notify message is empty\n"); return; } - + bytes = 0; bytes += qq_get16(&(info->send_seq), data + bytes);