changeset 23050:51dbe83ebbd3

patch-04-tcp-pending
author SHiNE CsyFeK <csyfek@gmail.com>
date Tue, 24 Jun 2008 12:22:40 +0000
parents 190bc4ecf6c3
children 55f986ccbb6a
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/Makefile.am libpurple/protocols/qq/Makefile.mingw libpurple/protocols/qq/buddy_info.c libpurple/protocols/qq/buddy_list.c libpurple/protocols/qq/buddy_opt.c libpurple/protocols/qq/buddy_status.c libpurple/protocols/qq/char_conv.c libpurple/protocols/qq/file_trans.c libpurple/protocols/qq/group_network.c libpurple/protocols/qq/im.c libpurple/protocols/qq/keep_alive.c libpurple/protocols/qq/login_logout.c libpurple/protocols/qq/packet_parse.c libpurple/protocols/qq/packet_parse.h libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq.h libpurple/protocols/qq/qq_network.c libpurple/protocols/qq/qq_network.h libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/send_file.c libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/sys_msg.c libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h libpurple/protocols/qq/utils.c libpurple/protocols/qq/utils.h
diffstat 33 files changed, 1712 insertions(+), 2168 deletions(-) [+]
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, ...);