changeset 23683:1c50f12b1c52

2008.08.02 - csyfek <csyfek(at)gmail.com> * Commit to Pidgin * Tickets: Fixes #1861 Fixes #1902 References #5112 2008.08.02 - ccpaging <ecc_hy(at)hotmail.com> * Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH] * Use random value in inikey * TEA header padding in crypt.c * Rewrite login part of qq_process 2008.07.31 - ccpaging <ecc_hy(at)hotmail.com> * Fixed: send reply when get duplicate server command. The server may not get our reply before. * Tag custom picture as text "(Broken)" 2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> * Change some debug message * Modify buddy status flag according to eva for QQ2006 * Modify buddy status parse and correspond to eva2 * Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str * Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation * Source file changed: Merge buddy_status into buddy_list Change login_logout to qq_base Merge keep_alive into qq_base New qq_process extract from qq_network * Fixed: Byte alignment bug in crypt.c, tested in ARM PDA * Fixed: group chat message may get in before getting group info, and so group info is empty * Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c * Add some new group command identify according eva but further program * Add some new QQ client version identify * Fixed: Identify buddy's client version by IM packet, and not by status * Add some new info in buddy's tooltip text * Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly * Use new timeout function to handle send keep_alive, resend packet, update buddy status * Add new advanced options: The end user may change interval of keep_alive, resend packet, update buddy status to feed their need. For example, saving network flow when use mobile phone. Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not. The intervals of keep alive and update status should be multiple of resend's interval, Since we use counter not time() in a single timeout function for efficiency. * Rewrite qq_trans.c, and use one g_list to manage: Store server packet before login, and prase all of them when get login Store client send packet for resend scanning, confirm server reply, filter duplicate server reply Store server packet for filter out duplicate * Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c * Rewrite qq_proc_cmd_reply and qq_proc_cmd_server: In QQ protocol, one packet reply may need a new packet send later. We may call it packet trigger. The triggers always is hided in every qq_process_reply. Now we try to extract those triggers and put into a single function, and then every trigger should be obviously and easy to manage.
author SHiNE CsyFeK <csyfek@gmail.com>
date Sat, 02 Aug 2008 15:00:46 +0000
parents 21e591b55339
children dd30b4323639
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_info.h libpurple/protocols/qq/buddy_list.c libpurple/protocols/qq/buddy_list.h libpurple/protocols/qq/buddy_opt.c libpurple/protocols/qq/buddy_status.c libpurple/protocols/qq/buddy_status.h libpurple/protocols/qq/char_conv.c libpurple/protocols/qq/crypt.c libpurple/protocols/qq/crypt.h libpurple/protocols/qq/file_trans.c libpurple/protocols/qq/group_conv.c libpurple/protocols/qq/group_find.c libpurple/protocols/qq/group_free.c libpurple/protocols/qq/group_im.c libpurple/protocols/qq/group_info.c libpurple/protocols/qq/group_info.h libpurple/protocols/qq/group_network.c libpurple/protocols/qq/group_network.h libpurple/protocols/qq/header_info.c libpurple/protocols/qq/header_info.h libpurple/protocols/qq/im.c libpurple/protocols/qq/keep_alive.c libpurple/protocols/qq/keep_alive.h libpurple/protocols/qq/login_logout.c libpurple/protocols/qq/login_logout.h 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_trans.c libpurple/protocols/qq/qq_trans.h libpurple/protocols/qq/send_file.c libpurple/protocols/qq/send_file.h libpurple/protocols/qq/sys_msg.c libpurple/protocols/qq/utils.c libpurple/protocols/qq/utils.h
diffstat 42 files changed, 1396 insertions(+), 2078 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Sat Aug 02 15:00:46 2008 +0000
@@ -1,3 +1,57 @@
+2008.08.02 - csyfek <csyfek(at)gmail.com>
+	* Commit to Pidgin
+	* Tickets:
+		Fixes #1861
+		Fixes #1902
+		References #5112
+
+2008.08.02 - ccpaging <ecc_hy(at)hotmail.com>
+	* Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH]
+	* Use random value in inikey
+	* TEA header padding in crypt.c
+	* Rewrite login part of qq_process
+
+2008.07.31 - ccpaging <ecc_hy(at)hotmail.com>
+	* Fixed: send reply when get duplicate server command. The server may not get our reply before.
+	* Tag custom picture as text "(Broken)"
+
+2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+	* Change some debug message
+	* Modify buddy status flag according to eva for QQ2006
+	* Modify buddy status parse and correspond to eva2
+	* Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str
+	* Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation
+	* Source file changed:
+   		Merge buddy_status into buddy_list
+   		Change login_logout to qq_base
+   		Merge keep_alive into qq_base
+   		New qq_process extract from qq_network
+	* Fixed: Byte alignment bug in crypt.c, tested in ARM PDA
+	* Fixed: group chat message may get in before getting group info, and so group info is empty
+	* Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c
+	* Add some new group command identify according eva but further program
+	* Add some new QQ client version identify
+	* Fixed: Identify buddy's client version by IM packet, and not by status
+	* Add some new info in buddy's tooltip text
+	* Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly
+	* Use new timeout function to handle send keep_alive, resend packet, update buddy status
+	* Add new advanced options:
+		The end user may change interval of keep_alive, resend packet, update buddy status to feed their need.
+		For example, saving network flow when use mobile phone.
+		Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not.
+		The intervals of keep alive and update status should be multiple of resend's interval,
+		Since we use counter not time() in a single timeout function for efficiency.
+	* Rewrite qq_trans.c, and use one g_list to manage:
+		Store server packet before login, and prase all of them when get login
+		Store client send packet for resend scanning, confirm server reply, filter duplicate server reply
+		Store server packet for filter out duplicate
+	* Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c
+	* Rewrite qq_proc_cmd_reply and qq_proc_cmd_server:
+		In QQ protocol, one packet reply may need a new packet send later.
+		We may call it packet trigger. The triggers always is hided in every qq_process_reply.
+		Now we try to extract those triggers and put into a single function, 
+		and then every trigger should be obviously and easy to manage.
+	
 2008.07.12 - ccpaging <ecc_hy(at)hotmail.com>
 	* Fixed: Always lost connection. Now send keep alive packet in every 30 seconds
 	* Minor fix for debug information
--- a/libpurple/protocols/qq/Makefile.am	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Sat Aug 02 15:00:46 2008 +0000
@@ -10,8 +10,6 @@
 	buddy_list.h \
 	buddy_opt.c \
 	buddy_opt.h \
-	buddy_status.c \
-	buddy_status.h \
 	char_conv.c \
 	char_conv.h \
 	crypt.c \
@@ -44,10 +42,10 @@
 	header_info.h \
 	im.c \
 	im.h \
-	keep_alive.c \
-	keep_alive.h \
-	login_logout.c \
-	login_logout.h \
+	qq_process.c \
+	qq_process.h \
+	qq_base.c \
+	qq_base.h \
 	packet_parse.c \
 	packet_parse.h \
 	qq.c \
--- a/libpurple/protocols/qq/Makefile.mingw	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Sat Aug 02 15:00:46 2008 +0000
@@ -59,8 +59,8 @@
 	group_search.c \
 	header_info.c \
 	im.c \
-	keep_alive.c \
-	login_logout.c \
+	qq_process.c \
+	qq_base.c \
 	packet_parse.c \
 	qq.c \
 	qq_network.c \
--- a/libpurple/protocols/qq/buddy_info.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Sat Aug 02 15:00:46 2008 +0000
@@ -29,11 +29,12 @@
 
 #include "utils.h"
 #include "packet_parse.h"
+#include "buddy_list.h"
 #include "buddy_info.h"
 #include "char_conv.h"
 #include "crypt.h"
 #include "header_info.h"
-#include "keep_alive.h"
+#include "qq_base.h"
 #include "qq_network.h"
 
 #define QQ_PRIMARY_INFORMATION _("Primary Information")
@@ -85,6 +86,7 @@
 };
 
 #define QQ_CONTACT_FIELDS                               37
+#define QQ_FACES	    100
 
 /* There is no user id stored in the reply packet for information query
  * we have to manually store the query, so that we know the query source */
@@ -954,23 +956,23 @@
 	GList *node = qd->buddies;
 	gint bytes = 0;
 
-	if (qd->buddies) {
-		/* server only sends back levels for online buddies, no point
-		 * in asking for anyone else */
-		size = 4 * g_list_length(qd->buddies) + 1;
-		buf = g_new0(guint8, size);
-		bytes += 1;
-
-		while (NULL != node) {
-			q_bud = (qq_buddy *) node->data;
-			if (NULL != q_bud) {
-				bytes += qq_put32(buf + bytes, q_bud->uid);
-			}
-			node = node->next;
+	if ( qd->buddies == NULL) {
+		return;
+	}
+	/* server only sends back levels for online buddies, no point
+	 * in asking for anyone else */
+	size = 4 * g_list_length(qd->buddies) + 1;
+	buf = g_newa(guint8, size);
+	bytes += qq_put8(buf + bytes, 0x00);
+	
+	while (NULL != node) {
+		q_bud = (qq_buddy *) node->data;
+		if (NULL != q_bud) {
+			bytes += qq_put32(buf + bytes, q_bud->uid);
 		}
-		qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
-		g_free(buf);
+		node = node->next;
 	}
+	qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
 }
 
 void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
@@ -1009,8 +1011,8 @@
 		bytes += qq_get32(&onlineTime, decr_buf + bytes);
 		bytes += qq_get16(&level, decr_buf + bytes);
 		bytes += qq_get16(&timeRemainder, decr_buf + bytes);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", 
-				"Level uid: %d, onlineTime: %d, level: %d, timeRemainder: %d\n", 
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_LEVEL", 
+				"%d, tmOnline: %d, level: %d, tmRemainder: %d\n", 
 				uid, onlineTime, level, timeRemainder);
 		purple_name = uid_to_purple_name(uid);
 		b = purple_find_buddy(account, purple_name);
@@ -1033,3 +1035,4 @@
 	}
 	g_free(decr_buf);
 }
+
--- a/libpurple/protocols/qq/buddy_info.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Sat Aug 02 15:00:46 2008 +0000
@@ -31,11 +31,34 @@
 #include "buddy_opt.h"
 #include "qq.h"
 
-#define QQ_COMM_FLAG_QQ_MEMBER      0x02
-#define QQ_COMM_FLAG_TCP_MODE       0x10
-#define QQ_COMM_FLAG_MOBILE         0x20
-#define QQ_COMM_FLAG_BIND_MOBILE    0x40
-#define QQ_COMM_FLAG_VIDEO          0x80
+/* use is openq2005
+ * ext_flag: (0-7)
+ *        bit1 => qq space
+ * comm_flag: (0-7)
+ *        bit1 => member
+ *        bit4 => TCP mode
+ *        bit5 => open mobile QQ
+ *        bit6 => bind to mobile
+ *        bit7 => whether having a video
+#define QQ_COMM_FLAG_QQ_MEMBER		0x02
+#define QQ_COMM_FLAG_TCP_MODE    	0x10
+#define QQ_COMM_FLAG_MOBILE       	0x20
+#define QQ_COMM_FLAG_BIND_MOBILE	0x40
+#define QQ_COMM_FLAG_VIDEO          	0x80
+ */
+/* status in eva for qq2006
+#define QQ_FRIEND_FLAG_QQ_MEMBER  0x01
+#define QQ_FRIEND_FLAG_MOBILE           0x10
+#define QQ_FRIEND_FLAG_BIND_MOBILE  0x20
+*/
+#define QQ_COMM_FLAG_QQ_MEMBER		0x02
+#define QQ_COMM_FLAG_QQ_VIP			0x04
+#define QQ_COMM_FLAG_TCP_MODE    	0x10
+#define QQ_COMM_FLAG_MOBILE       	0x20
+#define QQ_COMM_FLAG_BIND_MOBILE	0x40
+#define QQ_COMM_FLAG_VIDEO          	0x80
+
+#define QQ_EXT_FLAG_SPACE				0x02
 
 #define QQ_BUDDY_GENDER_GG          0x00
 #define QQ_BUDDY_GENDER_MM          0x01
@@ -54,5 +77,4 @@
 void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid);
 void qq_send_packet_get_buddies_levels(PurpleConnection *gc);
 void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-
 #endif
--- a/libpurple/protocols/qq/buddy_list.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Sat Aug 02 15:00:46 2008 +0000
@@ -32,12 +32,11 @@
 #include "packet_parse.h"
 #include "buddy_info.h"
 #include "buddy_list.h"
-#include "buddy_status.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
 #include "crypt.h"
 #include "header_info.h"
-#include "keep_alive.h"
+#include "qq_base.h"
 #include "group.h"
 #include "group_find.h"
 #include "group_internal.h"
@@ -48,16 +47,14 @@
 #define QQ_GET_ONLINE_BUDDY_02          0x02
 #define QQ_GET_ONLINE_BUDDY_03          0x03	/* unknown function */
 
-#define QQ_ONLINE_BUDDY_ENTRY_LEN       38
-
-typedef struct _qq_friends_online_entry {
-	qq_buddy_status *s;
+typedef struct _qq_buddy_online {
+	qq_buddy_status bs;
 	guint16 unknown1;
-	guint8 flag1;
+	guint8 ext_flag;
 	guint8 comm_flag;
 	guint16 unknown2;
 	guint8 ending;		/* 0x00 */
-} qq_friends_online_entry;
+} qq_buddy_online;
 
 /* get a list of online_buddies */
 void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
@@ -81,7 +78,7 @@
 	/* 003-004 */
 	bytes += qq_put16(raw_data + bytes, 0x0000);
 
-	qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_ONLINE, raw_data, 5);
+	qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5);
 	qd->last_get_online = time(NULL);
 }
 
@@ -102,7 +99,7 @@
 	 * March 22, found the 00,00,00 starts to work as well */
 	bytes += qq_put8(raw_data + bytes, 0x00);
 
-	qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_LIST, raw_data, bytes);
+	qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes);
 }
 
 /* get all list, buddies & Quns with groupsid support */
@@ -123,27 +120,45 @@
 	qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes);
 }
 
-static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe)
+/* parse the data into qq_buddy_status */
+static gint get_buddy_status(qq_buddy_status *bs, guint8 *data)
 {
-	GString *dump;
+	gint bytes = 0;
 
-	g_return_if_fail(fe != NULL);
-
-	qq_buddy_status_dump_unclear(fe->s);
+	g_return_val_if_fail(data != NULL && bs != NULL, -1);
 
-	dump = g_string_new("");
-	g_string_append_printf(dump, "unclear fields for [%d]:\n", fe->s->uid);
-	g_string_append_printf(dump, "031-032: %04x (unknown)\n", fe->unknown1);
-	g_string_append_printf(dump, "033:     %02x   (flag1)\n", fe->flag1);
-	g_string_append_printf(dump, "034:     %02x   (comm_flag)\n", fe->comm_flag);
-	g_string_append_printf(dump, "035-036: %04x (unknown)\n", fe->unknown2);
+	/* 000-003: uid */
+	bytes += qq_get32(&bs->uid, data + bytes);
+	/* 004-004: 0x01 */
+	bytes += qq_get8(&bs->unknown1, data + bytes);
+	/* this is no longer the IP, it seems QQ (as of 2006) no longer sends
+	 * the buddy's IP in this packet. all 0s */
+	/* 005-008: ip */
+	bytes += qq_getIP(&bs->ip, data + bytes);
+	/* port info is no longer here either */
+	/* 009-010: port */
+	bytes += qq_get16(&bs->port, data + bytes);
+	/* 011-011: 0x00 */
+	bytes += qq_get8(&bs->unknown2, data + bytes);
+	/* 012-012: status */
+	bytes += qq_get8(&bs->status, data + bytes);
+	/* 013-014: client_version */
+	bytes += qq_get16(&bs->unknown3, data + bytes);
+	/* 015-030: unknown key */
+	bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Online buddy entry, %s", dump->str);
-	g_string_free(dump, TRUE);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_STATUS", 
+			"uid: %d, U1: %d, ip: %s:%d, U2:%d, status:%d, U3:%04X\n", 
+			bs->uid, bs->unknown1, inet_ntoa(bs->ip), bs->port,
+			bs->unknown2, bs->status, bs->unknown3);
+
+	return bytes;
 }
 
+#define QQ_ONLINE_BUDDY_ENTRY_LEN       38
+
 /* process the reply packet for get_buddies_online packet */
-void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint8 qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gint len, bytes, bytes_buddy;
@@ -151,9 +166,9 @@
 	guint8 *data, position;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
-	qq_friends_online_entry *fe;
+	qq_buddy_online bo;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
 
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
@@ -163,70 +178,68 @@
 
 	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
-		return;
+		return -1;
 	}
 
-	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);
 
