Mercurial > pidgin
changeset 23050:51dbe83ebbd3
patch-04-tcp-pending
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/ChangeLog Tue Jun 24 12:22:40 2008 +0000 @@ -1,3 +1,31 @@ +2008.05.14 - ccpaging <ecc_hy(at)hotmail.com> + * Move function for before login packets storing to sendqueue + * Use transaction data structure to store before login packets + * Rewrite tcp_pending and packet_process in qq_network.c + +2008.05.09 - ccpaging <ecc_hy(at)hotmail.com> + * Remove function _create_packet_head_seq in qq_network.c + * Create new function encap in qq_netowork.c + * Clean code of qq_send_packet_request_login_token and qq_send_packet_login in login_out.c + +2008.05.09 - ccpaging <ecc_hy(at)hotmail.com> + * Clean code of packet_parse.c, enable PARSER_DEBUG + * Rewrite send_queue + +2008.05.08 - ccpaging <ecc_hy(at)hotmail.com> + * Rewrite qq_network + * Add srv resolve function when qq_login + * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect + * Move orignal qq_disconnect to qq_close + * qq_data alloc in qq_open and release in qq_close + * Network connect of QQ is created in qq_connect, and release in qq_disconnect + +2008.05.05 - ccpaging <ecc_hy(at)hotmail.com> + * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect + * Move orignal qq_disconnect to qq_close + * qq_data alloc in qq_open and release in qq_close + * Network connect of QQ is created in qq_connect, and release in qq_disconnect + 2008.05.05 - ccpaging <ecc_hy(at)hotmail.com> * Add qq_hex_dump function
--- a/libpurple/protocols/qq/Makefile.am Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.am Tue Jun 24 12:22:40 2008 +0000 @@ -52,20 +52,14 @@ packet_parse.h \ qq.c \ qq.h \ - qq_proxy.c \ - qq_proxy.h \ - recv_core.c \ - recv_core.h \ - send_core.c \ - send_core.h \ + qq_network.c \ + qq_network.h \ send_file.c \ send_file.h \ sendqueue.c \ sendqueue.h \ sys_msg.c \ sys_msg.h \ - udp_proxy_s5.c \ - udp_proxy_s5.h \ utils.c \ utils.h
--- a/libpurple/protocols/qq/Makefile.mingw Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.mingw Tue Jun 24 12:22:40 2008 +0000 @@ -63,13 +63,10 @@ login_logout.c \ packet_parse.c \ qq.c \ - qq_proxy.c \ - recv_core.c \ - send_core.c \ + qq_network.c \ send_file.c \ sendqueue.c \ sys_msg.c \ - udp_proxy_s5.c \ utils.c OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/qq/buddy_info.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Tue Jun 24 12:22:40 2008 +0000 @@ -34,7 +34,7 @@ #include "crypt.h" #include "header_info.h" #include "keep_alive.h" -#include "send_core.h" +#include "qq_network.h" #define QQ_PRIMARY_INFORMATION _("Primary Information") #define QQ_ADDITIONAL_INFORMATION _("Additional Information")
--- a/libpurple/protocols/qq/buddy_list.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/buddy_list.c Tue Jun 24 12:22:40 2008 +0000 @@ -38,13 +38,12 @@ #include "crypt.h" #include "header_info.h" #include "keep_alive.h" -#include "send_core.h" #include "group.h" #include "group_find.h" #include "group_internal.h" #include "group_info.h" -#include "qq_proxy.h" +#include "qq_network.h" #define QQ_GET_ONLINE_BUDDY_02 0x02 #define QQ_GET_ONLINE_BUDDY_03 0x03 /* unknown function */ @@ -164,7 +163,7 @@ return; } - _qq_show_packet("Get buddies online reply packet", data, len); + qq_show_packet("Get buddies online reply packet", data, len); bytes = 0; bytes += qq_get8(&position, data + bytes);
--- a/libpurple/protocols/qq/buddy_opt.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/buddy_opt.c Tue Jun 24 12:22:40 2008 +0000 @@ -36,7 +36,7 @@ #include "im.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" #define PURPLE_GROUP_QQ_FORMAT "QQ (%s)"
--- a/libpurple/protocols/qq/buddy_status.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/buddy_status.c Tue Jun 24 12:22:40 2008 +0000 @@ -33,10 +33,9 @@ #include "header_info.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" #include "utils.h" -#include "qq_proxy.h" +#include "qq_network.h" #define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001 #define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */ @@ -57,7 +56,7 @@ g_string_append_printf(dump, "013-014: %04x (client_version)\n", s->client_version); /* g_string_append_printf(dump, "015-030: %s (unknown key)\n", s->unknown_key); */ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str); - _qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH); + qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH); g_string_free(dump, TRUE); }
--- a/libpurple/protocols/qq/char_conv.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.c Tue Jun 24 12:22:40 2008 +0000 @@ -39,9 +39,6 @@ #define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */ #define QQ_NULL_SMILEY "(SM)" /* return this if smiley conversion fails */ -/* a debug function */ -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = { 0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73, @@ -111,18 +108,19 @@ ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error); - if (error == NULL) + if (error == NULL) { return ret; /* conversion is OK */ - else { /* conversion error */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message); + } + + /* conversion error */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message); - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", - (guint8 *) str, (len == -1) ? strlen(str) : len, - "Dump failed text"); + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + (guint8 *) str, (len == -1) ? strlen(str) : len, + "Dump failed text"); - g_error_free(error); - return g_strdup(QQ_NULL_MSG); - } + g_error_free(error); + return g_strdup(QQ_NULL_MSG); } /* take the input as a pascal string and return a converted c-string in UTF-8 @@ -150,8 +148,8 @@ gchar *font_name, *color_code, *msg_utf8, *tmp, *ret; gint bytes = 0; - /* checked _qq_show_packet OK */ - _qq_show_packet("QQ_MESG recv for font style", data, len); + /* checked qq_show_packet OK */ + qq_show_packet("QQ_MESG recv for font style", data, len); bytes += qq_get8(&font_attr, data + bytes); bytes += qq_getdata(color, 3, data + bytes); /* red,green,blue */
--- a/libpurple/protocols/qq/file_trans.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/file_trans.c Tue Jun 24 12:22:40 2008 +0000 @@ -38,7 +38,7 @@ #include "im.h" #include "packet_parse.h" #include "proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h"
--- a/libpurple/protocols/qq/group_network.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/group_network.c Tue Jun 24 12:22:40 2008 +0000 @@ -39,7 +39,7 @@ #include "group_opt.h" #include "group_search.h" #include "header_info.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" enum {
--- a/libpurple/protocols/qq/im.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/im.c Tue Jun 24 12:22:40 2008 +0000 @@ -40,16 +40,13 @@ #include "header_info.h" #include "im.h" #include "packet_parse.h" -#include "send_core.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 -/* a debug function */ -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - enum { QQ_NORMAL_IM_TEXT = 0x000b, @@ -182,7 +179,7 @@ send_im_tail[5] = 0x00; send_im_tail[6] = 0x86; send_im_tail[7] = 0x22; /* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */ - _qq_show_packet("QQ_MESG", send_im_tail, tail_len); + qq_show_packet("QQ_MESG", send_im_tail, tail_len); return (guint8 *) send_im_tail; } @@ -306,8 +303,6 @@ } else /* not im_text->is_there_font_attr */ im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); } /* if im_text->msg_type */ - /* XXX _qq_show_packet here should not be used here */ - /* _qq_show_packet("QQ_MESG recv", data, *cursor - data); */ name = uid_to_purple_name(common->sender_uid); if (purple_find_buddy(gc->account, name) == NULL) @@ -386,11 +381,8 @@ im_unprocessed->length = len - bytes; /* a simple process here, maybe more later */ purple_debug (PURPLE_DEBUG_WARNING, "QQ", - "Normal IM, unprocessed type [0x%04x]\n", - common->normal_im_type); - purple_debug (PURPLE_DEBUG_WARNING, "QQ", - im_unprocessed->unknown, im_unprocessed->length, - "Dump unknown part."); + "Normal IM, unprocessed type [0x%04x], unknown [0x%02x], len %d\n", + common->normal_im_type, im_unprocessed->unknown, im_unprocessed->length); g_free (common->session_md5); return; } @@ -522,10 +514,10 @@ 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_send_im_tail debug", 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_raw_data debug", raw_data, bytes); if (bytes == raw_len) /* create packet OK */ qq_send_cmd(gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, bytes);
--- a/libpurple/protocols/qq/keep_alive.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/keep_alive.c Tue Jun 24 12:22:40 2008 +0000 @@ -40,7 +40,7 @@ #include "header_info.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" #define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */
--- a/libpurple/protocols/qq/login_logout.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/login_logout.c Tue Jun 24 12:22:40 2008 +0000 @@ -25,6 +25,7 @@ #include "debug.h" #include "internal.h" #include "server.h" +#include "cipher.h" #include "buddy_info.h" #include "buddy_list.h" @@ -36,8 +37,7 @@ #include "login_logout.h" #include "packet_parse.h" #include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" #define QQ_LOGIN_DATA_LENGTH 416 @@ -71,32 +71,35 @@ /* for QQ 2005? copy from lumaqq */ // Fixme: change to guint8 -static const gint8 login_23_51[29] = { - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, -122, - -52, 76, 53, 44, -45, 115, 108, 20, -10, -10, - -81, -61, -6, 51, -92, 1 +static const guint8 login_23_51[29] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35, + 0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf, + 0xc3, 0xfa, 0x33, 0xa4, 0x01 }; -static const gint8 login_53_68[16] = { - -115, -117, -6, -20, -43, 82, 23, 74, -122, -7, - -89, 117, -26, 50, -47, 109 +static const guint8 login_53_68[16] = { + 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A, + 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D }; -static const gint8 login_100_bytes[100] = { - 64, - 11, 4, 2, 0, 1, 0, 0, 0, 0, 0, - 3, 9, 0, 0, 0, 0, 0, 0, 0, 1, - -23, 3, 1, 0, 0, 0, 0, 0, 1, -13, - 3, 0, 0, 0, 0, 0, 0, 1, -19, 3, - 0, 0, 0, 0, 0, 0, 1, -20, 3, 0, - 0, 0, 0, 0, 0, 3, 5, 0, 0, 0, - 0, 0, 0, 0, 3, 7, 0, 0, 0, 0, - 0, 0, 0, 1, -18, 3, 0, 0, 0, 0, - 0, 0, 1, -17, 3, 0, 0, 0, 0, 0, - 0, 1, -21, 3, 0, 0, 0, 0, 0 +static const guint8 login_100_bytes[100] = { + 0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + /* fixed value, not affected by version, or mac address */ /* static const guint8 login_53_68[16] = { @@ -139,17 +142,24 @@ guint16 new_server_port; }; -extern gint /* defined in send_core.c */ - _create_packet_head_seq(guint8 *buf, PurpleConnection *gc, - guint16 cmd, gboolean is_auto_seq, guint16 *seq); -extern gint /* defined in send_core.c */ - _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd); +/* generate a md5 key using uid and session_key */ +static guint8 *gen_session_md5(gint uid, guint8 *session_key) +{ + guint8 *src, md5_str[QQ_KEY_LENGTH]; + PurpleCipher *cipher; + PurpleCipherContext *context; -/* It is fixed to 16 bytes 0x01 for QQ2003, - * Any value works (or a random 16 bytes string) */ -static guint8 *_gen_login_key(void) -{ - return (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01); + src = g_newa(guint8, 20); + memcpy(src, &uid, 4); + memcpy(src, session_key, QQ_KEY_LENGTH); + + cipher = purple_ciphers_find_cipher("md5"); + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, src, 20); + purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL); + purple_cipher_context_destroy(context); + + return g_memdup(md5_str, QQ_KEY_LENGTH); } /* process login reply which says OK */ @@ -211,9 +221,15 @@ QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes); } /* but we still go on as login OK */ + g_return_val_if_fail(qd->session_key == NULL, QQ_LOGIN_REPLY_MISC_ERROR); qd->session_key = lrop.session_key; - qd->session_md5 = _gen_session_md5(qd->uid, qd->session_key); + + g_return_val_if_fail(qd->session_md5 == NULL, QQ_LOGIN_REPLY_MISC_ERROR); + qd->session_md5 = gen_session_md5(qd->uid, qd->session_key); + + g_return_val_if_fail(qd->my_ip == NULL, QQ_LOGIN_REPLY_MISC_ERROR); qd->my_ip = gen_ip_str(lrop.client_ip); + qd->my_port = lrop.client_port; qd->login_time = lrop.login_time; qd->last_login_time = lrop.last_login_time; @@ -245,7 +261,6 @@ static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len) { gint bytes, ret; - gchar *new_server_str; qq_data *qd; qq_login_reply_redirect_packet lrrp; @@ -265,12 +280,21 @@ "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n", QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes); ret = QQ_LOGIN_REPLY_MISC_ERROR; - } else { /* start new connection */ - new_server_str = gen_ip_str(lrrp.new_server_ip); + } else { + // redirect to new server, do not disconnect or connect here + // those connect should be called at packet_process + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + qd->real_hostname = gen_ip_str(lrrp.new_server_ip); + qd->real_port = lrrp.new_server_port; + qd->is_redirect = TRUE; + purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Redirected to new server: %s:%d\n", new_server_str, lrrp.new_server_port); - qq_connect(gc->account, new_server_str, lrrp.new_server_port, qd->use_tcp, TRUE); - g_free(new_server_str); + "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port); + ret = QQ_LOGIN_REPLY_REDIRECT; } @@ -295,50 +319,37 @@ void qq_send_packet_request_login_token(PurpleConnection *gc) { qq_data *qd; - guint8 *buf; - guint16 seq_ret; - gint bytes, bytes_sent; - - qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); + guint8 buf[16] = {0}; + gint bytes = 0; - bytes = 0; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "=BEGIN= send_packet_request_login, bytes: %d\n", bytes); - bytes += _create_packet_head_seq(buf + bytes, gc, QQ_CMD_REQUEST_LOGIN_TOKEN, TRUE, &seq_ret); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_packet_request_login, bytes: %d\n", bytes); - if (bytes <= 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create request login token packet\n"); - return; - } - bytes += qq_put32(buf + bytes, qd->uid); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_packet_request_login, bytes: %d\n", bytes); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + bytes += qq_put8(buf + bytes, 0); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_packet_request_login, bytes: %d\n", bytes); - bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_packet_request_login, bytes: %d\n", bytes); - - /* debugging info, s3e, 20070628 */ - bytes_sent = _qq_send_packet(gc, buf, bytes, QQ_CMD_REQUEST_LOGIN_TOKEN); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "world<==me %s, %d bytes\n", - qq_get_cmd_desc(QQ_CMD_REQUEST_LOGIN_TOKEN), bytes_sent); - + + qq_send_data(gc, QQ_CMD_REQUEST_LOGIN_TOKEN, buf, bytes); } /* send login packet to QQ server */ static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token) { qq_data *qd; - guint8 *buf, *raw_data, *encrypted_data; - guint16 seq_ret; - gint encrypted_len, bytes; + 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; - buf = g_newa(guint8, MAX_PACKET_SIZE); + 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 */ - qd->inikey = _gen_login_key(); + if (qd->inikey) { + g_free(qd->inikey); + } + qd->inikey = (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01); bytes = 0; /* now generate the encrypted data @@ -357,7 +368,6 @@ bytes += qq_put8(raw_data + bytes, qd->login_mode); /* 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, token_length); /* 070-093, login token, normally 24 bytes */ @@ -368,23 +378,19 @@ qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len); + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); bytes = 0; - bytes += _create_packet_head_seq(buf, gc, QQ_CMD_LOGIN, TRUE, &seq_ret); - if (bytes <= 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create login packet\n"); - return; - } - bytes += qq_put32(buf + bytes, qd->uid); bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH); bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); - bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); - _qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN); + qq_send_data(gc, QQ_CMD_LOGIN, buf, bytes); } void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) { qq_data *qd; + gchar *error_msg; g_return_if_fail(buf != NULL && buf_len != 0); @@ -406,8 +412,14 @@ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", buf, buf_len, ">>> [default] decrypt and dump"); - try_dump_as_gbk(buf, buf_len); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error requesting login token")); + error_msg = try_dump_as_gbk(buf, buf_len); + if (error_msg) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + } else { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Error requesting login token")); + } } } @@ -430,6 +442,7 @@ gint len, ret, bytes; guint8 *data; qq_data *qd; + gchar* error_msg; g_return_if_fail(buf != NULL && buf_len != 0); @@ -465,8 +478,11 @@ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, len, ">>> [default] decrypt and dump"); - try_dump_as_gbk(data, len); - + error_msg = try_dump_as_gbk(data, len); + if (error_msg) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + } ret = QQ_LOGIN_REPLY_MISC_ERROR; } } else { /* no idea how to decrypt */
--- a/libpurple/protocols/qq/packet_parse.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/packet_parse.c Tue Jun 24 12:22:40 2008 +0000 @@ -43,178 +43,57 @@ /* read one byte from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -/* -gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b) -{ - guint8 *b_ship = NULL; -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_b] buf addr: 0x%x\n", (gpointer)buf); -#endif - if (*cursor <= buf + buflen - sizeof(*b)) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_b] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, (gpointer)(buf + buflen - sizeof(*b))); -#endif - b_ship = g_new0(guint8, sizeof(guint8)); - g_memmove(b_ship, *cursor, sizeof(guint8)); - *b = *b_ship; -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_b] data: 0x%02x->0x%02x\n", - **(guint8 **)cursor, *b); -#endif - *cursor += sizeof(*b); - // free - g_free(b_ship); - b_ship = NULL; - - return sizeof(*b); - } else { - return -1; - } -} -*/ gint qq_get8(guint8 *b, guint8 *buf) { guint8 b_dest; memcpy(&b_dest, buf, sizeof(b_dest)); *b = b_dest; - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get8] buf %lu\n", (void *)buf); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get8] buf %lu\n", (void *)buf); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b); +#endif return sizeof(b_dest); } /* read two bytes as "guint16" from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -/* -gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w) -{ - guint8 *w_ship = NULL; - guint16 w_dest; -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_w] buf addr: 0x%x\n", (gpointer)buf); -#endif - if (*cursor <= buf + buflen - sizeof(*w)) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_w] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, (gpointer)(buf + buflen - sizeof(*w))); -#endif - // type should match memory buffer - w_ship = (guint8 *)g_new0(guint16, 1); - // copy bytes into temporary buffer - g_memmove(w_ship, *cursor, sizeof(guint16)); - // type convert and assign value - w_dest = *(guint16 *)w_ship; - // ntohs - *w = g_ntohs(w_dest); -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_w] data: 0x%04x->0x%04x-g_ntohs->0x%04x\n", - **(guint16 **)cursor, w_dest, *w); -#endif - // *cursor goes on - *cursor += sizeof(*w); - - // free mem - g_free(w_ship); - w_ship = NULL; - - return sizeof(*w); - } else { - return -1; - } -} -*/ gint qq_get16(guint16 *w, guint8 *buf) { guint16 w_dest; memcpy(&w_dest, buf, sizeof(w_dest)); *w = g_ntohs(w_dest); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get16] buf %lu\n", (void *)buf); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get16] buf %lu\n", (void *)buf); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w); +#endif return sizeof(w_dest); } /* read four bytes as "guint32" from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -/* -gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw) -{ - guint8 *dw_ship = NULL; - guint32 dw_dest; -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_dw] buf addr: 0x%x\n", (gpointer)buf); -#endif - if (*cursor <= buf + buflen - sizeof(*dw)) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_dw] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, (gpointer)(buf + buflen - sizeof(*dw))); -#endif - dw_ship = (guint8 *)g_new0(guint32, 1); - g_memmove(dw_ship, *cursor, sizeof(guint32)); - dw_dest = *(guint32 *)dw_ship; - *dw = g_ntohl(dw_dest); -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_dw] data: 0x%08x->0x%08x-g_ntohl->0x%08x\n", - **(guint32 **)cursor, dw_dest, *dw); -#endif - *cursor += sizeof(*dw); - - g_free(dw_ship); - dw_ship = NULL; - - return sizeof(*dw); - } else { - return -1; - } -} -*/ gint qq_get32(guint32 *dw, guint8 *buf) { guint32 dw_dest; memcpy(&dw_dest, buf, sizeof(dw_dest)); *dw = g_ntohl(dw_dest); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get32] buf %lu\n", (void *)buf); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get32] buf %lu\n", (void *)buf); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw); +#endif return sizeof(dw_dest); } /* read datalen bytes from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -/* -gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_data] buf addr: 0x%x\n", (gpointer)buf); -#endif - if (*cursor <= buf + buflen - datalen) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[read_data] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, (gpointer)(buf + buflen - datalen)); -#endif - g_memmove(data, *cursor, datalen); - *cursor += datalen; - return datalen; - } else { - return -1; - } -} -*/ gint qq_getdata(guint8 *data, gint datalen, guint8 *buf) { memcpy(data, buf, datalen); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getdata] buf %lu\n", (void *)buf); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getdata] buf %lu\n", (void *)buf); +#endif return datalen; } @@ -222,25 +101,18 @@ /* read four bytes as "time_t" from buf, * return the number of bytes read if succeeds, otherwise return -1 * This function is a wrapper around read_packet_dw() to avoid casting. */ -/* -gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t) -{ - guint32 time; - gint ret = read_packet_dw(buf, cursor, buflen, &time); - if (ret != -1 ) { - *t = time; - } - return ret; -} -*/ gint qq_getime(time_t *t, guint8 *buf) { guint32 dw_dest; memcpy(&dw_dest, buf, sizeof(dw_dest)); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getime] buf %lu\n", (void *)buf); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getime] buf %lu\n", (void *)buf); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest); +#endif dw_dest = g_ntohl(dw_dest); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest); +#endif memcpy(t, &dw_dest, sizeof(dw_dest)); return sizeof(dw_dest); } @@ -248,87 +120,27 @@ /*------------------------------------------------PUT------------------------------------------------*/ /* pack one byte into buf * return the number of bytes packed, otherwise return -1 */ -/* -gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b) -{ - guint8 b_dest; -#ifdef PARSER_DEBUG - // show me the address! - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_b] buf addr: 0x%x\n", (gpointer)buf); -#endif - // using gpointer is more safe, s3e, 20070704 - if ((gpointer)*cursor <= (gpointer)(buf + MAX_PACKET_SIZE - sizeof(guint8))) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_b] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, - (gpointer)(buf + MAX_PACKET_SIZE - sizeof(guint8))); -#endif - b_dest = b; - g_memmove(*cursor, &b_dest, sizeof(guint8)); -#ifdef PARSER_DEBUG - // show data - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_b] data: 0x%02x->0x%02x\n", b, **(guint8 **)cursor); -#endif - *cursor += sizeof(guint8); - return sizeof(guint8); - } else { - return -1; - } -} -*/ gint qq_put8(guint8 *buf, guint8 b) { memcpy(buf, &b, sizeof(b)); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put8] buf %lu\n", (void *)buf); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put8] b 0x%02x\n", b); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put8] buf %lu\n", (void *)buf); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put8] b 0x%02x\n", b); +#endif return sizeof(b); } /* pack two bytes as "guint16" into buf * return the number of bytes packed, otherwise return -1 */ -/* -gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w) -{ - guint16 w_dest; - guint8 *w_ship = NULL; -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_w] buf addr: 0x%x\n", (gpointer)buf); -#endif - if ((gpointer)*cursor <= (gpointer)(buf + MAX_PACKET_SIZE - sizeof(guint16))) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_w] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, - (gpointer)(buf + MAX_PACKET_SIZE - sizeof(guint16))); -#endif - // obscure bugs found by ccpaging, patches from him. - // similar bugs have been fixed, s3e, 20070710 - w_dest = g_htons(w); - w_ship = (guint8 *)&w_dest; - g_memmove(*cursor, w_ship, sizeof(guint16)); -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_w] data: 0x%04x-g_htons->0x%04x->0x%04x\n", - w, w_dest, **(guint16 **)cursor); -#endif - *cursor += sizeof(guint16); - return sizeof(guint16); - } else { - return -1; - } -} -*/ gint qq_put16(guint8 *buf, guint16 w) { guint16 w_porter; w_porter = g_htons(w); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put16] buf %lu\n", (void *)buf); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put16] buf %lu\n", (void *)buf); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter); +#endif memcpy(buf, &w_porter, sizeof(w_porter)); return sizeof(w_porter); } @@ -336,42 +148,14 @@ /* pack four bytes as "guint32" into buf * return the number of bytes packed, otherwise return -1 */ -/* -gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw) -{ - guint32 dw_dest; - guint8 *dw_ship = NULL; -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", "[create_dw] buf addr: 0x%x\n", (gpointer)buf); -#endif - if ((gpointer)*cursor <= (gpointer)(buf + MAX_PACKET_SIZE - sizeof(guint32))) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_dw] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, - (gpointer)(buf + MAX_PACKET_SIZE -sizeof(guint32))); -#endif - dw_dest = g_htonl(dw); - dw_ship = (guint8 *)&dw_dest; - g_memmove(*cursor, dw_ship, sizeof(guint32)); -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_dw] data: 0x%08x-g_htonl->0x%08x->0x%08x\n", - dw, dw_dest, **(guint32 **)cursor); -#endif - *cursor += sizeof(guint32); - return sizeof(guint32); - } else { - return -1; - } -} -*/ gint qq_put32(guint8 *buf, guint32 dw) { guint32 dw_porter; dw_porter = g_htonl(dw); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put32] buf %lu\n", (void *)buf); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put32] buf %lu\n", (void *)buf); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter); +#endif memcpy(buf, &dw_porter, sizeof(dw_porter)); return sizeof(dw_porter); } @@ -379,33 +163,11 @@ /* pack datalen bytes into buf * return the number of bytes packed, otherwise return -1 */ -/* -gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen) -{ -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_data] buf addr: 0x%x\n", (gpointer)buf); -#endif - if ((gpointer)*cursor <= (gpointer)(buf + MAX_PACKET_SIZE - datalen)) { -#ifdef PARSER_DEBUG - purple_debug(PURPLE_DEBUG_INFO, "QQ_DEBUGGER", - "[create_data] *cursor addr: 0x%x, buf expected addr: 0x%x\n", - (gpointer)*cursor, - (gpointer)(buf + MAX_PACKET_SIZE - datalen)); -#endif - g_memmove(*cursor, data, datalen); - *cursor += datalen; - return datalen; - } else { - return -1; - } -} -*/ -gint qq_putdata(guint8 *buf, guint8 *data, const int datalen) +gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen) { memcpy(buf, data, datalen); +#ifdef PARSER_DEBUG purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[DBG][putdata] buf %lu\n", (void *)buf); +#endif return datalen; } - -
--- a/libpurple/protocols/qq/packet_parse.h Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/packet_parse.h Tue Jun 24 12:22:40 2008 +0000 @@ -46,7 +46,7 @@ gint qq_put8(guint8 *buf, guint8 b); gint qq_put16(guint8 *buf, guint16 w); gint qq_put32(guint8 *buf, guint32 dw); -gint qq_putdata(guint8 *buf, guint8 *data, const int datalen); +gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen); //gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b); //gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w);
--- a/libpurple/protocols/qq/qq.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Tue Jun 24 12:22:40 2008 +0000 @@ -55,16 +55,15 @@ #include "login_logout.h" #include "packet_parse.h" #include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h" #include "version.h" #define OPENQ_AUTHOR "Puzzlebird" #define OPENQ_WEBSITE "http://openq.sourceforge.net" -#define QQ_TCP_QUERY_PORT "8000" -#define QQ_UDP_PORT "8000" +#define QQ_TCP_PORT 8000 +#define QQ_UDP_PORT 8000 const gchar *udp_server_list[] = { "sz.tencent.com", @@ -90,13 +89,61 @@ }; const gint tcp_server_amount = (sizeof(tcp_server_list) / sizeof(tcp_server_list[0])); -static void _qq_login(PurpleAccount *account) +static void srv_resolved(PurpleSrvResponse *resp, int results, gpointer account) { - const gchar *qq_server, *qq_port; + PurpleConnection *gc; qq_data *qd; + gchar *hostname; + int port; + + gc = purple_account_get_connection(account); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + qd->srv_query_data = NULL; + + /* find the host to connect to */ + port = purple_account_get_int(account, "port", 0); + if (port == 0) { + if (qd->use_tcp) { + port = QQ_TCP_PORT; + } else { + port = QQ_UDP_PORT; + } + } + + if(results) { + hostname = g_strdup(resp->hostname); + if(!port) + port = resp->port; + g_free(resp); + } else { + if(!purple_account_get_bool(account, "useproxy", FALSE)) { + hostname = g_strdup(qd->server_name); + } else { + hostname = g_strdup(purple_account_get_string(account, + "proxy", qd->server_name)); + } + } + + /* + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "using with server %s and port %d\n", hostname, port); + */ + qd->real_hostname = g_strdup(hostname); + qd->real_port = port; + qq_connect(account); + + g_free(hostname); +} + +static void qq_login(PurpleAccount *account) +{ + const gchar *userserver; + qq_data *qd; + gchar *host2connect; PurpleConnection *gc; PurplePresence *presence; - gboolean use_tcp; g_return_if_fail(account != NULL); @@ -109,13 +156,7 @@ qd->gc = gc; gc->proto_data = qd; - qq_server = purple_account_get_string(account, "server", NULL); - qq_port = purple_account_get_string(account, "port", NULL); - use_tcp = purple_account_get_bool(account, "use_tcp", FALSE); presence = purple_account_get_presence(account); - - qd->use_tcp = use_tcp; - if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { qd->login_mode = QQ_LOGIN_MODE_HIDDEN; } else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) @@ -125,26 +166,54 @@ qd->login_mode = QQ_LOGIN_MODE_NORMAL; } - if (qq_server == NULL || strlen(qq_server) == 0) - qq_server = use_tcp ? - tcp_server_list[random() % tcp_server_amount] : - udp_server_list[random() % udp_server_amount]; + userserver = purple_account_get_string(account, "server", NULL); + qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE); - if (qq_port == NULL || strtol(qq_port, NULL, 10) == 0) - qq_port = use_tcp ? QQ_TCP_QUERY_PORT : QQ_UDP_PORT; + if (userserver == NULL || strlen(userserver) == 0) { + if (qd->use_tcp) { + qd->server_name = g_strdup(tcp_server_list[random() % tcp_server_amount]); + } else { + qd->server_name = g_strdup(udp_server_list[random() % udp_server_amount]); + } + } else { + qd->server_name = g_strdup(userserver); + } purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS); - if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0) - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect.")); + if(!purple_account_get_bool(account, "useproxy", FALSE)) { + host2connect = g_strdup(qd->server_name); + } else { + host2connect = g_strdup(purple_account_get_string(account, "proxy", qd->server_name)); + } + + qd->srv_query_data = purple_srv_resolve("QQ", + qd->use_tcp ? "tcp" : "udp", host2connect, srv_resolved, account); + g_free(host2connect); } -/* directly goes for qq_disconnect */ -static void _qq_close(PurpleConnection *gc) +/* clean up the given QQ connection and free all resources */ +static void qq_close(PurpleConnection *gc) { + qq_data *qd; + g_return_if_fail(gc != NULL); + qd = gc->proto_data; + qq_disconnect(gc); + + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + if (qd->srv_query_data != NULL) + purple_srv_cancel(qd->srv_query_data); + + g_free(qd->server_name); + g_free(qd); + + gc->proto_data = NULL; } /* returns the icon name for a buddy or protocol */ @@ -442,8 +511,9 @@ g_string_append(info, "<hr>\n"); + g_string_append_printf(info, _("<b>Server</b>: %s: %d<br>\n"), qd->server_name, qd->real_port); g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP"); - g_string_append_printf(info, _("<b>Server IP</b>: %s: %d<br>\n"), qd->server_ip, qd->server_port); + g_string_append_printf(info, _("<b>Real hostname</b>: %s: %d<br>\n"), qd->real_hostname, qd->real_port); g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), qd->my_ip); g_string_append(info, "<hr>\n"); @@ -594,7 +664,7 @@ } -static void _qq_keep_alive(PurpleConnection *gc) +static void qq_keep_alive(PurpleConnection *gc) { qq_group *group; qq_data *qd; @@ -651,8 +721,8 @@ _qq_buddy_menu, /* blist_node_menu */ qq_chat_info, /* chat_info */ qq_chat_info_defaults, /* chat_info_defaults */ - _qq_login, /* login */ - _qq_close, /* close */ + qq_login, /* open */ + qq_close, /* close */ _qq_send_im, /* send_im */ NULL, /* set_info */ NULL, /* send_typing */ @@ -676,7 +746,7 @@ NULL, /* chat_leave */ NULL, /* chat_whisper */ _qq_chat_send, /* chat_send */ - _qq_keep_alive, /* keepalive */ + qq_keep_alive, /* keepalive */ NULL, /* register_user */ _qq_get_chat_buddy_info, /* get_cb_info */ NULL, /* get_cb_away */ @@ -750,13 +820,13 @@ { PurpleAccountOption *option; - option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_string_new(_("Server"), "server", NULL); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_string_new(_("Port"), "port", NULL); + option = purple_account_option_int_new(_("Port"), "port", 0); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + + option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); my_protocol = plugin;
--- a/libpurple/protocols/qq/qq.h Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/qq.h Tue Jun 24 12:22:40 2008 +0000 @@ -28,6 +28,9 @@ #include <glib.h> #include "internal.h" #include "ft.h" +#include "circbuffer.h" +#include "dnsquery.h" +#include "dnssrv.h" #include "proxy.h" #include "roomlist.h" @@ -66,7 +69,34 @@ }; struct _qq_data { - gint fd; /* socket file handler */ + PurpleConnection *gc; + gchar *server_name; + + // common network resource + PurpleSrvQueryData *srv_query_data; // srv resolve + gboolean is_redirect; + gchar *real_hostname; // from real connction + guint16 real_port; + gboolean use_tcp; // network in tcp or udp + + PurpleProxyConnectData *connect_data; + gint fd; // socket file handler + gint tx_handler; // socket can_write handle, use in udp connecting and tcp send out + + GList *transactions; // check ack packet and resend + guint resend_timeout; + + guint8 rcv_window[1 << 13]; // windows for check duplicate packet + GQueue *rcv_trans; // queue to store packet can not process before login + + // tcp related + PurpleCircBuffer *tcp_txbuf; + guint8 *tcp_rxqueue; + int tcp_rxlen; + + // udp related + PurpleDnsQueryData *udp_query_data; + guint32 uid; /* QQ number */ guint8 *inikey; /* initial key to encrypt login packet */ guint8 *pwkey; /* password in md5 (or md5' md5) */ @@ -76,17 +106,10 @@ guint16 send_seq; /* send sequence number */ guint8 login_mode; /* online of invisible */ gboolean logged_in; /* used by qq-add_buddy */ - gboolean use_tcp; /* network in tcp or udp */ - - PurpleProxyType proxy_type; - PurpleConnection *gc; PurpleXfer *xfer; /* file transfer handler */ struct sockaddr_in dest_sin; - /* from real connction */ - gchar *server_ip; - guint16 server_port; /* get from login reply packet */ time_t login_time; time_t last_login_time; @@ -99,9 +122,6 @@ guint32 all_online; /* the number of online QQ users */ time_t last_get_online; /* last time send get_friends_online packet */ - guint8 window[1 << 13]; /* check up for duplicated packet */ - gint sendqueue_timeout; - PurpleRoomlist *roomlist; gint channel; /* the id for opened chat conversation */ @@ -112,10 +132,8 @@ GList *buddies; GList *contact_info_window; GList *group_info_window; - GList *sendqueue; GList *info_query; GList *add_buddy_request; - GQueue *before_login_packets; /* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */ gboolean modifying_info;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_network.c Tue Jun 24 12:22:40 2008 +0000 @@ -0,0 +1,1124 @@ +/** + * @file qq_network.c + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "cipher.h" +#include "debug.h" +#include "internal.h" + +#ifdef _WIN32 +#define random rand +#define srandom srand +#endif + +#include "buddy_info.h" +#include "buddy_list.h" +#include "buddy_opt.h" +#include "buddy_status.h" +#include "group_free.h" +#include "char_conv.h" +#include "crypt.h" +#include "group_network.h" +#include "header_info.h" +#include "keep_alive.h" +#include "im.h" +#include "login_logout.h" +#include "packet_parse.h" +#include "qq_network.h" +#include "sendqueue.h" +#include "sys_msg.h" +#include "utils.h" + +/* These functions are used only in development phase */ +/* + static void _qq_show_socket(gchar *desc, gint fd) { + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + getsockname(fd, (struct sockaddr *)&sin, &len); + purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", + inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); + } + */ + +/* QQ 2003iii uses double MD5 for the pwkey to get the session key */ +static guint8 *encrypt_account_password(const gchar *pwd) +{ + PurpleCipher *cipher; + PurpleCipherContext *context; + + guchar pwkey_tmp[QQ_KEY_LENGTH]; + + cipher = purple_ciphers_find_cipher("md5"); + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd)); + purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); + purple_cipher_context_destroy(context); + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH); + purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); + purple_cipher_context_destroy(context); + + return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); +} + +/* default process, decrypt and dump */ +static void packet_process_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) +{ + qq_data *qd; + guint8 *data; + gint data_len; + gchar *msg_utf8 = NULL; + + g_return_if_fail(buf != NULL && buf_len != 0); + + qq_show_packet("Processing unknown packet", buf, buf_len); + + qd = (qq_data *) gc->proto_data; + + data_len = buf_len; + data = g_newa(guint8, data_len); + memset(data, 0, data_len); + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); + return; + } + + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + data, data_len, + ">>> [%d] %s -> [default] decrypt and dump", + seq, qq_get_cmd_desc(cmd)); + + msg_utf8 = try_dump_as_gbk(data, data_len); + if (msg_utf8) { + g_free(msg_utf8); + } +} + +static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, + guint16 *cmd, guint16 *seq, guint8 *buf) +{ + gint bytes = 0; + bytes += qq_get8(header_tag, buf + bytes); + bytes += qq_get16(source_tag, buf + bytes); + bytes += qq_get16(cmd, buf + bytes); + bytes += qq_get16(seq, buf + bytes); + return bytes; +} + +/* check whether one sequence number is duplicated or not + * return TRUE if it is duplicated, otherwise FALSE */ +static gboolean packet_is_dup(qq_data *qd, guint16 seq) +{ + guint8 *byte, mask; + + g_return_val_if_fail(qd != NULL, FALSE); + + byte = &(qd->rcv_window[seq / 8]); + mask = (1 << (seq % 8)); + + if ((*byte) & mask) + return TRUE; /* check mask */ + (*byte) |= mask; + return FALSE; /* set mask */ +} + +static gboolean packet_check_ack(qq_data *qd, guint16 seq) +{ + gpointer trans; + + g_return_val_if_fail(qd != NULL, FALSE); + + trans = qq_trans_find(qd, seq); + if (trans == NULL) { + return FALSE; + } + + qq_trans_remove(qd, trans); + return TRUE; +} + +static void packet_process_cmd( + PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + /* now process the packet */ + switch (cmd) { + case QQ_CMD_KEEP_ALIVE: + qq_process_keep_alive_reply(data, data_len, gc); + break; + case QQ_CMD_UPDATE_INFO: + qq_process_modify_info_reply(data, data_len, gc); + break; + case QQ_CMD_ADD_FRIEND_WO_AUTH: + qq_process_add_buddy_reply(data, data_len, seq, gc); + break; + case QQ_CMD_DEL_FRIEND: + qq_process_remove_buddy_reply(data, data_len, gc); + break; + case QQ_CMD_REMOVE_SELF: + qq_process_remove_self_reply(data, data_len, gc); + break; + case QQ_CMD_BUDDY_AUTH: + qq_process_add_buddy_auth_reply(data, data_len, gc); + break; + case QQ_CMD_GET_USER_INFO: + qq_process_get_info_reply(data, data_len, gc); + break; + case QQ_CMD_CHANGE_ONLINE_STATUS: + qq_process_change_status_reply(data, data_len, gc); + break; + case QQ_CMD_SEND_IM: + qq_process_send_im_reply(data, data_len, gc); + break; + case QQ_CMD_RECV_IM: + qq_process_recv_im(data, data_len, seq, gc); + break; + case QQ_CMD_LOGIN: + qq_process_login_reply(data, data_len, gc); + break; + case QQ_CMD_GET_FRIENDS_LIST: + qq_process_get_buddies_list_reply(data, data_len, gc); + break; + case QQ_CMD_GET_FRIENDS_ONLINE: + qq_process_get_buddies_online_reply(data, data_len, gc); + break; + case QQ_CMD_GROUP_CMD: + qq_process_group_cmd_reply(data, data_len, seq, gc); + break; + case QQ_CMD_GET_ALL_LIST_WITH_GROUP: + qq_process_get_all_list_with_group_reply(data, data_len, gc); + break; + case QQ_CMD_GET_LEVEL: + qq_process_get_level_reply(data, data_len, gc); + break; + case QQ_CMD_REQUEST_LOGIN_TOKEN: + qq_process_request_login_token_reply(data, data_len, gc); + break; + case QQ_CMD_RECV_MSG_SYS: + qq_process_msg_sys(data, data_len, seq, gc); + break; + case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: + qq_process_friend_change_status(data, data_len, gc); + break; + default: + packet_process_unknow(gc, data, data_len, cmd, seq); + break; + } +} + +/* process the incoming packet from qq_pending */ +static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) +{ + qq_data *qd; + gint bytes, bytes_not_read; + + gboolean prev_login_status; + guint8 *new_data; + gint new_data_len; + + guint8 header_tag; + guint16 source_tag; + guint16 cmd; + guint16 seq; // May be ack_seq or send_seq, depends on cmd + + g_return_if_fail(buf != NULL && buf_len > 0); + + qd = (qq_data *) gc->proto_data; + + prev_login_status = qd->logged_in; + + // Len, header and tail tag have been checked before + bytes = 0; + bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); + + if (QQ_DEBUG) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "==> [%05d] 0x%04X %s, from (0x%04X %s)\n", + seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag)); + } + + bytes_not_read = buf_len - bytes - 1; + + if ( !qd->logged_in ) { + if (cmd != QQ_CMD_LOGIN && cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) { + /* packets before login */ + qq_packet_push(qd, cmd, seq, buf + bytes, bytes_not_read); + return; /* do not process it now */ + } + } + + /* whether it is an ack */ + switch (cmd) { + case QQ_CMD_RECV_IM: + case QQ_CMD_RECV_MSG_SYS: + case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: + /* server intiated packet, we need to send ack and check duplicaion + * this must be put after processing b4_packet + * as these packets will be passed in twice */ + if (packet_is_dup(qd, seq)) { + purple_debug(PURPLE_DEBUG_WARNING, + "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); + return; + } + break; + default: + /* ack packet, we need to update sendqueue */ + /* we do not check duplication for server ack */ + packet_check_ack(qd, seq); + } + + /* this is the length of all the encrypted data (also remove tail tag */ + packet_process_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); + + // check is redirect or not, and do it now + if (qd->is_redirect) { + // free resource except real_hostname and port + qq_disconnect(gc); + qq_connect(gc->account); + return; + } + + if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { + /* logged_in, but we have packets before login */ + new_data = g_newa(guint8, MAX_PACKET_SIZE); + while (1) { + new_data_len = qq_packet_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE); + if (new_data_len < 0) { + break; + } + if (new_data_len == 0) { + continue; + } + packet_process_cmd(gc, seq, cmd, new_data, new_data_len); + } + } +} + +static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 buf[1024]; // set to 16 when test tcp_rxqueue + gint buf_len; + gint bytes; + + guint8 *pkt; + guint16 pkt_len; + + gchar *error_msg; + guint8 *jump; + gint jump_len; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + if(cond != PURPLE_INPUT_READ) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Socket error")); + return; + } + + qd = (qq_data *) gc->proto_data; + + /* test code, not using tcp_rxqueue + memset(pkt,0, sizeof(pkt)); + buf_len = read(qd->fd, pkt, sizeof(pkt)); + if (buf_len > 2) { + packet_process(gc, pkt + 2, buf_len - 2); + } + return; + */ + + buf_len = read(qd->fd, buf, sizeof(buf)); + if (buf_len < 0) { + if (errno == EAGAIN) + /* No worries */ + return; + + error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno)); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + return; + } else if (buf_len == 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Server closed the connection.")); + return; + } + + gc->last_received = time(NULL); + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen); + qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen); + memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len); + qd->tcp_rxlen += buf_len; + + pkt = g_newa(guint8, MAX_PACKET_SIZE); + while (1) { + if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { + break; + } + + bytes = 0; + bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes); + if (qd->tcp_rxlen < pkt_len) { + break; + } + + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); + + if ( pkt_len < QQ_TCP_HEADER_LENGTH + || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG + || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { + // HEY! This isn't even a QQ. What are you trying to pull? + + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", + "Packet error, failed to check header and tail tag\n"); + + jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); + if ( !jump ) { + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Failed to find next QQ_PACKET_TAIL, clear receive buffer\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + qd->tcp_rxlen = 0; + return; + } + + // jump and over QQ_PACKET_TAIL + jump_len = (jump - qd->tcp_rxqueue) + 1; + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1); + g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len); + qd->tcp_rxlen -= jump_len; + continue; + } + + memset(pkt, 0, MAX_PACKET_SIZE); + g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); + + // jump to next packet + qd->tcp_rxlen -= pkt_len; + if (qd->tcp_rxlen) { + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", + "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); + jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = jump; + } else { + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", + "free tcp_rxqueue\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + } + + if (pkt == NULL) { + continue; + } + // do not call packet_process before jump + // packet_process may call disconnect and destory tcp_rxqueue + packet_process(gc, pkt, pkt_len - bytes); + } +} + +static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 *buf; + gint buf_len; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + if(cond != PURPLE_INPUT_READ) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Socket error")); + return; + } + + qd = (qq_data *) gc->proto_data; + g_return_if_fail(qd->fd >= 0); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + + /* here we have UDP proxy suppport */ + buf_len = read(qd->fd, buf, MAX_PACKET_SIZE); + if (buf_len <= 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to read from socket")); + return; + } + + gc->last_received = time(NULL); + + if (buf_len < QQ_UDP_HEADER_LENGTH) { + if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) { + qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING", + buf, buf_len, + "Received packet is too short, or no header and tail tag"); + return; + } + } + + packet_process(gc, buf, buf_len); +} + +static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len) +{ + gint ret; + + g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Send %d bytes to socket %d\n", data_len, qd->fd); + + errno = 0; + ret = send(qd->fd, data, data_len, 0); + if (ret < 0 && errno == EAGAIN) { + return ret; + } + + if (ret < 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Send failed: %d, %s\n", errno, g_strerror(errno)); + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + } + return ret; +} + +static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) +{ + qq_data *qd = data; + int ret, writelen; + + writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf); + if (writelen == 0) { + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + return; + } + + ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen); + purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE", + "total %d bytes is sent %d\n", writelen, ret); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret < 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Write Error")); + return; + } + + purple_circ_buffer_mark_read(qd->tcp_txbuf, ret); +} + +static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len) +{ + gint ret; + + g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + + // purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); + + if (qd->tx_handler == 0) { + ret = write(qd->fd, data, data_len); + } else { + ret = -1; + errno = EAGAIN; + } + + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); + if (ret < 0 && errno == EAGAIN) { + // socket is busy, send later + // purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); + ret = 0; + } else if (ret <= 0) { + // TODO: what to do here - do we really have to disconnect? + purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", + "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + return ret; + } + + if (ret < data_len) { + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + "Add %d bytes to buffer\n", data_len - ret); + if (qd->tx_handler == 0) { + qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd); + } + purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret); + } + return ret; +} + +static gboolean trans_timeout(gpointer data) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 *buf; + gint buf_len = 0; + guint16 cmd; + gint retries = 0; + int index; + + gc = (PurpleConnection *) data; + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); + + qd = (qq_data *) gc->proto_data; + + index = 0; + buf = g_newa(guint8, MAX_PACKET_SIZE); + + while (1) { + if (index < 0) { + // next record is NULL + break; + } + // purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); + memset(buf, 0, MAX_PACKET_SIZE); + buf_len = qq_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries); + if (buf_len <= 0) { + // curr record is empty, whole trans is NULL + break; + } + // index = -1, when get last record of transactions + + // purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); + if (retries > 0) { + if (qd->use_tcp) { + tcp_send_out(qd, buf, buf_len); + } else { + udp_send_out(qd, buf, buf_len); + } + continue; + } + + // retries <= 0 + switch (cmd) { + case QQ_CMD_KEEP_ALIVE: + if (qd->logged_in) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); + qd->logged_in = FALSE; + } + break; + case QQ_CMD_LOGIN: + case QQ_CMD_REQUEST_LOGIN_TOKEN: + if (!qd->logged_in) { + /* cancel login progress */ + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply")); + } + break; + default: + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "%s packet lost.\n", qq_get_cmd_desc(cmd)); + } + } + + return TRUE; /* if return FALSE, timeout callback stops */ +} + +/* the callback function after socket is built + * we setup the qq protocol related configuration here */ +static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) +{ + qq_data *qd; + PurpleConnection *gc; + gchar *buf; + const gchar *passwd; + + gc = (PurpleConnection *) data; + + if (!PURPLE_CONNECTION_IS_VALID(gc)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n"); + close(source); + return; + } + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + /* Connect is now complete; clear the PurpleProxyConnectData */ + qd->connect_data = NULL; + + if (source < 0) { /* socket returns -1 */ + purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Source is < 0\n"); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); + return; + } + + // _qq_show_socket("Got login socket", source); + + /* QQ use random seq, to minimize duplicated packets */ + srandom(time(NULL)); + qd->send_seq = random() & 0x0000ffff; + qd->fd = source; + qd->logged_in = FALSE; + qd->channel = 1; + qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); + + /* now generate md5 processed passwd */ + passwd = purple_account_get_password(purple_connection_get_account(gc)); + g_return_if_fail(qd->pwkey == NULL); + qd->pwkey = encrypt_account_password(passwd); + + g_return_if_fail(qd->resend_timeout == 0); + /* call trans_timeout every 5 seconds */ + qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc); + + if (qd->use_tcp) + gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); + else + gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); + + /* Update the login progress status display */ + buf = g_strdup_printf("Login as %d", qd->uid); + purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS); + g_free(buf); + + qq_send_packet_request_login_token(gc); +} + +static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + socklen_t len; + int error=0, ret; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + + purple_debug_info("proxy", "Connected.\n"); + + /* + * getsockopt after a non-blocking connect returns -1 if something is + * really messed up (bad descriptor, usually). Otherwise, it returns 0 and + * error holds what connect would have returned if it blocked until now. + * Thus, error == 0 is success, error == EINPROGRESS means "try again", + * and anything else is a real error. + * + * (error == EINPROGRESS can happen after a select because the kernel can + * be overly optimistic sometimes. select is just a hint that you might be + * able to do something.) + */ + len = sizeof(error); + ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == 0 && error == EINPROGRESS) + return; /* we'll be called again later */ + + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + if (ret < 0 || error != 0) { + if(ret != 0) + error = errno; + + close(source); + + purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); + + qq_connect_cb(gc, -1, _("Unable to connect")); + return; + } + + qq_connect_cb(gc, source, NULL); +} + +static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { + PurpleConnection *gc; + qq_data *qd; + struct sockaddr server_addr; + int addr_size; + gint fd = -1; + int flags; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + // udp_query_data must be set as NULL. + // Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error + qd->udp_query_data = NULL; + + if (!hosts || !hosts->data) { + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Couldn't resolve host")); + return; + } + + addr_size = GPOINTER_TO_INT(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + memcpy(&server_addr, hosts->data, addr_size); + g_free(hosts->data); + + hosts = g_slist_remove(hosts, hosts->data); + while(hosts) { + hosts = g_slist_remove(hosts, hosts->data); + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + } + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Unable to create socket: %s\n", g_strerror(errno)); + return; + } + + /* we use non-blocking mode to speed up connection */ + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ + * + * If a UDP socket is unconnected, which is the normal state after a + * bind() call, then send() or write() are not allowed, since no + * destination is available; only sendto() can be used to send data. + * + * Calling connect() on the socket simply records the specified address + * and port number as being the desired communications partner. That + * means that send() or write() are now allowed; they use the destination + * address and port given on the connect call as the destination of packets. + */ + if (connect(fd, &server_addr, addr_size) >= 0) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); + qq_connect_cb(gc, fd, NULL); + return; + } + + /* [EINPROGRESS] + * The socket is marked as non-blocking and the connection cannot be + * completed immediately. It is possible to select for completion by + * selecting the socket for writing. + * [EINTR] + * A signal interrupted the call. + * The connection is established asynchronously. + */ + if ((errno == EINPROGRESS) || (errno == EINTR)) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); + qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc); + return; + } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %d\n", g_strerror(errno)); + close(fd); +} + +/* establish a generic QQ connection + * TCP/UDP, and direct/redirected */ +void qq_connect(PurpleAccount *account) +{ + PurpleConnection *gc; + qq_data *qd; + + gc = purple_account_get_connection(account); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + if (qd->real_hostname == NULL || qd->real_port == 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("hostname is NULL or port is 0")); + return; + } + + if (qd->is_redirect) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", + qd->real_hostname, qd->real_port); + } + qd->is_redirect = FALSE; + + qd->fd = -1; + qd->tx_handler = 0; + + g_return_if_fail(qd->real_hostname != NULL); + + /* QQ connection via UDP/TCP. + * Now use Purple proxy function to provide TCP proxy support, + * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ + if(qd->use_tcp) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", + qd->real_hostname, qd->real_port); + + /* TODO: is there a good default grow size? */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n"); + qd->tcp_txbuf = purple_circ_buffer_new(0); + + qd->connect_data = purple_proxy_connect(NULL, account, + qd->real_hostname, qd->real_port, qq_connect_cb, gc); + if (qd->connect_data == NULL) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect.")); + } + return; + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n", + qd->real_hostname, qd->real_port); + + g_return_if_fail(qd->udp_query_data == NULL); + qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port, + udp_host_resolved, gc); + if (qd->udp_query_data == NULL) { + purple_connection_error_reason(qd->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Could not resolve hostname")); + } +} + +/* clean up qq_data structure and all its components + * always used before a redirectly connection */ +void qq_disconnect(PurpleConnection *gc) +{ + qq_data *qd; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n"); + /* finish all I/O */ + if (qd->fd >= 0 && qd->logged_in) { + qq_send_packet_logout(gc); + } + + if (qd->resend_timeout > 0) { + purple_timeout_remove(qd->resend_timeout); + qd->resend_timeout = 0; + } + + if (gc->inpa > 0) { + purple_input_remove(gc->inpa); + gc->inpa = 0; + } + + if (qd->fd >= 0) { + close(qd->fd); + qd->fd = -1; + } + + if (qd->connect_data != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); + purple_proxy_connect_cancel(qd->connect_data); + } + + if(qd->tcp_txbuf != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); + purple_circ_buffer_destroy(qd->tcp_txbuf); + } + + if (qd->tx_handler) { + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + } + if (qd->tcp_rxqueue != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + qd->tcp_rxlen = 0; + } + + if (qd->udp_query_data != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); + purple_dnsquery_destroy(qd->udp_query_data); + qd->udp_query_data = NULL; + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy transactions\n"); + qq_trans_remove_all(qd); + + if (qd->inikey) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n"); + g_free(qd->inikey); + qd->inikey = NULL; + } + if (qd->pwkey) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n"); + g_free(qd->pwkey); + qd->pwkey = NULL; + } + if (qd->session_key) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n"); + g_free(qd->session_key); + qd->session_key = NULL; + } + if (qd->session_md5) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n"); + g_free(qd->session_md5); + qd->session_md5 = NULL; + } + if (qd->my_ip) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n"); + g_free(qd->my_ip); + qd->my_ip = NULL; + } + + qq_packet_remove_all(qd); + qq_group_packets_free(qd); + qq_group_free_all(qd); + qq_add_buddy_request_free(qd); + qq_info_query_free(qd); + qq_buddies_list_free(gc->account, qd); +} + +static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, + guint8 *data, gint data_len) +{ + gint bytes = 0; + g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); + + if (data == NULL) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n"); + return -1; + } + if (data_len <= 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n"); + return -1; + } + + /* QQ TCP packet has two bytes in the begining defines packet length + * so leave room here to store packet size */ + if (qd->use_tcp) { + bytes += qq_put16(buf + bytes, 0x0000); + } + /* 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, cmd); + + bytes += qq_put16(buf + bytes, seq); + + bytes += qq_put32(buf + bytes, qd->uid); + bytes += qq_putdata(buf + bytes, data, data_len); + bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); + + // set TCP packet length at begin of the packet + if (qd->use_tcp) { + qq_put16(buf, bytes); + } + + return bytes; +} + +gint qq_send_data(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len) +{ + qq_data *qd; + guint8 *buf; + gint buf_len; + gint bytes_sent; + gint seq; + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *) gc->proto_data; + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + seq = ++(qd->send_seq); + buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); + if (buf_len <= 0) { + return -1; + } + + if (qd->use_tcp) { + bytes_sent = tcp_send_out(qd, buf, buf_len); + } else { + bytes_sent = udp_send_out(qd, buf, buf_len); + } + + // always need ack + qq_trans_append(qd, buf, buf_len, cmd, seq); + + if (QQ_DEBUG) { + qq_show_packet("QQ_SEND_DATA", buf, buf_len); + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "<== [%05d], %s, total %d bytes is sent %d\n", + seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); + } + return bytes_sent; +} + +/* send the packet generated with the given cmd and data + * return the number of bytes sent to socket if succeeds + * return -1 if there is any error */ +gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, + gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint data_len) +{ + qq_data *qd; + guint8 *buf; + gint buf_len; + guint8 *encrypted_data; + gint encrypted_len; + gint real_seq; + gint bytes_sent; + + qd = (qq_data *) gc->proto_data; + g_return_val_if_fail(qd->session_key != NULL, -1); + + encrypted_len = data_len + 16; /* at most 16 bytes more */ + encrypted_data = g_newa(guint8, encrypted_len); + + qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len); + + real_seq = seq; + if (is_auto_seq) real_seq = ++(qd->send_seq); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, real_seq, encrypted_data, encrypted_len); + if (buf_len <= 0) { + return -1; + } + + if (QQ_DEBUG) { + qq_show_packet("QQ_SEND_CMD", buf, buf_len); + } + if (qd->use_tcp) { + bytes_sent = tcp_send_out(qd, buf, buf_len); + } else { + bytes_sent = udp_send_out(qd, buf, buf_len); + } + + /* if it does not need ACK, we send ACK manually several times */ + if (need_ack) { + qq_trans_append(qd, buf, buf_len, cmd, real_seq); + } + + if (QQ_DEBUG) { + qq_show_packet("QQ_SEND_CMD", buf, buf_len); + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "<== [%05d], %s, total %d bytes is sent %d\n", + real_seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); + } + return bytes_sent; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_network.h Tue Jun 24 12:22:40 2008 +0000 @@ -0,0 +1,42 @@ +/** + * @file qq_network.h + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _QQ_PROXY_H +#define _QQ_PROXY_H + +#include <glib.h> +#include "connection.h" + +#include "qq.h" + +#define QQ_CONNECT_STEPS 2 /* steps in connection */ + +void qq_connect(PurpleAccount *account); +void qq_disconnect(PurpleConnection *gc); + +gint qq_send_data(PurpleConnection *gc, guint16 cmd, guint8 *data, gint datalen); +gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq, + gboolean need_ack, guint8 *data, gint datalen); + +#endif
--- a/libpurple/protocols/qq/qq_proxy.c Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,537 +0,0 @@ -/** - * @file qq_proxy.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "cipher.h" -#include "debug.h" -#include "internal.h" - -#ifdef _WIN32 -#define random rand -#define srandom srand -#endif - -#include "packet_parse.h" -#include "buddy_info.h" -#include "buddy_opt.h" -#include "char_conv.h" -#include "group_free.h" -#include "login_logout.h" -#include "qq_proxy.h" -#include "recv_core.h" -#include "send_core.h" -#include "sendqueue.h" -#include "udp_proxy_s5.h" -#include "utils.h" - -/* These functions are used only in development phase */ -/* - static void _qq_show_socket(gchar *desc, gint fd) { - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - getsockname(fd, (struct sockaddr *)&sin, &len); - purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", - inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); - } - */ - -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len) -{ - /* - char buf1[8*len+2], buf2[10]; - int i; - buf1[0] = 0; - for (i = 0; i < len; i++) { - sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff); - strcat(buf1, buf2); - } - strcat(buf1, "\n"); - purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1); - */ - - /* modified by s3e, 20080424 */ - qq_hex_dump(PURPLE_DEBUG_INFO, desc, - buf, len, - ""); -} - -/* QQ 2003iii uses double MD5 for the pwkey to get the session key */ -static guint8 *_gen_pwkey(const gchar *pwd) -{ - PurpleCipher *cipher; - PurpleCipherContext *context; - - guchar pwkey_tmp[QQ_KEY_LENGTH]; - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd)); - purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); - purple_cipher_context_destroy(context); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH); - purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); - purple_cipher_context_destroy(context); - - return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); -} - -static gboolean _qq_fill_host(GSList *hosts, struct sockaddr_in *addr, gint *addr_size) -{ - if (!hosts || !hosts->data) - return FALSE; - - *addr_size = GPOINTER_TO_INT(hosts->data); - - hosts = g_slist_remove(hosts, hosts->data); - memcpy(addr, hosts->data, *addr_size); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - while(hosts) { - hosts = g_slist_remove(hosts, hosts->data); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - } - - return TRUE; -} - -/* set up any finalizing start-up stuff */ -static void _qq_start_services(PurpleConnection *gc) -{ - /* start watching for IMs about to be sent */ - /* - purple_signal_connect(purple_conversations_get_handle(), - "sending-im-msg", gc, - PURPLE_CALLBACK(qq_sending_im_msg_cb), NULL); - */ -} - -/* the callback function after socket is built - * we setup the qq protocol related configuration here */ -static void _qq_got_login(gpointer data, gint source, const gchar *error_message) -{ - qq_data *qd; - PurpleConnection *gc; - gchar *buf; - const gchar *passwd; - - gc = (PurpleConnection *) data; - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - close(source); - return; - } - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - - if (source < 0) { /* socket returns -1 */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); - return; - } - - qd = (qq_data *) gc->proto_data; - - /* - _qq_show_socket("Got login socket", source); - */ - - /* QQ use random seq, to minimize duplicated packets */ - srandom(time(NULL)); - qd->send_seq = random() & 0x0000ffff; - qd->fd = source; - qd->logged_in = FALSE; - qd->channel = 1; - qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); - - /* now generate md5 processed passwd */ - passwd = purple_account_get_password(purple_connection_get_account(gc)); - qd->pwkey = _gen_pwkey(passwd); - - qd->sendqueue_timeout = purple_timeout_add(QQ_SENDQUEUE_TIMEOUT, qq_sendqueue_timeout_callback, gc); - gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, qq_input_pending, gc); - - /* Update the login progress status display */ - buf = g_strdup_printf("Login as %d", qd->uid); - purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS); - g_free(buf); - - _qq_start_services(gc); - - qq_send_packet_request_login_token(gc); -} - -/* clean up qq_data structure and all its components - * always used before a redirectly connection */ -static void _qq_common_clean(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - /* finish all I/O */ - if (qd->fd >= 0 && qd->logged_in) - qq_send_packet_logout(gc); - close(qd->fd); - - if (qd->sendqueue_timeout > 0) { - purple_timeout_remove(qd->sendqueue_timeout); - qd->sendqueue_timeout = 0; - } - - if (gc->inpa > 0) { - purple_input_remove(gc->inpa); - gc->inpa = 0; - } - - qq_b4_packets_free(qd); - qq_sendqueue_free(qd); - qq_group_packets_free(qd); - qq_group_free_all(qd); - qq_add_buddy_request_free(qd); - qq_info_query_free(qd); - qq_buddies_list_free(gc->account, qd); -} - -static void no_one_calls(gpointer data, gint source, PurpleInputCondition cond) -{ - struct PHB *phb = data; - socklen_t len; - int error=0, ret; - - purple_debug_info("proxy", "Connected.\n"); - - len = sizeof(error); - - /* - * getsockopt after a non-blocking connect returns -1 if something is - * really messed up (bad descriptor, usually). Otherwise, it returns 0 and - * error holds what connect would have returned if it blocked until now. - * Thus, error == 0 is success, error == EINPROGRESS means "try again", - * and anything else is a real error. - * - * (error == EINPROGRESS can happen after a select because the kernel can - * be overly optimistic sometimes. select is just a hint that you might be - * able to do something.) - */ - ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); - if (ret == 0 && error == EINPROGRESS) - return; /* we'll be called again later */ - if (ret < 0 || error != 0) { - if(ret!=0) - error = errno; - close(source); - purple_input_remove(phb->inpa); - - purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); - - phb->func(phb->data, -1, _("Unable to connect")); - return; - } - - purple_input_remove(phb->inpa); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, source, NULL); - } - - g_free(phb->host); - g_free(phb); -} - -/* returns -1 if fails, otherwise returns the file handle */ -static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) -{ - gint fd = -1; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Using UDP without proxy\n"); - fd = socket(PF_INET, SOCK_DGRAM, 0); - - if (fd < 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ Redirect", - "Unable to create socket: %s\n", g_strerror(errno)); - return -1; - } - - /* we use non-blocking mode to speed up connection */ - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - - /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ - * - * If a UDP socket is unconnected, which is the normal state after a - * bind() call, then send() or write() are not allowed, since no - * destination is available; only sendto() can be used to send data. - * - * Calling connect() on the socket simply records the specified address - * and port number as being the desired communications partner. That - * means that send() or write() are now allowed; they use the destination - * address and port given on the connect call as the destination of packets. - */ - if (connect(fd, addr, addrlen) < 0) { - /* [EINPROGRESS] - * The socket is marked as non-blocking and the connection cannot be - * completed immediately. It is possible to select for completion by - * selecting the socket for writing. - * [EINTR] - * A signal interrupted the call. - * The connection is established asynchronously. - */ - if ((errno == EINPROGRESS) || (errno == EINTR)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); - phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, no_one_calls, phb); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %d\n", g_strerror(errno)); - close(fd); - return -1; - } /* if errno */ - } else { /* connect returns 0 */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - phb->func(phb->data, fd, NULL); - } - - return fd; -} - -static void _qq_proxy_resolved(GSList *hosts, gpointer data, const char *error_message) -{ - struct PHB *phb = (struct PHB *) data; - struct sockaddr_in addr; - gint addr_size, ret = -1; - - if(_qq_fill_host(hosts, &addr, &addr_size)) - ret = qq_proxy_socks5(phb, (struct sockaddr *) &addr, addr_size); - - if (ret < 0) { - phb->func(phb->data, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - } -} - -static void _qq_server_resolved(GSList *hosts, gpointer data, const char *error_message) -{ - struct PHB *phb = (struct PHB *) data; - PurpleConnection *gc = (PurpleConnection *) phb->data; - qq_data *qd = (qq_data *) gc->proto_data; - struct sockaddr_in addr; - gint addr_size, ret = -1; - - if(_qq_fill_host(hosts, &addr, &addr_size)) { - switch (purple_proxy_info_get_type(phb->gpi)) { - case PURPLE_PROXY_NONE: - ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); - break; - case PURPLE_PROXY_SOCKS5: - ret = 0; - if (purple_proxy_info_get_host(phb->gpi) == NULL || - purple_proxy_info_get_port(phb->gpi) == 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Use of socks5 proxy selected but host or port info doesn't exist.\n"); - ret = -1; - } else { - /* as the destination is always QQ server during the session, - * we can set dest_sin here, instead of _qq_s5_canread_again */ - memcpy(&qd->dest_sin, &addr, addr_size); - if (purple_dnsquery_a(purple_proxy_info_get_host(phb->gpi), - purple_proxy_info_get_port(phb->gpi), - _qq_proxy_resolved, phb) == NULL) - ret = -1; - } - break; - default: - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Proxy type %i is unsupported, not using a proxy.\n", - purple_proxy_info_get_type(phb->gpi)); - ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); - } - } - - if (ret < 0) { - phb->func(gc, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - } -} - -/* returns -1 if dns lookup fails, otherwise returns 0 */ -static gint _qq_udp_proxy_connect(PurpleAccount *account, - const gchar *server, guint16 port, - void callback(gpointer, gint, const gchar *error_message), - PurpleConnection *gc) -{ - PurpleProxyInfo *info; - struct PHB *phb; - qq_data *qd = (qq_data *) gc->proto_data; - - g_return_val_if_fail(gc != NULL && qd != NULL, -1); - - info = purple_proxy_get_setup(account); - - phb = g_new0(struct PHB, 1); - phb->host = g_strdup(server); - phb->port = port; - phb->account = account; - phb->gpi = info; - phb->func = callback; - phb->data = gc; - qd->proxy_type = purple_proxy_info_get_type(phb->gpi); - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Choosing proxy type %d\n", - purple_proxy_info_get_type(phb->gpi)); - - if (purple_dnsquery_a(server, port, _qq_server_resolved, phb) == NULL) { - phb->func(gc, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - return -1; - } else { - return 0; - } -} - -/* QQ connection via UDP/TCP. - * I use Purple proxy function to provide TCP proxy support, - * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ -static gint _proxy_connect_full (PurpleAccount *account, const gchar *host, guint16 port, - PurpleProxyConnectFunction func, gpointer data, gboolean use_tcp) -{ - PurpleConnection *gc; - qq_data *qd; - - gc = purple_account_get_connection(account); - qd = (qq_data *) gc->proto_data; - qd->server_ip = g_strdup(host); - qd->server_port = port; - - if(use_tcp) - return (purple_proxy_connect(NULL, account, host, port, func, data) == NULL); - else - return _qq_udp_proxy_connect(account, host, port, func, data); -} - -/* establish a generic QQ connection - * TCP/UDP, and direct/redirected */ -gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, - gboolean use_tcp, gboolean is_redirect) -{ - PurpleConnection *gc; - qq_data *qd; - - g_return_val_if_fail(host != NULL, -1); - g_return_val_if_fail(port > 0, -1); - - gc = purple_account_get_connection(account); - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - - if (is_redirect) - _qq_common_clean(gc); - - qd = (qq_data *) gc->proto_data; - qd->before_login_packets = g_queue_new(); - - return _proxy_connect_full(account, host, port, _qq_got_login, gc, use_tcp); -} - -/* clean up the given QQ connection and free all resources */ -void qq_disconnect(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL); - - _qq_common_clean(gc); - - qd = gc->proto_data; - g_free(qd->inikey); - g_free(qd->pwkey); - g_free(qd->session_key); - g_free(qd->session_md5); - g_free(qd->my_ip); - g_free(qd); - - gc->proto_data = NULL; -} - -/* send packet with proxy support */ -gint qq_proxy_write(qq_data *qd, guint8 *data, gint len) -{ - guint8 *buf; - gint ret; - - g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && len > 0, -1); - - /* TCP sock5 may be processed twice - * so we need to check qd->use_tcp as well */ - if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */ - buf = g_newa(guint8, len + 10); - buf[0] = 0x00; - buf[1] = 0x00; /* reserved */ - buf[2] = 0x00; /* frag */ - buf[3] = 0x01; /* type */ - g_memmove(buf + 4, &(qd->dest_sin.sin_addr.s_addr), 4); - g_memmove(buf + 8, &(qd->dest_sin.sin_port), 2); - g_memmove(buf + 10, data, len); - errno = 0; - ret = send(qd->fd, buf, len + 10, 0); - } else { - errno = 0; - ret = send(qd->fd, data, len, 0); - } - - if (ret == -1) - purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); - - return ret; -} - -/* read packet input with proxy support */ -gint qq_proxy_read(qq_data *qd, guint8 *data, gint len) -{ - guint8 *buf; - gint bytes; - buf = g_newa(guint8, MAX_PACKET_SIZE + 10); - - g_return_val_if_fail(qd != NULL && data != NULL && len > 0, -1); - g_return_val_if_fail(qd->fd > 0, -1); - - bytes = read(qd->fd, buf, len + 10); - if (bytes < 0) - return -1; - - if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */ - if (bytes < 10) - return -1; - bytes -= 10; - g_memmove(data, buf + 10, bytes); /* cut off the header */ - } else { - g_memmove(data, buf, bytes); - } - - return bytes; -}
--- a/libpurple/protocols/qq/qq_proxy.h Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * @file qq_proxy.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_PROXY_H -#define _QQ_PROXY_H - -#include <glib.h> -#include "dnsquery.h" -#include "proxy.h" - -#include "qq.h" - -#define QQ_CONNECT_STEPS 2 /* steps in connection */ - -struct PHB { - PurpleProxyConnectFunction func; - gpointer data; - gchar *host; - gint port; - gint inpa; - PurpleProxyInfo *gpi; - PurpleAccount *account; - gint udpsock; - gpointer sockbuf; -}; - -gint qq_proxy_read(qq_data *qd, guint8 *data, gint len); -gint qq_proxy_write(qq_data *qd, guint8 *data, gint len); - -gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect); -void qq_disconnect(PurpleConnection *gc); - -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - -#endif
--- a/libpurple/protocols/qq/recv_core.c Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,321 +0,0 @@ -/** - * @file recv_core.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "debug.h" -#include "internal.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_opt.h" -#include "buddy_status.h" -#include "char_conv.h" -#include "crypt.h" -#include "group_network.h" -#include "header_info.h" -#include "keep_alive.h" -#include "im.h" -#include "login_logout.h" -#include "packet_parse.h" -#include "qq_proxy.h" -#include "recv_core.h" -#include "sendqueue.h" -#include "sys_msg.h" -#include "utils.h" - -typedef struct _packet_before_login packet_before_login; -typedef struct _qq_recv_msg_header qq_recv_msg_header; - -struct _packet_before_login { - guint8 *buf; - gint len; -}; - -struct _qq_recv_msg_header { - guint8 header_tag; - guint16 source_tag; - guint16 cmd; - guint16 seq; /* can be ack_seq or send_seq, depends on cmd */ -}; - -/* check whether one sequence number is duplicated or not - * return TRUE if it is duplicated, otherwise FALSE */ -static gboolean _qq_check_packet_set_window(guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - guint8 *byte, mask; - - qd = (qq_data *) gc->proto_data; - byte = &(qd->window[seq / 8]); - mask = (1 << (seq % 8)); - - if ((*byte) & mask) - return TRUE; /* check mask */ - (*byte) |= mask; - return FALSE; /* set mask */ -} - -/* default process, decrypt and dump */ -static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - guint8 *data; - gchar *msg_utf8; - gint len; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - msg_utf8 = NULL; - - _qq_show_packet("Processing unknown packet", buf, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", - data, len, - ">>> [%d] %s -> [default] decrypt and dump", - seq, qq_get_cmd_desc(cmd)); - try_dump_as_gbk(data, len); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); - } -} - -/* process the incoming packet from qq_pending */ -static void _qq_packet_process(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gint bytes_notread, bytes_expected, bytes; - guint16 buf_len_read; /* two bytes in the begining of TCP packet */ - qq_recv_msg_header header; - packet_before_login *b4_packet; - - g_return_if_fail(buf != NULL && buf_len > 0); - - qd = (qq_data *) gc->proto_data; - bytes_expected = qd->use_tcp ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH; - - if (buf_len < bytes_expected) { - qq_hex_dump(PURPLE_DEBUG_ERROR, "QQ", - buf, buf_len, - "Received packet is too short, dump and drop"); - return; - } - - /* initialize */ - bytes = 0; - /* QQ TCP packet returns first 2 bytes the length of this packet */ - if (qd->use_tcp) { - bytes += qq_get16(&buf_len_read, buf + bytes); - if (buf_len_read != buf_len) { /* wrong */ - purple_debug - (PURPLE_DEBUG_ERROR, - "QQ", - "TCP read %d bytes, header says %d bytes, use header anyway\n", buf_len, buf_len_read); - buf_len = buf_len_read; /* we believe header is more accurate */ - } - } - - /* now goes the normal QQ packet as UDP packet */ - bytes += qq_get8(&header.header_tag, buf + bytes); - bytes += qq_get16(&header.source_tag, buf + bytes); - bytes += qq_get16(&header.cmd, buf + bytes); - bytes += qq_get16(&header.seq, buf + bytes); - - if (bytes != bytes_expected) { /* read error */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail reading packet header, expect %d bytes, read %d bytes\n", - bytes_expected, bytes); - return; - } - - if ((buf[buf_len - 1] != QQ_PACKET_TAIL) || (header.header_tag != QQ_PACKET_TAG)) { - qq_hex_dump(PURPLE_DEBUG_ERROR, "QQ", - buf, buf_len, - "Unknown QQ proctocol, dump and drop"); - return; - } - - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "==> [%05d] %s, from (%s)\n", - header.seq, qq_get_cmd_desc(header.cmd), qq_get_source_str(header.source_tag)); - - if (header.cmd != QQ_CMD_LOGIN && header.cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) { - if (!qd->logged_in) { /* packets before login */ - b4_packet = g_new0(packet_before_login, 1); - /* must duplicate, buffer will be freed after exiting this function */ - b4_packet->buf = g_memdup(buf, buf_len); - b4_packet->len = buf_len; - if (qd->before_login_packets == NULL) - qd->before_login_packets = g_queue_new(); - g_queue_push_head(qd->before_login_packets, b4_packet); - return; /* do not process it now */ - } else if (!g_queue_is_empty(qd->before_login_packets)) { - /* logged_in, but we have packets before login */ - b4_packet = (packet_before_login *) - g_queue_pop_head(qd->before_login_packets); - _qq_packet_process(b4_packet->buf, b4_packet->len, gc); - /* in fact this is a recursive call, - * all packets before login will be processed before goes on */ - g_free(b4_packet->buf); /* the buf is duplicated, need to be freed */ - g_free(b4_packet); - } - } - - /* this is the length of all the encrypted data (also remove tail tag */ - bytes_notread = buf_len - bytes - 1; - - /* whether it is an ack */ - switch (header.cmd) { - case QQ_CMD_RECV_IM: - case QQ_CMD_RECV_MSG_SYS: - case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: - /* server intiated packet, we need to send ack and check duplicaion - * this must be put after processing b4_packet - * as these packets will be passed in twice */ - if (_qq_check_packet_set_window(header.seq, gc)) { - purple_debug(PURPLE_DEBUG_WARNING, - "QQ", "dup [%05d] %s, discard...\n", header.seq, qq_get_cmd_desc(header.cmd)); - return; - } - break; - default:{ /* ack packet, we need to update sendqueue */ - /* we do not check duplication for server ack */ - qq_sendqueue_remove(qd, header.seq); - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "ack [%05d] %s, remove from sendqueue\n", - header.seq, qq_get_cmd_desc(header.cmd)); - } - } - - /* now process the packet */ - switch (header.cmd) { - case QQ_CMD_KEEP_ALIVE: - qq_process_keep_alive_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_UPDATE_INFO: - qq_process_modify_info_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_ADD_FRIEND_WO_AUTH: - qq_process_add_buddy_reply(buf + bytes, bytes_notread, header.seq, gc); - break; - case QQ_CMD_DEL_FRIEND: - qq_process_remove_buddy_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_REMOVE_SELF: - qq_process_remove_self_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_BUDDY_AUTH: - qq_process_add_buddy_auth_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_GET_USER_INFO: - qq_process_get_info_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_CHANGE_ONLINE_STATUS: - qq_process_change_status_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_SEND_IM: - qq_process_send_im_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_RECV_IM: - qq_process_recv_im(buf + bytes, bytes_notread, header.seq, gc); - break; - case QQ_CMD_LOGIN: - qq_process_login_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_GET_FRIENDS_LIST: - qq_process_get_buddies_list_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_GET_FRIENDS_ONLINE: - qq_process_get_buddies_online_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_GROUP_CMD: - qq_process_group_cmd_reply(buf + bytes, bytes_notread, header.seq, gc); - break; - case QQ_CMD_GET_ALL_LIST_WITH_GROUP: - qq_process_get_all_list_with_group_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_GET_LEVEL: - qq_process_get_level_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_REQUEST_LOGIN_TOKEN: - qq_process_request_login_token_reply(buf + bytes, bytes_notread, gc); - break; - case QQ_CMD_RECV_MSG_SYS: - qq_process_msg_sys(buf + bytes, bytes_notread, header.seq, gc); - break; - case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: - qq_process_friend_change_status(buf + bytes, bytes_notread, gc); - break; - default: - _qq_process_packet_default(buf + bytes, bytes_notread, header.cmd, header.seq, gc); - break; - } -} - -/* clean up the packets before login */ -void qq_b4_packets_free(qq_data *qd) -{ - packet_before_login *b4_packet; - g_return_if_fail(qd != NULL); - /* now clean up my own data structures */ - if (qd->before_login_packets != NULL) { - while (NULL != (b4_packet = g_queue_pop_tail(qd->before_login_packets))) { - g_free(b4_packet->buf); - g_free(b4_packet); - } - g_queue_free(qd->before_login_packets); - } -} - -void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc; - qq_data *qd; - guint8 *buf; - gint len; - - gc = (PurpleConnection *) data; - - if(cond != PURPLE_INPUT_READ) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Socket error")); - return; - } - - qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); - - /* here we have UDP proxy suppport */ - len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE); - if (len <= 0) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to read from socket")); - return; - } else { - _qq_packet_process(buf, len, gc); - } -}
--- a/libpurple/protocols/qq/recv_core.h Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/** - * @file recv_core.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_RECV_CORE_H_ -#define _QQ_RECV_CORE_H_ - -#include <glib.h> -#include "connection.h" -#include "qq.h" - -void qq_b4_packets_free(qq_data *qd); - -void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond); - -#endif
--- a/libpurple/protocols/qq/send_core.c Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -/** - * @file send_core.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "debug.h" -#include "internal.h" - -#include "crypt.h" -#include "header_info.h" -#include "packet_parse.h" -#include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" -#include "sendqueue.h" - -/* create qq packet header with given sequence - * return the number of bytes in header if succeeds - * return -1 if there is any error */ -gint _create_packet_head_seq(guint8 *buf, PurpleConnection *gc, - guint16 cmd, gboolean is_auto_seq, guint16 *seq) -{ - qq_data *qd; - gint bytes_expected, bytes; - - g_return_val_if_fail(buf != NULL, -1); - - qd = (qq_data *) gc->proto_data; - if (is_auto_seq) - *seq = ++(qd->send_seq); - - bytes = 0; - bytes_expected = (qd->use_tcp) ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH; - - /* QQ TCP packet has two bytes in the begining defines packet length - * so I leave room here for size */ - if (qd->use_tcp) { - bytes += qq_put16(buf + bytes, 0x0000); - } - /* 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, cmd); - bytes += qq_put16(buf + bytes, *seq); - - if (bytes != bytes_expected) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create qq header, expect %d bytes, written %d bytes\n", bytes_expected, bytes); - bytes = -1; - } - return bytes; -} - -/* for those need ack and resend no ack feed back from server - * return number of bytes written to the socket, - * return -1 if there is any error */ -gint _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd) -{ - qq_data *qd; - qq_sendpacket *p; - gint bytes = 0; - - qd = (qq_data *) gc->proto_data; - - if (qd->use_tcp) { - if (len > MAX_PACKET_SIZE) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "xxx [%05d] %s, %d bytes is too large, do not send\n", - qq_get_cmd_desc(cmd), qd->send_seq, len); - return -1; - } else { /* I update the len for TCP packet */ - /* set TCP packet length - * _create_packet_head_seq has reserved two byte for storing pkt length, ccpaging */ - qq_put16(buf, len); - } - } - - /* bytes actually returned */ - bytes = qq_proxy_write(qd, buf, len); - - if (bytes >= 0) { /* put to queue, for matching server ACK usage */ - p = g_new0(qq_sendpacket, 1); - p->fd = qd->fd; - p->cmd = cmd; - p->send_seq = qd->send_seq; - p->resend_times = 0; - p->sendtime = time(NULL); - p->buf = g_memdup(buf, len); /* don't use g_strdup, may have 0x00 */ - p->len = len; - qd->sendqueue = g_list_append(qd->sendqueue, p); - } - - /* for debugging, s3e, 20070622 */ - _qq_show_packet("QQ_SEND_PACKET", p->buf, p->len); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d bytes written to the socket.\n", bytes); - - return bytes; -} - -/* send the packet generated with the given cmd and data - * return the number of bytes sent to socket if succeeds - * return -1 if there is any error */ -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, - gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint len) -{ - qq_data *qd; - guint8 *buf, *encrypted_data; - guint16 seq_ret; - gint encrypted_len, bytes, bytes_header, bytes_expected, bytes_sent; - - qd = (qq_data *) gc->proto_data; - g_return_val_if_fail(qd->session_key != NULL, -1); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - encrypted_len = len + 16; /* at most 16 bytes more */ - encrypted_data = g_newa(guint8, encrypted_len); - - qq_encrypt(data, len, qd->session_key, encrypted_data, &encrypted_len); - - seq_ret = seq; - - bytes = 0; - bytes += _create_packet_head_seq(buf + bytes, gc, cmd, is_auto_seq, &seq_ret); - if (bytes <= 0) { - /* _create_packet_head_seq warned before */ - return -1; - } - - bytes_header = bytes; - bytes_expected = 4 + encrypted_len + 1; - bytes += qq_put32(buf + bytes, (guint32) qd->uid); - bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); - bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); - - if ((bytes - bytes_header) != bytes_expected) { /* bad packet */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail creating packet, expect %d bytes, written %d bytes\n", - bytes_expected, bytes - bytes_header); - return -1; - } - - /* if it does not need ACK, we send ACK manually several times */ - if (need_ack) /* my request, send it */ - bytes_sent = _qq_send_packet(gc, buf, bytes, cmd); - else /* server's request, send ACK */ - bytes_sent = qq_proxy_write(qd, buf, bytes); - - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "<== [%05d] %s, %d bytes\n", seq_ret, qq_get_cmd_desc(cmd), bytes_sent); - return bytes_sent; -}
--- a/libpurple/protocols/qq/send_core.h Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/** - * @file send_core.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_SEND_CORE_H_ -#define _QQ_SEND_CORE_H_ - -#include <glib.h> -#include "connection.h" - -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq, - gboolean need_ack, guint8 *data, gint len); -gint _qq_send_packet(PurpleConnection * gc, guint8 *buf, gint len, guint16 cmd); -gint _create_packet_head_seq(guint8 *buf, PurpleConnection *gc, - guint16 cmd, gboolean is_auto_seq, guint16 *seq); - -#endif
--- a/libpurple/protocols/qq/send_file.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/send_file.c Tue Jun 24 12:22:40 2008 +0000 @@ -36,7 +36,7 @@ #include "im.h" #include "keep_alive.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" enum
--- a/libpurple/protocols/qq/sendqueue.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/sendqueue.c Tue Jun 24 12:22:40 2008 +0000 @@ -31,126 +31,213 @@ #include "request.h" #include "header_info.h" -#include "qq_proxy.h" +#include "qq_network.h" #include "sendqueue.h" #define QQ_RESEND_MAX 8 /* max resend per packet */ -typedef struct _gc_and_packet gc_and_packet; +typedef struct _transaction { + guint16 seq; + guint16 cmd; + guint8 *buf; + gint buf_len; + + gint fd; + gint retries; + time_t create_time; +} transaction; + +void qq_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) +{ + transaction *trans = g_new0(transaction, 1); + + g_return_if_fail(trans != NULL); -struct _gc_and_packet { - PurpleConnection *gc; - qq_sendpacket *packet; -}; + trans->fd = qd->fd; + trans->cmd = cmd; + trans->seq = seq; + trans->retries = QQ_RESEND_MAX; + trans->create_time = time(NULL); + trans->buf = g_memdup(buf, buf_len); /* don't use g_strdup, may have 0x00 */ + trans->buf_len = buf_len; -/* Remove a packet with send_seq from sendqueue */ -void qq_sendqueue_remove(qq_data *qd, guint16 send_seq) + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Add to transaction, seq = %d, buf = %lu, len = %d\n", + trans->seq, trans->buf, trans->buf_len); + qd->transactions = g_list_append(qd->transactions, trans); +} + +/* Remove a packet with seq from sendqueue */ +void qq_trans_remove(qq_data *qd, gpointer data) { - GList *list; - qq_sendpacket *p; + transaction *trans = (transaction *)data; + + g_return_if_fail(qd != NULL && data != NULL); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "ack [%05d] %s, remove from sendqueue\n", + trans->seq, qq_get_cmd_desc(trans->cmd)); - list = qd->sendqueue; - while (list != NULL) { - p = (qq_sendpacket *) (list->data); - if (p->send_seq == send_seq) { - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - break; + if (trans->buf) g_free(trans->buf); + qd->transactions = g_list_remove(qd->transactions, trans); + g_free(trans); +} + +gpointer qq_trans_find(qq_data *qd, guint16 seq) +{ + GList *curr; + GList *next; + transaction *trans; + + curr = qd->transactions; + while(curr) { + next = curr->next; + trans = (transaction *) (curr->data); + if(trans->seq == seq) { + return trans; } - list = list->next; + curr = next; } + + return NULL; } - + /* clean up sendqueue and free all contents */ -void qq_sendqueue_free(qq_data *qd) +void qq_trans_remove_all(qq_data *qd) { - qq_sendpacket *p; - gint i; + GList *curr; + GList *next; + transaction *trans; + gint count = 0; - i = 0; - while (qd->sendqueue != NULL) { - p = (qq_sendpacket *) (qd->sendqueue->data); - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - i++; + curr = qd->transactions; + while(curr) { + next = curr->next; + + trans = (transaction *) (curr->data); + /* + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Remove to transaction, seq = %d, buf = %lu, len = %d\n", + trans->seq, trans->buf, trans->len); + */ + qq_trans_remove(qd, trans); + + count++; + curr = next; } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", i); + g_list_free(qd->transactions); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", count); } -/* FIXME We shouldn't be dropping packets, but for now we have to because - * somewhere we're generating invalid packets that the server won't ack. - * Given enough time, a buildup of those packets would crash the client. */ -gboolean qq_sendqueue_timeout_callback(gpointer data) +gint qq_trans_scan(qq_data *qd, gint *start, + guint8 *buf, gint maxlen, guint16 *cmd, gint *retries) { - PurpleConnection *gc; - qq_data *qd; - GList *list; - qq_sendpacket *p; - time_t now; - gint wait_time; + GList *curr; + GList *next = NULL; + transaction *trans; + gint copylen; - gc = (PurpleConnection *) data; - qd = (qq_data *) gc->proto_data; - now = time(NULL); - list = qd->sendqueue; - - /* empty queue, return TRUE so that timeout continues functioning */ - if (qd->sendqueue == NULL) - return TRUE; + g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1); + + //purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); + curr = g_list_nth(qd->transactions, *start); + while(curr) { + next = curr->next; + *start = g_list_position(qd->transactions, next); + + trans = (transaction *) (curr->data); + if (trans->buf == NULL || trans->buf_len <= 0) { + qq_trans_remove(qd, trans); + curr = next; + continue; + } - while (list != NULL) { /* remove all packet whose resend_times == -1 */ - p = (qq_sendpacket *) list->data; - if (p->resend_times == -1) { /* to remove */ - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - list = qd->sendqueue; - } else { - list = list->next; + if (trans->retries < 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Remove transaction, seq %d, buf %lu, len %d, retries %d, next %d\n", + trans->seq, trans->buf, trans->buf_len, trans->retries, *start); + qq_trans_remove(qd, trans); + curr = next; + continue; } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Resend transaction, seq %d, buf %lu, len %d, retries %d, next %d\n", + trans->seq, trans->buf, trans->buf_len, trans->retries, *start); + copylen = MIN(trans->buf_len, maxlen); + g_memmove(buf, trans->buf, copylen); + + *cmd = trans->cmd; + *retries = trans->retries; + trans->retries--; + return copylen; } - list = qd->sendqueue; - while (list != NULL) { - p = (qq_sendpacket *) list->data; - if (p->resend_times == QQ_RESEND_MAX) { /* reach max */ - switch (p->cmd) { - case QQ_CMD_KEEP_ALIVE: - if (qd->logged_in) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); - qd->logged_in = FALSE; - } - p->resend_times = -1; - break; - case QQ_CMD_LOGIN: - case QQ_CMD_REQUEST_LOGIN_TOKEN: - if (!qd->logged_in) /* cancel login progress */ - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply")); - p->resend_times = -1; - break; - default:{ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "%s packet sent %d times but not acked. Not resending it.\n", - qq_get_cmd_desc(p->cmd), QQ_RESEND_MAX); - } - p->resend_times = -1; - } - } else { /* resend_times < QQ_RESEND_MAX, so sent it again */ - wait_time = (gint) (QQ_SENDQUEUE_TIMEOUT / 1000); - if (difftime(now, p->sendtime) > (wait_time * (p->resend_times + 1))) { - qq_proxy_write(qd, p->buf, p->len); - p->resend_times++; - purple_debug(PURPLE_DEBUG_INFO, - "QQ", "<<< [%05d] send again for %d times!\n", - p->send_seq, p->resend_times); - } + // purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); + return -1; +} + +void qq_packet_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + transaction *trans = g_new0(transaction, 1); + + g_return_if_fail(data != NULL && data_len > 0); + g_return_if_fail(trans != NULL); + + trans->cmd = cmd; + trans->seq = seq; + trans->buf = g_memdup(data, data_len); + trans->buf_len = data_len; + trans->create_time = time(NULL); + + if (qd->rcv_trans == NULL) + qd->rcv_trans = g_queue_new(); + + g_queue_push_head(qd->rcv_trans, trans); +} + +gint qq_packet_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len) +{ + transaction *trans = NULL; + gint copy_len; + + g_return_val_if_fail(data != NULL && max_len > 0, -1); + + if (g_queue_is_empty(qd->rcv_trans)) { + return -1; + } + trans = (transaction *) g_queue_pop_head(qd->rcv_trans); + if (trans == NULL) { + return 0; + } + if (trans->buf == NULL || trans->buf_len <= 0) { + return 0; + } + + copy_len = MIN(max_len, trans->buf_len); + g_memmove(data, trans->buf, copy_len); + *cmd = trans->cmd; + *seq = trans->seq; + + g_free(trans->buf); + g_free(trans); + return copy_len; +} + +/* clean up the packets before login */ +void qq_packet_remove_all(qq_data *qd) +{ + transaction *trans = NULL; + + g_return_if_fail(qd != NULL); + + /* now clean up my own data structures */ + if (qd->rcv_trans != NULL) { + while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) { + g_free(trans->buf); + g_free(trans); } - list = list->next; + g_queue_free(qd->rcv_trans); } - return TRUE; /* if we return FALSE, the timeout callback stops functioning */ }
--- a/libpurple/protocols/qq/sendqueue.h Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/sendqueue.h Tue Jun 24 12:22:40 2008 +0000 @@ -28,23 +28,15 @@ #include <glib.h> #include "qq.h" -#define QQ_SENDQUEUE_TIMEOUT 5000 /* in 1/1000 sec */ - -typedef struct _qq_sendpacket qq_sendpacket; +void qq_trans_append(qq_data *qd, guint8 *buf, gint bus_len, guint16 cmd, guint16 seq); +void qq_trans_remove(qq_data *qd, gpointer data); +gpointer qq_trans_find(qq_data *qd, guint16 seq); +void qq_trans_remove_all(qq_data *qd); -struct _qq_sendpacket { - gint fd; - gint len; - guint8 *buf; - guint16 cmd; - guint16 send_seq; - gint resend_times; - time_t sendtime; -}; +gint qq_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries); -void qq_sendqueue_free(qq_data *qd); - -void qq_sendqueue_remove(qq_data *qd, guint16 send_seq); -gboolean qq_sendqueue_timeout_callback(gpointer data); +void qq_packet_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len); +gint qq_packet_pop(qq_data *qd, guint16 *cmd, guint16* seq, guint8 *data, gint max_len); +void qq_packet_remove_all(qq_data *qd); #endif
--- a/libpurple/protocols/qq/sys_msg.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/sys_msg.c Tue Jun 24 12:22:40 2008 +0000 @@ -35,7 +35,7 @@ #include "header_info.h" #include "packet_parse.h" #include "qq.h" -#include "send_core.h" +#include "qq_network.h" #include "sys_msg.h" #include "utils.h"
--- a/libpurple/protocols/qq/udp_proxy_s5.c Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,381 +0,0 @@ -/** - * @file udp_proxy_s5.c - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "debug.h" - -#include "udp_proxy_s5.h" - -static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb = data; - struct sockaddr_in sin; - int len, error; - socklen_t errlen; - int flags; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n"); - - len = read(source, buf, 10); - if (len < 10) { - purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, source, NULL); - } - - g_free(phb->host); - g_free(phb); - return; - } - if ((buf[0] != 0x05) || (buf[1] != 0x00)) { - if ((buf[0] == 0x05) && (buf[1] < 0x09)) - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]); - else - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - sin.sin_family = AF_INET; - memcpy(&sin.sin_addr.s_addr, buf + 4, 4); - memcpy(&sin.sin_port, buf + 8, 2); - - if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno)); - close(phb->udpsock); - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - error = ETIMEDOUT; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n"); - errlen = sizeof(error); - if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n"); - close(phb->udpsock); - return; - } - flags = fcntl(phb->udpsock, F_GETFL); - fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, phb->udpsock, NULL); - } - - g_free(phb->host); - g_free(phb); -} - -static void _qq_s5_sendconnect(gpointer data, gint source) -{ - unsigned char buf[512]; - struct PHB *phb = data; - struct sockaddr_in sin, ctlsin; - int port; - socklen_t ctllen; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port); - - buf[0] = 0x05; - buf[1] = 0x03; /* udp relay */ - buf[2] = 0x00; /* reserved */ - buf[3] = 0x01; /* address type -- ipv4 */ - memset(buf + 4, 0, 0x04); - - ctllen = sizeof(ctlsin); - if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno)); - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0); - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock); - if (phb->udpsock < 0) { - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - flags = fcntl(phb->udpsock, F_GETFL); - fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK); - - port = g_ntohs(ctlsin.sin_port) + 1; - while (1) { - inet_aton("0.0.0.0", &(sin.sin_addr)); - sin.sin_family = AF_INET; - sin.sin_port = g_htons(port); - if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - port++; - if (port > 65500) { - close(source); - g_free(phb->host); - g_free(phb); - return; - } - } else - break; - } - - memset(buf + 4, 0, 0x04); - memcpy(buf + 8, &(sin.sin_port), 0x02); - - if (write(source, buf, 10) < 10) { - close(source); - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n"); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb); -} - -static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb = data; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n"); - - if (read(source, buf, 2) < 2) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if ((buf[0] != 0x01) || (buf[1] != 0x00)) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - _qq_s5_sendconnect(phb, source); -} - -static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb; - int ret; - - phb = data; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n"); - - ret = read(source, buf, 2); - if (ret < 2) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if ((buf[0] != 0x05) || (buf[1] == 0xff)) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if (buf[1] == 0x02) { - unsigned int i, j; - - i = strlen(purple_proxy_info_get_username(phb->gpi)); - j = strlen(purple_proxy_info_get_password(phb->gpi)); - - buf[0] = 0x01; /* version 1 */ - buf[1] = i; - memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i); - buf[2 + i] = j; - memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j); - - if (write(source, buf, 3 + i + j) < 3 + i + j) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb); - } else { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n"); - _qq_s5_sendconnect(phb, source); - } -} - -static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - int i; - struct PHB *phb = data; - socklen_t len; - int error = ETIMEDOUT; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n"); - - if (phb->inpa > 0) - purple_input_remove(phb->inpa); - - len = sizeof(error); - if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno)); - close(source); - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - flags = fcntl(source, F_GETFL); - fcntl(source, F_SETFL, flags & ~O_NONBLOCK); - - i = 0; - buf[0] = 0x05; /* SOCKS version 5 */ - - if (purple_proxy_info_get_username(phb->gpi) != NULL) { - buf[1] = 0x02; /* two methods */ - buf[2] = 0x00; /* no authentication */ - buf[3] = 0x02; /* username/password authentication */ - i = 4; - } else { - buf[1] = 0x01; - buf[2] = 0x00; - i = 3; - } - - if (write(source, buf, i) < i) { - purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno)); - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb); -} - -gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) -{ - gint fd; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Connecting to %s:%d via %s:%d using SOCKS5\n", - phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi)); - - if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) - return -1; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd); - - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - if (connect(fd, addr, addrlen) < 0) { - if ((errno == EINPROGRESS) || (errno == EINTR)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); - phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb); - } else { - close(fd); - return -1; - } - } else { - purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - _qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE); - } - - return fd; -}
--- a/libpurple/protocols/qq/udp_proxy_s5.h Tue Jun 24 12:09:16 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/** - * @file udp_proxy_s5.h - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _QQ_UDP_PROXY_S5_H_ -#define _QQ_UDP_PROXY_S5_H_ - -#include "internal.h" /* for socket stuff */ - -#include "qq_proxy.h" - -gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen); - -#endif
--- a/libpurple/protocols/qq/utils.c Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/utils.c Tue Jun 24 12:22:40 2008 +0000 @@ -22,7 +22,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include "cipher.h" #include "limits.h" #include "stdlib.h" #include "string.h" @@ -113,26 +112,6 @@ return segments; } -/* generate a md5 key using uid and session_key */ -guint8 *_gen_session_md5(gint uid, guint8 *session_key) -{ - guint8 *src, md5_str[QQ_KEY_LENGTH]; - PurpleCipher *cipher; - PurpleCipherContext *context; - - src = g_newa(guint8, 20); - memcpy(src, &uid, 4); - memcpy(src, session_key, QQ_KEY_LENGTH); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, src, 20); - purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL); - purple_cipher_context_destroy(context); - - return g_memdup(md5_str, QQ_KEY_LENGTH); -} - /* given a four-byte ip data, convert it into a human readable ip string * the return needs to be freed */ gchar *gen_ip_str(guint8 *ip) @@ -194,7 +173,7 @@ } /* try to dump the data as GBK */ -void try_dump_as_gbk(const guint8 *const data, gint len) +gchar* try_dump_as_gbk(const guint8 *const data, gint len) { gint i; guint8 *incoming; @@ -215,8 +194,8 @@ if (msg_utf8 != NULL) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8); - g_free(msg_utf8); } + return msg_utf8; } /* strips whitespace */ @@ -356,6 +335,26 @@ g_free(phex); } +void qq_show_packet(const gchar *desc, const guint8 *buf, gint len) +{ + /* + char buf1[8*len+2], buf2[10]; + int i; + buf1[0] = 0; + for (i = 0; i < len; i++) { + sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff); + strcat(buf1, buf2); + } + strcat(buf1, "\n"); + purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1); + */ + + /* modified by s3e, 20080424 */ + qq_hex_dump(PURPLE_DEBUG_INFO, desc, + buf, len, + ""); +} + /* convert face num from packet (0-299) to local face (1-100) */ gchar *face_to_icon_str(gint face) {
--- a/libpurple/protocols/qq/utils.h Tue Jun 24 12:09:16 2008 +0000 +++ b/libpurple/protocols/qq/utils.h Tue Jun 24 12:22:40 2008 +0000 @@ -35,7 +35,6 @@ gint qq_string_to_dec_value(const gchar *str); gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields); -guint8 *_gen_session_md5(gint uid, guint8 *session_key); gchar *gen_ip_str(guint8 *ip); guint8 *str_ip_gen(gchar *str); @@ -46,8 +45,9 @@ gchar *face_to_icon_str(gint face); -void try_dump_as_gbk(const guint8 *const data, gint len); +gchar *try_dump_as_gbk(const guint8 *const data, gint len); +void qq_show_packet(const gchar *desc, const guint8 *buf, gint len); void qq_hex_dump(PurpleDebugLevel level, const char *category, const guint8 *pdata, gint bytes, const char *format, ...);