pidgin: be1a4761: 2008.08.02 - csyfek <csyfek(at)gmail.com...

csyfek at gmail.com csyfek at gmail.com
Sat Aug 2 11:01:03 EDT 2008


-----------------------------------------------------------------
Revision: be1a47612286073925019e591d62e83d15029d43
Ancestor: d3a76f1c86220df41fa903d3d4153e57d619ba72
Author: csyfek at gmail.com
Date: 2008-08-02T15:00:46
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/be1a47612286073925019e591d62e83d15029d43

Deleted entries:
        libpurple/protocols/qq/buddy_status.c
        libpurple/protocols/qq/buddy_status.h
        libpurple/protocols/qq/keep_alive.c
        libpurple/protocols/qq/keep_alive.h
        libpurple/protocols/qq/login_logout.c
        libpurple/protocols/qq/login_logout.h
Modified 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/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/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

ChangeLog: 

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.

-------------- next part --------------
============================================================
--- libpurple/protocols/qq/ChangeLog	73a8bc69881e60328d0e872cd5a55257665a6483
+++ libpurple/protocols/qq/ChangeLog	7e7c75bdf3dc9c5f02470a641ce2dba6711be52e
@@ -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
============================================================
--- libpurple/protocols/qq/Makefile.am	a9b20ab3e91464d161b7c78ba9cfc9d25bd1bcdc
+++ libpurple/protocols/qq/Makefile.am	ff326d6ead2597bd6b18c857daefd45e9451763b
@@ -10,8 +10,6 @@ QQSOURCES = \
 	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 @@ QQSOURCES = \
 	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 \
============================================================
--- libpurple/protocols/qq/Makefile.mingw	23928c33a77a8e87581e0641e317e9314a2279b6
+++ libpurple/protocols/qq/Makefile.mingw	20f61ca6face03378f28f46641e88f9a5cf80324
@@ -59,8 +59,8 @@ C_SRC = \
 	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 \
============================================================
--- libpurple/protocols/qq/buddy_info.c	3cff03ecf256f61a931ead954f452c2b137d0a89
+++ libpurple/protocols/qq/buddy_info.c	9d5e271dfec3c9a89f7470d2af373ba227d1f0f9
@@ -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 @@ static const gchar *genders[] = {
 };
 
 #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 @@ void qq_send_packet_get_buddies_levels(P
 	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 @@ void qq_process_get_level_reply(guint8 *
 		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 @@ void qq_process_get_level_reply(guint8 *
 	}
 	g_free(decr_buf);
 }
+
============================================================
--- libpurple/protocols/qq/buddy_info.h	9f3aa1b853dfc67aaafd2b0d97049bc5717d5bd5
+++ libpurple/protocols/qq/buddy_info.h	b63009733e1b5cca08c936c36e4f8cef73b61918
@@ -31,12 +31,35 @@
 #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
 #define QQ_BUDDY_GENDER_UNKNOWN     0xff