-	fe = g_newa(qq_friends_online_entry, 1);
-	fe->s = g_newa(qq_buddy_status, 1);
-
 	count = 0;
 	while (bytes < len) {
+		if (len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) {
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+					"[buddies online] only %d, need %d", 
+					(len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
+			break;
+		}
+		memset(&bo, 0 ,sizeof(bo));
+		
 		/* set flag */
 		bytes_buddy = bytes;
 		/* based on one online buddy entry */
-		/* ATTTENTION! NEWED in the sub function, but FREED here */
 		/* 000-030 qq_buddy_status */
-		bytes += qq_buddy_status_read(fe->s, data + bytes);
-		/* 031-032: unknown4 */
-		bytes += qq_get16(&fe->unknown1, data + bytes);
-		/* 033-033: flag1 */
-		bytes += qq_get8(&fe->flag1, data + bytes);
+		bytes += get_buddy_status(&(bo.bs), data + bytes);
+		/* 031-032: */
+		bytes += qq_get16(&bo.unknown1, data + bytes);
+		/* 033-033: ext_flag */
+		bytes += qq_get8(&bo.ext_flag, data + bytes);
 		/* 034-034: comm_flag */
-		bytes += qq_get8(&fe->comm_flag, data + bytes);
+		bytes += qq_get8(&bo.comm_flag, data + bytes);
 		/* 035-036: */
-		bytes += qq_get16(&fe->unknown2, data + bytes);
+		bytes += qq_get16(&bo.unknown2, data + bytes);
 		/* 037-037: */
-		bytes += qq_get8(&fe->ending, data + bytes);	/* 0x00 */
+		bytes += qq_get8(&bo.ending, data + bytes);	/* 0x00 */
 
-		if (fe->s->uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
+		if (bo.bs.uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
 			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
 					"uid=0 or entry complete len(%d) != %d", 
 					(bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
-			g_free(fe->s->ip);
-			g_free(fe->s->unknown_key);
 			continue;
 		}	/* check if it is a valid entry */
 
-		if (QQ_DEBUG) {
-			_qq_buddies_online_reply_dump_unclear(fe);
-		}
-
 		/* update buddy information */
-		b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid));
+		b = purple_find_buddy(purple_connection_get_account(gc), 
+												uid_to_purple_name(bo.bs.uid) );
 		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-
-		if (q_bud != NULL) {	/* we find one and update qq_buddy */
-			if(0 != fe->s->client_version)
-				q_bud->client_version = fe->s->client_version;
-			g_memmove(q_bud->ip, fe->s->ip, 4);
-			q_bud->port = fe->s->port;
-			q_bud->status = fe->s->status;
-			q_bud->flag1 = fe->flag1;
-			q_bud->comm_flag = fe->comm_flag;
-			qq_update_buddy_contact(gc, q_bud);
-			count++;
-		} else {
+		if (q_bud == NULL) {
 			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
+					"Got an online buddy %d, but not in my buddy list\n", bo.bs.uid);
+			continue;
 		}
-
-		g_free(fe->s->ip);
-		g_free(fe->s->unknown_key);
+		/* we find one and update qq_buddy */
+		/*
+		if(0 != fe->s->client_version)
+			q_bud->client_version = fe->s->client_version;
+		*/
+		q_bud->ip.s_addr = bo.bs.ip.s_addr;
+		q_bud->port = bo.bs.port;
+		q_bud->status = bo.bs.status;
+		q_bud->ext_flag = bo.ext_flag;
+		q_bud->comm_flag = bo.comm_flag;
+		qq_update_buddy_contact(gc, q_bud);
+		count++;
 	}
 
 	if(bytes > len) {
@@ -236,20 +249,12 @@
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d online buddies, nextposition=%u\n",
 							count, (guint) position);
-	if (position != QQ_FRIENDS_ONLINE_POSITION_END
-		  && position != QQ_FRIENDS_ONLINE_POSITION_START) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more online buddies\n"); 
-		qq_send_packet_get_buddies_online(gc, position);
-	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "All online buddies received\n"); 
-		qq_send_packet_get_buddies_levels(gc);
-		qq_refresh_all_buddy_status(gc);
-	}
+	return position;
 }
 
 
 /* process reply for get_buddies_list */
-void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint16 qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	qq_buddy *q_bud;
@@ -260,7 +265,7 @@
 	gchar *name;
 	PurpleBuddy *b;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
 
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
@@ -268,7 +273,7 @@
 
 	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
-		return;
+		return -1;
 	}
 	bytes = 0;
 	bytes += qq_get16(&position, data + bytes);
@@ -291,16 +296,7 @@
 		bytes += pascal_len;
 
 		bytes += qq_get16(&unknown, data + bytes);
-		/* flag1: (0-7)
-		 *        bit1 => qq show
-		 * comm_flag: (0-7)
-		 *        bit1 => member
-		 *        bit4 => TCP mode
-		 *        bit5 => open mobile QQ
-		 *        bit6 => bind to mobile
-		 *        bit7 => whether having a video
-		 */
-		bytes += qq_get8(&q_bud->flag1, data + bytes);
+		bytes += qq_get8(&q_bud->ext_flag, data + bytes);
 		bytes += qq_get8(&q_bud->comm_flag, data + bytes);
 
 		bytes_expected = 12 + pascal_len;
@@ -317,8 +313,8 @@
 
 		if (QQ_DEBUG) {
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x, nick=%s\n",
-					q_bud->uid, q_bud->flag1, q_bud->comm_flag, q_bud->nickname);
+					"buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
+					q_bud->uid, q_bud->ext_flag, q_bud->comm_flag, q_bud->nickname);
 		}
 
 		name = uid_to_purple_name(q_bud->uid);
@@ -341,17 +337,10 @@
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies, nextposition=%u\n",
 		count, (guint) position);
-	if (position != QQ_FRIENDS_LIST_POSITION_START
-		&& position != QQ_FRIENDS_LIST_POSITION_END) { 
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies\n"); 
-		qq_send_packet_get_buddies_list(gc, position);
-	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies received. Requesting for online buddies list\n");
-		qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_LIST_POSITION_START); 
-	}
+	return position;
 }
 
-void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint32 qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gint len, i, j;
@@ -363,7 +352,7 @@
 	guint8 type, groupid;
 	qq_group *group;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
 
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
@@ -371,11 +360,11 @@
 
 	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
-		return;
+		return -1;
 	}
 
 	bytes += qq_get8(&sub_cmd, data + bytes);
-	g_return_if_fail(sub_cmd == 0x01);
+	g_return_val_if_fail(sub_cmd == 0x01, -1);
 
 	bytes += qq_get8(&reply_code, data + bytes);
 	if(0 != reply_code) {
@@ -429,14 +418,273 @@
 				"qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j);
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position);
+	return position;
+}
+
+#define QQ_MISC_STATUS_HAVING_VIIDEO      0x00000001
+#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 	0x30	/* ASCII value of "0" */
+
+/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses, 
+ * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy, 
+ * using different accounts to get info. */
+
+/* check if status means online or offline */
+gboolean is_online(guint8 status)
+{
+	switch(status) {
+		case QQ_BUDDY_ONLINE_NORMAL:
+		case QQ_BUDDY_ONLINE_AWAY:
+		case QQ_BUDDY_ONLINE_INVISIBLE:
+			return TRUE;
+		case QQ_BUDDY_ONLINE_OFFLINE:
+			return FALSE;
+	}
+	return FALSE;
+}
+
+/* Help calculate the correct icon index to tell the server. */
+gint get_icon_offset(PurpleConnection *gc)
+{ 
+	PurpleAccount *account;
+	PurplePresence *presence; 
+
+	account = purple_connection_get_account(gc);
+	presence = purple_account_get_presence(account);
+
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
+		return 2;
+	} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
+			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
+			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/* send a packet to change my online status */
+void qq_send_packet_change_status(PurpleConnection *gc)
+{
+	qq_data *qd;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
+	guint8 away_cmd;
+	guint32 misc_status;
+	gboolean fake_video;
+	PurpleAccount *account;
+	PurplePresence *presence; 
+
+	account = purple_connection_get_account(gc);
+	presence = purple_account_get_presence(account);
 
-	if (position != QQ_FRIENDS_ALL_LIST_POSITION_START
-		&& position != QQ_FRIENDS_ALL_LIST_POSITION_END) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies and groups\n");
-		qq_send_packet_get_all_list_with_group(gc, position);
+	qd = (qq_data *) gc->proto_data;
+	if (!qd->logged_in)
+		return;
+
+	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
+		away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
+	} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
+			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
+			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
+		away_cmd = QQ_BUDDY_ONLINE_AWAY;
 	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies and groups received\n"); 
+		away_cmd = QQ_BUDDY_ONLINE_NORMAL;
+	}
+
+	misc_status = 0x00000000;
+	fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video");
+	if (fake_video)
+		misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
+
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, away_cmd);
+	bytes += qq_put32(raw_data + bytes, misc_status);
+
+	qq_send_cmd(qd, QQ_CMD_CHANGE_ONLINE_STATUS, raw_data, bytes);
+}
+
+/* parse the reply packet for change_status */
+void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+{
+	qq_data *qd;
+	gint len, bytes;
+	guint8 *data, reply;
+	PurpleBuddy *b;
+	qq_buddy *q_bud;
+	gchar *name;
+
+	g_return_if_fail(buf != NULL && buf_len != 0);
+
+	qd = (qq_data *) gc->proto_data;
+	len = buf_len;
+	data = g_newa(guint8, len);
+
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
+		return;
+	}
+
+	bytes = 0;
+	bytes = qq_get8(&reply, data + bytes);
+	if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail 0x%02X\n", reply);
+		return;
+	}
+
+	/* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n"); */
+	name = uid_to_purple_name(qd->uid);
+	b = purple_find_buddy(gc->account, name);
+	g_free(name);
+	q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+	if (q_bud != NULL) {
+		qq_update_buddy_contact(gc, q_bud);
 	}
 }
+
+/* it is a server message indicating that one of my buddies has changed its status */
+void qq_process_buddy_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc) 
+{
+	qq_data *qd;
+	gint bytes;
+	guint32 my_uid;
+	guint8 *data;
+	gint data_len;
+	PurpleBuddy *b;
+	qq_buddy *q_bud;
+	qq_buddy_status bs;
+	gchar *name;
+
+	g_return_if_fail(buf != NULL && buf_len != 0);
+
+	qd = (qq_data *) gc->proto_data;
+	data_len = buf_len;
+	data = g_newa(guint8, data_len);
+
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] Failed decrypt\n");
+		return;
+	}
+
+	if (data_len < 35) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] only %d, need 35 bytes\n", data_len);
+		return;
+	}
+	
+	memset(&bs, 0, sizeof(bs));
+	bytes = 0;
+	/* 000-030: qq_buddy_status */
+	bytes += get_buddy_status(&bs, data + bytes);
+	/* 031-034:  Unknow, maybe my uid */ 
+	/* This has a value of 0 when we've changed our status to 
+	 * QQ_BUDDY_ONLINE_INVISIBLE */
+	bytes += qq_get32(&my_uid, data + bytes);
+
+	name = uid_to_purple_name(bs.uid);
+	b = purple_find_buddy(gc->account, name);
+	g_free(name);
+	q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+	if (q_bud == NULL) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"got information of unknown buddy %d\n", bs.uid);
+		return;
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "status:.uid = %d, q_bud->uid = %d\n", bs.uid , q_bud->uid);
+	if(bs.ip.s_addr != 0) { 
+		q_bud->ip.s_addr = bs.ip.s_addr;
+		q_bud->port = bs.port;
+	}
+	q_bud->status =bs.status;
+
+	if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) {
+		qq_send_packet_get_level(gc, q_bud->uid);
+	}
+	qq_update_buddy_contact(gc, q_bud);
+}
+
+/*TODO: maybe this should be qq_update_buddy_status() ?*/
+void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud)
+{
+	gchar *name;
+	PurpleBuddy *bud;
+	gchar *status_id;
+	
+	g_return_if_fail(q_bud != NULL);
+
+	name = uid_to_purple_name(q_bud->uid);
+	bud = purple_find_buddy(gc->account, name);
+
+	if (bud == NULL) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid);
+		g_free(name);
+		return;
+	}
+	
+	purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */
+	q_bud->last_refresh = time(NULL);
+
+	/* purple supports signon and idle time
+	 * but it is not much use for QQ, I do not use them */
+	/* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
+	status_id = "available";
+	switch(q_bud->status) {
+	case QQ_BUDDY_OFFLINE:
+		status_id = "offline";
+		break;
+	case QQ_BUDDY_ONLINE_NORMAL:
+		status_id = "available";
+		break;
+	case QQ_BUDDY_ONLINE_OFFLINE:
+		status_id = "offline";
+		break;
+	case QQ_BUDDY_ONLINE_AWAY:
+		status_id = "away";
+		break;
+	case QQ_BUDDY_ONLINE_INVISIBLE:
+		status_id = "invisible";
+		break;
+	default:
+		status_id = "invisible";
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown status: %x\n", q_bud->status);
+		break;
+	}
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "buddy %d %s\n", q_bud->uid, status_id);
+	purple_prpl_got_user_status(gc->account, name, status_id, NULL);
+
+	if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE)
+		purple_prpl_got_user_status(gc->account, name, "mobile", NULL);
+	else
+		purple_prpl_got_user_status_deactive(gc->account, name, "mobile");
+
+	if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO && q_bud->status != QQ_BUDDY_OFFLINE)
+		purple_prpl_got_user_status(gc->account, name, "video", NULL);
+	else
+		purple_prpl_got_user_status_deactive(gc->account, name, "video");
+
+	g_free(name);
+}
+
+/* refresh all buddies online/offline,
+ * after receiving reply for get_buddies_online packet */
+void qq_refresh_all_buddy_status(PurpleConnection *gc)
+{
+	time_t now;
+	GList *list;
+	qq_data *qd;
+	qq_buddy *q_bud;
+
+	qd = (qq_data *) (gc->proto_data);
+	now = time(NULL);
+	list = qd->buddies;
+
+	while (list != NULL) {
+		q_bud = (qq_buddy *) list->data;
+		if (q_bud != NULL && now > q_bud->last_refresh + QQ_UPDATE_ONLINE_INTERVAL
+				&& q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) {
+			q_bud->status = QQ_BUDDY_ONLINE_OFFLINE;
+			qq_update_buddy_contact(gc, q_bud);
+		}
+		list = list->next;
+	}
+}
--- a/libpurple/protocols/qq/buddy_list.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.h	Sat Aug 02 15:00:46 2008 +0000
@@ -28,18 +28,45 @@
 #include <glib.h>
 #include "connection.h"
 
-#define QQ_FRIENDS_LIST_POSITION_START 		0x0000
-#define QQ_FRIENDS_LIST_POSITION_END 		0xffff
-#define QQ_FRIENDS_ONLINE_POSITION_START 	0x00
-#define QQ_FRIENDS_ONLINE_POSITION_END 		0xff
-#define QQ_FRIENDS_ALL_LIST_POSITION_START  0x00000000
-#define QQ_FRIENDS_ALL_LIST_POSITION_END  0xffffffff
+#include "qq.h"
+typedef struct _qq_buddy_status {
+	guint32 uid;
+	guint8 unknown1;
+	struct in_addr ip;
+	guint16 port;
+	guint8 unknown2;
+	guint8 status;
+	guint16 unknown3;
+	guint8 unknown_key[QQ_KEY_LENGTH];
+} qq_buddy_status;
+
+enum {
+	QQ_BUDDY_OFFLINE = 0x00,
+	QQ_BUDDY_ONLINE_NORMAL = 0x0a,
+	QQ_BUDDY_ONLINE_OFFLINE = 0x14,
+	QQ_BUDDY_ONLINE_AWAY = 0x1e,
+	QQ_BUDDY_ONLINE_INVISIBLE = 0x28
+};
 
 void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position);
-void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+guint8 qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+
 void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position);
-void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+guint16 qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+
 void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position);
-void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+guint32 qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+
+void qq_refresh_all_buddy_status(PurpleConnection *gc);
+
+gboolean is_online(guint8 status);
+
+gint get_icon_offset(PurpleConnection *gc);
 
+void qq_send_packet_change_status(PurpleConnection *gc);
+void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+void qq_process_buddy_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc);
+
+void qq_refresh_all_buddy_status(PurpleConnection *gc);
+void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud);
 #endif
--- a/libpurple/protocols/qq/buddy_opt.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Sat Aug 02 15:00:46 2008 +0000
@@ -34,7 +34,7 @@
 #include "crypt.h"
 #include "header_info.h"
 #include "im.h"
-#include "keep_alive.h"
+#include "qq_base.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "utils.h"
@@ -67,7 +67,7 @@
 	g_return_if_fail(uid > 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(qd, QQ_CMD_DEL_FRIEND, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_DEL_BUDDY, (guint8 *) uid_str, strlen(uid_str));
 }
 
 /* try to remove myself from someone's buddy list */
@@ -95,7 +95,7 @@
 
 	/* we need to send the ascii code of this uid to qq server */
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(qd, QQ_CMD_ADD_FRIEND_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_ADD_BUDDY_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
 
 	/* must be set after sending packet to get the correct send_seq */
 	req = g_new0(qq_add_buddy_request, 1);
@@ -481,7 +481,7 @@
 		b->proto_data = q_bud;
 		qd->buddies = g_list_append(qd->buddies, q_bud);
 		qq_send_packet_get_info(gc, q_bud->uid, FALSE);
-		qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
+		qq_send_packet_get_buddies_online(gc, 0);
 	}
 
 	purple_blist_add_buddy(b, NULL, g, NULL);
--- a/libpurple/protocols/qq/buddy_status.c	Sat Aug 02 02:40:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,280 +0,0 @@
-/**
- * @file buddy_status.c
- *
- * purple
- *
- * Purple is the legal property ofr 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 <string.h>
-#include "internal.h"
-#include "debug.h"
-#include "prefs.h"
-
-#include "buddy_info.h"
-#include "buddy_status.h"
-#include "crypt.h"
-#include "header_info.h"
-#include "keep_alive.h"
-#include "packet_parse.h"
-#include "utils.h"
-
-#include "qq_network.h"
-
-#define QQ_MISC_STATUS_HAVING_VIIDEO      0x00000001
-#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 	0x30	/* ASCII value of "0" */
-
-void qq_buddy_status_dump_unclear(qq_buddy_status *s)
-{
-	GString *dump;
-
-	g_return_if_fail(s != NULL);
-
-	dump = g_string_new("");
-	g_string_append_printf(dump, "unclear fields for [%d]:\n", s->uid);
-	g_string_append_printf(dump, "004:     %02x   (unknown)\n", s->unknown1);
-	/* g_string_append_printf(dump, "005-008:     %09x   (ip)\n", *(s->ip)); */
-	g_string_append_printf(dump, "009-010:     %04x   (port)\n", s->port);
-	g_string_append_printf(dump, "011:     %02x   (unknown)\n", s->unknown2);
-	g_string_append_printf(dump, "012:     %02x   (status)\n", s->status);
-	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);
-	g_string_free(dump, TRUE);
-}
-
-/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses, 
- * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy, 
- * using different accounts to get info. */
-
-/* parse the data into qq_buddy_status */
-gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data)
-{
-	gint bytes = 0;
-
-	g_return_val_if_fail(data != NULL && s != NULL, -1);
-
-	/* 000-003: uid */
-	bytes += qq_get32(&s->uid, data + bytes);
-	/* 004-004: 0x01 */
-	bytes += qq_get8(&s->unknown1, data + bytes);
-	/* this is no longer the IP, it seems QQ (as of 2006) no longer sends
-	 * the buddy's IP in this packet. all 0s */
-	/* 005-008: ip */
-	s->ip = g_new0(guint8, 4);
-	bytes += qq_getdata(s->ip, 4, data + bytes);
-	/* port info is no longer here either */
-	/* 009-010: port */
-	bytes += qq_get16(&s->port, data + bytes);
-	/* 011-011: 0x00 */
-	bytes += qq_get8(&s->unknown2, data + bytes);
-	/* 012-012: status */
-	bytes += qq_get8(&s->status, data + bytes);
-	/* 013-014: client_version */
-	bytes += qq_get16(&s->client_version, data + bytes);
-	/* 015-030: unknown key */
-	s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH);
-	bytes += qq_getdata(s->unknown_key, QQ_KEY_LENGTH, data + bytes);
-
-	if (s->uid == 0 || bytes != 31)
-		return -1;
-
-	return bytes;
-}
-
-/* check if status means online or offline */
-gboolean is_online(guint8 status)
-{
-	switch(status) {
-		case QQ_BUDDY_ONLINE_NORMAL:
-		case QQ_BUDDY_ONLINE_AWAY:
-		case QQ_BUDDY_ONLINE_INVISIBLE:
-			return TRUE;
-		case QQ_BUDDY_ONLINE_OFFLINE:
-			return FALSE;
-	}
-	return FALSE;
-}
-
-/* Help calculate the correct icon index to tell the server. */
-gint get_icon_offset(PurpleConnection *gc)
-{ 
-	PurpleAccount *account;
-	PurplePresence *presence; 
-
-	account = purple_connection_get_account(gc);
-	presence = purple_account_get_presence(account);
-
-	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
-		return 2;
-	} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
-			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
-			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
-		return 1;
-	} else {
-		return 0;
-	}
-}
-
-/* send a packet to change my online status */
-void qq_send_packet_change_status(PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 raw_data[16] = {0};
-	gint bytes = 0;
-	guint8 away_cmd;
-	guint32 misc_status;
-	gboolean fake_video;
-	PurpleAccount *account;
-	PurplePresence *presence; 
-
-	account = purple_connection_get_account(gc);
-	presence = purple_account_get_presence(account);
-
-	qd = (qq_data *) gc->proto_data;
-	if (!qd->logged_in)
-		return;
-
-	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
-		away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
-	} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
-			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
-			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
-		away_cmd = QQ_BUDDY_ONLINE_AWAY;
-	} else {
-		away_cmd = QQ_BUDDY_ONLINE_NORMAL;
-	}
-
-	misc_status = 0x00000000;
-	fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video");
-	if (fake_video)
-		misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
-
-	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, away_cmd);
-	bytes += qq_put32(raw_data + bytes, misc_status);
-
-	qq_send_cmd(qd, QQ_CMD_CHANGE_ONLINE_STATUS, raw_data, bytes);
-}
-
-/* parse the reply packet for change_status */
-void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
-	qq_data *qd;
-	gint len, bytes;
-	guint8 *data, reply;
-	PurpleBuddy *b;
-	qq_buddy *q_bud;
-	gchar *name;
-
-	g_return_if_fail(buf != NULL && buf_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
-		return;
-	}
-
-	bytes = 0;
-	bytes = qq_get8(&reply, data + bytes);
-	if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n");
-	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n");
-		name = uid_to_purple_name(qd->uid);
-		b = purple_find_buddy(gc->account, name);
-		g_free(name);
-		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-		if (q_bud != NULL) {
-			qq_update_buddy_contact(gc, q_bud);
-		}
-	}
-}
-
-/* it is a server message indicating that one of my buddies has changed its status */
-void qq_process_friend_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc) 
-{
-	qq_data *qd;
-	gint len, bytes;
-	guint32 my_uid;
-	guint8 *data;
-	PurpleBuddy *b;
-	qq_buddy *q_bud;
-	qq_buddy_status *s;
-	gchar *name;
-
-	g_return_if_fail(buf != NULL && buf_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
-		return;
-	}
-
-	s = g_new0(qq_buddy_status, 1);
-	bytes = 0;
-	/* 000-030: qq_buddy_status */
-	bytes += qq_buddy_status_read(s, data + bytes);
-	/* 031-034: my uid */ 
-	/* This has a value of 0 when we've changed our status to 
-	 * QQ_BUDDY_ONLINE_INVISIBLE */
-	bytes += qq_get32(&my_uid, data + bytes);
-
-	if (bytes != 35) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes);
-		g_free(s->ip);
-		g_free(s->unknown_key);
-		g_free(s);
-		return;
-	}
-
-	name = uid_to_purple_name(s->uid);
-	b = purple_find_buddy(gc->account, name);
-	g_free(name);
-	q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-	if (q_bud) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid);
-		if(0 != *((guint32 *)s->ip)) { 
-			g_memmove(q_bud->ip, s->ip, 4);
-			q_bud->port = s->port;
-		}
-		q_bud->status = s->status;
-		if(0 != s->client_version) {
-			q_bud->client_version = s->client_version; 
-		}
-		if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) {
-			qq_send_packet_get_level(gc, q_bud->uid);
-		}
-		qq_update_buddy_contact(gc, q_bud);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-				"got information of unknown buddy %d\n", s->uid);
-	}
-
-	g_free(s->ip);
-	g_free(s->unknown_key);
-	g_free(s);
-}
--- a/libpurple/protocols/qq/buddy_status.h	Sat Aug 02 02:40:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/**
- * @file buddy_status.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_BUDDY_STATUS_H_
-#define _QQ_BUDDY_STATUS_H_
-
-#include <glib.h>
-#include "connection.h"
-#include "qq.h"
-
-typedef struct _qq_buddy_status {
-	guint32 uid;
-	guint8 unknown1;
-	guint8 *ip;
-	guint16 port;
-	guint8 unknown2;
-	guint8 status;
-	guint16 client_version;
-	guint8 *unknown_key;
-} qq_buddy_status;
-
-enum {
-	QQ_BUDDY_OFFLINE = 0x00,
-	QQ_BUDDY_ONLINE_NORMAL = 0x0a,
-	QQ_BUDDY_ONLINE_OFFLINE = 0x14,
-	QQ_BUDDY_ONLINE_AWAY = 0x1e,
-	QQ_BUDDY_ONLINE_INVISIBLE = 0x28
-};
-
-void qq_buddy_status_dump_unclear(qq_buddy_status *s);
-gboolean is_online(guint8 status);
-
-gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data);
-gint get_icon_offset(PurpleConnection *gc);
-
-void qq_send_packet_change_status(PurpleConnection *gc);
-
-void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_friend_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc);
-#endif
--- a/libpurple/protocols/qq/char_conv.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Sat Aug 02 15:00:46 2008 +0000
@@ -37,7 +37,7 @@
 #define QQ_CHARSET_ENG        "ISO-8859-1"
 
 #define QQ_NULL_MSG           "(NULL)"	/* return this if conversion fails */
-#define QQ_NULL_SMILEY        "(SM)"	/* return this if smiley conversion fails */
+#define QQ_NULL_SMILEY        "(Broken)"	/* return this if smiley conversion fails */
 
 const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
 	0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
@@ -113,9 +113,9 @@
 	}
 	
 	/* conversion error */
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message);
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ_CONVERT", "%s\n", error->message);
 
-	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ_CONVERT",
 		(guint8 *) str, (len == -1) ? strlen(str) : len,
 		"Dump failed text");
 
@@ -151,7 +151,7 @@
 	gint bytes = 0;
 
 	/* checked qq_show_packet OK */
-	qq_show_packet("QQ_MESG recv for font style", data, len);
+	/* 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 */
@@ -231,7 +231,7 @@
 	GString *converted;
 
 	converted = g_string_new("");
-	segments = split_data((guint8 *) text, strlen(text), "\x14", 0);
+	segments = split_data((guint8 *) text, strlen(text), "\x14\x15", 0);
 	g_string_append(converted, segments[0]);
 
 	while ((*(++segments)) != NULL) {
@@ -286,7 +286,7 @@
 	}
 
 	for (temp = str; *temp != 0; temp++) {
-		if (*temp == '\r' || *temp == '\n')  *temp = 0x20;
+		if (*temp == '\r' || *temp == '\n')  *temp = ' ';
 	}
 	g_strstrip(str);
 }
--- a/libpurple/protocols/qq/crypt.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/crypt.c	Sat Aug 02 15:00:46 2008 +0000
@@ -19,7 +19,7 @@
  *
  * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  *
  * QQ encryption algorithm
@@ -28,7 +28,7 @@
  * Puzzlebird, Nov-Dec 2002
  */
 
-/*Notes: (QQ uses 16 rounds, and modified something...)
+/* Notes: (QQ uses 16 rounds, and modified something...)
 
 IN : 64  bits of data in v[0] - v[1].
 OUT: 64  bits of data in w[0] - w[1].
@@ -45,6 +45,13 @@
 #include "crypt.h"
 #include "debug.h"
 
+/* 1, fixed alignment problem, when compiled on different platform
+ * 2, whether we need core debug
+ * 20070717, s3e */
+#if 0 
+#define CORE_DEBUG
+#endif
+
 /********************************************************************
  * encryption 
  *******************************************************************/
@@ -52,7 +59,8 @@
 /* Tiny Encryption Algorithm (TEA) */
 static void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w)
 {
-	register guint32 y = g_ntohl(v[0]), 
+	register guint32
+		y = g_ntohl(v[0]), 
 		 z = g_ntohl(v[1]), 
 		 a = g_ntohl(k[0]), 
 		 b = g_ntohl(k[1]), 
@@ -72,24 +80,86 @@
 	w[1] = g_htonl(z);
 }
 
-static gint rand(void) {	/* it can be the real random seed function */
-	return 0xdead;
-}			/* override with number, convenient for debug */
+/* it can be the real random seed function */
+/* override with number, convenient for debug */
+#ifdef DEBUG
+static gint rand(void) {	
+	return 0xdead; 
+}
+#else
+#include <stdlib.h>
+#endif
 
 /* 64-bit blocks and some kind of feedback mode of operation */
-static void encrypt_block(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted, 
+static inline void encrypt_block(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted, 
 		guint8 **crypted_pre_8, const guint8 *const key, gint *count, 
 		gint *pos_in_block, gint *is_header) 
 {
+	/* loop it */
+	int j;
+	/* ships in encipher */
+	guint32 ptr_p[2];	/* 64 bits, guint32[2] */
+	guint32 ptr_k[4];	/* 128 bits, guint32[4] */
+	guint32 ptr_c[2];	/* 64 bits, guint32[2] */
+
 	/* prepare input text */
-	if (!*is_header)
-		*(guint64 *) plain ^= **(guint64 **) crypted_pre_8;
+#ifdef CORE_DEBUG
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ_CORE_DEBUG",
+		"!we are in encrypt_block! *pos_in_block comes: %d, *is_header comes: %d\n",
+		*pos_in_block, *is_header);
+#endif
+	for(j = 0; j < 8; j++) {
+#ifdef CORE_DEBUG
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
+			"plain[%d]: 0x%02x, plain_pre_8[%d]: 0x%02x\n",
+			j, plain[j], j, plain_pre_8[j]);
+#endif
+		if (!*is_header) {
+#ifdef CORE_DEBUG
+			purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
+				"(*crypted_pre_8 + %d): 0x%02x\n",
+				j, *(*crypted_pre_8 + j));
+#endif
+			plain[j] ^= (*(*crypted_pre_8 + j));
+#ifdef CORE_DEBUG
+			purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
+				"NOW plain[%d]: 0x%02x\n",
+				j, plain[j]);
+#endif
+		} else {
+			plain[j] ^= plain_pre_8[j];
+#ifdef CORE_DEBUG
+			purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
+				"NOW plain[%d]: 0x%02x\n",
+				j, plain[j]);
+#endif
+		}
+	}
+
+	g_memmove(ptr_p, plain, 8);
+	g_memmove(ptr_k, key, 16);
+	g_memmove(ptr_c, *crypted, 8);
 
 	/* encrypt it */
-	qq_encipher((guint32 *) plain, (guint32 *) key, (guint32 *) *crypted);
+	qq_encipher(ptr_p, ptr_k, ptr_c);
+	
+	g_memmove(plain, ptr_p, 8);
+	g_memmove(*crypted, ptr_c, 8);
 
-	**(guint64 **) crypted ^= *(guint64 *) plain_pre_8;
-
+	for(j = 0; j < 8; j++) {
+#ifdef CORE_DEBUG
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
+			"j: %d, *(*crypted + %d): 0x%02x, plain_pre_8[%d]: 0x%02x\n",
+			j, j, *(*crypted + j), j, plain_pre_8[j]);
+#endif
+		(*(*crypted + j)) ^= plain_pre_8[j];
+#ifdef CORE_DEBUG
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
+			"NOW *(*crypted + [%d]): 0x%02x\n",
+			j, *(*crypted + j));
+#endif
+	}
+	
 	memcpy(plain_pre_8, plain, 8);	/* prepare next */
 
 	*crypted_pre_8 = *crypted;	/* store position of previous 8 byte */
@@ -171,7 +241,8 @@
 
 static void qq_decipher(guint32 *const v, const guint32 *const k, guint32 *const w)
 {
-	register guint32 y = g_ntohl(v[0]), 
+	register guint32
+		y = g_ntohl(v[0]), 
 		z = g_ntohl(v[1]), 
 		a = g_ntohl(k[0]), 
 		b = g_ntohl(k[1]), 
@@ -196,12 +267,25 @@
 		const guint8 *const key, gint *context_start, 
 		guint8 *decrypted, gint *pos_in_block)
 {
+	/* loop */
+	int i;
+	/* ships in decipher */
+	guint32 ptr_v[2];
+	guint32 ptr_k[4];
+
 	if (*context_start == instrlen)
 		return 1;
 
-	*(guint64 *) decrypted ^= **(guint64 **) crypt_buff;
+	for(i = 0; i < 8; i++) {
+		decrypted[i] ^= (*(*crypt_buff + i));
+	}
+	
+	g_memmove(ptr_v, decrypted, 8);
+	g_memmove(ptr_k, key, 16);
 
-	qq_decipher((guint32 *) decrypted, (guint32 *) key, (guint32 *) decrypted);
+	qq_decipher(ptr_v, ptr_k, ptr_v);
+
+	g_memmove(decrypted, ptr_v, 8);
 
 	*context_start += 8;
 	*crypt_buff += 8;
@@ -218,6 +302,10 @@
 	guint8 decrypted[8], m[8], *outp;
 	const guint8 *crypt_buff, *crypt_buff_pre_8;
 	gint count, context_start, pos_in_block, padding;
+	/* ships */
+	guint32 ptr_instr[2];
+	guint32 ptr_key[4];
+	guint32 ptr_decr[2];
 
 	/* at least 16 bytes and %8 == 0 */
 	if ((instrlen % 8) || (instrlen < 16)) { 
@@ -226,8 +314,14 @@
 			instrlen);
 		return 0;
 	}
-	/* get information from header */
-	qq_decipher((guint32 *) instr, (guint32 *) key, (guint32 *) decrypted);
+	g_memmove(ptr_instr, instr, 8);
+	g_memmove(ptr_key, key, 16);
+	g_memmove(ptr_decr, decrypted, 8);
+
+	qq_decipher(ptr_instr, ptr_key, ptr_decr);
+
+	g_memmove(decrypted, ptr_decr, 8);
+
 	pos_in_block = decrypted[0] & 0x7;
 	count = instrlen - pos_in_block - 10;	/* this is the plaintext length */
 	/* return if outstr buffer is not large enough or error plaintext length */
@@ -294,22 +388,6 @@
 			}
 		}
 	}
-	return 1;
-}
-
-/* return 1 is succeed, otherwise return 0
-gint qq_crypt(gint flag,
-		const guint8 *const instr, gint instrlen, 
-		const guint8 *const key, 
-		guint8 *outstr, gint *outstrlen_ptr)
-{
-	if (flag == DECRYPT)
-		return qq_decrypt(instr, instrlen, key, outstr, outstrlen_ptr);
-	else if (flag == ENCRYPT)
-		qq_encrypt(instr, instrlen, key, outstr, outstrlen_ptr);
-	else 
-		return 0;
 
 	return 1;
 }
-*/
--- a/libpurple/protocols/qq/crypt.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/crypt.h	Sat Aug 02 15:00:46 2008 +0000
@@ -1,4 +1,4 @@
-/**
+ /**
  * @file crypt.h
  *
  * purple
@@ -19,7 +19,7 @@
  *
  * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #ifndef _QQ_CRYPT_H_
@@ -27,6 +27,9 @@
 
 #include <glib.h>
 
+#define DECRYPT 0x00
+#define ENCRYPT 0x01
+
 void qq_encrypt(const guint8 *const instr, gint instrlen, 
 		const guint8 *const key, 
 		guint8 *outstr, gint *outstrlen_ptr);
@@ -34,14 +37,4 @@
 gint qq_decrypt(const guint8 *const instr, gint instrlen, 
 		const guint8 *const key,
 		guint8 *outstr, gint *outstrlen_ptr);
-		
-/*
-#define DECRYPT 0x00
-#define ENCRYPT 0x01
-
-gint qq_crypt(gint flag,
-	     const guint8 *const instr, gint instrlen, 
-	     const guint8 *const key, 
-	     guint8 *outstr, gint *outstrlen_ptr);
-*/
 #endif
--- a/libpurple/protocols/qq/file_trans.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/file_trans.c	Sat Aug 02 15:00:46 2008 +0000
@@ -76,26 +76,10 @@
 	return (~uid) ^ key;
 }
 