@@ -54,5 +77,4 @@ void qq_process_get_level_reply(guint8 *
 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
============================================================
--- libpurple/protocols/qq/buddy_list.c	906b0927b04501015c5124a010240d4b5f857f04
+++ libpurple/protocols/qq/buddy_list.c	76d8307bbed0e359d116c9dc2ef1a14220fb68e0
@@ -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 @@ void qq_send_packet_get_buddies_online(P
 	/* 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 @@ void qq_send_packet_get_buddies_list(Pur
 	 * 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 @@ void qq_send_packet_get_all_list_with_gr
 	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);
+	g_return_val_if_fail(data != NULL && bs != NULL, -1);
 
-	qq_buddy_status_dump_unclear(fe->s);
+	/* 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);
 
-	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);
+	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);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Online buddy entry, %s", dump->str);
-	g_string_free(dump, TRUE);
+	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 @@ void qq_process_get_buddies_online_reply
 	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 @@ void qq_process_get_buddies_online_reply
 
 	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 @@ void qq_process_get_buddies_online_reply
 
 	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 @@ void qq_process_get_buddies_list_reply(g
 	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 @@ void qq_process_get_buddies_list_reply(g
 
 	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 @@ void qq_process_get_buddies_list_reply(g
 		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 @@ void qq_process_get_buddies_list_reply(g
 
 		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 @@ void qq_process_get_buddies_list_reply(g
 
 	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 @@ void qq_process_get_all_list_with_group_
 	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 @@ void qq_process_get_all_list_with_group_
 
 	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 @@ void qq_process_get_all_list_with_group_
 				"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;
+}
 
-	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);
+#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 {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies and groups received\n"); 
+		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 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;
+	}
+}
============================================================
--- libpurple/protocols/qq/buddy_list.h	2a72afb23291c4bdb83877f7e76f14ecc4fb96ee
+++ libpurple/protocols/qq/buddy_list.h	77ac1b00f63233dda88e29b697645cc01f5493c4
@@ -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
============================================================
--- libpurple/protocols/qq/buddy_opt.c	146cf2fc1c753795a8600427c2ebf102594a087d
+++ libpurple/protocols/qq/buddy_opt.c	fb4951e03720cc67cc1fc6cfb53f68a36324f5e6
@@ -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 @@ static void _qq_send_packet_remove_buddy
 	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 @@ static void _qq_send_packet_add_buddy(Pu
 
 	/* 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 @@ PurpleBuddy *qq_add_buddy_by_recv_packet
 		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);
============================================================
--- libpurple/protocols/qq/char_conv.c	316f649d60fd1d167e8c038238600efa2ee39e88
+++ libpurple/protocols/qq/char_conv.c	56df688312bd04d8a02cd864840335c7ee7a0f06
@@ -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 @@ static gchar *_my_convert(const gchar *s
 	}
 	
 	/* 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 @@ gchar *qq_encode_to_purple(guint8 *data,
 	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 @@ gchar *qq_smiley_to_purple(gchar *text)
 	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 @@ void qq_filter_str(gchar *str) {
 	}
 
 	for (temp = str; *temp != 0; temp++) {
-		if (*temp == '\r' || *temp == '\n')  *temp = 0x20;
+		if (*temp == '\r' || *temp == '\n')  *temp = ' ';
 	}
 	g_strstrip(str);
 }
============================================================
--- libpurple/protocols/qq/crypt.c	4a7a050f9607ba9138880ef943217e060f773be6
+++ libpurple/protocols/qq/crypt.c	5b4886002db0f2d9c8a152b74e3616484c9d02b1
@@ -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 @@ 0x61C88647 is what we can track on the A
 #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 @@ static void qq_encipher(guint32 *const v
 /* 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 @@ static void qq_encipher(guint32 *const v
 	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
+		}
+	}
 
-	/* encrypt it */
-	qq_encipher((guint32 *) plain, (guint32 *) key, (guint32 *) *crypted);
+	g_memmove(ptr_p, plain, 8);
+	g_memmove(ptr_k, key, 16);
+	g_memmove(ptr_c, *crypted, 8);
 
-	**(guint64 **) crypted ^= *(guint64 *) plain_pre_8;
+	/* encrypt it */
+	qq_encipher(ptr_p, ptr_k, ptr_c);
+	
+	g_memmove(plain, ptr_p, 8);
+	g_memmove(*crypted, ptr_c, 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
 
 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,13 +267,26 @@ static gint decrypt_block(const guint8 *
 		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;
 	*pos_in_block = 0;
@@ -218,6 +302,10 @@ gint qq_decrypt(const guint8 *const inst
 	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 @@ gint qq_decrypt(const guint8 *const inst
 			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 @@ gint qq_decrypt(const guint8 *const inst
 			}
 		}
 	}
-	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;
 }
-*/
============================================================
--- libpurple/protocols/qq/crypt.h	16df22183eec44028772c661d7327f4930d7a2f9
+++ libpurple/protocols/qq/crypt.h	ed5302af7842ce7b31d39a03b300ee8d995ff5a7
@@ -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 inst
 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
============================================================
--- libpurple/protocols/qq/file_trans.c	4b99d365724c1f0e4fbef73a1febe2fbe78a46ab
+++ libpurple/protocols/qq/file_trans.c	afa2d56d1161520729e657c6d4fa8e7f134decd5
@@ -76,26 +76,10 @@ static guint32 _encrypt_qq_uid(guint32 u
 	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 @@ static void _fill_file_md5(const gchar *
 		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 @@ static gint _qq_send_file(PurpleConnecti
 	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 @@ static void _qq_send_file_data_packet(Pu
 	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 @@ static void _qq_send_file_data_packet(Pu
 	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 @@ static void _qq_send_file_data_packet(Pu
 			{
 				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;
 
============================================================
--- libpurple/protocols/qq/group_conv.c	06a153c6094980988b32679ba7b6f872810ee7e9
+++ libpurple/protocols/qq/group_conv.c	a98dc976b1feae948a46b3b27d97234555cb5d70
@@ -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 */
============================================================
--- libpurple/protocols/qq/group_find.c	c6b6a73c90a5eb16cd29152b82f29401eaddf970
+++ libpurple/protocols/qq/group_find.c	cb294c1cc3021fc018e4a39e8e7c9907d7faae3d
@@ -138,6 +138,9 @@ qq_group *qq_group_find_by_channel(Purpl
 	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;
============================================================
--- libpurple/protocols/qq/group_free.c	a6e02a48de11d2ecd297a223947daa62a5e5531c
+++ libpurple/protocols/qq/group_free.c	61ab0607233c10f6e75d2b19f436814edfc31b11
@@ -26,7 +26,7 @@
 
 #include "debug.h"
 
-#include "buddy_status.h"
+#include "buddy_list.h"
 #include "group_free.h"
 #include "group_network.h"
 
============================================================
--- libpurple/protocols/qq/group_im.c	49ed251aa1f57b3bc77d439219c5cd39d6f52495
+++ libpurple/protocols/qq/group_im.c	a6e45f13c82a305174b6f5269c82585e2bd16faf
@@ -65,7 +65,7 @@ void qq_send_packet_group_im(PurpleConne
 	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 @@ void qq_process_recv_group_im(guint8 *da
 
 	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 @@ void qq_process_recv_group_im(guint8 *da
 
 	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));
 	}
============================================================
--- libpurple/protocols/qq/group_info.c	c5dbfee778718acbbeac596e79d5f23bc86e99be
+++ libpurple/protocols/qq/group_info.c	39bcdf25ad56aebdccc5a87ce8e1066e770c06b9
@@ -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 @@ void qq_send_cmd_group_get_group_info(Pu
 }
 
 /* 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 @@ void qq_send_cmd_group_get_online_member
 	/* 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 @@ void qq_send_cmd_group_get_members_info(
 	}
 
 	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 @@ void qq_process_group_cmd_get_group_info
 		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 @@ void qq_process_group_cmd_get_group_info
 			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 @@ void qq_process_group_cmd_get_members_in
 		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 @@ void qq_process_group_cmd_get_members_in
 		/*
 		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);
 		}
 		*/
 
============================================================
--- libpurple/protocols/qq/group_info.h	52eec1ab02b318b78afb8a06aeebba460cf527d2
+++ libpurple/protocols/qq/group_info.h	9127235043443f56f167c0789974d00f099ff9e8
@@ -31,7 +31,10 @@ void qq_send_cmd_group_get_online_member
 
 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);
============================================================
--- libpurple/protocols/qq/group_network.c	7a8524aa1a8ffa073f272f1f14757f681784f18f
+++ libpurple/protocols/qq/group_network.c	093a1526f80f99ea6c694cecf4a0be196c767b7c
@@ -75,6 +75,30 @@ const gchar *qq_group_cmd_get_desc(qq_gr
 		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";
 	}
============================================================
--- libpurple/protocols/qq/group_network.h	662b2dd15c60b3bcd0f02560a57e3c1485c4fc0b
+++ libpurple/protocols/qq/group_network.h	ab9c13d22127ff0de73a6bd9f70d590c1e9341a6
@@ -42,7 +42,20 @@ typedef enum {
 	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 {
============================================================
--- libpurple/protocols/qq/header_info.c	56a3ab75a575cc12394e74fa365c006be046a7fc
+++ libpurple/protocols/qq/header_info.c	c6c9cf5f72e960b0a7772174c2a14cbedcf9f011
@@ -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 @@ const gchar *qq_get_cmd_desc(gint type)
 		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 @@ const gchar *qq_get_cmd_desc(gint type)
 		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 @@ const gchar *qq_get_source_str(gint sour
 		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";
 	}
 }
============================================================
--- libpurple/protocols/qq/header_info.h	56412374395e0c968eb4e52afc777a552970c71d
+++ libpurple/protocols/qq/header_info.h	f85e775fc860c2e17409a57da6220bfbaec4b545
@@ -42,8 +42,8 @@ enum {
 	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 @@ enum {
 	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
============================================================
--- libpurple/protocols/qq/im.c	d8305a7e2561f666f2f8e7f594d8e70786ab255c
+++ libpurple/protocols/qq/im.c	2cd45a4fefd3dc05b7a4da3220e19254b8fff42b
@@ -79,7 +79,7 @@ struct _qq_recv_normal_im_common {
 	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 @@ struct _qq_recv_im_header {
 	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 @@ guint8 *qq_get_send_im_tail(const gchar 
 	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 @@ static gint _qq_normal_im_common_read(gu
 	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 @@ static void _qq_process_recv_normal_im_t
 	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 @@ static void _qq_process_recv_normal_im_t
 	}			/* 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 @@ static void _qq_process_recv_normal_im(g
 	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 @@ static void _qq_process_recv_normal_im(g
 					"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 @@ void qq_process_recv_im(guint8 *buf, gin
 	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 */
============================================================
--- libpurple/protocols/qq/packet_parse.c	feec9428b30781aa23b804a7011a7923558de6a0
+++ libpurple/protocols/qq/packet_parse.c	803b01df2d219f62afc4b1813faca0f67c82e5ae
@@ -27,7 +27,6 @@
 #include "packet_parse.h"
 #include "debug.h"
 
-
 /*------------------------------------------------PUT------------------------------------------------*/
 
 /* note:
@@ -68,7 +67,6 @@ gint qq_get16(guint16 *w, guint8 *buf)
 	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 @@ gint qq_get32(guint32 *dw, guint8 *buf)
 	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 @@ gint qq_put32(guint8 *buf, guint32 dw)
     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 */
============================================================
--- libpurple/protocols/qq/packet_parse.h	3da5208644cd48d00eb4f39622119a237a14d023
+++ libpurple/protocols/qq/packet_parse.h	90c1324d256d6a85ad0baf0f7aa577c52dae4234
@@ -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);
 
 /*
============================================================
--- libpurple/protocols/qq/qq.c	ec0c1145646c14c17ea491407e2cc6ac75adac39
+++ libpurple/protocols/qq/qq.c	9305c65d6c76fa48de937bfad5daa3b940cc04ec
@@ -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,79 +237,111 @@ static void _qq_tooltip_text(PurpleBuddy
 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)
-	{
-		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);
+	/* 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_free(ip_str);
+		g_string_free(str, TRUE);
+	}
 
-		tmp = g_strdup_printf("%d", q_bud->age);
-		purple_notify_user_info_add_pair(user_info, _("Age"), tmp);
+	tmp = g_strdup_printf("%d", q_bud->age);
+	purple_notify_user_info_add_pair(user_info, _("Age"), 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);
+	}
 
-		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;
+	
+	if (!b || !(q_bud = b->proto_data)) {
+		return NULL;
+	}
 
-	qq_buddy *q_bud = b->proto_data;
+	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";
 
-	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";
-		*/
-	}
-
 	return NULL;
 }
 
@@ -393,6 +425,7 @@ static int _qq_chat_send(PurpleConnectio
 	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 @@ static void _qq_menu_show_login_info(Pur
 	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 @@ static void _qq_menu_show_login_info(Pur
 	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 @@ static GList *_qq_buddy_menu(PurpleBlist
 	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 @@ static PurplePluginProtocolInfo prpl_inf
 	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 @@ static PurplePluginProtocolInfo prpl_inf
 	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 @@ static void init_plugin(PurplePlugin *pl
 	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");
============================================================
--- libpurple/protocols/qq/qq.h	94529661816228150ae7d79580c7007509cc9afc
+++ libpurple/protocols/qq/qq.h	d7c03768b041fdfbecb593d720bdf9e795b2e56b
@@ -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,17 +44,24 @@ typedef struct _qq_buddy qq_buddy;
 
 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;
 	guint16 face;		/* index: 0 - 299 */
 	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 @@ struct _qq_data {
 	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;
-
-	guint8 rcv_window[1 << 13];		/* windows for check duplicate packet */
-	GQueue *rcv_trans;		/* queue to store packet can not process before login */
+	qq_interval itv_config;
+	qq_interval itv_count;
+	guint network_timeout;
 	
+	GList *transactions;	/* check ack packet and resend */
+
 	/* tcp related */
 	PurpleCircBuffer *tcp_txbuf;
 	guint8 *tcp_rxqueue;
@@ -103,10 +109,12 @@ struct _qq_data {
 	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 @@ struct _qq_data {
 	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;
============================================================
--- libpurple/protocols/qq/qq_network.c	6ad4cae7efb0f7cd017ebc0ac3beebbf0c90570f
+++ libpurple/protocols/qq/qq_network.c	07fffe30f3c3831cb1fd50cc4b4bc2d1a8223510
@@ -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 @@ static gboolean set_new_server(qq_data *
 	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 @@ static gint packet_get_header(guint8 *he
 	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 @@ static void reconnect_later(PurpleConnec
 		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 @@ static void packet_process(PurpleConnect
 	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 @@ static void packet_process(PurpleConnect
 	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 @@ static void packet_process(PurpleConnect
 
 	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 @@ static void tcp_pending(gpointer data, g
 	 *  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 @@ static void tcp_pending(gpointer data, g
 			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 @@ static void tcp_pending(gpointer data, g
 		/* 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 @@ static gint udp_send_out(qq_data *qd, gu
 
 	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 @@ static gint udp_send_out(qq_data *qd, gu
 	
 	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 @@ static gint tcp_send_out(qq_data *qd, gu
 	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 @@ static gint tcp_send_out(qq_data *qd, gu
 		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 @@ static gint tcp_send_out(qq_data *qd, gu
 	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;
+	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;
+	}
+
+	if ( !qd->logged_in ) {
+		return TRUE;
+	}
 	
-	index = 0;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	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;
+	}
 
-	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->itv_config.update <= 0) {
+		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));
-		}
+	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 @@ static void qq_connect_cb(gpointer data,
 	PurpleConnection *gc;
 	gchar *conn_msg;
 	const gchar *passwd;
+	PurpleAccount *account ;
 
 	gc = (PurpleConnection *) data;
 
@@ -768,6 +570,7 @@ static void qq_connect_cb(gpointer data,
 	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 @@ static void qq_connect_cb(gpointer data,
 
 	/* now generate md5 processed passwd */
 	passwd = purple_account_get_password(purple_connection_get_account(gc));
-	g_return_if_fail(qd->pwkey == NULL);
-	qd->pwkey = encrypt_account_password(passwd);
 
-	g_return_if_fail(qd->resend_timeout == 0);
-	/* call trans_timeout every 5 seconds */
-	qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc);
+	/* 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;
+
+	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 @@ static void qq_connect_cb(gpointer data,
 	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 @@ void qq_disconnect(PurpleConnection *gc)
 	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,36 +918,21 @@ void qq_disconnect(PurpleConnection *gc)
 		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->token) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n");
+		g_free(qd->token);
+		qd->token = NULL;
+		qd->token_len = 0;
 	}
-	if (qd->pwkey) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n");
-		g_free(qd->pwkey);
-		qd->pwkey = NULL;
-	}
-	if (qd->session_key) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n");
-		g_free(qd->session_key);
-		qd->session_key = NULL;
-	}
-	if (qd->session_md5) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n");
-		g_free(qd->session_md5);
-		qd->session_md5 = NULL;
-	}
-	if (qd->my_ip) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n");
-		g_free(qd->my_ip);
-		qd->my_ip = NULL;
-	}
+	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);
 	qq_add_buddy_request_free(qd);
@@ -1168,19 +979,19 @@ static gint encap(qq_data *qd, guint8 *b
 	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 @@ gint qq_send_data(qq_data *qd, guint16 c
 		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 @@ gint qq_send_data(qq_data *qd, guint16 c
 	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 @@ gint qq_send_cmd_detail(qq_data *qd, gui
 
 	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);
============================================================
--- libpurple/protocols/qq/qq_network.h	c90f2507036e9f1dddd640837bf37d61fd08ea7a
+++ libpurple/protocols/qq/qq_network.h	2f70387ea9748845a277706943610bed929cac2f
@@ -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_connect_later(PurpleConnection *
 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);
 
============================================================
--- libpurple/protocols/qq/qq_trans.c	c7e1d07b62f30ee8f95051c33568438fbd31ceb8
+++ libpurple/protocols/qq/qq_trans.c	da3d12ff33f5c9faaf6581a4b60a3ee9bda911bc
@@ -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 */
+#define QQ_RESEND_MAX               3	/* max resend per packet */
 
-typedef struct _transaction {
-	guint16 seq;
-	guint16 cmd;
-	guint8 *buf;
-	gint buf_len;
+qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq)
+{
+	GList *curr;
+	GList *next;
+	qq_transaction *trans;
 
-	gint fd;
-	gint retries;
-	time_t create_time;
-} transaction;
+	if (qd->transactions == NULL) {
+		return NULL;
+	}
 
-void qq_send_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
-{
-	transaction *trans = g_new0(transaction, 1);
+	next = qd->transactions;
+	while( (curr = next) ) {
+		next = curr->next;
+		
+		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;
+		}
+	}
 
-	g_return_if_fail(trans != NULL);
+	return NULL;
+}
 
-	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;
+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;
+}
 
-	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);
+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 */
-void qq_send_trans_remove(qq_data *qd, gpointer data) 
+static void trans_remove(qq_data *qd, qq_transaction *trans) 
 {
-	transaction *trans = (transaction *)data;
-
-	g_return_if_fail(qd != NULL && data != NULL);
+	g_return_if_fail(qd != NULL && trans != NULL);
 	
-	purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"ack [%05d] %s, remove from send tranactions\n",
-				trans->seq, qq_get_cmd_desc(trans->cmd));
+	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->buf)	g_free(trans->buf);
-	qd->send_trans = g_list_remove(qd->send_trans, trans);
+	if (trans->data)	g_free(trans->data);
+	qd->transactions = g_list_remove(qd->transactions, trans);
 	g_free(trans);
 }
 
-gpointer qq_send_trans_find(qq_data *qd, guint16 cmd, guint16 seq)
+void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
 {
-	GList *curr;
-	GList *next;
-	transaction *trans;
+	qq_transaction *trans = g_new0(qq_transaction, 1);
 
-	curr = qd->send_trans;
-	while(curr) {
-		next = curr->next;
-		trans = (transaction *) (curr->data);
-		if(trans->cmd == cmd && trans->seq == seq) {
-			return trans;
-		}
-		curr = next;
+	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;
 	}
-
-	return NULL;
+	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);
 }
 
-/* clean up send trans and free all contents */
-void qq_send_trans_remove_all(qq_data *qd)
+void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
 {
-	GList *curr;
-	GList *next;
-	transaction *trans;
-	gint count = 0;
+	qq_transaction *trans = g_new0(qq_transaction, 1);
 
-	curr = qd->send_trans;
-	while(curr) {
-		next = curr->next;
-		
-		trans = (transaction *) (curr->data);
-		/*
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			"Remove to transaction, seq = %d, buf = %p, len = %d\n",
-			trans->seq, trans->buf, trans->len);
-		*/
-		qq_send_trans_remove(qd, trans);
+	g_return_if_fail(trans != NULL);
 
-		count++;
-		curr = next;
+	trans->flag = QQ_TRANS_IS_SERVER;
+	if ( !qd->logged_in ) {
+		trans->flag |= QQ_TRANS_BEFORE_LOGIN;
 	}
-	g_list_free(qd->send_trans);
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count);
+	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);
 }
 
-gint qq_send_trans_scan(qq_data *qd, gint *start,
-	guint8 *buf, gint maxlen, guint16 *cmd, gint *retries)
+void qq_trans_process_before_login(qq_data *qd)
 {
 	GList *curr;
-	GList *next = NULL;
-	transaction *trans;
-	gint copylen;
+	GList *next;
+	qq_transaction *trans;
 
-	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) {
+	g_return_if_fail(qd != NULL);
+
+	next = qd->transactions;
+	while( (curr = next) ) {
 		next = curr->next;
-		*start = g_list_position(qd->send_trans, next);
+		trans = (qq_transaction *) (curr->data);
+		/* purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
 		
-		trans = (transaction *) (curr->data);
-		if (trans->buf == NULL || trans->buf_len <= 0) {
-			qq_send_trans_remove(qd, trans);
-			curr = next;
+		if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) {
 			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;
+		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",
-				"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);
+		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);
 
-		*cmd = trans->cmd;
-		*retries = trans->retries;
-		trans->retries--;
-		return copylen;
+		qq_proc_cmd_reply(qd->gc, trans->seq, trans->cmd, trans->data, trans->data_len);
 	}
 
-	/* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */
-	return -1;
+	/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+	return;
 }
 
-void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+gboolean qq_trans_scan(qq_data *qd)
 {
-	transaction *trans = g_new0(transaction, 1);
+	GList *curr;
+	GList *next;
+	qq_transaction *trans;
 
-	g_return_if_fail(data != NULL && data_len > 0);
-	g_return_if_fail(trans != NULL);
+	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->cmd = cmd;
-	trans->seq = seq;
-	trans->buf = g_memdup(data, data_len);
-	trans->buf_len = data_len;
-	trans->create_time = time(NULL);
+		trans->scan_times++;
+		if (trans->scan_times <= 1) {
+			/* skip in 10 seconds */
+			continue;
+		}
 
-	if (qd->rcv_trans == NULL)
-		qd->rcv_trans = g_queue_new();
+		if (trans->rcved_times > 0) {
+			/* Has been received */
+			trans_remove(qd, trans);
+			continue;
+		}
 
-	g_queue_push_head(qd->rcv_trans, trans);
-}
+		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;
+			}
 
-gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len)
-{
-	transaction *trans = NULL;
-	gint copy_len;
+			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;
+		}
 
-	g_return_val_if_fail(data != NULL && max_len > 0, -1);
-
-	if (g_queue_is_empty(qd->rcv_trans)) {
-		return -1;
+		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);
 	}
-	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;
+	/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+	return FALSE;
 }
 
-/* clean up the packets before login */
-void qq_rcv_trans_remove_all(qq_data *qd)
+/* clean up send trans and free all contents */
+void qq_trans_remove_all(qq_data *qd)
 {
-	transaction *trans = NULL;
+	GList *curr;
+	GList *next;
+	qq_transaction *trans;
 	gint count = 0;
 
-	g_return_if_fail(qd != NULL);
+	curr = qd->transactions;
+	while(curr) {
+		next = curr->next;
+		
+		trans = (qq_transaction *) (curr->data);
+		/*
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+			"Remove to transaction, seq = %d, buf = %p, len = %d\n",
+			trans->seq, trans->buf, trans->len);
+		*/
+		trans_remove(qd, trans);
 
-	/* 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);
+		count++;
+		curr = next;
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count);
+	g_list_free(qd->transactions);
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Free all %d packets\n", count);
 }
============================================================
--- libpurple/protocols/qq/qq_trans.h	c385d8b826f9efcbe8b48e9d0cef782be95dfc96
+++ libpurple/protocols/qq/qq_trans.h	152a4c325a00e270481adb20c678c261adad8e1b
@@ -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*/
+};
 
-gint qq_send_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries);
+typedef struct _qq_transaction {
+	guint8 flag;
+	guint16 seq;
+	guint16 cmd;
+	guint8 *data;
+	gint data_len;
 
-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);
+	gint fd;
+	gint send_retries;
+	gint rcved_times;
+	gint scan_times;
+} qq_transaction;
 
+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
============================================================
--- libpurple/protocols/qq/send_file.c	d11d0018e84e3afb67be3475bd9c19ea974711ca
+++ libpurple/protocols/qq/send_file.c	5a7c5b24f8d233f6da1d169dd27f54bb9011e3c5
@@ -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 @@ static void _qq_send_packet_file_request
 	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 @@ void qq_process_recv_file_request(guint8
 	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 @@ void qq_process_recv_file_request(guint8
 		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 @@ void qq_process_recv_file_request(guint8
 
 		}
 		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);
============================================================
--- libpurple/protocols/qq/send_file.h	0c49d07f93f5bfc229c98ef10b87925551f044c2
+++ libpurple/protocols/qq/send_file.h	5208ab6e8ace611f701df25b6c37299b65d32760
@@ -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;
============================================================
--- libpurple/protocols/qq/sys_msg.c	119ebdf9d586b1690044ac4ac828225ae8d48958
+++ libpurple/protocols/qq/sys_msg.c	612ba2d5f58891f8a8d660600c5b4f1196d1699d
@@ -44,6 +44,7 @@ enum {
 	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 @@ static void _qq_process_msg_sys_add_cont
 	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 @@ void qq_process_msg_sys(guint8 *buf, gin
 		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);
============================================================
--- libpurple/protocols/qq/utils.c	c1a41cb28f8fc57805e23c4bf2edcaa2ef9d8bac
+++ libpurple/protocols/qq/utils.c	e62d6b6fabdb3e5595da57a860caf125293a0c23
@@ -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 @@ gchar **split_data(guint8 *data, gint le
 	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 @@ gchar **split_data(guint8 *data, gint le
 	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 @@ guint8 *str_ip_gen(gchar *str) {
 	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)
============================================================
--- libpurple/protocols/qq/utils.h	2e90d8ad0ec426c0a9c85fb8e3999f0ec26c3a5a
+++ libpurple/protocols/qq/utils.h	a391181cf3622cac8ce340dc75b39779c362456b
@@ -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);


More information about the Commits mailing list