-static void _fill_filename_md5(const gchar *filename, guint8 *md5)
-{
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-
-	g_return_if_fail(filename != NULL && md5 != NULL);
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, (guint8 *) filename, strlen(filename));
-	purple_cipher_context_digest(context, 16, md5, NULL);
-	purple_cipher_context_destroy(context);
-}
-
 static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5)
 {
 	FILE *fp;
 	guint8 *buffer;
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
 	size_t wc;
 
 	const gint QQ_MAX_FILE_MD5_LENGTH = 10002432;
@@ -118,11 +102,7 @@
 		return;
 	}
 
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, buffer, filelen);
-	purple_cipher_context_digest(context, 16, md5, NULL);
-	purple_cipher_context_destroy(context);
+	qq_get_md5(md5, QQ_KEY_LENGTH, buffer, filelen);
 }
 
 static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf)
@@ -265,7 +245,7 @@
 	ft_info *info;
 
 	qd = (qq_data *) gc->proto_data;
-	g_return_val_if_fail(qd->session_key != NULL, -1);
+
 	info = (ft_info *) qd->xfer->data;
 
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE);
@@ -395,7 +375,7 @@
 	guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
 	gint bytes;
 	guint32 fragment_size = 1000;
-	gchar *filename;
+	const char *filename;
 	gint filename_len, filesize;
 	qq_data *qd;
 	ft_info *info;
@@ -403,7 +383,7 @@
 	qd = (qq_data *) gc->proto_data;
 	info = (ft_info *) qd->xfer->data;
 
-	filename = (gchar *) purple_xfer_get_filename(qd->xfer);
+	filename = purple_xfer_get_filename(qd->xfer);
 	filesize = purple_xfer_get_size(qd->xfer);
 
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE);
@@ -423,11 +403,11 @@
 			{
 				case QQ_FILE_BASIC_INFO:
 					filename_len = strlen(filename);
-					_fill_filename_md5(filename, filename_md5);
+					qq_get_md5(filename_md5, sizeof(filename_md5), (guint8 *)filename, filename_len);
 					_fill_file_md5(purple_xfer_get_local_filename(qd->xfer),
 							purple_xfer_get_size(qd->xfer),
 							file_md5);
-
+					
 					info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1;
 					info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN;
 
--- a/libpurple/protocols/qq/group_conv.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_conv.c	Sat Aug 02 15:00:46 2008 +0000
@@ -27,8 +27,8 @@
 
 #include "conversation.h"
 
-#include "buddy_status.h"
 #include "group_conv.h"
+#include "buddy_list.h"
 #include "utils.h"
 
 /* show group conversation window */
--- a/libpurple/protocols/qq/group_find.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_find.c	Sat Aug 02 15:00:46 2008 +0000
@@ -138,6 +138,9 @@
 	group = NULL;
 	while (list != NULL) {
 		group = (qq_group *) list->data;
+		if (group->group_name_utf8 == NULL) {
+			continue;
+		}
 		if (!g_ascii_strcasecmp(purple_conversation_get_name(conv), group->group_name_utf8))
 			break;
 		list = list->next;
--- a/libpurple/protocols/qq/group_free.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_free.c	Sat Aug 02 15:00:46 2008 +0000
@@ -26,7 +26,7 @@
 
 #include "debug.h"
 
-#include "buddy_status.h"
+#include "buddy_list.h"
 #include "group_free.h"
 #include "group_network.h"
 
--- a/libpurple/protocols/qq/group_im.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Sat Aug 02 15:00:46 2008 +0000
@@ -65,7 +65,7 @@
 	g_return_if_fail(group != NULL && msg != NULL);
 
 	msg_filtered = purple_markup_strip_html(msg);
-	purple_debug_info("QQ_MESG", "filterd qq qun mesg: %s\n", msg_filtered);
+	purple_debug_info("QQ_MESG", "Send qun mesg filterd: %s\n", msg_filtered);
 	msg_len = strlen(msg_filtered);
 
 	data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
@@ -311,9 +311,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
-		data, data_len,
-		"group im hex dump");
+	/* qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump"); */
 
 	im_group = g_newa(qq_recv_group_im, 1);
 
@@ -379,6 +377,9 @@
 
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc));
 	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/prompt_group_msg_on_recv")) {
+		/* New conv should open, get group info*/
+		qq_send_cmd_group_get_group_info(gc, group);
+		
 		serv_got_joined_chat(gc, qd->channel++, group->group_name_utf8);
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc));
 	}
--- a/libpurple/protocols/qq/group_info.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Sat Aug 02 15:00:46 2008 +0000
@@ -27,12 +27,11 @@
 #include "conversation.h"
 #include "debug.h"
 
-#include "buddy_status.h"
 #include "char_conv.h"
 #include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
-#include "buddy_status.h"
+#include "buddy_list.h"
 #include "group_network.h"
 
 /* we check who needs to update member info every minutes
@@ -77,6 +76,27 @@
 }
 
 /* send packet to get online group member, called by keep_alive */
+void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc)
+{
+	qq_data *qd;
+	qq_group *group;
+	GList *list;
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	list = qd->groups;
+	while (list != NULL) {
+		group = (qq_group *) list->data;
+		if (group->my_status == QQ_GROUP_MEMBER_STATUS_IS_MEMBER ||
+		    group->my_status == QQ_GROUP_MEMBER_STATUS_IS_ADMIN)
+			/* no need to get info time and time again, online members enough */
+			qq_send_cmd_group_get_online_members(gc, group);
+
+		list = list->next;
+	}
+}
+
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group)
 {
 	guint8 raw_data[16] = {0};
@@ -87,7 +107,7 @@
 	/* only get online members when conversation window is on */
 	if (NULL == purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,group->group_name_utf8, purple_connection_get_account(gc))) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				"Conversation for \"%s\" is not open, ignore to get online members\n", group->group_name_utf8);
+				"Conversation \"%s\" is not open, ignore to get online members\n", group->group_name_utf8);
 		return;
 	}
 
@@ -113,7 +133,7 @@
 	}
 
 	if (num <= 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n");
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member info needs to be updated now.\n");
 		return;
 	}
 
@@ -209,9 +229,11 @@
 		bytes += qq_get8(&organization, data + bytes);
 		bytes += qq_get8(&role, data + bytes);
 
+		/*
 		if(organization != 0 || role != 0) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "group member %d: organization=%d, role=%d\n", member_uid, organization, role);
+			purple_debug(PURPLE_DEBUG_INFO, "QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role);
 		}
+		*/
 		member = qq_group_find_or_add_member(gc, group, member_uid);
 		if (member != NULL)
 			member->role = role;
@@ -231,7 +253,7 @@
 			group->group_name_utf8, purple_connection_get_account(gc));
 	if(NULL == purple_conv) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				"Conversation for \"%s\" is not open, do not set topic\n", group->group_name_utf8);
+				"Conversation \"%s\" is not open, do not set topic\n", group->group_name_utf8);
 		return;
 	}
 
@@ -322,7 +344,7 @@
 		bytes += qq_get8(&(member->gender), data + bytes);
 		bytes += convert_as_pascal_string(data + bytes, &nick, QQ_CHARSET_DEFAULT);
 		bytes += qq_get16(&unknown, data + bytes);
-		bytes += qq_get8(&(member->flag1), data + bytes);
+		bytes += qq_get8(&(member->ext_flag), data + bytes);
 		bytes += qq_get8(&(member->comm_flag), data + bytes);
 
 		/* filter \r\n in nick */
@@ -333,8 +355,8 @@
 		/*
 		if (QQ_DEBUG) {
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"member [%09d]: flag1=0x%02x, comm_flag=0x%02x, nick=%s\n",
-					member_uid, member->flag1, member->comm_flag, member->nickname);
+					"member [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
+					member_uid, member->ext_flag, member->comm_flag, member->nickname);
 		}
 		*/
 
--- a/libpurple/protocols/qq/group_info.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_info.h	Sat Aug 02 15:00:46 2008 +0000
@@ -31,7 +31,10 @@
 
 void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group);
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group);
+void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc);
+
 void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group);
+
 void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc);
--- a/libpurple/protocols/qq/group_network.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_network.c	Sat Aug 02 15:00:46 2008 +0000
@@ -75,6 +75,30 @@
 		return "QQ_GROUP_CMD_GET_ONLINE_MEMBER";
 	case QQ_GROUP_CMD_GET_MEMBER_INFO:
 		return "QQ_GROUP_CMD_GET_MEMBER_INFO";
+	case QQ_GROUP_CMD_MODIFY_CARD:
+		return "QQ_GROUP_CMD_MODIFY_CARD";
+	case QQ_GROUP_CMD_REQUEST_ALL_REALNAMES:
+		return "QQ_GROUP_CMD_REQUEST_ALL_REALNAMES";
+	case QQ_GROUP_CMD_REQUEST_CARD:
+		return "QQ_GROUP_CMD_REQUEST_CARD";
+	case QQ_GROUP_CMD_SEND_IM_EX:
+		return "QQ_GROUP_CMD_SEND_IM_EX";
+	case QQ_GROUP_CMD_ADMIN:
+		return "QQ_GROUP_CMD_ADMIN";
+	case QQ_GROUP_CMD_TRANSFER:
+		return "QQ_GROUP_CMD_TRANSFER";
+	case QQ_GROUP_CMD_CREATE_TEMP_QUN:
+		return "QQ_GROUP_CMD_CREATE_TEMP_QUN";
+	case QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER:
+		return "QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER";
+	case QQ_GROUP_CMD_EXIT_TEMP_QUN:
+		return "QQ_GROUP_CMD_EXIT_TEMP_QUN";
+	case QQ_GROUP_CMD_GET_TEMP_QUN_INFO:
+		return "QQ_GROUP_CMD_GET_TEMP_QUN_INFO";
+	case QQ_GROUP_CMD_SEND_TEMP_QUN_IM:
+		return "QQ_GROUP_CMD_SEND_TEMP_QUN_IM";
+	case QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS:
+		return "QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS";
 	default:
 		return "Unknown QQ Group Command";
 	}
--- a/libpurple/protocols/qq/group_network.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/group_network.h	Sat Aug 02 15:00:46 2008 +0000
@@ -42,7 +42,20 @@
 	QQ_GROUP_CMD_EXIT_GROUP = 0x09,
 	QQ_GROUP_CMD_SEND_MSG = 0x0a,
 	QQ_GROUP_CMD_GET_ONLINE_MEMBER = 0x0b,
-	QQ_GROUP_CMD_GET_MEMBER_INFO = 0x0c
+	QQ_GROUP_CMD_GET_MEMBER_INFO = 0x0c,
+
+	QQ_GROUP_CMD_MODIFY_CARD = 0x0E,
+	QQ_GROUP_CMD_REQUEST_ALL_REALNAMES = 0x0F,
+	QQ_GROUP_CMD_REQUEST_CARD = 0x10,
+	QQ_GROUP_CMD_SEND_IM_EX = 0x1A,
+	QQ_GROUP_CMD_ADMIN = 0x1B,
+	QQ_GROUP_CMD_TRANSFER = 0x1C,
+	QQ_GROUP_CMD_CREATE_TEMP_QUN = 0x30,
+	QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER = 0x31,
+	QQ_GROUP_CMD_EXIT_TEMP_QUN = 0x32,
+	QQ_GROUP_CMD_GET_TEMP_QUN_INFO = 0x33,
+	QQ_GROUP_CMD_SEND_TEMP_QUN_IM = 0x35,
+	QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS = 0x37,
 } qq_group_cmd;
 
 typedef struct _group_packet {
--- a/libpurple/protocols/qq/header_info.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/header_info.c	Sat Aug 02 15:00:46 2008 +0000
@@ -34,11 +34,31 @@
 #define QQ_CLIENT_0B2F 0x0b2f	/* GB QQ2003iii build 0117 */
 #define QQ_CLIENT_0B35 0x0b35	/* GB QQ2003iii build 0304 (offical release) */
 #define QQ_CLIENT_0B37 0x0b37	/* GB QQ2003iii build 0304 (April 05 updates) */
-#define QQ_CLIENT_0E1B 0x0e1b	/* QQ2005? QQ2006? */
+#define QQ_CLIENT_0E1B 0x0e1b	/* QQ2005 ? */
 #define QQ_CLIENT_0E35 0x0e35	/* EN QQ2005 V05.0.200.020 */
 #define QQ_CLIENT_0F15 0x0f15	/* QQ2006 Spring Festival build */
 #define QQ_CLIENT_0F5F 0x0f5f	/* QQ2006 final build */
 
+#define QQ_CLIENT_0C0B 0x0C0B	/* QQ2004 */
+#define QQ_CLIENT_0C0D 0x0C0D	/* QQ2004 preview*/
+#define QQ_CLIENT_0C21 0x0C21	/* QQ2004 */
+#define QQ_CLIENT_0C49 0x0C49	/* QQ2004II */
+#define QQ_CLIENT_0D05 0x0D05	/* QQ2005 beta1 */
+#define QQ_CLIENT_0D51 0x0D51	/* QQ2005 beta2 */
+#define QQ_CLIENT_0D61 0x0D61	/* QQ2005 */
+#define QQ_CLIENT_05A5 0x05A5	/* ? */
+#define QQ_CLIENT_05F1 0x0F15	/* QQ2006 Spring Festival */
+#define QQ_CLIENT_0F4B 0x0F4B	/* QQ2006 Beta 3  */
+
+#define QQ_CLIENT_1105 0x1105	/* QQ2007 beta4*/
+#define QQ_CLIENT_111D 0x111D	/* QQ2007 */
+#define QQ_CLIENT_115B 0x115B	/* QQ2008 */
+#define QQ_CLIENT_1203 0x1203	/* QQ2008 */
+#define QQ_CLIENT_1205 0x1205	/* QQ2008 */
+#define QQ_CLIENT_120B 0x120B	/* QQ2008 July 8.0.978.400 */
+#define QQ_CLIENT_1412 0x1412	/* QQMac 1.0 preview1 build 670 */
+#define QQ_CLIENT_1441 0x1441	/* QQ2009 preview2 */
+
 #define QQ_SERVER_0100 0x0100	/* server */
 
 /* given command alias, return the command name accordingly */
@@ -55,10 +75,10 @@
 		return "QQ_CMD_SEARCH_USER";
 	case QQ_CMD_GET_USER_INFO:
 		return "QQ_CMD_GET_USER_INFO";
-	case QQ_CMD_ADD_FRIEND_WO_AUTH:
-		return "QQ_CMD_ADD_FRIEND_WO_AUTH";
-	case QQ_CMD_DEL_FRIEND:
-		return "QQ_CMD_DEL_FRIEND";
+	case QQ_CMD_ADD_BUDDY_WO_AUTH:
+		return "QQ_CMD_ADD_BUDDY_WO_AUTH";
+	case QQ_CMD_DEL_BUDDY:
+		return "QQ_CMD_DEL_BUDDY";
 	case QQ_CMD_BUDDY_AUTH:
 		return "QQ_CMD_BUDDY_AUTH";
 	case QQ_CMD_CHANGE_ONLINE_STATUS:
@@ -73,29 +93,29 @@
 		return "QQ_CMD_REMOVE_SELF";
 	case QQ_CMD_LOGIN:
 		return "QQ_CMD_LOGIN";
-	case QQ_CMD_GET_FRIENDS_LIST:
-		return "QQ_CMD_GET_FRIENDS_LIST";
-	case QQ_CMD_GET_FRIENDS_ONLINE:
-		return "QQ_CMD_GET_FRIENDS_ONLINE";
+	case QQ_CMD_GET_BUDDIES_LIST:
+		return "QQ_CMD_GET_BUDDIES_LIST";
+	case QQ_CMD_GET_BUDDIES_ONLINE:
+		return "QQ_CMD_GET_BUDDIES_ONLINE";
 	case QQ_CMD_GROUP_CMD:
 		return "QQ_CMD_GROUP_CMD";
 	case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
 		return "QQ_CMD_GET_ALL_LIST_WITH_GROUP";
 	case QQ_CMD_GET_LEVEL:
 		return "QQ_CMD_GET_LEVEL";
-	case QQ_CMD_REQUEST_LOGIN_TOKEN:
-		return "QQ_CMD_REQUEST_LOGIN_TOKEN";
+	case QQ_CMD_TOKEN:
+		return "QQ_CMD_TOKEN";
 	case QQ_CMD_RECV_MSG_SYS:
 		return "QQ_CMD_RECV_MSG_SYS";
-	case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
-		return "QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS";
+	case QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS:
+		return "QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS";
 	default:
-		return "UNKNOWN_TYPE";
+		return "Unknown";
 	}
 }
 
 /* given source tag, return its description accordingly */
-const gchar *qq_get_source_str(gint source)
+const gchar *qq_get_ver_desc(gint source)
 {
 	switch (source) {
 	case QQ_CLIENT_062E:
@@ -114,17 +134,46 @@
 		return "GB QQ2003iii build 0304";
 	case QQ_CLIENT_0B37:
 		return "GB QQ2003iii build 0304 (April 5 update)";
+	case QQ_CLIENT_0C0B:
+		return "QQ2004";
+	case QQ_CLIENT_0C0D:
+		return "QQ2004 preview";
+	case QQ_CLIENT_0C21:
+		return "QQ2004";
+	case QQ_CLIENT_0C49:
+		return "QQ2004II";
+	case QQ_CLIENT_0D05:
+		return "QQ2005 beta1";
+	case QQ_CLIENT_0D51:
+		return "QQ2005 beta2";
+	case QQ_CLIENT_0D61:
+		return "QQ2005";
 	case QQ_CLIENT_0E1B:
 		return "QQ2005 or QQ2006";
 	case QQ_CLIENT_0E35:
 		return "En QQ2005 V05.0.200.020";
 	case QQ_CLIENT_0F15:
-		return "QQ2006 Spring Festival build";
+		return "QQ2006 Spring Festival";
+	case QQ_CLIENT_0F4B:
+		return "QQ2006 beta3";
 	case QQ_CLIENT_0F5F:
 		return "QQ2006 final build";
+	case QQ_CLIENT_1105:
+		return "QQ2007 beta4";
+	case QQ_CLIENT_111D:
+		return "QQ2007";
+	case QQ_CLIENT_115B:
+	case QQ_CLIENT_1203:
+	case QQ_CLIENT_1205:
+	case QQ_CLIENT_120B:
+		return "QQ2008";
+	case QQ_CLIENT_1412:
+		return "QQMac 1.0 preview1 build 670";
+	case QQ_CLIENT_1441:
+		return "QQ2009 preview2";
 	case QQ_SERVER_0100:
 		return "QQ Server 0100";
 	default:
-		return "QQ unknown version";
+		return "Unknown";
 	}
 }
--- a/libpurple/protocols/qq/header_info.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/header_info.h	Sat Aug 02 15:00:46 2008 +0000
@@ -42,8 +42,8 @@
 	QQ_CMD_UPDATE_INFO = 0x0004,			/* update information */
 	QQ_CMD_SEARCH_USER = 0x0005,			/* search for user */
 	QQ_CMD_GET_USER_INFO = 0x0006,			/* get user information */
-	QQ_CMD_ADD_FRIEND_WO_AUTH = 0x0009,		/* add friend without auth */
-	QQ_CMD_DEL_FRIEND = 0x000a,			/* delete a friend  */
+	QQ_CMD_ADD_BUDDY_WO_AUTH = 0x0009,		/* add buddy without auth */
+	QQ_CMD_DEL_BUDDY = 0x000a,			/* delete a buddy  */
 	QQ_CMD_BUDDY_AUTH = 0x000b,			/* buddy authentication */
 	QQ_CMD_CHANGE_ONLINE_STATUS = 0x000d,		/* change my online status */
 	QQ_CMD_ACK_SYS_MSG = 0x0012,			/* ack system message */
@@ -53,19 +53,19 @@
 	QQ_CMD_REQUEST_KEY = 0x001d,			/* request key for file transfer */
 	QQ_CMD_CELL_PHONE_1 = 0x0021,			/* cell phone 1 */
 	QQ_CMD_LOGIN = 0x0022,				/* login */
-	QQ_CMD_GET_FRIENDS_LIST = 0x0026,		/* retrieve my freinds list */
-	QQ_CMD_GET_FRIENDS_ONLINE = 0x0027,		/* get my online friends list */
+	QQ_CMD_GET_BUDDIES_LIST = 0x0026,		/* get buddies list */
+	QQ_CMD_GET_BUDDIES_ONLINE = 0x0027,		/* get online buddies list */
 	QQ_CMD_CELL_PHONE_2 = 0x0029,			/* cell phone 2 */
 	QQ_CMD_GROUP_CMD = 0x0030,			/* group command */
 	QQ_CMD_GET_ALL_LIST_WITH_GROUP = 0x0058,  
 	QQ_CMD_GET_LEVEL = 0x005C,			/* get level for one or more buddies */
-	QQ_CMD_REQUEST_LOGIN_TOKEN  = 0x0062, 		/* get login token */
+	QQ_CMD_TOKEN  = 0x0062, 		/* get login token */
 	QQ_CMD_RECV_MSG_SYS = 0x0080,			/* receive a system message */
-	QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS = 0x0081,	/* friends change status */
+	QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS = 0x0081,	/* buddy change status */
 };
 
 const gchar *qq_get_cmd_desc(gint type);
 
-const gchar *qq_get_source_str(gint source);
+const gchar *qq_get_ver_desc(gint source);
 
 #endif
--- a/libpurple/protocols/qq/im.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Sat Aug 02 15:00:46 2008 +0000
@@ -79,7 +79,7 @@
 	guint16 sender_ver;
 	guint32 sender_uid;
 	guint32 receiver_uid;
-	guint8 *session_md5;
+	guint8 session_md5[QQ_KEY_LENGTH];
 	guint16 normal_im_type;
 };
 
@@ -109,7 +109,7 @@
 	guint32 sender_uid;
 	guint32 receiver_uid;
 	guint32 server_im_seq;
-	guint8 sender_ip[4];
+	struct in_addr sender_ip;
 	guint16 sender_port;
 	guint16 im_type;
 };
@@ -179,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;
 }
 
@@ -237,10 +237,7 @@
 	bytes += qq_get16(&(common->sender_ver), data + bytes);
 	bytes += qq_get32(&(common->sender_uid), data + bytes);
 	bytes += qq_get32(&(common->receiver_uid), data + bytes);
-
-	common->session_md5 = g_memdup(data + bytes, QQ_KEY_LENGTH);
-	bytes += QQ_KEY_LENGTH;
-
+	bytes += qq_getdata(common->session_md5, QQ_KEY_LENGTH, data + bytes);
 	bytes += qq_get16(&(common->normal_im_type), data + bytes);
 
 	if (bytes != 28) {	/* read common place fail */
@@ -261,6 +258,8 @@
 	qq_data *qd;
 	qq_recv_normal_im_text *im_text;
 	gint bytes = 0;
+	PurpleBuddy *b;
+	qq_buddy *qq_b;
 
 	g_return_if_fail(common != NULL);
 	qd = (qq_data *) gc->proto_data;
@@ -308,9 +307,16 @@
 	}			/* if im_text->msg_type */
 
 	name = uid_to_purple_name(common->sender_uid);
-	if (purple_find_buddy(gc->account, name) == NULL)
+	b = purple_find_buddy(gc->account, name);
+	if (b == NULL) {
 		qq_add_buddy_by_recv_packet(gc, common->sender_uid, FALSE, TRUE);
-
+		b = purple_find_buddy(gc->account, name);
+	}
+	qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+	if (qq_b != NULL) {
+		qq_b->client_version = common->sender_ver; 
+	}
+	
 	purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
 
 	msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg);
@@ -353,9 +359,9 @@
 	switch (common->normal_im_type) {
 		case QQ_NORMAL_IM_TEXT:
 			purple_debug (PURPLE_DEBUG_INFO, "QQ",
-					"Normal IM, text type:\n [%d] => [%d], src: %s\n",
+					"Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n",
 					common->sender_uid, common->receiver_uid,
-					qq_get_source_str (common->sender_ver));
+					qq_get_ver_desc (common->sender_ver), common->sender_ver);
 			if (bytes >= len - 1) {
 				purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
 				return;
@@ -387,11 +393,8 @@
 					"Normal IM, unprocessed type [0x%04x], len %d\n",
 					common->normal_im_type, im_unprocessed->length);
 			qq_show_packet ("QQ unk-im", im_unprocessed->unknown, im_unprocessed->length);
-			g_free (common->session_md5);
 			return;
 	}
-
-	g_free (common->session_md5);
 }
 
 /* process im from system administrator */
@@ -603,7 +606,7 @@
 	bytes += qq_get32(&(im_header->receiver_uid), data + bytes);
 	bytes += qq_get32(&(im_header->server_im_seq), data + bytes);
 	/* if the message is delivered via server, it is server IP/port */
-	bytes += qq_getdata((guint8 *) & (im_header->sender_ip), 4, data + bytes);
+	bytes += qq_getIP(&(im_header->sender_ip), data + bytes);
 	bytes += qq_get16(&(im_header->sender_port), data + bytes);
 	bytes += qq_get16(&(im_header->im_type), data + bytes);
 	/* im_header prepared */
--- a/libpurple/protocols/qq/keep_alive.c	Sat Aug 02 02:40:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-/**
- * @file keep_alive.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
- *
- *
- * OICQ encryption algorithm
- * Convert from ASM code provided by PerlOICQ
- * 
- * Puzzlebird, Nov-Dec 2002
- */
-
-#include "internal.h"
-
-#include "debug.h"
-#include "server.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_status.h"
-#include "crypt.h"
-#include "header_info.h"
-#include "keep_alive.h"
-#include "packet_parse.h"
-#include "qq_network.h"
-#include "utils.h"
-
-#define QQ_UPDATE_ONLINE_INTERVAL   300	/* in sec */
-
-/* send keep-alive packet to QQ server (it is a heart-beat) */
-void qq_send_packet_keep_alive(PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 raw_data[16] = {0};
-	gint bytes= 0;
-
-	qd = (qq_data *) gc->proto_data;
-
-	/* In fact, we can send whatever we like to server
-	 * with this command, server return the same result including
-	 * the amount of online QQ users, my ip and port */
-	bytes += qq_put32(raw_data + bytes, qd->uid);
-
-	qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4);
-}
-
-/* parse the return of keep-alive packet, it includes some system information */
-void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) 
-{
-	qq_data *qd;
-	gint len;
-	gchar **segments;
-	guint8 *data;
-
-	g_return_if_fail(buf != NULL && buf_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		/* the last one is 60, don't know what it is */
-		if (NULL == (segments = split_data(data, len, "\x1f", 6)))
-			return;
-		/* segments[0] and segment[1] are all 0x30 ("0") */
-		qd->all_online = strtol(segments[2], NULL, 10);
-		if(0 == qd->all_online)
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-					_("Keep alive error"));
-		g_free(qd->my_ip);
-		qd->my_ip = g_strdup(segments[3]);
-		qd->my_port = strtol(segments[4], NULL, 10);
-		g_strfreev(segments);
-	} else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt keep alive reply\n");
-
-	/* we refresh buddies's online status periodically */
-	/* qd->last_get_online is updated when setting get_buddies_online packet */
-	if ((time(NULL) - qd->last_get_online) >= QQ_UPDATE_ONLINE_INTERVAL)
-		qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
-}
-
-/* refresh all buddies online/offline,
- * after receiving reply for get_buddies_online packet */
-void qq_refresh_all_buddy_status(PurpleConnection *gc)
-{
-	time_t now;
-	GList *list;
-	qq_data *qd;
-	qq_buddy *q_bud;
-
-	qd = (qq_data *) (gc->proto_data);
-	now = time(NULL);
-	list = qd->buddies;
-
-	while (list != NULL) {
-		q_bud = (qq_buddy *) list->data;
-		if (q_bud != NULL && now > q_bud->last_refresh + QQ_UPDATE_ONLINE_INTERVAL
-				&& q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) {
-			q_bud->status = QQ_BUDDY_ONLINE_OFFLINE;
-			qq_update_buddy_contact(gc, q_bud);
-		}
-		list = list->next;
-	}
-}
-
-/*TODO: maybe this should be qq_update_buddy_status() ?*/
-void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud)
-{
-	gchar *name;
-	PurpleBuddy *bud;
-	gchar *status_id;
-	
-	g_return_if_fail(q_bud != NULL);
-
-	name = uid_to_purple_name(q_bud->uid);
-	bud = purple_find_buddy(gc->account, name);
-	g_return_if_fail(bud != NULL);
-
-	if (bud != NULL) {
-		purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */
-		q_bud->last_refresh = time(NULL);
-
-		/* purple supports signon and idle time
-		 * but it is not much use for QQ, I do not use them */
-		/* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
-		status_id = "available";
-		switch(q_bud->status) {
-		case QQ_BUDDY_OFFLINE:
-			status_id = "offline";
-			break;
-		case QQ_BUDDY_ONLINE_NORMAL:
-			status_id = "available";
-			break;
-		case QQ_BUDDY_ONLINE_OFFLINE:
-			status_id = "offline";
-			break;
-	        case QQ_BUDDY_ONLINE_AWAY:
-			status_id = "away";
-			break;
-	       	case QQ_BUDDY_ONLINE_INVISIBLE:
-			status_id = "invisible";
-			break;
-		default:
-			status_id = "invisible";
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown status: %x\n", q_bud->status);
-			break;
-		}
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "set buddy %d to %s\n", q_bud->uid, status_id);
-		purple_prpl_got_user_status(gc->account, name, status_id, NULL);
-
-		if (q_bud->comm_flag & QQ_COMM_FLAG_BIND_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE)
-			purple_prpl_got_user_status(gc->account, name, "mobile", NULL);
-		else
-			purple_prpl_got_user_status_deactive(gc->account, name, "mobile");
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid);
-	}
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "qq_update_buddy_contact, client=%04x\n", q_bud->client_version);
-	g_free(name);
-}
--- a/libpurple/protocols/qq/keep_alive.h	Sat Aug 02 02:40:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/**
- * @file keep_alive.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_KEEP_ALIVE_H_
-#define _QQ_KEEP_ALIVE_H_
-
-#include <glib.h>
-#include "connection.h"
-#include "qq.h"
-
-void qq_send_packet_keep_alive(PurpleConnection *gc);
-
-void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_refresh_all_buddy_status(PurpleConnection *gc);
-
-void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud);
-
-#endif
--- a/libpurple/protocols/qq/login_logout.c	Sat Aug 02 02:40:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,520 +0,0 @@
-/**
- * @file login_logout.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 "server.h"
-#include "cipher.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_status.h"
-#include "char_conv.h"
-#include "crypt.h"
-#include "group.h"
-#include "header_info.h"
-#include "login_logout.h"
-#include "packet_parse.h"
-#include "qq.h"
-#include "qq_network.h"
-#include "utils.h"
-
-#define QQ_LOGIN_DATA_LENGTH		    416
-#define QQ_LOGIN_REPLY_OK_PACKET_LEN        139
-#define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN  11
-
-#define QQ_REQUEST_LOGIN_TOKEN_REPLY_OK 	0x00
-
-#define QQ_LOGIN_REPLY_OK                   0x00
-#define QQ_LOGIN_REPLY_REDIRECT             0x01
-#define QQ_LOGIN_REPLY_PWD_ERROR            0x05
-#define QQ_LOGIN_REPLY_MISC_ERROR           0xff	/* defined by myself */
-
-/* for QQ 2003iii 0117, fixed value */
-/* static const guint8 login_23_51[29] = {
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
-	0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
-	0x95, 0x67, 0xda, 0x2c, 0x01 
-}; */
-
-/* for QQ 2003iii 0304, fixed value */
-/*
-static const guint8 login_23_51[29] = {
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
-	0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
-	0x40, 0xb8, 0xac, 0x32, 0x01
-};
-*/
-
-/* for QQ 2005? copy from lumaqq */
-/* FIXME: change to guint8 */
-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 guint8 login_53_68[16] = {
- 	0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
- 	0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
-};
-
-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] = {
-	0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
-	0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
-};
-*/
-
-
-typedef struct _qq_login_reply_ok qq_login_reply_ok_packet;
-typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet;
-
-struct _qq_login_reply_ok {
-	guint8 result;
-	guint8 *session_key;
-	guint32 uid;
-	guint8 client_ip[4];	/* those detected by server */
-	guint16 client_port;
-	guint8 server_ip[4];
-	guint16 server_port;
-	time_t login_time;
-	guint8 unknown1[26];
-	guint8 unknown_server1_ip[4];
-	guint16 unknown_server1_port;
-	guint8 unknown_server2_ip[4];
-	guint16 unknown_server2_port;
-	guint16 unknown2;	/* 0x0001 */
-	guint16 unknown3;	/* 0x0000 */
-	guint8 unknown4[32];
-	guint8 unknown5[12];
-	guint8 last_client_ip[4];
-	time_t last_login_time;
-	guint8 unknown6[8];
-};
-
-struct _qq_login_reply_redirect {
-	guint8 result;
-	guint32 uid;
-	guint8 new_server_ip[4];
-	guint16 new_server_port;
-};
-
-/* 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;
-
-	src = g_newa(guint8, 20);
-	/* bug found by QuLogic */
-	memcpy(src, &uid, sizeof(uid));
-	memcpy(src + sizeof(uid), 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 */
-static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
-{
-	gint bytes;
-	qq_data *qd;
-	qq_login_reply_ok_packet lrop;
-
-	qd = (qq_data *) gc->proto_data;
-	/* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
-	bytes = 0;
-
-	/* 000-000: reply code */
-	bytes += qq_get8(&lrop.result, data + bytes);
-	/* 001-016: session key */
-	lrop.session_key = g_memdup(data + bytes, QQ_KEY_LENGTH);
-	bytes += QQ_KEY_LENGTH;
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n");
-	/* 017-020: login uid */
-	bytes += qq_get32(&lrop.uid, data + bytes);
-	/* 021-024: server detected user public IP */
-	bytes += qq_getdata((guint8 *) &lrop.client_ip, 4, data + bytes);
-	/* 025-026: server detected user port */
-	bytes += qq_get16(&lrop.client_port, data + bytes);
-	/* 027-030: server detected itself ip 127.0.0.1 ? */
-	bytes += qq_getdata((guint8 *) &lrop.server_ip, 4, data + bytes);
-	/* 031-032: server listening port */
-	bytes += qq_get16(&lrop.server_port, data + bytes);
-	/* 033-036: login time for current session */
-	bytes += qq_getime(&lrop.login_time, data + bytes);
-	/* 037-062: 26 bytes, unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
-	/* 063-066: unknown server1 ip address */
-	bytes += qq_getdata((guint8 *) &lrop.unknown_server1_ip, 4, data + bytes);
-	/* 067-068: unknown server1 port */
-	bytes += qq_get16(&lrop.unknown_server1_port, data + bytes);
-	/* 069-072: unknown server2 ip address */
-	bytes += qq_getdata((guint8 *) &lrop.unknown_server2_ip, 4, data + bytes);
-	/* 073-074: unknown server2 port */
-	bytes += qq_get16(&lrop.unknown_server2_port, data + bytes);
-	/* 075-076: 2 bytes unknown */
-	bytes += qq_get16(&lrop.unknown2, data + bytes);
-	/* 077-078: 2 bytes unknown */
-	bytes += qq_get16(&lrop.unknown3, data + bytes);
-	/* 079-110: 32 bytes unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes);
-	/* 111-122: 12 bytes unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
-	/* 123-126: login IP of last session */
-	bytes += qq_getdata((guint8 *) &lrop.last_client_ip, 4, data + bytes);
-	/* 127-130: login time of last session */
-	bytes += qq_getime(&lrop.last_login_time, data + bytes);
-	/* 131-138: 8 bytes unknown */
-	bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes);
-
-	if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) {	/* fail parsing login info */
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Fail parsing login info, expect %d bytes, read %d bytes\n",
-			   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;
-	
-	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;
-	qd->last_login_ip = gen_ip_str(lrop.last_client_ip);
-
-	purple_connection_set_state(gc, PURPLE_CONNECTED);
-	qd->logged_in = TRUE;	/* must be defined after sev_finish_login */
-
-	/* now initiate QQ Qun, do it first as it may take longer to finish */
-	qq_group_init(gc);
-
-	/* Now goes on updating my icon/nickname, not showing info_window */
-	qd->modifying_face = FALSE;
-	qq_send_packet_get_info(gc, qd->uid, FALSE);
-	/* grab my level */
-	qq_send_packet_get_level(gc, qd->uid);
-
-	qq_send_packet_change_status(gc);
-
-	/* refresh buddies */
-	qq_send_packet_get_buddies_list(gc, QQ_FRIENDS_LIST_POSITION_START);
-	/* refresh groups */
-	qq_send_packet_get_all_list_with_group(gc, QQ_FRIENDS_LIST_POSITION_START);
-
-	return QQ_LOGIN_REPLY_OK;
-}
-
-/* process login reply packet which includes redirected new server address */
-static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
-{
-	gint bytes, ret;
-	qq_data *qd;
-	qq_login_reply_redirect_packet lrrp;
-
-	qd = (qq_data *) gc->proto_data;
-	bytes = 0;
-	/* 000-000: reply code */
-	bytes += qq_get8(&lrrp.result, data + bytes);
-	/* 001-004: login uid */
-	bytes += qq_get32(&lrrp.uid, data + bytes);
-	/* 005-008: redirected new server IP */
-	bytes += qq_getdata(lrrp.new_server_ip, 4, data + bytes);
-	/* 009-010: redirected new server port */
-	bytes += qq_get16(&lrrp.new_server_port, data + bytes);
-
-	if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "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 {
-		/* 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", qd->real_hostname, qd->real_port);
-
-		ret = QQ_LOGIN_REPLY_REDIRECT;
-	}
-
-	return ret;
-}
-
-/* process login reply which says wrong password */
-static gint _qq_process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len)
-{
-	gchar *server_reply, *server_reply_utf8;
-	server_reply = g_new0(gchar, len);
-	g_memmove(server_reply, data + 1, len - 1);
-	server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Wrong password, server msg in UTF8: %s\n", server_reply_utf8);
-	g_free(server_reply);
-	g_free(server_reply_utf8);
-
-	return QQ_LOGIN_REPLY_PWD_ERROR;
-}
-
-/* request before login */
-void qq_send_packet_request_login_token(PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 buf[16] = {0};
-	gint bytes = 0;
-
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	bytes += qq_put8(buf + bytes, 0);
-	
-	qq_send_data(qd, 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;
-	gint bytes;
-	guint8 *encrypted_data;
-	gint encrypted_len;
-
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
-	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
-
-	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
-	if (qd->inikey) {
-		g_free(qd->inikey);
-	}
-	qd->inikey = (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
-
-	bytes = 0;
-	/* now generate the encrypted data
-	 * 000-015 use pwkey as key to encrypt empty string */
-	qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data + bytes, &encrypted_len);
-	bytes += 16;
-	/* 016-016 */
-	bytes += qq_put8(raw_data + bytes, 0x00);
-	/* 017-020, used to be IP, now zero */
-	bytes += qq_put32(raw_data + bytes, 0x00000000);
-	/* 021-022, used to be port, now zero */
-	bytes += qq_put16(raw_data + bytes, 0x0000);
-	/* 023-051, fixed value, unknown */
-	bytes += qq_putdata(raw_data + bytes, login_23_51, 29);
-	/* 052-052, login mode */
-	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 */
-	bytes += qq_putdata(raw_data + bytes, token, token_length);
-	/* 100 bytes unknown */
-	bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
-	/* all zero left */
-
-	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 += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
-	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
-
-	qq_send_data(qd, 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);
-
-	qd = (qq_data *) gc->proto_data;
-
-	if (buf[0] == QQ_REQUEST_LOGIN_TOKEN_REPLY_OK) {
-		if (buf[1] != buf_len-2) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"Malformed login token reply packet. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2);
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"Attempting to proceed with the actual packet length.\n");
-		}
-		qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
-				buf+2, buf_len-2,
-				"<<< got a token -> [default] decrypt and dump");
-		qq_send_packet_login(gc, buf_len-2, buf+2);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]);
-		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
-				buf, buf_len,
-				">>> [default] decrypt and dump");
-		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"));
-		}
-	}
-}
-
-/* send logout packets to QQ server */
-void qq_send_packet_logout(PurpleConnection *gc)
-{
-	gint i;
-	qq_data *qd;
-
-	qd = (qq_data *) gc->proto_data;
-	for (i = 0; i < 4; i++)
-		qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
-
-	qd->logged_in = FALSE;	/* update login status AFTER sending logout packets */
-}
-
-/* process the login reply packet */
-void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
-	gint len, ret, bytes;
-	guint8 *data;
-	qq_data *qd;
-	gchar* error_msg;
-
-	g_return_if_fail(buf != NULL && buf_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	if (qq_decrypt(buf, buf_len, qd->pwkey, data, &len)) {
-		/* should be able to decrypt with pwkey */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Decrypt login reply packet with pwkey, %d bytes\n", len);
-		if (data[0] == QQ_LOGIN_REPLY_OK) {
-			ret = _qq_process_login_ok(gc, data, len);
-		} else {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown login reply code : %d\n", data[0]);
-			ret = QQ_LOGIN_REPLY_MISC_ERROR;
-		}
-	} else {		/* decrypt with pwkey error */
-		len = buf_len;	/* reset len, decrypt will fail if len is too short */
-		if (qq_decrypt(buf, buf_len, qd->inikey, data, &len)) {
-			/* decrypt ok with inipwd, it might be password error */
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"Decrypt login reply packet with inikey, %d bytes\n", len);
-			bytes = 0;
-			switch (data[0]) {
-			case QQ_LOGIN_REPLY_REDIRECT:
-				ret = _qq_process_login_redirect(gc, data, len);
-				break;
-			case QQ_LOGIN_REPLY_PWD_ERROR:
-				ret = _qq_process_login_wrong_pwd(gc, data, len);
-				break;
-			default:
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]);
-				qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
-						data, len,
-						">>> [default] decrypt and dump");
-				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 */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "No idea how to decrypt login reply\n");
-			ret = QQ_LOGIN_REPLY_MISC_ERROR;
-		}
-	}
-
-	switch (ret) {
-	case QQ_LOGIN_REPLY_PWD_ERROR:
-		if (!purple_account_get_remember_password(gc->account))
-			purple_account_set_password(gc->account, NULL);
-		purple_connection_error_reason(gc,
-			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password."));
-		break;
-	case QQ_LOGIN_REPLY_MISC_ERROR:
-			if (purple_debug_is_enabled())
-				purple_connection_error_reason(gc,
-					PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login. Check debug log."));
-			else
-				purple_connection_error_reason(gc,
-					PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login"));				
-		break;
-	case QQ_LOGIN_REPLY_OK:
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login repliess OK; everything is fine\n");
-		break;
-	case QQ_LOGIN_REPLY_REDIRECT:
-		/* the redirect has been done in _qq_process_login_reply */
-		break;
-	default:{;
-		}
-	}
-}
--- a/libpurple/protocols/qq/login_logout.h	Sat Aug 02 02:40:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/**
- * file login_logout.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_LOGIN_LOGOUT_H_
-#define _QQ_LOGIN_LOGOUT_H_
-
-#include <glib.h>
-#include "connection.h"
-
-#define QQ_LOGIN_MODE_NORMAL        0x0a
-#define QQ_LOGIN_MODE_AWAY	    0x1e
-#define QQ_LOGIN_MODE_HIDDEN        0x28
-
-void qq_send_packet_request_login_token(PurpleConnection *gc);
-void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_send_packet_logout(PurpleConnection *gc);
-
-#endif
--- a/libpurple/protocols/qq/packet_parse.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.c	Sat Aug 02 15:00:46 2008 +0000
@@ -27,7 +27,6 @@
 #include "packet_parse.h"
 #include "debug.h"
 
-
 /*------------------------------------------------PUT------------------------------------------------*/
 
 /* note:
@@ -68,7 +67,6 @@
 	return sizeof(w_dest);
 }
 
-
 /* read four bytes as "guint32" from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
 gint qq_get32(guint32 *dw, guint8 *buf)
@@ -83,6 +81,11 @@
 	return sizeof(dw_dest);
 }
 
+gint qq_getIP(struct in_addr *ip, guint8 *buf)
+{
+	memcpy(ip, buf, sizeof(struct in_addr));
+	return sizeof(struct in_addr);
+}
 
 /* read datalen bytes from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
@@ -158,6 +161,11 @@
     return sizeof(dw_porter);
 }
 
+gint qq_putIP(guint8* buf, struct in_addr *ip)
+{
+    memcpy(buf, ip, sizeof(struct in_addr));
+    return sizeof(struct in_addr);
+}
 
 /* pack datalen bytes into buf
  * return the number of bytes packed, otherwise return -1 */
--- a/libpurple/protocols/qq/packet_parse.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.h	Sat Aug 02 15:00:46 2008 +0000
@@ -37,15 +37,19 @@
  */
 #define MAX_PACKET_SIZE 65535
 
+#include <netinet/in.h>
+
 gint qq_get8(guint8 *b, guint8 *buf);
 gint qq_get16(guint16 *w, guint8 *buf);
 gint qq_get32(guint32 *dw,  guint8 *buf);
+gint qq_getIP(struct in_addr *ip, guint8 *buf);
 gint qq_getime(time_t *t, guint8 *buf);
 gint qq_getdata(guint8 *data, gint datalen, guint8 *buf);
 
 gint qq_put8(guint8 *buf, guint8 b);
 gint qq_put16(guint8 *buf, guint16 w);
 gint qq_put32(guint8 *buf, guint32 dw);
+gint qq_putIP(guint8* buf, struct in_addr *ip);
 gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen);
 
 /*
--- a/libpurple/protocols/qq/qq.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Sat Aug 02 15:00:46 2008 +0000
@@ -40,7 +40,7 @@
 
 #include "buddy_info.h"
 #include "buddy_opt.h"
-#include "buddy_status.h"
+#include "buddy_list.h"
 #include "char_conv.h"
 #include "crypt.h"
 #include "group.h"
@@ -51,8 +51,8 @@
 #include "group_opt.h"
 #include "header_info.h"
 #include "im.h"
-#include "keep_alive.h"
-#include "login_logout.h"
+#include "qq_process.h"
+#include "qq_base.h"
 #include "packet_parse.h"
 #include "qq.h"
 #include "qq_network.h"
@@ -237,78 +237,110 @@
 static void _qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	qq_buddy *q_bud;
-	gchar *ip_str;
-	char *tmp;
-	const char *tmp2;
+	gchar *tmp;
+	GString *str;
 
 	g_return_if_fail(b != NULL);
 
 	q_bud = (qq_buddy *) b->proto_data;
-	g_return_if_fail(q_bud != NULL);
+	if (q_bud == NULL)
+		return;
+
+	/* if (PURPLE_BUDDY_IS_ONLINE(b) && q_bud != NULL) */
+	if (q_bud->ip.s_addr != 0) {
+		str = g_string_new(NULL);
+		g_string_printf(str, "%s:%d", inet_ntoa(q_bud->ip), q_bud->port);
+		if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
+			g_string_append(str, " TCP");
+		} else {
+			g_string_append(str, " UDP");
+		}
+		g_string_free(str, TRUE);
+	}
 
-	if (PURPLE_BUDDY_IS_ONLINE(b) && q_bud != NULL)
-	{
-		ip_str = gen_ip_str(q_bud->ip);
-		if (strlen(ip_str) != 0) {
-			if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE)
-				tmp2 = _("TCP Address");
-			else
-				tmp2 = _("UDP Address");
-			tmp = g_strdup_printf("%s:%d", ip_str, q_bud->port);
-			purple_notify_user_info_add_pair(user_info, tmp2, tmp);
-			g_free(tmp);
-		}
-		g_free(ip_str);
+	tmp = g_strdup_printf("%d", q_bud->age);
+	purple_notify_user_info_add_pair(user_info, _("Age"), tmp);
+	g_free(tmp);
 
-		tmp = g_strdup_printf("%d", q_bud->age);
-		purple_notify_user_info_add_pair(user_info, _("Age"), tmp);
+	switch (q_bud->gender) {
+	case QQ_BUDDY_GENDER_GG:
+		purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male"));
+		break;
+	case QQ_BUDDY_GENDER_MM:
+		purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female"));
+		break;
+	case QQ_BUDDY_GENDER_UNKNOWN:
+		purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown"));
+		break;
+	default:
+		tmp = g_strdup_printf("Error (%d)", q_bud->gender);
+		purple_notify_user_info_add_pair(user_info, _("Gender"), tmp);
 		g_free(tmp);
+	}
 
-		switch (q_bud->gender) {
-		case QQ_BUDDY_GENDER_GG:
-			purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male"));
-			break;
-		case QQ_BUDDY_GENDER_MM:
-			purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female"));
-			break;
-		case QQ_BUDDY_GENDER_UNKNOWN:
-			purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown"));
-			break;
-		default:
-			tmp = g_strdup_printf("Error (%d)", q_bud->gender);
-			purple_notify_user_info_add_pair(user_info, _("Gender"), tmp);
-			g_free(tmp);
-		}
+	if (q_bud->level) {
+		tmp = g_strdup_printf("%d", q_bud->level);
+		purple_notify_user_info_add_pair(user_info, _("Level"), tmp);
+		g_free(tmp);
+	}
 
-		if (q_bud->level) {
-			tmp = g_strdup_printf("%d", q_bud->level);
-			purple_notify_user_info_add_pair(user_info, _("Level"), tmp);
-			g_free(tmp);
-		}
-		/* For debugging */
-		/*
-		g_string_append_printf(tooltip, "\n<b>Flag:</b> %01x", q_bud->flag1);
-		g_string_append_printf(tooltip, "\n<b>CommFlag:</b> %01x", q_bud->comm_flag);
-		g_string_append_printf(tooltip, "\n<b>Client:</b> %04x", q_bud->client_version);
-		*/
+	str = g_string_new(NULL);
+	if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) {
+		g_string_append( str, _("Member") );
+	}
+	if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_VIP) {
+		g_string_append( str, _(" VIP") );
+	}
+	if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
+		g_string_append( str, _(" TCP") );
+	}
+	if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE) {
+		g_string_append( str, _(" FromMobile") );
+	}
+	if (q_bud->comm_flag & QQ_COMM_FLAG_BIND_MOBILE) {
+		g_string_append( str, _(" BindMobile") );
+	}
+	if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO) {
+		g_string_append( str, _(" Video") );
 	}
+
+	if (q_bud->ext_flag & QQ_EXT_FLAG_SPACE) {
+		g_string_append( str, _(" Space") );
+	}
+	purple_notify_user_info_add_pair(user_info, _("Flag"), str->str);
+
+	g_string_free(str, TRUE);
+
+#ifdef DEBUG
+	tmp = g_strdup_printf( "%s (%04X)",
+										qq_get_ver_desc(q_bud->client_version),
+										q_bud->client_version );
+	purple_notify_user_info_add_pair(user_info, _("Ver"), tmp);
+	g_free(tmp);
+
+	tmp = g_strdup_printf( "Ext 0x%X, Comm 0x%X",
+												q_bud->ext_flag, q_bud->comm_flag );
+	purple_notify_user_info_add_pair(user_info, _("Flag"), tmp);
+	g_free(tmp);
+#endif
 }
 
 /* we can show tiny icons on the four corners of buddy icon, */
 static const char *_qq_list_emblem(PurpleBuddy *b)
 {
 	/* each char** are refering to a filename in pixmaps/purple/status/default/ */
-
-	qq_buddy *q_bud = b->proto_data;
+	qq_buddy *q_bud;
+	
+	if (!b || !(q_bud = b->proto_data)) {
+		return NULL;
+	}
 
-	if (q_bud) {
-		if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER)
-			return "qq_member";
-		/*
-		if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO)
-			return "video";
-		*/
-	}
+	if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE)
+		return "mobile";
+	if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO)
+		return "video";
+	if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER)
+		return "qq_member";
 
 	return NULL;
 }
@@ -393,6 +425,7 @@
 	group = qq_group_find_by_channel(gc, channel);
 	g_return_val_if_fail(group != NULL, -1);
 
+	purple_debug_info("QQ_MESG", "Send qun mesg in utf8: %s\n", message);
 	msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT);
 	msg_with_qq_smiley = purple_smiley_to_qq(msg);
 	qq_send_packet_group_im(gc, group, msg_with_qq_smiley);
@@ -479,7 +512,7 @@
 	qd = (qq_data *) gc->proto_data;
 	info = g_string_new("<html><body>\n");
 
-	g_string_append_printf(info, _("<b>Current Online</b>: %d<br>\n"), qd->all_online);
+	g_string_append_printf(info, _("<b>Current Online</b>: %d<br>\n"), qd->total_online);
 	g_string_append_printf(info, _("<b>Last Refresh</b>: %s<br>\n"), ctime(&qd->last_get_online));
 
 	g_string_append(info, "<hr>\n");
@@ -487,7 +520,7 @@
 	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>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_printf(info, _("<b>My Public IP</b>: %s<br>\n"), inet_ntoa(qd->my_ip));
 
 	g_string_append(info, "<hr>\n");
 	g_string_append(info, "<i>Information below may not be accurate</i><br>\n");
@@ -636,30 +669,6 @@
 	return m;
 }
 
-
-static void qq_keep_alive(PurpleConnection *gc)
-{
-	qq_group *group;
-	qq_data *qd;
-	GList *list;
-
-	if (NULL == (qd = (qq_data *) gc->proto_data))
-		return;
-
-	list = qd->groups;
-	while (list != NULL) {
-		group = (qq_group *) list->data;
-		if (group->my_status == QQ_GROUP_MEMBER_STATUS_IS_MEMBER ||
-		    group->my_status == QQ_GROUP_MEMBER_STATUS_IS_ADMIN)
-			/* no need to get info time and time again, online members enough */
-			qq_send_cmd_group_get_online_members(gc, group);
-
-		list = list->next;
-	}
-
-	qq_send_packet_keep_alive(gc);
-}
-
 /* convert chat nickname to qq-uid to get this buddy info */
 /* who is the nickname of buddy in QQ chat-room (Qun) */
 static void _qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who)
@@ -718,8 +727,8 @@
 	NULL,							/* chat_invite	*/
 	NULL,							/* chat_leave */
 	NULL,							/* chat_whisper */
-	_qq_chat_send,						/* chat_send */
-	qq_keep_alive,						/* keepalive */
+	_qq_chat_send,			/* chat_send */
+	NULL,							/* keepalive */
 	NULL,							/* register_user */
 	_qq_get_chat_buddy_info,				/* get_cb_info	*/
 	NULL,							/* get_cb_away	*/
@@ -738,7 +747,7 @@
 	qq_roomlist_cancel,					/* roomlist_cancel */
 	NULL,							/* roomlist_expand_category */
 	NULL,							/* can_receive_file */
-	qq_send_file,						/* send_file */
+	NULL,							/* qq_send_file send_file */
 	NULL,							/* new xfer */
 	NULL,							/* offline_message */
 	NULL,							/* PurpleWhiteboardPrplOps */
@@ -802,6 +811,15 @@
 	option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
+	option = purple_account_option_int_new(_("resend interval(s)"), "resend_interval", 10);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("Keep alive interval(s)"), "keep_alive_interval", 60);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("Update interval(s)"), "update_interval", 300);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
 	my_protocol = plugin;
 
 	purple_prefs_add_none("/plugins/prpl/qq");
--- a/libpurple/protocols/qq/qq.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Sat Aug 02 15:00:46 2008 +0000
@@ -34,7 +34,6 @@
 #include "proxy.h"
 #include "roomlist.h"
 
-#define QQ_FACES	    100
 #define QQ_KEY_LENGTH       16
 #define QQ_DEBUG            1	/* whether we are doing DEBUG */
 
@@ -45,6 +44,13 @@
 
 typedef struct _qq_data qq_data;
 typedef struct _qq_buddy qq_buddy;
+typedef struct _qq_interval qq_interval;
+
+struct _qq_interval {
+	gint resend;
+	gint keep_alive;
+	gint update; 
+};
 
 struct _qq_buddy {
 	guint32 uid;
@@ -52,10 +58,10 @@
 	guint8 age;
 	guint8 gender;
 	gchar *nickname;
-	guint8 ip[4];
+	struct in_addr ip;
 	guint16 port;
 	guint8 status;
-	guint8 flag1;
+	guint8 ext_flag;
 	guint8 comm_flag;	/* details in qq_buddy_list.c */
 	guint16 client_version;
 	guint8 onlineTime;
@@ -88,12 +94,12 @@
 	gint fd;				/* socket file handler */
 	gint tx_handler; 	/* socket can_write handle, use in udp connecting and tcp send out */
 
-	GList *send_trans;	/* check ack packet and resend */
-	guint resend_timeout;
+	qq_interval itv_config;
+	qq_interval itv_count;
+	guint network_timeout;
+	
+	GList *transactions;	/* check ack packet and resend */
 
-	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;
@@ -103,10 +109,12 @@
 	PurpleDnsQueryData *udp_query_data;
 
 	guint32 uid;			/* QQ number */
-	guint8 *inikey;			/* initial key to encrypt login packet */
-	guint8 *pwkey;			/* password in md5 (or md5' md5) */
-	guint8 *session_key;		/* later use this as key in this session */
-	guint8 *session_md5;		/* concatenate my uid with session_key and md5 it */
+	guint8 *token;		/* get from server*/
+	int token_len;
+	guint8 inikey[QQ_KEY_LENGTH];			/* initial key to encrypt login packet */
+	guint8 password_twice_md5[QQ_KEY_LENGTH];			/* password in md5 (or md5' md5) */
+	guint8 session_key[QQ_KEY_LENGTH];		/* later use this as key in this session */
+	guint8 session_md5[QQ_KEY_LENGTH];		/* concatenate my uid with session_key and md5 it */
 
 	guint16 send_seq;		/* send sequence number */
 	guint8 login_mode;		/* online of invisible */
@@ -119,11 +127,11 @@
 	time_t last_login_time;
 	gchar *last_login_ip;
 	/* get from keep_alive packet */
-	gchar *my_ip;			/* my ip address detected by server */
+	struct in_addr my_ip;			/* my ip address detected by server */
 	guint16 my_port;		/* my port detected by server */
 	guint16 my_icon;		/* my icon index */
 	guint16 my_level;		/* my level */
-	guint32 all_online;		/* the number of online QQ users */
+	guint32 total_online;		/* the number of online QQ users */
 	time_t last_get_online;		/* last time send get_friends_online packet */
 
 	PurpleRoomlist *roomlist;
--- a/libpurple/protocols/qq/qq_network.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Sat Aug 02 15:00:46 2008 +0000
@@ -32,26 +32,23 @@
 #endif
 
 #include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "buddy_status.h"
+#include "group_info.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 "qq_base.h"
+#include "buddy_list.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "qq_trans.h"
-#include "sys_msg.h"
 #include "utils.h"
+#include "qq_process.h"
 
 /* set QQ_RECONNECT_MAX to 1, when test reconnecting */
 #define QQ_RECONNECT_MAX					4
 #define QQ_RECONNECT_INTERVAL		5000
+#define QQ_KEEP_ALIVE_INTERVAL		60000
+#define QQ_TRANS_INTERVAL				10000
 
 static gboolean set_new_server(qq_data *qd)
 {
@@ -107,60 +104,6 @@
 	return TRUE;
 }
 
-/* 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 process_cmd_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)
 {
@@ -172,38 +115,6 @@
 	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 cmd, guint16 seq)
-{
-	gpointer trans;
-
-	g_return_val_if_fail(qd != NULL, FALSE);
-
-	trans = qq_send_trans_find(qd, cmd, seq);
-	if (trans == NULL) {
-		return FALSE;
-	}
-	
-	qq_send_trans_remove(qd, trans);
-	return TRUE;
-}
-
 static gboolean reconnect_later_cb(gpointer data)
 {
 	PurpleConnection *gc;
@@ -244,85 +155,6 @@
 		reconnect_later_cb, gc);
 }
 
-static void process_cmd_server(
-	PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
-{
-	/* now process the packet */
-	switch (cmd) {
-		case QQ_CMD_RECV_IM:
-			qq_process_recv_im(data, data_len, seq, 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:
-			process_cmd_unknow(gc, data, data_len, cmd, seq);
-			break;
-	}
-}
-
-static void process_cmd_reply(
-	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_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;
-		default:
-			process_cmd_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)
 {
@@ -330,15 +162,13 @@
 	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 */
 
-	gboolean is_reply;
+	qq_transaction *trans;
 
 	g_return_if_fail(buf != NULL && buf_len > 0);
 
@@ -353,35 +183,38 @@
 	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));
+				seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag));
 	}
 	
 	bytes_not_read = buf_len - bytes - 1;
 
 	/* ack packet, we need to update send tranactions */
 	/* we do not check duplication for server ack */
-	is_reply = packet_check_ack(qd, cmd, seq);
-	if ( !is_reply ) {
-		if ( !qd->logged_in ) {
-			/* packets before login */
-			qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read);
-			return;	/* do not process it now */
+	trans = qq_trans_find_rcved(qd, cmd, seq);
+	if (trans == NULL) {
+		/* new server command */
+		qq_trans_add_server_cmd(qd, cmd, seq, buf + bytes, bytes_not_read);
+		if ( qd->logged_in ) {
+			qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
 		}
-		
-		/* 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;
+		return;
+	}
+
+	if (qq_trans_is_dup(trans)) {
+		purple_debug(PURPLE_DEBUG_WARNING,
+				"QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
+		return;
+	}
+
+	if (qq_trans_is_server(trans)) {
+		if ( qd->logged_in ) {
+			qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
 		}
-		process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
 		return;
 	}
 
 	/* this is the length of all the encrypted data (also remove tail tag */
-	process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
+	qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
 
 	/* check is redirect or not, and do it now */
 	if (qd->is_redirect) {
@@ -394,18 +227,7 @@
 
 	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) {
-			memset(new_data, 0, MAX_PACKET_SIZE);
-			new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE);
-			if (new_data_len < 0) {
-				break;
-			}
-			if (new_data_len == 0) {
-				continue;
-			}
-			process_cmd_reply(gc, seq, cmd, new_data, new_data_len);
-		}
+		qq_trans_process_before_login(qd);
 	}
 }
 
@@ -464,8 +286,10 @@
 	 *  QQ need a keep alive packet in every 60 seconds
 	 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;
@@ -482,9 +306,10 @@
 			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) {
@@ -518,14 +343,14 @@
 		/* 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);		
+			/*
+			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");		
+			/* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */
 			g_free(qd->tcp_rxqueue);
 			qd->tcp_rxqueue = NULL;
 		}
@@ -591,8 +416,10 @@
 
 	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);
-
+	/*
+	purple_debug(PURPLE_DEBUG_INFO, "UDP_SEND_OUT", "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) {
@@ -601,7 +428,7 @@
 	
 	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_debug(PURPLE_DEBUG_ERROR, "UDP_SEND_OUT", "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;
@@ -642,7 +469,7 @@
 	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);
+	purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
 	 */
 
 	if (qd->tx_handler == 0) {
@@ -652,13 +479,13 @@
 		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");
-		 */
+		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? */
@@ -679,70 +506,44 @@
 	return ret;
 }
 
-static gboolean trans_timeout(gpointer data)
+static gboolean network_timeout(gpointer data)
 {
-	PurpleConnection *gc;
+	PurpleConnection *gc = (PurpleConnection *) data;
 	qq_data *qd;
-	guint8 *buf;
-	gint buf_len = 0;
-	guint16 cmd;
-	gint retries = 0;
-	int index;
-	
-	gc = (PurpleConnection *) data;
+	gboolean is_lost_conn;
+
 	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
+	qd = (qq_data *) gc->proto_data;
 
-	qd = (qq_data *) gc->proto_data;
-	
-	index = 0;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	is_lost_conn = qq_trans_scan(qd);
+	if (is_lost_conn) {
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+		return TRUE;
+	}
 
-	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_send_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;
-		}
+	if ( !qd->logged_in ) {
+		return TRUE;
+	}
+	
+	qd->itv_count.keep_alive--;
+	if (qd->itv_count.keep_alive <= 0) {
+		qd->itv_count.keep_alive = qd->itv_config.keep_alive;
+		qq_send_packet_keep_alive(gc);
+		return TRUE;
+	}
 
-		/* 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));
-		}
+	if (qd->itv_config.update <= 0) {
+		return TRUE;
+	}
+
+	qd->itv_count.update--;
+	if (qd->itv_count.update <= 0) {
+		qd->itv_count.update = qd->itv_config.update;
+		qq_send_packet_get_buddies_online(gc, 0);
+
+		qq_send_cmd_group_all_get_online_members(gc);
+		return TRUE;
 	}
 
 	return TRUE;		/* if return FALSE, timeout callback stops */
@@ -756,6 +557,7 @@
 	PurpleConnection *gc;
 	gchar *conn_msg;
 	const gchar *passwd;
+	PurpleAccount *account ;
 
 	gc = (PurpleConnection *) data;
 
@@ -768,6 +570,7 @@
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 
 	qd = (qq_data *) gc->proto_data;
+	account = purple_connection_get_account(gc);
 
 	/* Connect is now complete; clear the PurpleProxyConnectData */
 	qd->connect_data = NULL;
@@ -791,12 +594,34 @@
 
 	/* 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);
+
+	/* use twice-md5 of user password as session key since QQ 2003iii */
+	qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
+		(guint8 *)passwd, strlen(passwd));
+	qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
+		qd->password_twice_md5, sizeof(qd->password_twice_md5));
+
+	g_return_if_fail(qd->network_timeout == 0);
+	qd->itv_config.resend = purple_account_get_int(account, "resend_interval", 10);
+	if (qd->itv_config.resend <= 0) qd->itv_config.resend = 10;
 
-	g_return_if_fail(qd->resend_timeout == 0);
-	/* call trans_timeout every 5 seconds */
-	qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc);
+	qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60);
+	if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30;
+	qd->itv_config.keep_alive /= qd->itv_config.resend;
+	qd->itv_count.keep_alive = qd->itv_config.keep_alive;
+
+	qd->itv_config.update = purple_account_get_int(account, "update_interval", 300);
+	if (qd->itv_config.update > 0) {
+		if (qd->itv_config.update < qd->itv_config.keep_alive) {
+			qd->itv_config.update = qd->itv_config.keep_alive;
+		}
+		qd->itv_config.update /= qd->itv_config.resend;
+		qd->itv_count.update = qd->itv_config.update;
+	} else {
+		qd->itv_config.update = 0;
+	}
+
+	qd->network_timeout = purple_timeout_add(qd->itv_config.resend *1000, network_timeout, gc);
 	
 	if (qd->use_tcp)
 		gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc);
@@ -808,7 +633,7 @@
 	purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
 	g_free(conn_msg);
 
-	qq_send_packet_request_login_token(gc);
+	qq_send_packet_token(gc);
 }
 
 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
@@ -1039,16 +864,17 @@
 	qd = (qq_data *) gc->proto_data;
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n");
+
+	if (qd->network_timeout > 0) {
+		purple_timeout_remove(qd->network_timeout);
+		qd->network_timeout = 0;
+	}
+
 	/* 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;
@@ -1092,35 +918,20 @@
 		qd->udp_query_data = NULL;
 	}
 
-	memset(qd->rcv_window, 0, sizeof(qd->rcv_window));
-	qq_rcv_trans_remove_all(qd);
-	qq_send_trans_remove_all(qd);
+	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->token) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n");
+		g_free(qd->token);
+		qd->token = NULL;
+		qd->token_len = 0;
 	}
-	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;
-	}
+	memset(qd->inikey, 0, sizeof(qd->inikey));
+	memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5));
+	memset(qd->session_key, 0, sizeof(qd->session_key));
+	memset(qd->session_md5, 0, sizeof(qd->session_md5));
+
+	qd->my_ip.s_addr = 0;
 
 	qq_group_packets_free(qd);
 	qq_group_free_all(qd);
@@ -1168,19 +979,19 @@
 	return bytes;
 }
 
-gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+/* data has been encrypted before */
+gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+	guint8 *data, gint data_len)
 {
 	guint8 *buf;
 	gint buf_len;
 	gint bytes_sent;
-	gint seq;
 
 	g_return_val_if_fail(qd != NULL, -1);
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
 	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;
@@ -1192,11 +1003,12 @@
 		bytes_sent = udp_send_out(qd, buf, buf_len);
 	}
 
-	/* always need ack */
-	qq_send_trans_append(qd, buf, buf_len, cmd, seq);
-
+	if (need_ack)  {
+		qq_trans_add_client_cmd(qd, cmd, seq, data, data_len);
+	}
+	
 	if (QQ_DEBUG) {
-		qq_show_packet("QQ_SEND_DATA", buf, buf_len);
+		/* 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);
@@ -1204,19 +1016,14 @@
 	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 */
+/* Encrypt data with session_key, then call qq_send_data */
 gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
 	guint8 *data, gint data_len)
 {
-	guint8 *buf;
-	gint buf_len;
 	guint8 *encrypted_data;
 	gint encrypted_len;
-	gint bytes_sent;
 
-	g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1);
+	g_return_val_if_fail(qd != NULL, -1);
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
 	encrypted_len = data_len + 16;	/* at most 16 bytes more */
@@ -1224,35 +1031,10 @@
 
 	qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len);
 
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-	memset(buf, 0, MAX_PACKET_SIZE);
-	buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, 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_send_trans_append(qd, buf, buf_len, cmd, seq);
-	}
-
-	if (QQ_DEBUG) {
-		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;
+	return qq_send_data(qd, cmd, seq, need_ack, encrypted_data, encrypted_len);
 }
 
+/* set seq and need_ack, then call qq_send_cmd_detail */
 gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
 {
 	g_return_val_if_fail(qd != NULL, -1);
--- a/libpurple/protocols/qq/qq_network.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.h	Sat Aug 02 15:00:46 2008 +0000
@@ -22,8 +22,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#ifndef _QQ_PROXY_H
-#define _QQ_PROXY_H
+#ifndef _QQ_NETWORK_H
+#define _QQ_NETWORK_H
 
 #include <glib.h>
 #include "connection.h"
@@ -36,8 +36,9 @@
 void qq_disconnect(PurpleConnection *gc);
 void qq_connect_later(PurpleConnection *gc);
 
-gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
 gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+	guint8 *data, gint data_len);
 gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
 	guint8 *data, gint data_len);
 
--- a/libpurple/protocols/qq/qq_trans.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Sat Aug 02 15:00:46 2008 +0000
@@ -32,215 +32,259 @@
 
 #include "header_info.h"
 #include "qq_network.h"
+#include "qq_process.h"
 #include "qq_trans.h"
 
-#define QQ_RESEND_MAX               8	/* max resend per packet */
-
-typedef struct _transaction {
-	guint16 seq;
-	guint16 cmd;
-	guint8 *buf;
-	gint buf_len;
-
-	gint fd;
-	gint retries;
-	time_t create_time;
-} transaction;
-
-void qq_send_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);
+#define QQ_RESEND_MAX               3	/* max resend per 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;
-
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			"Add to transaction, seq = %d, buf = %p, len = %d\n",
-			trans->seq, trans->buf, trans->buf_len);
-	qd->send_trans = g_list_append(qd->send_trans, trans);
-}
-
-/* Remove a packet with seq from send trans */
-void qq_send_trans_remove(qq_data *qd, gpointer data) 
-{
-	transaction *trans = (transaction *)data;
-
-	g_return_if_fail(qd != NULL && data != NULL);
-	
-	purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"ack [%05d] %s, remove from send tranactions\n",
-				trans->seq, qq_get_cmd_desc(trans->cmd));
-
-	if (trans->buf)	g_free(trans->buf);
-	qd->send_trans = g_list_remove(qd->send_trans, trans);
-	g_free(trans);
-}
-
-gpointer qq_send_trans_find(qq_data *qd, guint16 cmd, guint16 seq)
+qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq)
 {
 	GList *curr;
 	GList *next;
-	transaction *trans;
+	qq_transaction *trans;
+
+	if (qd->transactions == NULL) {
+		return NULL;
+	}
 
-	curr = qd->send_trans;
-	while(curr) {
+	next = qd->transactions;
+	while( (curr = next) ) {
 		next = curr->next;
-		trans = (transaction *) (curr->data);
+		
+		trans = (qq_transaction *) (curr->data);
 		if(trans->cmd == cmd && trans->seq == seq) {
+			if (trans->rcved_times == 0) {
+				trans->scan_times = 0;
+			}
+			trans->rcved_times++;
+			if (qq_trans_is_server(trans) && qq_trans_is_dup(trans)) {
+				/* server may not get our confirm reply before, send reply again*/
+				if (trans->data != NULL && trans->data_len > 0) {
+					qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len);
+				}
+			}
 			return trans;
 		}
-		curr = next;
 	}
 
 	return NULL;
 }
 
-/* clean up send trans and free all contents */
-void qq_send_trans_remove_all(qq_data *qd)
+gboolean qq_trans_is_server(qq_transaction *trans) 
+{
+	g_return_val_if_fail(trans != NULL, FALSE);
+	
+	if (trans->flag & QQ_TRANS_IS_SERVER)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+gboolean qq_trans_is_dup(qq_transaction *trans) 
+{
+	g_return_val_if_fail(trans != NULL, TRUE);
+	
+	if (trans->rcved_times > 1)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+/* Remove a packet with seq from send trans */
+static void trans_remove(qq_data *qd, qq_transaction *trans) 
+{
+	g_return_if_fail(qd != NULL && trans != NULL);
+	
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS",
+				"Remove [%s%05d] retry %d rcved %d scan %d %s\n",
+				(trans->flag & QQ_TRANS_IS_SERVER) ? "SRV-" : "",
+				trans->seq,
+				trans->send_retries, trans->rcved_times, trans->scan_times,
+				qq_get_cmd_desc(trans->cmd));
+
+	if (trans->data)	g_free(trans->data);
+	qd->transactions = g_list_remove(qd->transactions, trans);
+	g_free(trans);
+}
+
+void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	qq_transaction *trans = g_new0(qq_transaction, 1);
+
+	g_return_if_fail(trans != NULL);
+
+	trans->flag = 0;
+	if (cmd == QQ_CMD_TOKEN || cmd == QQ_CMD_LOGIN || cmd == QQ_CMD_KEEP_ALIVE) {
+		trans->flag |= QQ_TRANS_CLI_IMPORT;
+	}
+	trans->fd = qd->fd;
+	trans->cmd = cmd;
+	trans->seq = seq;
+	trans->send_retries = QQ_RESEND_MAX;
+	trans->rcved_times = 0;
+	trans->scan_times = 0;
+	trans->data = NULL;
+	trans->data_len = 0;
+	if (data != NULL && data_len > 0) {
+		trans->data = g_memdup(data, data_len);	/* don't use g_strdup, may have 0x00 */
+		trans->data_len = data_len;
+	}
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+			"Add client cmd, seq = %d, data = %p, len = %d\n",
+			trans->seq, trans->data, trans->data_len);
+	qd->transactions = g_list_append(qd->transactions, trans);
+}
+
+void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	qq_transaction *trans = g_new0(qq_transaction, 1);
+
+	g_return_if_fail(trans != NULL);
+
+	trans->flag = QQ_TRANS_IS_SERVER;
+	if ( !qd->logged_in ) {
+		trans->flag |= QQ_TRANS_BEFORE_LOGIN;
+	}
+	trans->fd = qd->fd;
+	trans->cmd = cmd;
+	trans->seq = seq;
+	trans->send_retries = 0;
+	trans->rcved_times = 1;
+	trans->scan_times = 0;
+	trans->data = NULL;
+	trans->data_len = 0;
+	if (data != NULL && data_len > 0) {
+		trans->data = g_memdup(data, data_len);	/* don't use g_strdup, may have 0x00 */
+		trans->data_len = data_len;
+	}
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+			"Add server cmd, seq = %d, data = %p, len = %d\n",
+			trans->seq, trans->data, trans->data_len);
+	qd->transactions = g_list_append(qd->transactions, trans);
+}
+
+void qq_trans_process_before_login(qq_data *qd)
 {
 	GList *curr;
 	GList *next;
-	transaction *trans;
+	qq_transaction *trans;
+
+	g_return_if_fail(qd != NULL);
+
+	next = qd->transactions;
+	while( (curr = next) ) {
+		next = curr->next;
+		trans = (qq_transaction *) (curr->data);
+		/* purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
+		
+		if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) {
+			continue;
+		}
+		if ( !(trans->flag & QQ_TRANS_BEFORE_LOGIN) ) {
+			continue;
+		}
+		// set QQ_TRANS_BEFORE_LOGIN off
+		trans->flag &= ~QQ_TRANS_BEFORE_LOGIN;
+
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+				"Process server cmd before login, seq %d, data %p, len %d, send_retries %d\n",
+				trans->seq, trans->data, trans->data_len, trans->send_retries);
+
+		qq_proc_cmd_reply(qd->gc, trans->seq, trans->cmd, trans->data, trans->data_len);
+	}
+
+	/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+	return;
+}
+
+gboolean qq_trans_scan(qq_data *qd)
+{
+	GList *curr;
+	GList *next;
+	qq_transaction *trans;
+
+	g_return_val_if_fail(qd != NULL, FALSE);
+	
+	next = qd->transactions;
+	while( (curr = next) ) {
+		next = curr->next;
+		trans = (qq_transaction *) (curr->data);
+		/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
+		
+		if (trans->flag & QQ_TRANS_BEFORE_LOGIN) {
+			/* keep server cmd before login*/
+			continue;
+		}
+
+		trans->scan_times++;
+		if (trans->scan_times <= 1) {
+			/* skip in 10 seconds */
+			continue;
+		}
+
+		if (trans->rcved_times > 0) {
+			/* Has been received */
+			trans_remove(qd, trans);
+			continue;
+		}
+
+		if (trans->flag & QQ_TRANS_IS_SERVER) {
+			continue;
+		}
+		
+		/* Never get reply */
+		trans->send_retries--;
+		if (trans->send_retries <= 0) {
+			purple_debug(PURPLE_DEBUG_WARNING, "QQ_TRANS",
+				"[%d] %s is lost.\n",
+				trans->seq, qq_get_cmd_desc(trans->cmd));
+			if (trans->flag & QQ_TRANS_CLI_IMPORT) {
+				return TRUE;
+			}
+
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+				"Lost [%d] %s, data %p, len %d, retries %d\n",
+				trans->seq, qq_get_cmd_desc(trans->cmd),
+				trans->data, trans->data_len, trans->send_retries);
+			trans_remove(qd, trans);
+			continue;
+		}
+
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+				"Resend [%d] %s data %p, len %d, send_retries %d\n",
+				trans->seq, qq_get_cmd_desc(trans->cmd),
+				trans->data, trans->data_len, trans->send_retries);
+		qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len);
+	}
+
+	/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+	return FALSE;
+}
+
+/* clean up send trans and free all contents */
+void qq_trans_remove_all(qq_data *qd)
+{
+	GList *curr;
+	GList *next;
+	qq_transaction *trans;
 	gint count = 0;
 
-	curr = qd->send_trans;
+	curr = qd->transactions;
 	while(curr) {
 		next = curr->next;
 		
-		trans = (transaction *) (curr->data);
+		trans = (qq_transaction *) (curr->data);
 		/*
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
 			"Remove to transaction, seq = %d, buf = %p, len = %d\n",
 			trans->seq, trans->buf, trans->len);
 		*/
-		qq_send_trans_remove(qd, trans);
+		trans_remove(qd, trans);
 
 		count++;
 		curr = next;
 	}
-	g_list_free(qd->send_trans);
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count);
-}
-
-gint qq_send_trans_scan(qq_data *qd, gint *start,
-	guint8 *buf, gint maxlen, guint16 *cmd, gint *retries)
-{
-	GList *curr;
-	GList *next = NULL;
-	transaction *trans;
-	gint copylen;
-
-	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->send_trans, *start);
-	while(curr) {
-		next = curr->next;
-		*start = g_list_position(qd->send_trans, next);
-		
-		trans = (transaction *) (curr->data);
-		if (trans->buf == NULL || trans->buf_len <= 0) {
-			qq_send_trans_remove(qd, trans);
-			curr = next;
-			continue;
-		}
-
-		if (trans->retries < 0) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				"Remove transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
-				trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
-			qq_send_trans_remove(qd, trans);
-			curr = next;
-			continue;
-		}
-
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				"Resend transaction, seq %d, buf %p, 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;
-	}
-
-	/* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */
-	return -1;
-}
-
-void qq_rcv_trans_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);
+	g_list_free(qd->transactions);
 
-	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);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Free all %d packets\n", count);
 }
-
-gint qq_rcv_trans_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_rcv_trans_remove_all(qq_data *qd)
-{
-	transaction *trans = NULL;
-	gint count = 0;
-
-	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);
-			count++;
-		}
-		g_queue_free(qd->rcv_trans);
-	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count);
-}
--- a/libpurple/protocols/qq/qq_trans.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_trans.h	Sat Aug 02 15:00:46 2008 +0000
@@ -28,15 +28,34 @@
 #include <glib.h>
 #include "qq.h"
 
-void qq_send_trans_append(qq_data *qd, guint8 *buf, gint bus_len, guint16 cmd, guint16 seq);
-void qq_send_trans_remove(qq_data *qd, gpointer data);
-gpointer qq_send_trans_find(qq_data *qd, guint16 cmd, guint16 seq);
-void qq_send_trans_remove_all(qq_data *qd);
+enum {
+	QQ_TRANS_IS_SERVER = 0x01,			/* Is server command or client command */
+	/* prefix QQ_TRANS_CLI is for client command*/
+	QQ_TRANS_CLI_EMERGE = 0x02,		/* send at once; or may wait for next reply*/
+	QQ_TRANS_CLI_IMPORT = 0x04,		/* Only notice if not get reply; or resend, disconn if reties get 0*/
+	QQ_TRANS_BEFORE_LOGIN = 0x08,	/* server command before login*/
+};
+
+typedef struct _qq_transaction {
+	guint8 flag;
+	guint16 seq;
+	guint16 cmd;
+	guint8 *data;
+	gint data_len;
 
-gint qq_send_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries);
+	gint fd;
+	gint send_retries;
+	gint rcved_times;
+	gint scan_times;
+} qq_transaction;
 
-void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
-gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16* seq, guint8 *data, gint max_len);
-void qq_rcv_trans_remove_all(qq_data *qd);
+qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq);
+gboolean qq_trans_is_server(qq_transaction *trans) ;
+gboolean qq_trans_is_dup(qq_transaction *trans);
+void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+void qq_trans_process_before_login(qq_data *qd);
+gboolean qq_trans_scan(qq_data *qd);
+void qq_trans_remove_all(qq_data *qd);
 
 #endif
--- a/libpurple/protocols/qq/send_file.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/send_file.c	Sat Aug 02 15:00:46 2008 +0000
@@ -29,12 +29,12 @@
 #include "network.h"
 #include "notify.h"
 
-#include "buddy_status.h"
+#include "buddy_list.h"
 #include "crypt.h"
 #include "file_trans.h"
 #include "header_info.h"
 #include "im.h"
-#include "keep_alive.h"
+#include "qq_base.h"
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "utils.h"
@@ -447,7 +447,7 @@
 	info = g_new0(ft_info, 1);
 	info->to_uid = to_uid;
 	info->send_seq = qd->send_seq;
-	info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip));
+	info->local_internet_ip = qd->my_ip.s_addr;
 	info->local_internet_port = qd->my_port;
 	info->local_real_ip = 0x00000000;
 	info->conn_method = 0x00;
@@ -784,7 +784,7 @@
 	qd = (qq_data *) gc->proto_data;
 
 	info = g_newa(ft_info, 1);
-	info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip));
+	info->local_internet_ip = qd->my_ip.s_addr;
 	info->local_internet_port = qd->my_port;
 	info->local_real_ip = 0x00000000;
 	info->to_uid = sender_uid;
@@ -814,11 +814,11 @@
 		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 		if (q_bud) {
 			if(0 != info->remote_real_ip) {
-				g_memmove(q_bud->ip, &info->remote_real_ip, 4);
+				g_memmove(&(q_bud->ip), &info->remote_real_ip, sizeof(q_bud->ip));
 				q_bud->port = info->remote_minor_port;
 			}
 			else if (0 != info->remote_internet_ip) {
-				g_memmove(q_bud->ip, &info->remote_internet_ip, 4);
+				g_memmove(&(q_bud->ip), &info->remote_internet_ip, sizeof(q_bud->ip));
 				q_bud->port = info->remote_major_port;
 			}
 
@@ -831,7 +831,7 @@
 
 		}
 		else 
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in my friendlist\n", sender_uid);
+			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in list\n", sender_uid);
 
 		g_free(sender_name);	    
 		g_strfreev(fileinfo);
--- a/libpurple/protocols/qq/send_file.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/send_file.h	Sat Aug 02 15:00:46 2008 +0000
@@ -26,11 +26,12 @@
 #define _QQ_QQ_SEND_FILE_H_
 
 #include "ft.h"
+#include "qq.h"
 
 typedef struct _ft_info {
 	guint32 to_uid;
 	guint16 send_seq;
-	guint8 file_session_key[16];
+	guint8 file_session_key[QQ_KEY_LENGTH];
 	guint8 conn_method;
 	guint32 remote_internet_ip;
 	guint16 remote_internet_port;
--- a/libpurple/protocols/qq/sys_msg.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/sys_msg.c	Sat Aug 02 15:00:46 2008 +0000
@@ -44,6 +44,7 @@
 	QQ_MSG_SYS_ADD_CONTACT_REQUEST = 0x02,
 	QQ_MSG_SYS_ADD_CONTACT_APPROVED = 0x03,
 	QQ_MSG_SYS_ADD_CONTACT_REJECTED = 0x04,
+	QQ_MSG_SYS_NOTICE= 0x06,
 	QQ_MSG_SYS_NEW_VERSION = 0x09
 };
 
@@ -277,6 +278,20 @@
 	g_free(name);
 }
 
+static void _qq_process_msg_sys_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+{
+	gchar *title, *content;
+
+	g_return_if_fail(from != NULL && to != NULL);
+
+	title = g_strdup_printf(_("Notice from: %s"), from);
+	content = g_strdup_printf(_("%s"), msg_utf8);
+
+	purple_notify_info(gc, NULL, title, content);
+	g_free(title);
+	g_free(content);
+}
+
 void qq_process_msg_sys(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
 {
 	qq_data *qd;
@@ -320,9 +335,12 @@
 		case QQ_MSG_SYS_ADD_CONTACT_REJECTED:
 			_qq_process_msg_sys_add_contact_rejected(gc, from, to, msg_utf8);
 			break;
+		case QQ_MSG_SYS_NOTICE:
+			_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
+			break;
 		case QQ_MSG_SYS_NEW_VERSION:
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "QQ server says there is newer version than %s\n", qq_get_source_str(QQ_CLIENT));
+				   "QQ server says there is newer version than %s\n", qq_get_ver_desc(QQ_CLIENT));
 			break;
 		default:
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Recv unknown sys msg code: %s\n", code);
--- a/libpurple/protocols/qq/utils.c	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Sat Aug 02 15:00:46 2008 +0000
@@ -30,6 +30,8 @@
 #include "win32dep.h"
 #endif
 
+#include "cipher.h"
+
 #include "char_conv.h"
 #include "debug.h"
 #include "prefs.h"
@@ -50,6 +52,21 @@
    }
    */
 
+void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len)
+{
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
+
+	g_return_if_fail(md5 != NULL && md5_len > 0);
+	g_return_if_fail(src != NULL && src_len > 0);
+
+	cipher = purple_ciphers_find_cipher("md5");
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, src, src_len);
+	purple_cipher_context_digest(context, md5_len, md5, NULL);
+	purple_cipher_context_destroy(context);
+}
+
 gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount)
 {
 	gint index;
@@ -97,7 +114,7 @@
 	g_memmove(input, data, len);
 	input[len] = 0x00;
 
-	segments = g_strsplit((gchar *) input, delimit, 0);
+	segments = g_strsplit_set((gchar *) input, delimit, 0);
 	if (expected_fields <= 0)
 		return segments;
 
@@ -123,10 +140,20 @@
 	return segments;
 }
 
-/* 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)
+/* convert Purple name to original QQ UID */
+guint32 purple_name_to_uid(const gchar *const name)
 {
+	guint32 ret;
+	g_return_val_if_fail(name != NULL, 0);
+
+	ret = strtol(name, NULL, 10);
+	if (errno == ERANGE)
+		return 0;
+	else
+		return ret;
+}
+
+gchar *gen_ip_str(guint8 *ip) {
 	gchar *ret;
 	if (ip == NULL || ip[0] == 0) {
 		ret = g_new(gchar, 1);
@@ -149,19 +176,6 @@
 	return ip;
 }
 
-/* convert Purple name to original QQ UID */
-guint32 purple_name_to_uid(const gchar *const name)
-{
-	guint32 ret;
-	g_return_val_if_fail(name != NULL, 0);
-
-	ret = strtol(name, NULL, 10);
-	if (errno == ERANGE)
-		return 0;
-	else
-		return ret;
-}
-
 /* convert a QQ UID to a unique name of Purple
  * the return needs to be freed */
 gchar *uid_to_purple_name(guint32 uid)
--- a/libpurple/protocols/qq/utils.h	Sat Aug 02 02:40:46 2008 +0000
+++ b/libpurple/protocols/qq/utils.h	Sat Aug 02 15:00:46 2008 +0000
@@ -30,6 +30,8 @@
 
 #include "debug.h"
 
+void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len);
+
 gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount);
 gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount);
 gint qq_string_to_dec_value(const gchar *str);