pidgin: 8cebefbc: 2008.08.16 - ccpaging <ecc_hy(at)hotmail...

csyfek at gmail.com csyfek at gmail.com
Fri Sep 12 10:30:41 EDT 2008


-----------------------------------------------------------------
Revision: 8cebefbc6cd5d84acb69c74e69e8821f11dd225d
Ancestor: f12c8903079425d7850fa183df0b3f937b2952be
Author: csyfek at gmail.com
Date: 2008-09-11T13:25:07
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/8cebefbc6cd5d84acb69c74e69e8821f11dd225d

Modified files:
        libpurple/protocols/qq/AUTHORS
        libpurple/protocols/qq/ChangeLog
        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/file_trans.c
        libpurple/protocols/qq/group.c
        libpurple/protocols/qq/group.h
        libpurple/protocols/qq/group_conv.c
        libpurple/protocols/qq/group_find.c
        libpurple/protocols/qq/group_find.h
        libpurple/protocols/qq/group_free.c
        libpurple/protocols/qq/group_im.c
        libpurple/protocols/qq/group_im.h
        libpurple/protocols/qq/group_info.c
        libpurple/protocols/qq/group_info.h
        libpurple/protocols/qq/group_internal.c
        libpurple/protocols/qq/group_internal.h
        libpurple/protocols/qq/group_join.c
        libpurple/protocols/qq/group_join.h
        libpurple/protocols/qq/group_opt.c
        libpurple/protocols/qq/group_opt.h
        libpurple/protocols/qq/group_search.c
        libpurple/protocols/qq/header_info.c
        libpurple/protocols/qq/header_info.h
        libpurple/protocols/qq/im.c libpurple/protocols/qq/im.h
        libpurple/protocols/qq/packet_parse.c
        libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq.h
        libpurple/protocols/qq/qq_base.c
        libpurple/protocols/qq/qq_base.h
        libpurple/protocols/qq/qq_network.c
        libpurple/protocols/qq/qq_network.h
        libpurple/protocols/qq/qq_process.c
        libpurple/protocols/qq/qq_process.h
        libpurple/protocols/qq/qq_trans.c
        libpurple/protocols/qq/qq_trans.h
        libpurple/protocols/qq/send_file.c
        libpurple/protocols/qq/sys_msg.c
        libpurple/protocols/qq/utils.c
        libpurple/protocols/qq/utils.h

ChangeLog: 

2008.08.16 - ccpaging <ecc_hy(at)hotmail.com>
	* Rename group to room. If you used pidginqq before, this may create a new room with same title, you may 
delete old one
	* Replace purple_debug with purple_debug_info, purple_debug_warning, purple_debug_error
	* Add server notice and server new, and two options to turn on/off
	* Minor modify for reducing transaction's debug infor
	* Minor modifies for system notice and QQ news.
	* Add 4 new strings need translate compare with p10.

-------------- next part --------------
============================================================
--- libpurple/protocols/qq/AUTHORS	c46c7dfe83578267955d8b41cb3a2e3df7c44577
+++ libpurple/protocols/qq/AUTHORS	9010c8d4979848a7d3c99d2a5338f11067a58ab9
@@ -1,35 +1,38 @@ Code Contributors
 Code Contributors
-=====
-puzzlebird  : original author
-gfhuang     : patches for libpurple 2.0.0beta2, maintainer
-henryouly   : file transfer, udp sock5 proxy and qq_show, maintainer
-hzhr        : maintainer
-joymarquis  : maintainer
-arfankai    : fixed bugs in char_conv.c
-rakescar    : provided filter for HTML tag
-yyw         : improved performance on PPC linux
-lvxiang     : provided ip to location original code
-markhuetsch : OpenQ merge into libpurple, maintainer 2006-2007
-ccpaging    : maintainer since 2007
-icesky      : maintainer since 2007
-csyfek      : faces, maintainer since 2007
+=========
+puzzlebird	: original author
+gfhuang		: patches for libpurple 2.0.0beta2, maintainer
+Yuan Qingyun	: patches for libpurple 1.5.0, maintainer
+henryouly	: file transfer, udp sock5 proxy and qq_show, maintainer
+hzhr		: maintainer
+joymarquis	: maintainer
+arfankai	: fixed bugs in char_conv.c
+rakescar	: provided filter for HTML tag
+yyw		: improved performance on PPC linux
+lvxiang		: provided ip to location original code
+markhuetsch	: OpenQ merge into libpurple, maintainer 2006-2007
+ccpaging	: maintainer since 2007
+icesky		: maintainer since 2007
+csyfek		: faces, maintainer since 2007
 
 Lovely Patch Writers
-=====
-gnap        : message displaying, documentation
-manphiz     : qun processing
-moo         : qun processing
-Coly Li     : qun processing
+=========
+gnap		: message displaying, documentation
+manphiz		: qun processing
+moo		: qun processing
+Coly Li		: qun processing
+Emil Alexiev	: captcha verification on login based on LumaQQ for MAC (2007), 
+		  login, add buddy, remove buddy, message exchange and logout
 
 Acknowledgement
-=====
-Shufeng Tan : http://sf.net/projects/perl-oicq
-Jeff Ye     : http://www.sinomac.com
-Hu Zheng    : http://forlinux.yeah.net
-yunfan      : http://www.myswear.net
+=========
+Shufeng Tan	: http://sf.net/projects/perl-oicq
+Jeff Ye		: http://www.sinomac.com
+Hu Zheng	: http://forlinux.yeah.net
+yunfan		: http://www.myswear.net
+OpenQ Team	: http://openq.linuxsir.org
+LumaQQ Team	: http://lumaqq.linuxsir.org
 khc at pidgin.im
 qulogic at pidgin.im
 rlaager at pidgin.im
-OpenQ Team
-LumaQQ Team
-OpenQ Google Group
+OpenQ Google Group	: http://groups.google.com/group/openq
============================================================
--- libpurple/protocols/qq/ChangeLog	4ed95b382ab28c61dc0a3833a75d776e61952d5e
+++ libpurple/protocols/qq/ChangeLog	d1c1aa5ec35e4616ee6b82d1902fb89aa8c8e8d8
@@ -1,6 +1,17 @@
+2008.08.16 - ccpaging <ecc_hy(at)hotmail.com>
+	* Rename group to room. If you used pidginqq before, this may create a new room with same title, you may delete old one
+	* Replace purple_debug with purple_debug_info, purple_debug_warning, purple_debug_error
+	* Add server notice and server new, and two options to turn on/off
+	* Minor modify for reducing transaction's debug infor
+	* Minor modifies for system notice and QQ news.
+	* Add 4 new strings need translate compare with p10.
+
 2008.08.10 - csyfek <csyfek(at)gmail.com>
 	* Commit to Pidgin
 
+2008.08.07 - ccpaging <ecc_hy(at)hotmail.com>
+	* Support managing multi-connections according to simple.c
+
 2008.08.06 - ccpaging <ecc_hy(at)hotmail.com>
 	* Rename names of variables, Group, to Room
 	* Functions of group_network merged into qq_network and qq_process
============================================================
--- libpurple/protocols/qq/buddy_info.c	6903a2b652ab9269e046c9bf44f9d11371a42a17
+++ libpurple/protocols/qq/buddy_info.c	8692742a441f53367bb7b6f606d7b59a74ecb2bc
@@ -284,7 +284,7 @@ void qq_send_packet_get_info(PurpleConne
 
 	qd = (qq_data *) gc->proto_data;
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(qd, QQ_CMD_GET_USER_INFO, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, (guint8 *) uid_str, strlen(uid_str));
 
 	query = g_new0(qq_info_query, 1);
 	query->uid = uid;
@@ -293,6 +293,20 @@ void qq_send_packet_get_info(PurpleConne
 	qd->info_query = g_list_append(qd->info_query, query);
 }
 
+void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
+		gint update_class, guint32 ship32)
+{
+	qq_data *qd;
+	gchar raw_data[16] = {0};
+
+	g_return_if_fail(uid != 0);
+
+	qd = (qq_data *) gc->proto_data;
+	g_snprintf(raw_data, sizeof(raw_data), "%d", uid);
+	qq_send_cmd_mess(gc, QQ_CMD_GET_USER_INFO, (guint8 *) raw_data, strlen(raw_data),
+			update_class, ship32);
+}
+
 /* set up the fields requesting personal information and send a get_info packet
  * for myself */
 void qq_prepare_modify_info(PurpleConnection *gc)
@@ -314,7 +328,6 @@ static void qq_send_packet_modify_info(P
 /* send packet to modify personal information */
 static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes = 0;
 	guint8 raw_data[MAX_PACKET_SIZE - 128] = {0};
 	guint8 bar;
@@ -446,7 +459,7 @@ static void qq_send_packet_modify_info(P
 
 	bytes += qq_put8(raw_data + bytes, bar);
 
-	qq_send_cmd(qd, QQ_CMD_UPDATE_INFO, raw_data, bytes);
+	qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, raw_data, bytes);
 
 }
 
@@ -697,8 +710,8 @@ void qq_process_modify_info_reply(guint8
 
 	data[data_len] = '\0';
 	if (qd->uid == atoi((gchar *) data)) {	/* return should be my uid */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Update info ACK OK\n");
-		purple_notify_info(gc, NULL, _("Your information has been updated"), NULL);
+		purple_debug_info("QQ", "Update info ACK OK\n");
+		purple_notify_info(gc, NULL, _("My information has been updated"), NULL);
 	}
 }
 
@@ -746,8 +759,8 @@ void qq_set_my_buddy_icon(PurpleConnecti
 	const gchar *buddy_icon_dir = qq_buddy_icon_dir();
 	gint prefix_len = strlen(QQ_ICON_PREFIX);
 	gint suffix_len = strlen(QQ_ICON_SUFFIX);
-	gint dir_len = buddy_icon_dir ? strlen(buddy_icon_dir) : 0;
-	gchar *errmsg = g_strdup_printf(_("Setting custom faces is not currently supported. Please choose an image from %s."), buddy_icon_dir ? buddy_icon_dir : "(null)");
+	gint dir_len = strlen(buddy_icon_dir);
+	gchar *errmsg = g_strdup_printf(_("Setting custom faces is not currently supported. Please choose an image from %s."), buddy_icon_dir);
 	gboolean icon_global = purple_account_get_bool(gc->account, "use-global-buddyicon", TRUE);
 
 	if (!icon_path)
@@ -756,13 +769,13 @@ void qq_set_my_buddy_icon(PurpleConnecti
 	icon_len = strlen(icon_path) - dir_len - 1 - prefix_len - suffix_len;
 
 	/* make sure we're using an appropriate icon */
-	if (buddy_icon_dir && !(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0
+	if (!(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0
 				&& icon_path[dir_len] == G_DIR_SEPARATOR
 				&& g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0
 				&& g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0
 				&& icon_len <= 3)) {
 		if (icon_global)
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", errmsg);
+			purple_debug_error("QQ", "%s\n", errmsg);
 		else
 			purple_notify_error(gc, _("Invalid QQ Face"), errmsg, NULL);
 		g_free(errmsg);
@@ -775,7 +788,7 @@ void qq_set_my_buddy_icon(PurpleConnecti
 	/* ensure face number in proper range */
 	if (icon_num > QQ_FACES) {
 		if (icon_global)
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", errmsg);
+			purple_debug_error("QQ", "%s\n", errmsg);
 		else
 			purple_notify_error(gc, _("Invalid QQ Face"), errmsg, NULL);
 		g_free(errmsg);
@@ -798,8 +811,8 @@ static void _qq_update_buddy_icon(Purple
 	if ((buddy = purple_find_buddy(account, name)))
 		old_icon_num = purple_buddy_icons_get_checksum_for_user(buddy);
 
-	if ((old_icon_num == NULL ||
-			strcmp(icon_num_str, old_icon_num)) && (qq_buddy_icon_dir() != NULL))
+	if (old_icon_num == NULL ||
+			strcmp(icon_num_str, old_icon_num))
 	{
 		gchar *icon_path;
 
@@ -902,19 +915,21 @@ void qq_info_query_free(qq_data *qd)
 
 void qq_info_query_free(qq_data *qd)
 {
-	gint i;
+	gint count;
 	qq_info_query *p;
 
 	g_return_if_fail(qd != NULL);
 
-	i = 0;
+	count = 0;
 	while (qd->info_query != NULL) {
 		p = (qq_info_query *) (qd->info_query->data);
 		qd->info_query = g_list_remove(qd->info_query, p);
 		g_free(p);
-		i++;
+		count++;
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d info queries are freed!\n", i);
+	if (count > 0) {
+		purple_debug_info("QQ", "%d info queries are freed!\n", count);
+	}
 }
 
 void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid)
@@ -927,10 +942,10 @@ void qq_send_packet_get_level(PurpleConn
 	bytes += qq_put32(buf + bytes, uid);
 
 	qd = (qq_data *) gc->proto_data;
-	qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, bytes);
+	qq_send_cmd(gc, QQ_CMD_GET_LEVEL, buf, bytes);
 }
 
-void qq_send_packet_get_buddies_levels(PurpleConnection *gc)
+void qq_request_get_buddies_levels(PurpleConnection *gc, gint update_class)
 {
 	guint8 *buf;
 	guint16 size;
@@ -942,12 +957,11 @@ void qq_send_packet_get_buddies_levels(P
 	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;
+	/* server only reply levels for online buddies */
+	size = 4 * g_list_length(qd->buddies) + 1 + 4;
 	buf = g_newa(guint8, size);
 	bytes += qq_put8(buf + bytes, 0x00);
-	
+
 	while (NULL != node) {
 		q_bud = (qq_buddy *) node->data;
 		if (NULL != q_bud) {
@@ -955,7 +969,10 @@ void qq_send_packet_get_buddies_levels(P
 		}
 		node = node->next;
 	}
-	qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
+
+	/* my id should be the end if included */
+	bytes += qq_put32(buf + bytes, qd->uid);
+	qq_send_cmd_mess(gc, QQ_CMD_GET_LEVEL, buf, size, update_class, 0);
 }
 
 void qq_process_get_level_reply(guint8 *decr_buf, gint decr_len, PurpleConnection *gc)
@@ -970,9 +987,9 @@ void qq_process_get_level_reply(guint8 *
 	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes = 0;
 
-	decr_len--; 
+	decr_len--;
 	if (decr_len % 12 != 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+		purple_debug_error("QQ",
 				"Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
 		decr_len -= (decr_len % 12);
 	}
@@ -980,19 +997,18 @@ void qq_process_get_level_reply(guint8 *
 	bytes += 1;
 	/* this byte seems random */
 	/*
-	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]);
+	   purple_debug_info("QQ", "Byte one of get_level packet: %d\n", buf[0]);
 	   */
 	for (i = 0; i < decr_len; i += 12) {
 		bytes += qq_get32(&uid, decr_buf + bytes);
 		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", 
-				"%d, tmOnline: %d, level: %d, tmRemainder: %d\n", 
+		purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
 				uid, onlineTime, level, timeRemainder);
 		if (uid == qd->uid) {
 			qd->my_level = level;
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Got my levels as %d\n", qd->my_level);
+			purple_debug_warning("QQ", "Got my levels as %d\n", qd->my_level);
 			continue;
 		}
 
@@ -1000,7 +1016,7 @@ void qq_process_get_level_reply(guint8 *
 		if (purple_name == NULL) {
 			continue;
 		}
-		
+
 		b = purple_find_buddy(account, purple_name);
 		g_free(purple_name);
 
@@ -1008,10 +1024,9 @@ void qq_process_get_level_reply(guint8 *
 		if (b != NULL) {
 			q_bud = (qq_buddy *) b->proto_data;
 		}
-		
+
 		if (q_bud == NULL) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"Got levels of %d not in my buddy list\n", uid);
+			purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid);
 			continue;
 		}
 
============================================================
--- libpurple/protocols/qq/buddy_info.h	62ff812350c859993cfed122110108f0d87b0b1b
+++ libpurple/protocols/qq/buddy_info.h	1788934386864124acf1163392f41bea2c5eaad9
@@ -51,14 +51,14 @@
 #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_QQ_VIP			0x02
+#define QQ_COMM_FLAG_QQ_MEMBER		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_EXT_FLAG_ZONE				0x02
 
 #define QQ_BUDDY_GENDER_GG          0x00
 #define QQ_BUDDY_GENDER_MM          0x01
@@ -67,7 +67,15 @@
 #define QQ_ICON_PREFIX "qq_"
 #define QQ_ICON_SUFFIX ".png"
 
+enum {
+	QQ_BUDDY_INFO_UPDATE_ONLY = 0,
+	QQ_BUDDY_INFO_DISPLAY,
+	QQ_BUDDY_INFO_MODIFY,
+};
+
 void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window);
+void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
+		gint update_class, guint32 ship32);
 void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
 void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile);
 void qq_prepare_modify_info(PurpleConnection *gc);
@@ -75,6 +83,6 @@ void qq_send_packet_get_level(PurpleConn
 void qq_process_get_info_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 void qq_info_query_free(qq_data *qd);
 void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid);
-void qq_send_packet_get_buddies_levels(PurpleConnection *gc);
+void qq_request_get_buddies_levels(PurpleConnection *gc, gint update_class);
 void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
 #endif
============================================================
--- libpurple/protocols/qq/buddy_list.c	19f9e2ad310d5eb343b54d6bd41b75019994d0ff
+++ libpurple/protocols/qq/buddy_list.c	902dfa24074b09d996a0a2d4ff89b38c00d3e947
@@ -56,7 +56,7 @@ typedef struct _qq_buddy_online {
 } qq_buddy_online;
 
 /* get a list of online_buddies */
-void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
+void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class)
 {
 	qq_data *qd;
 	guint8 *raw_data;
@@ -77,15 +77,14 @@ void qq_send_packet_get_buddies_online(P
 	/* 003-004 */
 	bytes += qq_put16(raw_data + bytes, 0x0000);
 
-	qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5);
+	qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5, update_class, 0);
 	qd->last_get_online = time(NULL);
 }
 
-/* position starts with 0x0000, 
+/* position starts with 0x0000,
  * server may return a position tag if list is too long for one packet */
-void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position)
+void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	guint8 raw_data[16] = {0};
 	gint bytes = 0;
 
@@ -98,13 +97,12 @@ 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_BUDDIES_LIST, raw_data, bytes);
+	qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes, update_class, 0);
 }
 
 /* get all list, buddies & Quns with groupsid support */
-void qq_send_packet_get_buddies_and_rooms(PurpleConnection *gc, guint32 position)
+void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, gint update_class)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	guint8 raw_data[16] = {0};
 	gint bytes = 0;
 
@@ -116,7 +114,7 @@ void qq_send_packet_get_buddies_and_room
 	bytes += qq_put32(raw_data + bytes, 0x00000000);
 	bytes += qq_put32(raw_data + bytes, position);
 
-	qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_AND_ROOMS, raw_data, bytes);
+	qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_AND_ROOMS, raw_data, bytes, update_class, 0);
 }
 
 /* parse the data into qq_buddy_status */
@@ -146,8 +144,8 @@ static gint get_buddy_status(qq_buddy_st
 	/* 015-030: unknown key */
 	bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_STATUS", 
-			"uid: %d, U1: %d, ip: %s:%d, U2:%d, status:%d, U3:%04X\n", 
+	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);
 
@@ -180,13 +178,12 @@ guint8 qq_process_get_buddies_online_rep
 	count = 0;
 	while (bytes < data_len) {
 		if (data_len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"[buddies online] only %d, need %d", 
+			purple_debug_error("QQ", "[buddies online] only %d, need %d",
 					(data_len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
 			break;
 		}
 		memset(&bo, 0 ,sizeof(bo));
-		
+
 		/* set flag */
 		bytes_buddy = bytes;
 		/* based on one online buddy entry */
@@ -204,31 +201,29 @@ guint8 qq_process_get_buddies_online_rep
 		bytes += qq_get8(&bo.ending, data + bytes);	/* 0x00 */
 
 		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", 
+			purple_debug_error("QQ", "uid=0 or entry complete len(%d) != %d",
 					(bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
 			continue;
 		}	/* check if it is a valid entry */
 
 		if (bo.bs.uid == qd->uid) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"I am in online list %d\n", bo.bs.uid);
+			purple_debug_warning("QQ", "I am in online list %d\n", bo.bs.uid);
 			continue;
 		}
 
 		/* update buddy information */
 		purple_name = uid_to_purple_name(bo.bs.uid);
 		if (purple_name == NULL) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+			purple_debug_error("QQ",
 					"Got an online buddy %d, but not find purple name\n", bo.bs.uid);
 			continue;
 		}
 		b = purple_find_buddy(purple_connection_get_account(gc), purple_name);
 		g_free(purple_name);
-		
+
 		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 		if (q_bud == NULL) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+			purple_debug_error("QQ",
 					"Got an online buddy %d, but not in my buddy list\n", bo.bs.uid);
 			continue;
 		}
@@ -247,11 +242,11 @@ guint8 qq_process_get_buddies_online_rep
 	}
 
 	if(bytes > data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+		purple_debug_error("QQ",
 				"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d online buddies, nextposition=%u\n",
+	purple_debug_info("QQ", "Received %d online buddies, nextposition=%u\n",
 							count, (guint) position);
 	return position;
 }
@@ -274,7 +269,7 @@ guint16 qq_process_get_buddies_list_repl
 	qd = (qq_data *) gc->proto_data;
 
 	if (data_len <= 2) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "empty buddies list");
+		purple_debug_error("QQ", "empty buddies list");
 		return -1;
 	}
 	/* qq_show_packet("QQ get buddies list", data, data_len); */
@@ -305,7 +300,7 @@ guint16 qq_process_get_buddies_list_repl
 		bytes_expected = 12 + pascal_len;
 
 		if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
+			purple_debug_info("QQ",
 					"Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes);
 			g_free(q_bud->nickname);
 			g_free(q_bud);
@@ -315,7 +310,7 @@ guint16 qq_process_get_buddies_list_repl
 		}
 
 #if 1
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		purple_debug_info("QQ",
 				"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);
 #endif
@@ -334,11 +329,11 @@ guint16 qq_process_get_buddies_list_repl
 	}
 
 	if(bytes > data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+		purple_debug_error("QQ",
 				"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies, nextposition=%u\n",
+	purple_debug_info("QQ", "Received %d buddies, nextposition=%u\n",
 		count, (guint) position);
 	return position;
 }
@@ -364,8 +359,7 @@ guint32 qq_process_get_buddies_and_rooms
 
 	bytes += qq_get8(&reply_code, data + bytes);
 	if(0 != reply_code) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-				"qq_process_get_buddies_and_rooms, %d", reply_code);
+		purple_debug_warning("QQ", "qq_process_get_buddies_and_rooms, %d", reply_code);
 	}
 
 	bytes += qq_get32(&unknown, data + bytes);
@@ -381,48 +375,45 @@ guint32 qq_process_get_buddies_and_rooms
 		/* 05: groupid*4 */ /* seems to always be 0 */
 		bytes += qq_get8(&groupid, data + bytes);
 		/*
-		   purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
+		   purple_debug_info("QQ", "groupid: %i\n", groupid);
 		   groupid >>= 2;
 		   */
 		if (uid == 0 || (type != 0x1 && type != 0x4)) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"Buddy entry, uid=%d, type=%d", uid, type);
+			purple_debug_info("QQ", "Buddy entry, uid=%d, type=%d", uid, type);
 			continue;
-		} 
+		}
 		if(0x1 == type) { /* a buddy */
-			/* don't do anything but count - buddies are handled by 
-			 * qq_send_packet_get_buddies_list */
+			/* don't do anything but count - buddies are handled by
+			 * qq_request_get_buddies_list */
 			++i;
 		} else { /* a group */
 			group = qq_room_search_id(gc, uid);
 			if(group == NULL) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				purple_debug_info("QQ",
 					"Not find room id %d in qq_process_get_buddies_and_rooms\n", uid);
 				qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
-				qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, uid);
 			} else {
-				group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+				group->my_role = QQ_ROOM_ROLE_YES;
 				qq_group_refresh(gc, group);
-				qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
 			}
 			++j;
 		}
 	}
 
 	if(bytes > data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+		purple_debug_error("QQ",
 				"qq_process_get_buddies_and_rooms: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position);
+	purple_debug_info("QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position);
 	return position;
 }
 
 #define QQ_MISC_STATUS_HAVING_VIIDEO      0x00000001
 #define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 	0x30	/* ASCII value of "0" */
 
-/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses, 
- * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy, 
+/* 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 */
@@ -441,9 +432,9 @@ gint get_icon_offset(PurpleConnection *g
 
 /* Help calculate the correct icon index to tell the server. */
 gint get_icon_offset(PurpleConnection *gc)
-{ 
+{
 	PurpleAccount *account;
-	PurplePresence *presence; 
+	PurplePresence *presence;
 
 	account = purple_connection_get_account(gc);
 	presence = purple_account_get_presence(account);
@@ -460,7 +451,7 @@ gint get_icon_offset(PurpleConnection *g
 }
 
 /* send a packet to change my online status */
-void qq_send_packet_change_status(PurpleConnection *gc)
+void qq_request_change_status(PurpleConnection *gc, gint update_class)
 {
 	qq_data *qd;
 	guint8 raw_data[16] = {0};
@@ -469,13 +460,13 @@ void qq_send_packet_change_status(Purple
 	guint32 misc_status;
 	gboolean fake_video;
 	PurpleAccount *account;
-	PurplePresence *presence; 
+	PurplePresence *presence;
 
 	account = purple_connection_get_account(gc);
 	presence = purple_account_get_presence(account);
 
 	qd = (qq_data *) gc->proto_data;
-	if (!qd->logged_in)
+	if (!qd->is_login)
 		return;
 
 	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
@@ -497,7 +488,7 @@ void qq_send_packet_change_status(Purple
 	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);
+	qq_send_cmd_mess(gc, QQ_CMD_CHANGE_STATUS, raw_data, bytes, update_class, 0);
 }
 
 /* parse the reply packet for change_status */
@@ -513,15 +504,15 @@ void qq_process_change_status_reply(guin
 	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	
+
 	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);
+		purple_debug_warning("QQ", "Change status fail 0x%02X\n", reply);
 		return;
 	}
 
-	/* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n"); */
+	/* 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);
@@ -532,7 +523,7 @@ void qq_process_change_status_reply(guin
 }
 
 /* it is a server message indicating that one of my buddies has changed its status */
-void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc) 
+void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gint bytes;
@@ -547,16 +538,16 @@ void qq_process_buddy_change_status(guin
 	qd = (qq_data *) gc->proto_data;
 
 	if (data_len < 35) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] only %d, need 35 bytes\n", data_len);
+		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 
+	/* 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);
 
@@ -565,13 +556,13 @@ void qq_process_buddy_change_status(guin
 	g_free(name);
 	q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 	if (q_bud == NULL) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+		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) { 
+	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;
 	}
@@ -589,24 +580,24 @@ void qq_update_buddy_contact(PurpleConne
 	gchar *purple_name;
 	PurpleBuddy *bud;
 	gchar *status_id;
-	
+
 	g_return_if_fail(q_bud != NULL);
 
 	purple_name = uid_to_purple_name(q_bud->uid);
 	if (purple_name == NULL) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Not find purple name: %d\n", q_bud->uid);
+		purple_debug_error("QQ", "Not find purple name: %d\n", q_bud->uid);
 		return;
 	}
 
 	bud = purple_find_buddy(gc->account, purple_name);
 	if (bud == NULL) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Not find buddy: %d\n", q_bud->uid);
+		purple_debug_error("QQ", "Not find buddy: %d\n", q_bud->uid);
 		g_free(purple_name);
 		return;
 	}
-	
+
 	purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */
-	q_bud->last_refresh = time(NULL);
+	q_bud->last_update = time(NULL);
 
 	/* purple supports signon and idle time
 	 * but it is not much use for QQ, I do not use them */
@@ -630,10 +621,10 @@ void qq_update_buddy_contact(PurpleConne
 		break;
 	default:
 		status_id = "invisible";
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown status: %x\n", q_bud->status);
+		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_debug_info("QQ", "buddy %d %s\n", q_bud->uid, status_id);
 	purple_prpl_got_user_status(gc->account, purple_name, status_id, NULL);
 
 	if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE)
@@ -664,7 +655,7 @@ void qq_refresh_all_buddy_status(PurpleC
 
 	while (list != NULL) {
 		q_bud = (qq_buddy *) list->data;
-		if (q_bud != NULL && now > q_bud->last_refresh + QQ_UPDATE_ONLINE_INTERVAL
+		if (q_bud != NULL && now > q_bud->last_update + 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);
============================================================
--- libpurple/protocols/qq/buddy_list.h	d69cbb9b2e26383154467b6d47cacbe6d38eb005
+++ libpurple/protocols/qq/buddy_list.h	794a9c7da4eb380f57107828529a94d654afc3db
@@ -42,19 +42,19 @@ enum {
 
 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
+	QQ_BUDDY_ONLINE_NORMAL = 10,
+	QQ_BUDDY_ONLINE_OFFLINE = 20,
+	QQ_BUDDY_ONLINE_AWAY = 30,
+	QQ_BUDDY_ONLINE_INVISIBLE = 40
 };
 
-void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position);
+void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class);
 guint8 qq_process_get_buddies_online_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position);
+void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class);
 guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_send_packet_get_buddies_and_rooms(PurpleConnection *gc, guint32 position);
+void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, gint update_class);
 guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc);
 
 void qq_refresh_all_buddy_status(PurpleConnection *gc);
@@ -63,7 +63,7 @@ gint get_icon_offset(PurpleConnection *g
 
 gint get_icon_offset(PurpleConnection *gc);
 
-void qq_send_packet_change_status(PurpleConnection *gc);
+void qq_request_change_status(PurpleConnection *gc, gint update_class);
 void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
 
============================================================
--- libpurple/protocols/qq/buddy_opt.c	d19581650142be4fa3db797f2ca04efae553818e
+++ libpurple/protocols/qq/buddy_opt.c	17be05a5e83a7c2dc34c5e332950e77304837fb7
@@ -60,19 +60,17 @@ static void _qq_send_packet_remove_buddy
 /* send packet to remove a buddy from my buddy list */
 static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar uid_str[11];
 
 	g_return_if_fail(uid > 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(qd, QQ_CMD_DEL_BUDDY, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(gc, QQ_CMD_DEL_BUDDY, (guint8 *) uid_str, strlen(uid_str));
 }
 
 /* try to remove myself from someone's buddy list */
 static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	guint8 raw_data[16] = {0};
 	gint bytes = 0;
 
@@ -80,7 +78,7 @@ static void _qq_send_packet_remove_self_
 
 	bytes += qq_put32(raw_data + bytes, uid);
 
-	qq_send_cmd(qd, QQ_CMD_REMOVE_SELF, raw_data, bytes);
+	qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, raw_data, bytes);
 }
 
 /* try to add a buddy without authentication */
@@ -94,7 +92,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_BUDDY_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(gc, 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);
@@ -106,7 +104,6 @@ static void _qq_send_packet_buddy_auth(P
 /* this buddy needs authentication, text conversion is done at lowest level */
 static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar *text_qq, uid_str[11];
 	guint8 bar, *raw_data;
 	gint bytes = 0;
@@ -128,7 +125,7 @@ static void _qq_send_packet_buddy_auth(P
 		g_free(text_qq);
 	}
 
-	qq_send_cmd(qd, QQ_CMD_BUDDY_AUTH, raw_data, bytes);
+	qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, raw_data, bytes);
 }
 
 static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text)
@@ -204,11 +201,11 @@ void qq_reject_add_request_with_gc_and_u
 	g2->uid = uid;
 
 	msg1 = g_strdup_printf(_("You rejected %d's request"), uid);
-	msg2 = g_strdup(_("Input your reason:"));
+	msg2 = g_strdup(_("Message:"));
 
 	nombre = uid_to_purple_name(uid);
 	purple_request_input(gc, _("Reject request"), msg1, msg2,
-			_("Sorry, you are not my type..."), TRUE, FALSE,
+			_("Sorry, you are not my style..."), TRUE, FALSE,
 			NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
 			purple_connection_get_account(gc), nombre, NULL,
 			g2);
@@ -261,7 +258,7 @@ void qq_process_add_buddy_auth_reply(gui
 	qd = (qq_data *) gc->proto_data;
 
 	if (data[0] != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
+		purple_debug_warning("QQ", "Add buddy with auth request failed\n");
 		if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) {
 			return;
 		}
@@ -269,7 +266,7 @@ void qq_process_add_buddy_auth_reply(gui
 		purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
 		g_free(msg_utf8);
 	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
+		purple_debug_info("QQ", "Add buddy with auth request OK\n");
 	}
 }
 
@@ -284,16 +281,16 @@ void qq_process_remove_buddy_reply(guint
 
 	if (data[0] != QQ_REMOVE_BUDDY_REPLY_OK) {
 		/* there is no reason return from server */
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
+		purple_debug_warning("QQ", "Remove buddy fails\n");
 	} else {		/* if reply */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n");
+		purple_debug_info("QQ", "Remove buddy OK\n");
 		/* TODO: We don't really need to notify the user about this, do we? */
 		purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
 	}
 }
 
 /* process the server reply for my request to remove myself from a buddy */
-void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc) 
+void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 
@@ -303,11 +300,12 @@ void qq_process_remove_self_reply(guint8
 
 	if (data[0] != QQ_REMOVE_SELF_REPLY_OK) {
 		/* there is no reason return from server */
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n");
+		purple_debug_warning("QQ", "Remove self fails\n");
+		purple_notify_info(gc, NULL, _("Failed removing from friend's buddy list"), NULL);
 	} else {		/* if reply */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
+		purple_debug_info("QQ", "Remove from a buddy OK\n");
 		/* TODO: Does the user really need to be notified about this? */
-		purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL);
+		purple_notify_info(gc, NULL, _("Successed removing from friend's buddy list"), NULL);
 	}
 }
 
@@ -340,25 +338,25 @@ void qq_process_add_buddy_reply(guint8 *
 	}
 
 	if (for_uid == 0) {	/* we have no record for this */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "We have no record for add buddy reply [%d], discard\n", seq);
+		purple_debug_error("QQ", "We have no record for add buddy reply [%d], discard\n", seq);
 		return;
 	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy reply [%d] is for id [%d]\n", seq, for_uid);
+		purple_debug_info("QQ", "Add buddy reply [%d] is for id [%d]\n", seq, for_uid);
 	}
 
 	if (NULL == (segments = split_data(data, data_len, "\x1f", 2)))
 		return;
-		
+
 	uid = segments[0];
 	reply = segments[1];
 	if (strtol(uid, NULL, 10) != qd->uid) {	/* should not happen */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Add buddy reply is to [%s], not me!", uid);
+		purple_debug_error("QQ", "Add buddy reply is to [%s], not me!", uid);
 		g_strfreev(segments);
 		return;
 	}
 
 	if (strtol(reply, NULL, 10) > 0) {	/* need auth */
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy attempt fails, need authentication\n");
+		purple_debug_warning("QQ", "Add buddy attempt fails, need authentication\n");
 		nombre = uid_to_purple_name(for_uid);
 		b = purple_find_buddy(gc->account, nombre);
 		if (b != NULL)
@@ -366,7 +364,7 @@ void qq_process_add_buddy_reply(guint8 *
 		g = g_new0(gc_and_uid, 1);
 		g->gc = gc;
 		g->uid = for_uid;
-		msg = g_strdup_printf(_("User %d needs authentication"), for_uid);
+		msg = g_strdup_printf(_("%d needs authentication"), for_uid);
 		purple_request_input(gc, NULL, msg,
 				_("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
 				_("Would you be my friend?"),
@@ -397,7 +395,7 @@ PurpleGroup *qq_get_purple_group(const g
 	if (g == NULL) {
 		g = purple_group_new(group_name);
 		purple_blist_add_group(g, NULL);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add new group: %s\n", group_name);
+		purple_debug_warning("QQ", "Add new group: %s\n", group_name);
 	}
 
 	return g;
@@ -440,11 +438,11 @@ 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, 0);
+		qq_request_get_buddies_online(gc, 0, 0);
 	}
 
 	purple_blist_add_buddy(b, NULL, g, NULL);
-	purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add new buddy: [%s]\n", name);
+	purple_debug_warning("QQ", "Add new buddy: [%s]\n", name);
 
 	g_free(name);
 	g_free(group_name);
@@ -454,8 +452,8 @@ PurpleBuddy *qq_add_buddy_by_recv_packet
 
 /* add a buddy and send packet to QQ server
  * note that when purple load local cached buddy list into its blist
- * it also calls this funtion, so we have to 
- * define qd->logged_in=TRUE AFTER serv_finish_login(gc) */
+ * it also calls this funtion, so we have to
+ * define qd->is_login=TRUE AFTER serv_finish_login(gc) */
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	qq_data *qd;
@@ -463,7 +461,7 @@ void qq_add_buddy(PurpleConnection *gc, 
 	PurpleBuddy *b;
 
 	qd = (qq_data *) gc->proto_data;
-	if (!qd->logged_in)
+	if (!qd->is_login)
 		return;		/* IMPORTANT ! */
 
 	uid = purple_name_to_uid(buddy->name);
@@ -474,8 +472,8 @@ void qq_add_buddy(PurpleConnection *gc, 
 		if (b != NULL)
 			purple_blist_remove_buddy(b);
 		purple_notify_error(gc, NULL,
-				_("QQid Error"),
-				_("Invalid QQid"));
+				_("QQ Number Error"),
+				_("Invalid QQ Number"));
 	}
 }
 
@@ -490,7 +488,7 @@ void qq_remove_buddy(PurpleConnection *g
 	qd = (qq_data *) gc->proto_data;
 	uid = purple_name_to_uid(buddy->name);
 
-	if (!qd->logged_in)
+	if (!qd->is_login)
 		return;
 
 	if (uid > 0)
@@ -502,7 +500,7 @@ void qq_remove_buddy(PurpleConnection *g
 		if (q_bud != NULL)
 			qd->buddies = g_list_remove(qd->buddies, q_bud);
 		else
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "We have no qq_buddy record for %s\n", buddy->name);
+			purple_debug_warning("QQ", "We have no qq_buddy record for %s\n", buddy->name);
 		/* remove buddy on blist, this does not trigger qq_remove_buddy again
 		 * do this only if the request comes from block request,
 		 * otherwise purple segmentation fault */
@@ -514,41 +512,45 @@ void qq_add_buddy_request_free(qq_data *
 /* free add buddy request queue */
 void qq_add_buddy_request_free(qq_data *qd)
 {
-	gint i;
+	gint count;
 	qq_add_buddy_request *p;
 
-	i = 0;
-	while (qd->add_buddy_request) {
+	count = 0;
+	while (qd->add_buddy_request != NULL) {
 		p = (qq_add_buddy_request *) (qd->add_buddy_request->data);
 		qd->add_buddy_request = g_list_remove(qd->add_buddy_request, p);
 		g_free(p);
-		i++;
+		count++;
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d add buddy requests are freed!\n", i);
+	if (count > 0) {
+		purple_debug_info("QQ", "%d add buddy requests are freed!\n", count);
+	}
 }
 
 /* free up all qq_buddy */
 void qq_buddies_list_free(PurpleAccount *account, qq_data *qd)
 {
-	gint i;
+	gint count;
 	qq_buddy *p;
 	gchar *name;
 	PurpleBuddy *b;
 
-	i = 0;
+	count = 0;
 	while (qd->buddies) {
 		p = (qq_buddy *) (qd->buddies->data);
 		qd->buddies = g_list_remove(qd->buddies, p);
 		name = uid_to_purple_name(p->uid);
-		b = purple_find_buddy(account, name);   	
-		if(b != NULL) 
+		b = purple_find_buddy(account, name);
+		if(b != NULL)
 			b->proto_data = NULL;
 		else
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "qq_buddy %s not found in purple proto_data\n", name);
+			purple_debug_info("QQ", "qq_buddy %s not found in purple proto_data\n", name);
 		g_free(name);
 
 		g_free(p);
-		i++;
+		count++;
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d qq_buddy structures are freed!\n", i);
+	if (count > 0) {
+		purple_debug_info("QQ", "%d qq_buddy structures are freed!\n", count);
+	}
 }
============================================================
--- libpurple/protocols/qq/char_conv.c	56df688312bd04d8a02cd864840335c7ee7a0f06
+++ libpurple/protocols/qq/char_conv.c	5ab4d96c8a4512a964b50263e2fd8588ecc37dc0
@@ -113,7 +113,7 @@ static gchar *_my_convert(const gchar *s
 	}
 	
 	/* conversion error */
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ_CONVERT", "%s\n", error->message);
+	purple_debug_error("QQ_CONVERT", "%s\n", error->message);
 
 	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ_CONVERT",
 		(guint8 *) str, (len == -1) ? strlen(str) : len,
@@ -182,7 +182,7 @@ gchar *qq_encode_to_purple(guint8 *data,
 	g_string_append_printf(encoded,
 			"<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
 			color_code, font_name, font_size / 3);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_MESG",
+	purple_debug_info("QQ_MESG",
 			"recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
 			color_code, font_name, font_size / 3);
 	g_string_append(encoded, msg_utf8);
============================================================
--- libpurple/protocols/qq/file_trans.c	6f016b37ef0b595dc8e1aa75c2177636c4eadba7
+++ libpurple/protocols/qq/file_trans.c	2775840f1f4b59ea87cddfc4d8a71b825f1ecd3a
@@ -22,10 +22,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#ifdef _WIN32
-#define random rand
-#endif
-
 #include "internal.h"
 
 #include "debug.h"
@@ -62,7 +58,7 @@ static guint32 _gen_file_key(void)
 {
 	guint8 seed;
 
-	seed = random();
+	seed = rand() & 0xFF;
 	return _get_file_key(seed);
 }
 
@@ -261,7 +257,7 @@ static gint _qq_send_file(PurpleConnecti
 	if (bytes == len + 12) {
 		_qq_xfer_write(raw_data, bytes, qd->xfer);
 	} else
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes);
+		purple_debug_info("QQ", "send_file: want %d but got %d\n", len + 12, bytes);
 	return bytes;
 }
 
@@ -323,13 +319,13 @@ void qq_send_file_ctl_packet(PurpleConne
 			bytes_expected = 61;
 			break;
 		default:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "qq_send_file_ctl_packet: Unknown packet type[%d]\n",
+			purple_debug_info("QQ", "qq_send_file_ctl_packet: Unknown packet type[%d]\n",
 					packet_type);
 			bytes_expected = 0;
 	}
 
 	if (bytes != bytes_expected) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d",
+		purple_debug_error("QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d",
 				bytes_expected, bytes);
 		return;
 	}
@@ -346,24 +342,24 @@ void qq_send_file_ctl_packet(PurpleConne
 	guint8 *buf;
 	int buflen;
 	hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
+	purple_debug_info("QQ", "encrypted packet: \n%s", hex_dump);
 	g_free(hex_dump);
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	buflen = encrypted_len;
 	if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n");
+		purple_debug_info("QQ", "decrypt success\n");
 	   if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n");
+			purple_debug_info("QQ", "checksum ok\n");
 
 		hex_dump = hex_dump_to_str(buf, buflen);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
+		purple_debug_info("QQ", "decrypted packet: \n%s", hex_dump);
 		g_free(hex_dump);
 	 } else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
+		purple_debug_info("QQ", "decrypt fail\n");
 	}
 #endif
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
+	purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
 	_qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
 }
 
@@ -410,7 +406,7 @@ static void _qq_send_file_data_packet(Pu
 					info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1;
 					info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN;
 
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+					purple_debug_info("QQ", 
 							"start transfering data, %d fragments with %d length each\n",
 							info->fragment_num, info->fragment_len);
 					/* Unknown */
@@ -435,7 +431,7 @@ static void _qq_send_file_data_packet(Pu
 							filename_len);
 					break;
 				case QQ_FILE_DATA_INFO:
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+					purple_debug_info("QQ", 
 							"sending %dth fragment with length %d, offset %d\n",
 							fragment_index, len, (fragment_index-1)*fragment_size);
 					/* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */
@@ -448,7 +444,7 @@ static void _qq_send_file_data_packet(Pu
 					bytes += qq_putdata(raw_data + bytes, data, len);
 					break;
 				case QQ_FILE_EOF:
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of sending data\n");
+					purple_debug_info("QQ", "end of sending data\n");
 					/* bytes += qq_put16(raw_data + bytes, info->fragment_num + 1); */
 					bytes += qq_put16(raw_data + bytes, info->fragment_num);
 					bytes += qq_put8(raw_data + bytes, sub_type);
@@ -474,7 +470,7 @@ static void _qq_send_file_data_packet(Pu
 					break;
 			}
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
+	purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
 	_qq_send_file(gc, raw_data, bytes, QQ_FILE_DATA_PACKET_TAG, info->to_uid);
 }
 
@@ -516,7 +512,7 @@ static void _qq_process_recv_file_ctl_pa
 	decrypted_data = g_newa(guint8, data_len);
 	decrypted_len = qq_decrypt(decrypted_data, data, data_len, qd->session_md5);
 	if ( decrypted_len <= 0 ) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rcv file ctrl packet\n");
+		purple_debug_error("QQ", "Error decrypt rcv file ctrl packet\n");
 		return;
 	}
 
@@ -526,7 +522,7 @@ static void _qq_process_recv_file_ctl_pa
 	decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes);
 	decryped_bytes += 4+1+1+19+1;	/* skip something */
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
+	purple_debug_info("QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
 	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
 		decrypted_data, decrypted_len,
 		"decrypted control packet received:");
@@ -566,7 +562,7 @@ static void _qq_process_recv_file_ctl_pa
 			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
 			break;
 		default:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
+			purple_debug_info("QQ", "unprocess file command %d\n", packet_type);
 	}
 }
 
@@ -577,7 +573,7 @@ static void _qq_recv_file_progess(Purple
 	ft_info *info = (ft_info *) xfer->data;
 	guint32 mask;
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+	purple_debug_info("QQ", 
 			"receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n", 
 			index, len, info->window, info->max_fragment_index);
 	if (info->window == 0 && info->max_fragment_index == 0) {
@@ -585,11 +581,11 @@ static void _qq_recv_file_progess(Purple
 			purple_xfer_cancel_local(xfer);
 			return;
 		}
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "object file opened for writing\n");
+		purple_debug_info("QQ", "object file opened for writing\n");
 	}
 	mask = 0x1 << (index % sizeof(info->window));
 	if (index < info->max_fragment_index || (info->window & mask)) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1);
+		purple_debug_info("QQ", "duplicate %dth fragment, drop it!\n", index+1);
 		return;
 	}
 
@@ -609,7 +605,7 @@ static void _qq_recv_file_progess(Purple
 		if (mask & 0x8000) mask = 0x0001;
 		else mask = mask << 1;
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n", 
+	purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n", 
 			index, info->window, info->max_fragment_index);
 }
 
@@ -654,12 +650,12 @@ static void _qq_update_send_progess(Purp
 	PurpleXfer *xfer = qd->xfer;
 	ft_info *info = (ft_info *) xfer->data;
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+	purple_debug_info("QQ", 
 			"receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n", 
 			fragment_index, info->window, info->max_fragment_index);
 	if (fragment_index < info->max_fragment_index || 
 			fragment_index >= info->max_fragment_index + sizeof(info->window)) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1);
+		purple_debug_info("QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1);
 		return;
 	}
 	mask = 0x1 << (fragment_index % sizeof(info->window));
@@ -696,7 +692,7 @@ static void _qq_update_send_progess(Purp
 			else mask = mask << 1;
 		}
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+	purple_debug_info("QQ", 
 			"procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n", 
 			fragment_index, info->window, info->max_fragment_index);
 }
@@ -737,7 +733,7 @@ static void _qq_process_recv_file_data(P
 
 					info->max_fragment_index = 0;
 					info->window = 0;
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+					purple_debug_info("QQ", 
 							"start receiving data, %d fragments with %d length each\n",
 							info->fragment_num, info->fragment_len);
 					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
@@ -747,7 +743,7 @@ static void _qq_process_recv_file_data(P
 					bytes += qq_get32(&fragment_index, data + bytes);
 					bytes += qq_get32(&fragment_offset, data + bytes);
 					bytes += qq_get16(&fragment_len, data + bytes);
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+					purple_debug_info("QQ", 
 							"received %dth fragment with length %d, offset %d\n",
 							fragment_index, fragment_len, fragment_offset);
 
@@ -756,7 +752,7 @@ static void _qq_process_recv_file_data(P
 					_qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset);
 					break;
 				case QQ_FILE_EOF:
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of receiving\n");
+					purple_debug_info("QQ", "end of receiving\n");
 					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
 							0, 0, NULL, 0);
 					break;
@@ -795,11 +791,11 @@ static void _qq_process_recv_file_data(P
 			purple_xfer_end(qd->xfer);
 			break;
 		case QQ_FILE_BASIC_INFO:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "here\n");
+			purple_debug_info("QQ", "here\n");
 			_qq_send_file_data_packet(gc, QQ_FILE_DATA_INFO, 0, 0, 0, NULL, 0);
 			break;
 		default:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n",
+			purple_debug_info("QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n",
 					packet_type);
 			break;
 	}
@@ -824,6 +820,6 @@ void qq_process_recv_file(PurpleConnecti
 			_qq_process_recv_file_data(gc, data + bytes, len - bytes);
 			break;
 		default:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "unknown packet tag");
+			purple_debug_info("QQ", "unknown packet tag");
 	}
 }
============================================================
--- libpurple/protocols/qq/group.c	a3e4159f84756290c5fa166286e2f5353db574a4
+++ libpurple/protocols/qq/group.c	b63a859e4c351c158e6a89d580e3f27d8c274c59
@@ -64,7 +64,7 @@ GList *qq_chat_info(PurpleConnection *gc
 
 	pce = g_new0(struct proto_chat_entry, 1);
 	pce->label = _("ID: ");
-	pce->identifier = QQ_GROUP_KEY_EXTERNAL_ID;
+	pce->identifier = QQ_ROOM_KEY_EXTERNAL_ID;
 	m = g_list_append(m, pce);
 	
 	return m;
@@ -77,7 +77,7 @@ GHashTable *qq_chat_info_defaults(Purple
 	defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
 
 	if (chat_name != NULL)
-		g_hash_table_insert(defaults, QQ_GROUP_KEY_EXTERNAL_ID, g_strdup(chat_name));
+		g_hash_table_insert(defaults, QQ_ROOM_KEY_EXTERNAL_ID, g_strdup(chat_name));
 
 	return defaults;
 }
@@ -96,30 +96,30 @@ PurpleRoomlist *qq_roomlist_get_list(Pur
 	rl = purple_roomlist_new(purple_connection_get_account(gc));
 	qd->roomlist = rl;
 
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Group ID"), QQ_GROUP_KEY_EXTERNAL_ID, FALSE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Group ID"), QQ_ROOM_KEY_EXTERNAL_ID, FALSE);
 	fields = g_list_append(fields, f);
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Creator"), QQ_GROUP_KEY_CREATOR_UID, FALSE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Creator"), QQ_ROOM_KEY_CREATOR_UID, FALSE);
 	fields = g_list_append(fields, f);
 	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
-				    _("Group Description"), QQ_GROUP_KEY_GROUP_DESC_UTF8, FALSE);
+				    _("Group Description"), QQ_ROOM_KEY_DESC_UTF8, FALSE);
 	fields = g_list_append(fields, f);
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_GROUP_KEY_INTERNAL_ID, TRUE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_INTERNAL_ID, TRUE);
 	fields = g_list_append(fields, f);
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_GROUP_KEY_TYPE, TRUE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_TYPE, TRUE);
 	fields = g_list_append(fields, f);
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Auth"), QQ_GROUP_KEY_AUTH_TYPE, TRUE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Auth"), QQ_ROOM_KEY_AUTH_TYPE, TRUE);
 	fields = g_list_append(fields, f);
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_GROUP_KEY_GROUP_CATEGORY, TRUE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_CATEGORY, TRUE);
 	fields = g_list_append(fields, f);
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_GROUP_KEY_GROUP_NAME_UTF8, TRUE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_TITLE_UTF8, TRUE);
 
 	fields = g_list_append(fields, f);
 	purple_roomlist_set_fields(rl, fields);
 	purple_roomlist_set_in_progress(qd->roomlist, TRUE);
 
 	purple_request_input(gc, _("QQ Qun"),
-			   _("Please enter external group ID"),
-			   _("You can only search for permanent QQ groups\n"),
+			   _("Please enter Qun number"),
+			   _("You can only search for permanent Qun\n"),
 			   NULL, FALSE, FALSE, NULL, 
 			   _("Search"), G_CALLBACK(_qq_group_search_callback), 
 			   _("Cancel"), G_CALLBACK(_qq_group_search_cancel_callback), 
@@ -157,7 +157,7 @@ void qq_group_init(PurpleConnection *gc)
 
 	purple_group = purple_find_group(PURPLE_GROUP_QQ_QUN);
 	if (purple_group == NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "We have no QQ Qun\n");
+		purple_debug_info("QQ", "We have no QQ Qun\n");
 		return;
 	}
 
@@ -178,8 +178,7 @@ void qq_group_init(PurpleConnection *gc)
 			continue;
 
 		count++;
-		qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Load %d QQ Qun configurations\n", count);
+	purple_debug_info("QQ", "Load %d QQ Qun configurations\n", count);
 }
============================================================
--- libpurple/protocols/qq/group.h	6d3c438063856a59dbc1f546de3e30ed96373e61
+++ libpurple/protocols/qq/group.h	138edcd1acfee105682a012c88f97c552670320b
@@ -34,27 +34,27 @@ typedef enum {
 #define PURPLE_GROUP_QQ_QUN         "QQ 群"
 
 typedef enum {
-	QQ_GROUP_MEMBER_STATUS_NOT_MEMBER = 0x00,	/* default 0x00 means not member */
-	QQ_GROUP_MEMBER_STATUS_IS_MEMBER,
-	QQ_GROUP_MEMBER_STATUS_APPLYING,
-	QQ_GROUP_MEMBER_STATUS_IS_ADMIN,
-} qq_group_member_status;
+	QQ_ROOM_ROLE_NO = 0x00,	/* default 0x00 means not member */
+	QQ_ROOM_ROLE_YES,
+	QQ_ROOM_ROLE_REQUESTING,
+	QQ_ROOM_ROLE_ADMIN,
+} qq_room_role;
 
 typedef struct _qq_group {
 	/* all these will be saved when we exit Purple */
-	qq_group_member_status my_status;	/* my status for this group */
-	gchar *my_status_desc;			/* my status description */
+	qq_room_role my_role;	/* my role for this room */
+	gchar *my_role_desc;			/* my role description */
 	guint32 id;
 	guint32 ext_id;
 	guint8 type8;			/* permanent or temporory */
 	guint32 creator_uid;
-	guint32 group_category;
+	guint32 category;
 	guint8 auth_type;
-	gchar *group_name_utf8;
-	gchar *group_desc_utf8;
+	gchar *title_utf8;
+	gchar *desc_utf8;
 	/* all these will be loaded from the network */
 	gchar *notice_utf8;	/* group notice by admin */
-	GList *members;	
+	GList *members;
 } qq_group;
 
 GList *qq_chat_info(PurpleConnection *gc);
============================================================
--- libpurple/protocols/qq/group_conv.c	a98dc976b1feae948a46b3b27d97234555cb5d70
+++ libpurple/protocols/qq/group_conv.c	e1a84ee06d272314ac5e9a7881414143368bc618
@@ -41,9 +41,9 @@ void qq_group_conv_show_window(PurpleCon
 	qd = (qq_data *) gc->proto_data;
 
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, 
-			group->group_name_utf8, purple_connection_get_account(gc));
+			group->title_utf8, purple_connection_get_account(gc));
 	if (conv == NULL)	/* show only one window per group */
-		serv_got_joined_chat(gc, qd->channel++, group->group_name_utf8);
+		serv_got_joined_chat(gc, qd->channel++, group->title_utf8);
 }
 
 /* refresh online member in group conversation window */
@@ -59,7 +59,7 @@ void qq_group_conv_refresh_online_member
 	names = NULL;
 	flags = NULL;
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-			group->group_name_utf8, purple_connection_get_account(gc));
+			group->title_utf8, purple_connection_get_account(gc));
 	if (conv != NULL && group->members != NULL) {
 		list = group->members;
 		while (list != NULL) {
============================================================
--- libpurple/protocols/qq/group_find.c	30312495840bc0286f16541ac847a387638d5b3e
+++ libpurple/protocols/qq/group_find.c	3770c52e75ed6cd6dd9d5528bc37b49f9dad436d
@@ -110,10 +110,10 @@ qq_group *qq_group_find_by_channel(Purpl
 	group = NULL;
 	while (list != NULL) {
 		group = (qq_group *) list->data;
-		if (group->group_name_utf8 == NULL) {
+		if (group->title_utf8 == NULL) {
 			continue;
 		}
-		if (!g_ascii_strcasecmp(purple_conversation_get_name(conv), group->group_name_utf8))
+		if (!g_ascii_strcasecmp(purple_conversation_get_name(conv), group->title_utf8))
 			break;
 		list = list->next;
 	}
@@ -167,3 +167,83 @@ qq_group *qq_room_search_id(PurpleConnec
 
 	return NULL;
 }
+
+qq_group *qq_room_get_next(PurpleConnection *gc, guint32 room_id)
+{
+	GList *list;
+	qq_group *group;
+	qq_data *qd;
+	gboolean is_find = FALSE;
+	
+	qd = (qq_data *) gc->proto_data;
+
+	if (qd->groups == NULL) {
+		return NULL;
+	}
+	
+	 if (room_id <= 0) {
+		return (qq_group *) qd->groups->data;
+	}
+	
+	list = qd->groups;
+	while (list != NULL) {
+		group = (qq_group *) list->data;
+		list = list->next;
+		if (group->id == room_id) {
+			is_find = TRUE;
+			break;
+		}
+	}
+
+	if ( !is_find || list == NULL) {
+		return NULL;
+	}
+
+	return (qq_group *)list->data;
+}
+
+qq_group *qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id)
+{
+	GList *list;
+	qq_group *group;
+	qq_data *qd;
+	gboolean is_find;
+
+	qd = (qq_data *) gc->proto_data;
+
+ 	list = qd->groups;
+	if (room_id > 0) {
+		/* search next room */
+		is_find = FALSE;
+		while (list != NULL) {
+			group = (qq_group *) list->data;
+			list = list->next;
+			if (group->id == room_id) {
+				is_find = TRUE;
+				break;
+			}
+		}
+		if ( !is_find || list == NULL) {
+			return NULL;
+		}
+	}
+	
+	is_find = FALSE;
+	while (list != NULL) {
+		group = (qq_group *) list->data;
+		if (group->my_role == QQ_ROOM_ROLE_YES || group->my_role == QQ_ROOM_ROLE_ADMIN) {
+			if (NULL != purple_find_conversation_with_account(
+						PURPLE_CONV_TYPE_CHAT,group->title_utf8, purple_connection_get_account(gc))) {
+				/* In convseration*/
+				is_find = TRUE;
+				break;
+			}
+		}
+		list = list->next;
+	}
+
+	if ( !is_find) {
+		return NULL;
+	}
+	return group;
+}
============================================================
--- libpurple/protocols/qq/group_find.h	8978a58ba32f8e2fd770e56de5b84c1051e3d7c0
+++ libpurple/protocols/qq/group_find.h	11c372e0accfd6fa0a0f1d725a619139cc9a27ec
@@ -32,10 +32,12 @@ qq_buddy *qq_group_find_or_add_member(Pu
 qq_buddy *qq_group_find_member_by_uid(qq_group *group, guint32 uid);
 void qq_group_remove_member_by_uid(qq_group *group, guint32 uid);
 qq_buddy *qq_group_find_or_add_member(PurpleConnection *gc, qq_group *group, guint32 member_uid);
-gboolean qq_group_find_id_by_seq(PurpleConnection *gc, guint16 seq, guint32 *id);
 qq_group *qq_group_find_by_channel(PurpleConnection *gc, gint channel);
 
 qq_group *qq_room_search_ext_id(PurpleConnection *gc, guint32 ext_id);
 qq_group *qq_room_search_id(PurpleConnection *gc, guint32 room_id);
 
+qq_group *qq_room_get_next(PurpleConnection *gc, guint32 room_id);
+qq_group *qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id);
+
 #endif
============================================================
--- libpurple/protocols/qq/group_free.c	3d3fb0dbfe4e3796b77a2d43596d330feaf73cd1
+++ libpurple/protocols/qq/group_free.c	9062c677324a6d7d94a07885f3f4656da177f617
@@ -54,9 +54,9 @@ void qq_group_free(qq_group *group)
 {
 	g_return_if_fail(group != NULL);
 	qq_group_free_member(group);
-	g_free(group->my_status_desc);
-	g_free(group->group_name_utf8);
-	g_free(group->group_desc_utf8);
+	g_free(group->my_role_desc);
+	g_free(group->title_utf8);
+	g_free(group->desc_utf8);
 	g_free(group->notice_utf8);
 	g_free(group);
 }
@@ -64,16 +64,18 @@ void qq_group_free_all(qq_data *qd)
 void qq_group_free_all(qq_data *qd)
 {
 	qq_group *group;
-	gint i;
+	gint count;
+
 	g_return_if_fail(qd != NULL);
-
-	i = 0;
+	count = 0;
 	while (qd->groups != NULL) {
-		i++;
 		group = (qq_group *) qd->groups->data;
 		qd->groups = g_list_remove(qd->groups, group);
 		qq_group_free(group);
+		count++;
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d groups are freed\n", i);
+	if (count > 0) {
+		purple_debug_info("QQ", "%d rooms are freed\n", count);
+	}
 }
============================================================
--- libpurple/protocols/qq/group_im.c	f9094b9361ff1fb22d670d6ddd1e937b9df0a7b4
+++ libpurple/protocols/qq/group_im.c	c46e53482e9fb4ee95c0a19023b3b85e3bd2fe06
@@ -41,6 +41,7 @@
 #include "header_info.h"
 #include "packet_parse.h"
 #include "qq_network.h"
+#include "qq_process.h"
 #include "utils.h"
 
 typedef struct _qq_recv_group_im {
@@ -85,12 +86,12 @@ void qq_send_packet_group_im(PurpleConne
 	if (bytes == data_len)	/* create OK */
 		qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_MSG, group->id, raw_data, data_len);
 	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 				"Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
 }
 
 /* this is the ACK */
-void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc) 
+void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc)
 {
 	/* return should be the internal group id
 	 * but we have nothing to do with it */
@@ -98,7 +99,7 @@ void qq_process_group_cmd_im(guint8 *dat
 }
 
 /* receive an application to join the group */
-void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+void qq_process_room_msg_apply_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
 	guint32 ext_id, user_uid;
 	guint8 type8;
@@ -119,8 +120,8 @@ void qq_process_recv_group_im_apply_join
 
 	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
-	msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, ext_id);
-	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
+	msg = g_strdup_printf(_("%d requested to join Qun %d"), user_uid, ext_id);
+	reason = g_strdup_printf(_("Message: %s"), reason_utf8);
 
 	g = g_new0(group_member_opt, 1);
 	g->gc = gc;
@@ -149,7 +150,7 @@ void qq_process_recv_group_im_apply_join
 }
 
 /* the request to join a group is rejected */
-void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+void qq_process_room_msg_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
 	guint32 ext_id, admin_uid;
 	guint8 type8;
@@ -170,14 +171,14 @@ void qq_process_recv_group_im_been_rejec
 	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf
-		(_("Your request to join group %d has been rejected by admin %d"), ext_id, admin_uid);
-	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
+		(_("Your request to join Qun %d has been rejected by admin %d"), ext_id, admin_uid);
+	reason = g_strdup_printf(_("Message: %s"), reason_utf8);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
 
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
-		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+		group->my_role = QQ_ROOM_ROLE_NO;
 		qq_group_refresh(gc, group);
 	}
 
@@ -187,7 +188,7 @@ void qq_process_recv_group_im_been_rejec
 }
 
 /* the request to join a group is approved */
-void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+void qq_process_room_msg_been_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
 	guint32 ext_id, admin_uid;
 	guint8 type8;
@@ -208,13 +209,13 @@ void qq_process_recv_group_im_been_appro
 	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf
-		(_("Your request to join group %d has been approved by admin %d"), ext_id, admin_uid);
+		(_("Your request to join Qun %d has been approved by admin %d"), ext_id, admin_uid);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL);
 
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
-		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+		group->my_role = QQ_ROOM_ROLE_YES;
 		qq_group_refresh(gc, group);
 	}
 
@@ -223,7 +224,7 @@ void qq_process_recv_group_im_been_appro
 }
 
 /* process the packet when removed from a group */
-void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+void qq_process_room_msg_been_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
 	guint32 ext_id, uid;
 	guint8 type8;
@@ -241,12 +242,12 @@ void qq_process_recv_group_im_been_remov
 
 	g_return_if_fail(ext_id > 0 && uid > 0);
 
-	msg = g_strdup_printf(_("You [%d] have left group \"%d\""), uid, ext_id);
+	msg = g_strdup_printf(_("[%d] removed from Qun \"%d\""), uid, ext_id);
 	purple_notify_info(gc, _("QQ Qun Operation"), msg, NULL);
 
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
-		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+		group->my_role = QQ_ROOM_ROLE_NO;
 		qq_group_refresh(gc, group);
 	}
 
@@ -254,7 +255,7 @@ void qq_process_recv_group_im_been_remov
 }
 
 /* process the packet when added to a group */
-void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+void qq_process_room_msg_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
 	guint32 ext_id, uid;
 	guint8 type8;
@@ -272,18 +273,18 @@ void qq_process_recv_group_im_been_added
 
 	g_return_if_fail(ext_id > 0 && uid > 0);
 
-	msg = g_strdup_printf(_("You [%d] have been added to group \"%d\""), uid, ext_id);
-	purple_notify_info(gc, _("QQ Qun Operation"), msg, _("This group has been added to your buddy list"));
+	msg = g_strdup_printf(_("[%d] added to Qun \"%d\""), uid, ext_id);
+	purple_notify_info(gc, _("QQ Qun Operation"), msg, _("Qun is in buddy list"));
 
 	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
-		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+		group->my_role = QQ_ROOM_ROLE_YES;
 		qq_group_refresh(gc, group);
 	} else {		/* no such group, try to create a dummy first, and then update */
 		group = qq_group_create_internal_record(gc, id, ext_id, NULL);
-		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+		group->my_role = QQ_ROOM_ROLE_YES;
 		qq_group_refresh(gc, group);
-		qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
+		qq_room_update(gc, 0, group->id);
 		/* the return of this cmd will automatically update the group in blist */
 	}
 
@@ -291,7 +292,7 @@ void qq_process_recv_group_im_been_added
 }
 
 /* recv an IM from a group chat */
-void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type)
+void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type)
 {
 	gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name;
 	guint16 unknown;
@@ -310,7 +311,9 @@ 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"); */
+#if 0
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump");
+#endif
 
 	im_group = g_newa(qq_recv_group_im, 1);
 
@@ -374,13 +377,13 @@ void qq_process_recv_group_im(guint8 *da
 	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc));
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_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_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
-		
-		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));
+		qq_room_update(gc, 0, group->id);
+
+		serv_got_joined_chat(gc, qd->channel++, group->title_utf8);
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
 	}
 
 	if (conv != NULL) {
============================================================
--- libpurple/protocols/qq/group_im.h	e031720f93defa4bf87c133d2d1640bf868f1ecd
+++ libpurple/protocols/qq/group_im.h	80dbc5dfa185c472df09adc543fa04eea527b8b8
@@ -31,30 +31,17 @@ void qq_send_packet_group_im(PurpleConne
 
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg);
 
-/* void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); */
 void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc);
 
-/* void qq_process_recv_group_im(guint8 *data, guint8 **cursor, 
- * gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type); */
-void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type);
+void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type);
 
-/* void qq_process_recv_group_im_apply_join(guint8 *data, guint8 **cursor, gint len, 
- * guint32 id, PurpleConnection *gc); */
-void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_msg_apply_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
 
-/* void qq_process_recv_group_im_been_rejected(guint8 *data, guint8 **cursor, gint len, 
- * guint32 id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_msg_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
 
-/* void qq_process_recv_group_im_been_approved(guint8 *data, guint8 **cursor, gint len, 
- * guint32 id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_msg_been_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
 
-/* void qq_process_recv_group_im_been_removed(guint8 *data, guint8 **cursor, gint len, 
- * guint32 id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_msg_been_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
 
-/* void qq_process_recv_group_im_been_added(guint8 *data,  guint8 **cursor, gint len, 
- * guint32 id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_msg_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
 #endif
============================================================
--- libpurple/protocols/qq/group_info.c	a1d15e05c9e82fd75464c23ac95f976054396e02
+++ libpurple/protocols/qq/group_info.c	3ea1d5d410f6718065865401f2ced4e7a05302bb
@@ -41,16 +41,16 @@
  * this interval determines if their member info is outdated */
 #define QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL  180
 
-static gboolean _is_group_member_need_update_info(qq_buddy *member)
+static gboolean check_update_interval(qq_buddy *member)
 {
 	g_return_val_if_fail(member != NULL, FALSE);
 	return (member->nickname == NULL) ||
-		(time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
+		(time(NULL) - member->last_update) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
 }
 
 /* this is done when we receive the reply to get_online_members sub_cmd
  * all member are set offline, and then only those in reply packets are online */
-static void _qq_group_set_members_all_offline(qq_group *group)
+static void set_all_offline(qq_group *group)
 {
 	GList *list;
 	qq_buddy *member;
@@ -64,60 +64,24 @@ static void _qq_group_set_members_all_of
 	}
 }
 
-/* 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)
-{
-	g_return_if_fail(group != NULL);
-
-	/* 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 \"%s\" is not open, ignore to get online members\n", group->group_name_utf8);
-		return;
-	}
-
-	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, group->id);
-}
-
 /* send packet to get info for each group member */
-void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group)
+gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint update_class)
 {
 	guint8 *raw_data;
 	gint bytes, num;
 	GList *list;
 	qq_buddy *member;
 
-	g_return_if_fail(group != NULL);
+	g_return_val_if_fail(group != NULL, 0);
 	for (num = 0, list = group->members; list != NULL; list = list->next) {
 		member = (qq_buddy *) list->data;
-		if (_is_group_member_need_update_info(member))
+		if (check_update_interval(member))
 			num++;
 	}
 
 	if (num <= 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member info needs to be updated now.\n");
-		return;
+		purple_debug_info("QQ", "No group member info needs to be updated now.\n");
+		return 0;
 	}
 
 	raw_data = g_newa(guint8, 4 * num);
@@ -127,12 +91,14 @@ void qq_send_cmd_group_get_members_info(
 	list = group->members;
 	while (list != NULL) {
 		member = (qq_buddy *) list->data;
-		if (_is_group_member_need_update_info(member))
+		if (check_update_interval(member))
 			bytes += qq_put32(raw_data + bytes, member->uid);
 		list = list->next;
 	}
 
-	qq_send_room_cmd(gc, QQ_ROOM_CMD_GET_MEMBER_INFO, group->id, raw_data, bytes);
+	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_BUDDIES, group->id, raw_data, bytes,
+			update_class, 0);
+	return num;
 }
 
 void qq_process_room_cmd_get_info(guint8 *data, gint data_len, PurpleConnection *gc)
@@ -174,8 +140,8 @@ void qq_process_room_cmd_get_info(guint8
 	bytes += qq_get32(&(group->creator_uid), data + bytes);
 	bytes += qq_get8(&(group->auth_type), data + bytes);
 	bytes += qq_get32(&unknown4, data + bytes);	/* oldCategory */
-	bytes += qq_get16(&unknown, data + bytes);	
-	bytes += qq_get32(&(group->group_category), data + bytes);
+	bytes += qq_get16(&unknown, data + bytes);
+	bytes += qq_get32(&(group->category), data + bytes);
 	bytes += qq_get16(&max_members, data + bytes);
 	bytes += qq_get8(&unknown1, data + bytes);
 	/* the following, while Eva:
@@ -183,17 +149,17 @@ void qq_process_room_cmd_get_info(guint8
 	 * 2(qunNoticeLen), qunNoticeLen(qunNoticeContent, 1(qunDescLen),
 	 * qunDestLen(qunDestcontent)) */
 	bytes += qq_get8(&unknown1, data + bytes);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
-			group->type8, group->creator_uid, group->group_category, max_members);
-	
+	purple_debug_info("QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
+			group->type8, group->creator_uid, group->category, max_members);
+
 	/* strlen + <str content> */
-	bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\"\n", group->group_name_utf8); 
+	bytes += convert_as_pascal_string(data + bytes, &(group->title_utf8), QQ_CHARSET_DEFAULT);
+	purple_debug_info("QQ", "group \"%s\"\n", group->title_utf8);
 	bytes += qq_get16(&unknown, data + bytes);	/* 0x0000 */
 	bytes += convert_as_pascal_string(data + bytes, &notice, QQ_CHARSET_DEFAULT);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "notice \"%s\"\n", notice); 
-	bytes += convert_as_pascal_string(data + bytes, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group_desc \"%s\"\n", group->group_desc_utf8); 
+	purple_debug_info("QQ", "notice \"%s\"\n", notice);
+	bytes += convert_as_pascal_string(data + bytes, &(group->desc_utf8), QQ_CHARSET_DEFAULT);
+	purple_debug_info("QQ", "group_desc \"%s\"\n", group->desc_utf8);
 
 	num = 0;
 	/* now comes the member list separated by 0x00 */
@@ -205,7 +171,7 @@ void qq_process_room_cmd_get_info(guint8
 
 #if 0
 		if(organization != 0 || role != 0) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role);
+			purple_debug_info("QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role);
 		}
 #endif
 
@@ -214,22 +180,22 @@ void qq_process_room_cmd_get_info(guint8
 			member->role = role;
 	}
 	if(bytes > data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 			"group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, num);
+	purple_debug_info("QQ", "group \"%s\" has %d members\n", group->title_utf8, num);
 
 	if (group->creator_uid == qd->uid)
-		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN;
+		group->my_role = QQ_ROOM_ROLE_ADMIN;
 
 	qq_group_refresh(gc, group);
 
-	purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, 
-			group->group_name_utf8, purple_connection_get_account(gc));
+	purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+			group->title_utf8, purple_connection_get_account(gc));
 	if(NULL == purple_conv) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				"Conversation \"%s\" is not open, do not set topic\n", group->group_name_utf8);
+		purple_debug_warning("QQ",
+				"Conversation \"%s\" is not open, do not set topic\n", group->title_utf8);
 		return;
 	}
 
@@ -237,7 +203,7 @@ void qq_process_room_cmd_get_info(guint8
 	qq_filter_str(notice);
 	group->notice_utf8 = strdup(notice);
 	g_free(notice);
-	
+
 	purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
 }
 
@@ -252,7 +218,7 @@ void qq_process_room_cmd_get_onlines(gui
 	g_return_if_fail(data != NULL && len > 0);
 
 	if (len <= 3) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid group online member reply, discard it!\n");
+		purple_debug_error("QQ", "Invalid group online member reply, discard it!\n");
 		return;
 	}
 
@@ -263,13 +229,12 @@ void qq_process_room_cmd_get_onlines(gui
 
 	group = qq_room_search_id(gc, id);
 	if (group == NULL) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-				"We have no group info for internal id [%d]\n", id);
+		purple_debug_error("QQ", "We have no group info for internal id [%d]\n", id);
 		return;
 	}
 
 	/* set all offline first, then update those online */
-	_qq_group_set_members_all_offline(group);
+	set_all_offline(group);
 	num = 0;
 	while (bytes < len) {
 		bytes += qq_get32(&member_uid, data + bytes);
@@ -279,15 +244,15 @@ void qq_process_room_cmd_get_onlines(gui
 			member->status = QQ_BUDDY_ONLINE_NORMAL;
 	}
 	if(bytes > len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-				"group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
+		purple_debug_error("QQ",
+			"group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, num);
+	purple_debug_info("QQ", "Group \"%s\" has %d online members\n", group->title_utf8, num);
 }
 
 /* process the reply to get_members_info packet */
-void qq_process_room_cmd_get_members(guint8 *data, gint len, PurpleConnection *gc)
+void qq_process_room_cmd_get_buddies(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
 	gint num;
@@ -300,7 +265,7 @@ void qq_process_room_cmd_get_members(gui
 	g_return_if_fail(data != NULL && len > 0);
 
 #if 0
-	qq_show_packet("qq_process_room_cmd_get_members", data, len);
+	qq_show_packet("qq_process_room_cmd_get_buddies", data, len);
 #endif
 
 	bytes = 0;
@@ -331,19 +296,19 @@ void qq_process_room_cmd_get_members(gui
 		qq_filter_str(nick);
 		member->nickname = g_strdup(nick);
 		g_free(nick);
-		
+
 #if 0
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		purple_debug_info("QQ",
 				"member [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
 				member_uid, member->ext_flag, member->comm_flag, member->nickname);
 #endif
 
-		member->last_refresh = time(NULL);
+		member->last_update = time(NULL);
 	}
 	if (bytes > len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+		purple_debug_error("QQ",
 				"group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, num);
+	purple_debug_info("QQ", "Group \"%s\" obtained %d member info\n", group->title_utf8, num);
 }
 
============================================================
--- libpurple/protocols/qq/group_info.h	ec4f93c82296b45db4f59a84f5d0502dd8c5d77a
+++ libpurple/protocols/qq/group_info.h	5acd27bb23463327232e8d73f22c6b33c7a2fe6d
@@ -29,12 +29,9 @@
 #include "connection.h"
 #include "group.h"
 
-void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group);
-void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc);
+gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint update_class);
 
-void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group);
-
 void qq_process_room_cmd_get_info(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc);
-void qq_process_room_cmd_get_members(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_room_cmd_get_buddies(guint8 *data, gint len, PurpleConnection *gc);
 #endif
============================================================
--- libpurple/protocols/qq/group_internal.c	67db78e571818d3665ddd87635ef513d4657d957
+++ libpurple/protocols/qq/group_internal.c	5b542d1d030bca842d226cd9a8045b6b53d6d190
@@ -31,49 +31,49 @@
 #include "group_internal.h"
 #include "utils.h"
 
-static gchar *_qq_group_set_my_status_desc(qq_group *group)
+static gchar *get_role_desc(qq_group *group)
 {
-	const char *status_desc;
+	const char *role_desc;
 	g_return_val_if_fail(group != NULL, g_strdup(""));
 
-	switch (group->my_status) {
-	case QQ_GROUP_MEMBER_STATUS_NOT_MEMBER:
-		status_desc = _("I am not a member");
+	switch (group->my_role) {
+	case QQ_ROOM_ROLE_NO:
+		role_desc = _("I am not a member");
 		break;
-	case QQ_GROUP_MEMBER_STATUS_IS_MEMBER:
-		status_desc = _("I am a member");
+	case QQ_ROOM_ROLE_YES:
+		role_desc = _("I am a member");
 		break;
-	case QQ_GROUP_MEMBER_STATUS_APPLYING:
-		status_desc = _("I am applying to join");
+	case QQ_ROOM_ROLE_REQUESTING:
+		role_desc = _("I am requesting");
 		break;
-	case QQ_GROUP_MEMBER_STATUS_IS_ADMIN:
-		status_desc = _("I am the admin");
+	case QQ_ROOM_ROLE_ADMIN:
+		role_desc = _("I am the admin");
 		break;
 	default:
-		status_desc = _("Unknown status");
+		role_desc = _("Unknown status");
 	}
 
-	return g_strdup(status_desc);
+	return g_strdup(role_desc);
 }
 
-static void _qq_group_add_to_blist(PurpleConnection *gc, qq_group *group)
+static void add_room_to_blist(PurpleConnection *gc, qq_group *group)
 {
 	GHashTable *components;
 	PurpleGroup *g;
 	PurpleChat *chat;
 	components = qq_group_to_hashtable(group);
-	chat = purple_chat_new(purple_connection_get_account(gc), group->group_name_utf8, components);
+	chat = purple_chat_new(purple_connection_get_account(gc), group->title_utf8, components);
 	g = qq_get_purple_group(PURPLE_GROUP_QQ_QUN);
 	purple_blist_add_chat(chat, g, NULL);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "You have added group \"%s\" to blist locally\n", group->group_name_utf8);
+	purple_debug_info("QQ", "You have added group \"%s\" to blist locally\n", group->title_utf8);
 }
 
 /* Create a dummy qq_group, which includes only internal_id, ext_id,
- * and potentially group_name_utf8, in case we need to call group_conv_show_window
+ * and potentially title_utf8, in case we need to call group_conv_show_window
  * right after creation. All other attributes are set to empty.
  * We need to send a get_group_info to the QQ server to update it right away */
 qq_group *qq_group_create_internal_record(PurpleConnection *gc,
-                guint32 internal_id, guint32 ext_id, gchar *group_name_utf8)
+                guint32 internal_id, guint32 ext_id, gchar *title_utf8)
 {
         qq_group *group;
         qq_data *qd;
@@ -82,21 +82,21 @@ qq_group *qq_group_create_internal_recor
         qd = (qq_data *) gc->proto_data;
 
         group = g_new0(qq_group, 1);
-        group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
-        group->my_status_desc = _qq_group_set_my_status_desc(group);
+        group->my_role = QQ_ROOM_ROLE_NO;
+        group->my_role_desc = get_role_desc(group);
         group->id = internal_id;
         group->ext_id = ext_id;
         group->type8 = 0x01;       /* assume permanent Qun */
         group->creator_uid = 10000;     /* assume by QQ admin */
-        group->group_category = 0x01;
+        group->category = 0x01;
         group->auth_type = 0x02;        /* assume need auth */
-        group->group_name_utf8 = g_strdup(group_name_utf8 == NULL ? "" : group_name_utf8);
-        group->group_desc_utf8 = g_strdup("");
+        group->title_utf8 = g_strdup(title_utf8 == NULL ? "" : title_utf8);
+        group->desc_utf8 = g_strdup("");
         group->notice_utf8 = g_strdup("");
         group->members = NULL;
 
         qd->groups = g_list_append(qd->groups, group);
-        _qq_group_add_to_blist(gc, group);
+        add_room_to_blist(gc, group);
 
         return group;
 }
@@ -124,21 +124,21 @@ GHashTable *qq_group_to_hashtable(qq_gro
 {
 	GHashTable *components;
 	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_MEMBER_STATUS), g_strdup_printf("%d", group->my_status));
-	group->my_status_desc = _qq_group_set_my_status_desc(group);
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_ROLE), g_strdup_printf("%d", group->my_role));
+	group->my_role_desc = get_role_desc(group);
 
 	g_hash_table_insert(components,
-			    g_strdup(QQ_GROUP_KEY_INTERNAL_ID), g_strdup_printf("%d", group->id));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_EXTERNAL_ID),
+			    g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%d", group->id));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
 			    g_strdup_printf("%d", group->ext_id));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_TYPE), g_strdup_printf("%d", group->type8));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_CREATOR_UID), g_strdup_printf("%d", group->creator_uid));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TYPE), g_strdup_printf("%d", group->type8));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_CREATOR_UID), g_strdup_printf("%d", group->creator_uid));
 	g_hash_table_insert(components,
-			    g_strdup(QQ_GROUP_KEY_GROUP_CATEGORY), g_strdup_printf("%d", group->group_category));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_AUTH_TYPE), g_strdup_printf("%d", group->auth_type));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_MEMBER_STATUS_DESC), g_strdup(group->my_status_desc));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_GROUP_NAME_UTF8), g_strdup(group->group_name_utf8));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_GROUP_DESC_UTF8), g_strdup(group->group_desc_utf8));
+			    g_strdup(QQ_ROOM_KEY_CATEGORY), g_strdup_printf("%d", group->category));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_AUTH_TYPE), g_strdup_printf("%d", group->auth_type));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_ROLE_DESC), g_strdup(group->my_role_desc));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(group->title_utf8));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_DESC_UTF8), g_strdup(group->desc_utf8));
 	return components;
 }
 
@@ -152,22 +152,22 @@ qq_group *qq_group_from_hashtable(Purple
 	qd = (qq_data *) gc->proto_data;
 
 	group = g_new0(qq_group, 1);
-	group->my_status =
+	group->my_role =
 	    qq_string_to_dec_value
 	    (NULL ==
 	     g_hash_table_lookup(data,
-				 QQ_GROUP_KEY_MEMBER_STATUS) ?
-	     g_strdup_printf("%d", QQ_GROUP_MEMBER_STATUS_NOT_MEMBER) :
-	     g_hash_table_lookup(data, QQ_GROUP_KEY_MEMBER_STATUS));
-	group->id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_INTERNAL_ID));
-	group->ext_id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_EXTERNAL_ID));
-	group->type8 = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_TYPE));
-	group->creator_uid = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_CREATOR_UID));
-	group->group_category = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_GROUP_CATEGORY));
-	group->auth_type = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_AUTH_TYPE));
-	group->group_name_utf8 = g_strdup(g_hash_table_lookup(data, QQ_GROUP_KEY_GROUP_NAME_UTF8));
-	group->group_desc_utf8 = g_strdup(g_hash_table_lookup(data, QQ_GROUP_KEY_GROUP_DESC_UTF8));
-	group->my_status_desc = _qq_group_set_my_status_desc(group);
+				 QQ_ROOM_KEY_ROLE) ?
+	     g_strdup_printf("%d", QQ_ROOM_ROLE_NO) :
+	     g_hash_table_lookup(data, QQ_ROOM_KEY_ROLE));
+	group->id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID));
+	group->ext_id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID));
+	group->type8 = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_TYPE));
+	group->creator_uid = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_CREATOR_UID));
+	group->category = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_CATEGORY));
+	group->auth_type = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_AUTH_TYPE));
+	group->title_utf8 = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8));
+	group->desc_utf8 = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_DESC_UTF8));
+	group->my_role_desc = get_role_desc(group);
 
 	qd->groups = g_list_append(qd->groups, group);
 
@@ -184,48 +184,54 @@ void qq_group_refresh(PurpleConnection *
 	ext_id = g_strdup_printf("%d", group->ext_id);
 	chat = purple_blist_find_chat(purple_connection_get_account(gc), ext_id);
 	g_free(ext_id);
-	if (chat == NULL && group->my_status != QQ_GROUP_MEMBER_STATUS_NOT_MEMBER) {
-		_qq_group_add_to_blist(gc, group);
-	} else if (chat != NULL) {	/* we have a local record, update its info */
-		/* if there is group_name_utf8, we update the group name */
-		if (group->group_name_utf8 != NULL && strlen(group->group_name_utf8) > 0)
-			purple_blist_alias_chat(chat, group->group_name_utf8);
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_MEMBER_STATUS), g_strdup_printf("%d", group->my_status));
-		group->my_status_desc = _qq_group_set_my_status_desc(group);
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_MEMBER_STATUS_DESC), g_strdup(group->my_status_desc));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_INTERNAL_ID),
-				     g_strdup_printf("%d", group->id));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_EXTERNAL_ID),
-				     g_strdup_printf("%d", group->ext_id));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_TYPE), g_strdup_printf("%d", group->type8));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_CREATOR_UID), g_strdup_printf("%d", group->creator_uid));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_GROUP_CATEGORY),
-				     g_strdup_printf("%d", group->group_category));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_AUTH_TYPE), g_strdup_printf("%d", group->auth_type));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_GROUP_NAME_UTF8), g_strdup(group->group_name_utf8));
-		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_GROUP_DESC_UTF8), g_strdup(group->group_desc_utf8));
+	if (chat == NULL && group->my_role != QQ_ROOM_ROLE_NO) {
+		add_room_to_blist(gc, group);
+		return;
 	}
+
+	if (chat == NULL) {
+		return;
+	}
+	
+	/* we have a local record, update its info */
+	/* if there is title_utf8, we update the group name */
+	if (group->title_utf8 != NULL && strlen(group->title_utf8) > 0)
+		purple_blist_alias_chat(chat, group->title_utf8);
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_ROLE), g_strdup_printf("%d", group->my_role));
+	group->my_role_desc = get_role_desc(group);
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_ROLE_DESC), g_strdup(group->my_role_desc));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_INTERNAL_ID),
+		     g_strdup_printf("%d", group->id));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
+		     g_strdup_printf("%d", group->ext_id));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_TYPE), g_strdup_printf("%d", group->type8));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_CREATOR_UID), g_strdup_printf("%d", group->creator_uid));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_CATEGORY),
+		     g_strdup_printf("%d", group->category));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_AUTH_TYPE), g_strdup_printf("%d", group->auth_type));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(group->title_utf8));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_DESC_UTF8), g_strdup(group->desc_utf8));
 }
 
-/* NOTE: If we knew how to convert between an external and internal group id, as the official 
+/* NOTE: If we knew how to convert between an external and internal group id, as the official
  * client seems to, the following would be unnecessary. That would be ideal. */
 
 /* Use list to specify if id's alternate id is pending discovery. */
 void qq_set_pending_id(GSList **list, guint32 id, gboolean pending)
 {
-	if (pending) 
+	if (pending)
 		*list = g_slist_prepend(*list, GINT_TO_POINTER(id));
-	else 
+	else
 		*list = g_slist_remove(*list, GINT_TO_POINTER(id));
 }
 
============================================================
--- libpurple/protocols/qq/group_internal.h	9dbed22a6a1c7c44af8faacaece208c705c2491b
+++ libpurple/protocols/qq/group_internal.h	07c09e08f1a14b607fcbd926aad0a5a0f5900a0f
@@ -28,18 +28,18 @@
 #include <glib.h>
 #include "group.h"
 
-#define QQ_GROUP_KEY_MEMBER_STATUS				"my_status_code"
-#define QQ_GROUP_KEY_MEMBER_STATUS_DESC	"my_status_desc"
-#define QQ_GROUP_KEY_INTERNAL_ID					"id"
-#define QQ_GROUP_KEY_EXTERNAL_ID					"ext_id"
-#define QQ_GROUP_KEY_TYPE								"type"
-#define QQ_GROUP_KEY_CREATOR_UID					"creator_uid"
-#define QQ_GROUP_KEY_GROUP_CATEGORY			"category"
-#define QQ_GROUP_KEY_AUTH_TYPE						"auth_type"
-#define QQ_GROUP_KEY_GROUP_NAME_UTF8			"name_utf8"
-#define QQ_GROUP_KEY_GROUP_DESC_UTF8			"desc_utf8"
+#define QQ_ROOM_KEY_ROLE									"my_role"
+#define QQ_ROOM_KEY_ROLE_DESC						"my_role_desc"
+#define QQ_ROOM_KEY_INTERNAL_ID					"id"
+#define QQ_ROOM_KEY_EXTERNAL_ID					"ext_id"
+#define QQ_ROOM_KEY_TYPE									"type"
+#define QQ_ROOM_KEY_CREATOR_UID					"creator_uid"
+#define QQ_ROOM_KEY_CATEGORY							"category"
+#define QQ_ROOM_KEY_AUTH_TYPE						"auth_type"
+#define QQ_ROOM_KEY_TITLE_UTF8						"title_utf8"
+#define QQ_ROOM_KEY_DESC_UTF8						"desc_utf8"
 
-qq_group *qq_group_create_internal_record(PurpleConnection *gc, 
+qq_group *qq_group_create_internal_record(PurpleConnection *gc,
 		guint32 internal_id, guint32 ext_id, gchar *group_name_utf8);
 void qq_group_delete_internal_record(qq_data *qd, guint32 id);
 
============================================================
--- libpurple/protocols/qq/group_join.c	792fdd633fd0b395d83701e185a7d007c26c76bd
+++ libpurple/protocols/qq/group_join.c	b67c1322991d3e6f1861628109c608deb68af6cf
@@ -42,10 +42,11 @@
 #include "header_info.h"
 #include "packet_parse.h"
 #include "qq_network.h"
+#include "qq_process.h"
 
 enum {
-	QQ_GROUP_JOIN_OK = 0x01,
-	QQ_GROUP_JOIN_NEED_AUTH = 0x02,
+	QQ_ROOM_JOIN_OK = 0x01,
+	QQ_ROOM_JOIN_NEED_AUTH = 0x02,
 };
 
 static void _qq_group_exit_with_gc_and_id(gc_and_uid *g)
@@ -68,20 +69,20 @@ void qq_send_cmd_group_join_group(Purple
 {
 	g_return_if_fail(group != NULL);
 
-	if (group->my_status == QQ_GROUP_MEMBER_STATUS_NOT_MEMBER) {
-		group->my_status = QQ_GROUP_MEMBER_STATUS_APPLYING;
+	if (group->my_role == QQ_ROOM_ROLE_NO) {
+		group->my_role = QQ_ROOM_ROLE_REQUESTING;
 		qq_group_refresh(gc, group);
 	}
 
 	switch (group->auth_type) {
-	case QQ_GROUP_AUTH_TYPE_NO_AUTH:
-	case QQ_GROUP_AUTH_TYPE_NEED_AUTH:
+	case QQ_ROOM_AUTH_TYPE_NO_AUTH:
+	case QQ_ROOM_AUTH_TYPE_NEED_AUTH:
 		break;
-	case QQ_GROUP_AUTH_TYPE_NO_ADD:
-		purple_notify_warning(gc, NULL, _("This group does not allow others to join"), NULL);
+	case QQ_ROOM_AUTH_TYPE_NO_ADD:
+		purple_notify_warning(gc, NULL, _("The Qun does not allow others to join"), NULL);
 		return;
 	default:
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown group auth type: %d\n", group->auth_type);
+		purple_debug_error("QQ", "Unknown room auth type: %d\n", group->auth_type);
 		break;
 	}
 
@@ -99,10 +100,10 @@ static void _qq_group_join_auth_with_gc_
 
 	group = qq_room_search_id(gc, id);
 	if (group == NULL) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Can not find qq_group by internal_id: %d\n", id);
+		purple_debug_error("QQ", "Can not find qq_group by internal_id: %d\n", id);
 		return;
 	} else {		/* everything is OK */
-		qq_send_cmd_group_auth(gc, group, QQ_GROUP_AUTH_REQUEST_APPLY, 0, reason_utf8);
+		qq_send_cmd_group_auth(gc, group, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
 	}
 }
 
@@ -112,10 +113,9 @@ static void _qq_group_join_auth(PurpleCo
 	gc_and_uid *g;
 	g_return_if_fail(group != NULL);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", 
-			"Group (internal id: %d) needs authentication\n", group->id);
+	purple_debug_info("QQ", "Group (internal id: %d) needs authentication\n", group->id);
 
-	msg = g_strdup_printf("Group \"%s\" needs authentication\n", group->group_name_utf8);
+	msg = g_strdup_printf("Group \"%s\" needs authentication\n", group->title_utf8);
 	g = g_new0(gc_and_uid, 1);
 	g->gc = gc;
 	g->uid = group->id;
@@ -125,7 +125,7 @@ static void _qq_group_join_auth(PurpleCo
 			   _("Send"),
 			   G_CALLBACK(_qq_group_join_auth_with_gc_and_id),
 			   _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
-			   purple_connection_get_account(gc), group->group_name_utf8, NULL,
+			   purple_connection_get_account(gc), group->title_utf8, NULL,
 			   g);
 	g_free(msg);
 }
@@ -143,8 +143,8 @@ void qq_send_cmd_group_auth(PurpleConnec
 	else
 		reason_qq = utf8_to_qq(reason_utf8, QQ_CHARSET_DEFAULT);
 
-	if (opt == QQ_GROUP_AUTH_REQUEST_APPLY) {
-		group->my_status = QQ_GROUP_MEMBER_STATUS_APPLYING;
+	if (opt == QQ_ROOM_AUTH_REQUEST_APPLY) {
+		group->my_role = QQ_ROOM_ROLE_REQUESTING;
 		qq_group_refresh(gc, group);
 		uid = 0;
 	}
@@ -173,8 +173,7 @@ void qq_process_group_cmd_exit_group(gui
 	qd = (qq_data *) gc->proto_data;
 
 	if (len < 4) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len);
+		purple_debug_error("QQ", "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len);
 		return;
 	}
 
@@ -189,7 +188,7 @@ void qq_process_group_cmd_exit_group(gui
 			purple_blist_remove_chat(chat);
 		qq_group_delete_internal_record(qd, id);
 	}
-	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
+	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the Qun"), NULL);
 }
 
 /* Process the reply to group_auth subcmd */
@@ -203,15 +202,15 @@ void qq_process_group_cmd_join_group_aut
 	qd = (qq_data *) gc->proto_data;
 
 	if (len < 4) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid join group reply, expect %d bytes, read %d bytes\n", 4, len);
+		purple_debug_error("QQ",
+			"Invalid join room reply, expect %d bytes, read %d bytes\n", 4, len);
 		return;
 	}
 	bytes = 0;
 	bytes += qq_get32(&id, data + bytes);
 	g_return_if_fail(id > 0);
 
-	purple_notify_info(gc, _("QQ Group Auth"),
+	purple_notify_info(gc, _("QQ Qun Auth"),
 		     _("Your authorization request has been accepted by the QQ server"), NULL);
 }
 
@@ -226,11 +225,11 @@ void qq_process_group_cmd_join_group(gui
 	g_return_if_fail(data != NULL && len > 0);
 
 	if (len < 5) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 			   "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len);
 		return;
 	}
-	
+
 	bytes = 0;
 	bytes += qq_get32(&id, data + bytes);
 	bytes += qq_get8(&reply, data + bytes);
@@ -240,26 +239,26 @@ void qq_process_group_cmd_join_group(gui
 	/* need to check if group is NULL or not. */
 	g_return_if_fail(group != NULL);
 	switch (reply) {
-	case QQ_GROUP_JOIN_OK:
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8);
-		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+	case QQ_ROOM_JOIN_OK:
+		purple_debug_info("QQ", "Succeed joining group \"%s\"\n", group->title_utf8);
+		group->my_role = QQ_ROOM_ROLE_YES;
 		qq_group_refresh(gc, group);
 		/* this must be shown before getting online members */
 		qq_group_conv_show_window(gc, group);
-		qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
+		qq_room_update(gc, 0, group->id);
 		break;
-	case QQ_GROUP_JOIN_NEED_AUTH:
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+	case QQ_ROOM_JOIN_NEED_AUTH:
+		purple_debug_info("QQ",
 			   "Fail joining group [%d] %s, needs authentication\n",
-			   group->ext_id, group->group_name_utf8);
-		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+			   group->ext_id, group->title_utf8);
+		group->my_role = QQ_ROOM_ROLE_NO;
 		qq_group_refresh(gc, group);
 		_qq_group_join_auth(gc, group);
 		break;
 	default:
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		purple_debug_info("QQ",
 			   "Error joining group [%d] %s, unknown reply: 0x%02x\n",
-			   group->ext_id, group->group_name_utf8, reply);
+			   group->ext_id, group->title_utf8, reply);
 	}
 }
 
@@ -274,7 +273,7 @@ void qq_group_join(PurpleConnection *gc,
 	g_return_if_fail(data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	ext_id_ptr = g_hash_table_lookup(data, QQ_GROUP_KEY_EXTERNAL_ID);
+	ext_id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
 	g_return_if_fail(ext_id_ptr != NULL);
 	errno = 0;
 	ext_id = strtol(ext_id_ptr, NULL, 10);
@@ -301,7 +300,7 @@ void qq_group_exit(PurpleConnection *gc,
 
 	g_return_if_fail(data != NULL);
 
-	id_ptr = g_hash_table_lookup(data, QQ_GROUP_KEY_INTERNAL_ID);
+	id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
 	id = strtol(id_ptr, NULL, 10);
 
 	g_return_if_fail(id > 0);
@@ -312,8 +311,7 @@ void qq_group_exit(PurpleConnection *gc,
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("Are you sure you want to leave this Qun?"),
-			    _
-			    ("Note, if you are the creator, \nthis operation will eventually remove this Qun."),
+			    _("Note, if you are the creator, \nthis operation will eventually remove this Qun."),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
 			    g, 2, _("Cancel"),
============================================================
--- libpurple/protocols/qq/group_join.h	0825fbbfaae115b886258169f07d0512140d20cb
+++ libpurple/protocols/qq/group_join.h	df3dae6e6bdfd0a3c12aaf50f0c33e8e66c87ac7
@@ -30,15 +30,15 @@ enum {
 #include "group.h"
 
 enum {
-	QQ_GROUP_AUTH_TYPE_NO_AUTH = 0x01,
-	QQ_GROUP_AUTH_TYPE_NEED_AUTH = 0x02,
-	QQ_GROUP_AUTH_TYPE_NO_ADD = 0x03
+	QQ_ROOM_AUTH_TYPE_NO_AUTH = 0x01,
+	QQ_ROOM_AUTH_TYPE_NEED_AUTH = 0x02,
+	QQ_ROOM_AUTH_TYPE_NO_ADD = 0x03
 };
 
 enum {
-	QQ_GROUP_AUTH_REQUEST_APPLY = 0x01,
-	QQ_GROUP_AUTH_REQUEST_APPROVE = 0x02,
-	QQ_GROUP_AUTH_REQUEST_REJECT = 0x03
+	QQ_ROOM_AUTH_REQUEST_APPLY = 0x01,
+	QQ_ROOM_AUTH_REQUEST_APPROVE = 0x02,
+	QQ_ROOM_AUTH_REQUEST_REJECT = 0x03
 };
 
 void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8);
============================================================
--- libpurple/protocols/qq/group_opt.c	cfe88c8ff984e466debb1c62c877f54686a56ac7
+++ libpurple/protocols/qq/group_opt.c	98dd986059a93ab53d153d458a1f9185e335ab2d
@@ -38,6 +38,7 @@
 #include "header_info.h"
 #include "packet_parse.h"
 #include "qq_network.h"
+#include "qq_process.h"
 #include "utils.h"
 
 static int _compare_guint32(const void *a,
@@ -67,7 +68,7 @@ static void _qq_group_member_opt(PurpleC
 	}
 	data_len = 6 + count * 4;
 	data = g_newa(guint8, data_len);
-	
+
 	bytes = 0;
 	bytes += qq_put8(data + bytes, operation);
 	for (i = 0; i < count; i++)
@@ -88,7 +89,7 @@ static void _qq_group_reject_application
 	g_return_if_fail(g != NULL && g->gc != NULL && g->id > 0 && g->member > 0);
 	group = qq_room_search_id(g->gc, g->id);
 	g_return_if_fail(group != NULL);
-	qq_send_cmd_group_auth(g->gc, group, QQ_GROUP_AUTH_REQUEST_REJECT, g->member, msg_utf8);
+	qq_send_cmd_group_auth(g->gc, group, QQ_ROOM_AUTH_REQUEST_REJECT, g->member, msg_utf8);
 	g_free(g);
 }
 
@@ -111,11 +112,11 @@ void qq_group_reject_application_with_st
 	g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0);
 
 	msg1 = g_strdup_printf(_("You rejected %d's request"), g->member);
-	msg2 = g_strdup(_("Enter your reason:"));
+	msg2 = g_strdup(_("Message:"));
 
 	nombre = uid_to_purple_name(g->member);
 	purple_request_input(g->gc, /* title */ NULL, msg1, msg2,
-			   _("Sorry, you are not my type..."), /* multiline */ TRUE, /* masked */ FALSE,
+			   _("Sorry, you are not my style..."), /* multiline */ TRUE, /* masked */ FALSE,
 			   /* hint */ NULL,
 			   _("Send"), G_CALLBACK(_qq_group_reject_application_real),
 			   _("Cancel"), G_CALLBACK(_qq_group_do_nothing_with_struct),
@@ -133,7 +134,7 @@ void qq_group_approve_application_with_s
 	g_return_if_fail(g != NULL && g->gc != NULL && g->id > 0 && g->member > 0);
 	group = qq_room_search_id(g->gc, g->id);
 	g_return_if_fail(group != NULL);
-	qq_send_cmd_group_auth(g->gc, group, QQ_GROUP_AUTH_REQUEST_APPROVE, g->member, "");
+	qq_send_cmd_group_auth(g->gc, group, QQ_ROOM_AUTH_REQUEST_APPROVE, g->member, "");
 	qq_group_find_or_add_member(g->gc, group, g->member);
 	g_free(g);
 }
@@ -189,9 +190,9 @@ void qq_group_modify_members(PurpleConne
 		qq_group_find_or_add_member(gc, group, add_members[i]);
 
 	if (del > 0)
-		_qq_group_member_opt(gc, group, QQ_GROUP_MEMBER_DEL, del_members);
+		_qq_group_member_opt(gc, group, QQ_ROOM_MEMBER_DEL, del_members);
 	if (add > 0)
-		_qq_group_member_opt(gc, group, QQ_GROUP_MEMBER_ADD, add_members);
+		_qq_group_member_opt(gc, group, QQ_ROOM_MEMBER_ADD, add_members);
 }
 
 void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc)
@@ -209,9 +210,10 @@ void qq_group_process_modify_members_rep
 	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in modify members for Qun %d\n", group->ext_id);
+	purple_debug_info("QQ", "Succeed in modify members for room %d\n", group->ext_id);
 
-	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully modified Qun member"), NULL);
+	purple_notify_info(gc, _("QQ Qun Operation"),
+			_("You have successfully modified Qun member"), NULL);
 }
 
 void qq_room_change_info(PurpleConnection *gc, qq_group *group)
@@ -223,8 +225,8 @@ void qq_room_change_info(PurpleConnectio
 
 	g_return_if_fail(group != NULL);
 
-	group_name = group->group_name_utf8 == NULL ? "" : utf8_to_qq(group->group_name_utf8, QQ_CHARSET_DEFAULT);
-	group_desc = group->group_desc_utf8 == NULL ? "" : utf8_to_qq(group->group_desc_utf8, QQ_CHARSET_DEFAULT);
+	group_name = group->title_utf8 == NULL ? "" : utf8_to_qq(group->title_utf8, QQ_CHARSET_DEFAULT);
+	group_desc = group->desc_utf8 == NULL ? "" : utf8_to_qq(group->desc_utf8, QQ_CHARSET_DEFAULT);
 	notice = group->notice_utf8 == NULL ? "" : utf8_to_qq(group->notice_utf8, QQ_CHARSET_DEFAULT);
 
 	data_len = 64 + strlen(group_name) + strlen(group_desc) + strlen(notice);
@@ -237,7 +239,7 @@ void qq_room_change_info(PurpleConnectio
 	/* 007-008 */
 	bytes += qq_put16(data + bytes, 0x0000);
 	/* 009-010 */
-	bytes += qq_put16(data + bytes, group->group_category);
+	bytes += qq_put16(data + bytes, group->category);
 
 	bytes += qq_put8(data + bytes, strlen(group_name));
 	bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name));
@@ -251,7 +253,7 @@ void qq_room_change_info(PurpleConnectio
 	bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc));
 
 	if (bytes > data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 			   "Overflow in qq_room_change_info, max %d bytes, now %d bytes\n",
 			   data_len, bytes);
 		return;
@@ -274,7 +276,7 @@ void qq_group_process_modify_info_reply(
 	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in modify info for Qun %d\n", group->ext_id);
+	purple_debug_info("QQ", "Succeed in modify info for Qun %d\n", group->ext_id);
 	qq_group_refresh(gc, group);
 
 	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully modified Qun information"), NULL);
@@ -297,9 +299,9 @@ void qq_room_create_new(PurpleConnection
 	bytes = 0;
 	/* we create the simpleset group, only group name is given */
 	/* 001 */
-	bytes += qq_put8(data + bytes, QQ_GROUP_TYPE_PERMANENT);
+	bytes += qq_put8(data + bytes, QQ_ROOM_TYPE_PERMANENT);
 	/* 002 */
-	bytes += qq_put8(data + bytes, QQ_GROUP_AUTH_TYPE_NEED_AUTH);
+	bytes += qq_put8(data + bytes, QQ_ROOM_AUTH_TYPE_NEED_AUTH);
 	/* 003-004 */
 	bytes += qq_put16(data + bytes, 0x0000);
 	/* 005-006 */
@@ -313,7 +315,7 @@ void qq_room_create_new(PurpleConnection
 	bytes += qq_put32(data + bytes, qd->uid);	/* I am member of coz */
 
 	if (bytes > data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 			   "Overflow in qq_room_create, max %d bytes, now %d bytes\n",
 			   data_len, bytes);
 		return;
@@ -352,14 +354,14 @@ void qq_group_process_create_group_reply
 	g_return_if_fail(id > 0 && ext_id);
 
 	group = qq_group_create_internal_record(gc, id, ext_id, NULL);
-	group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN;
+	group->my_role = QQ_ROOM_ROLE_ADMIN;
 	group->creator_uid = qd->uid;
 	qq_group_refresh(gc, group);
 
 	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_ACTIVATE, id);
-	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, id);
+	qq_room_update(gc, 0, group->id);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in create Qun, external ID %d\n", group->ext_id);
+	purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", group->ext_id);
 
 	g = g_new0(gc_and_uid, 1);
 	g->gc = gc;
@@ -368,7 +370,7 @@ void qq_group_process_create_group_reply
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("You have successfully created a Qun"),
 			    _
-			    ("Would you like to set up the Qun details now?"),
+			    ("Would you like to set up the detail information now?"),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
 				g, 2,
@@ -391,7 +393,7 @@ void qq_group_process_activate_group_rep
 	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in activate Qun %d\n", group->ext_id);
+	purple_debug_info("QQ", "Succeed in activate Qun %d\n", group->ext_id);
 }
 
 void qq_group_manage_group(PurpleConnection *gc, GHashTable *data)
@@ -402,7 +404,7 @@ void qq_group_manage_group(PurpleConnect
 
 	g_return_if_fail(data != NULL);
 
-	id_ptr = g_hash_table_lookup(data, QQ_GROUP_KEY_INTERNAL_ID);
+	id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
 	id = strtol(id_ptr, NULL, 10);
 	g_return_if_fail(id > 0);
 
============================================================
--- libpurple/protocols/qq/group_opt.h	c4a0487f1ba87e92a56d484b63a0ccdbaeda83ef
+++ libpurple/protocols/qq/group_opt.h	59af741aa7f62b30f0697d841497bb2b6798c546
@@ -38,13 +38,13 @@ enum {
 } group_member_opt;
 
 enum {
-	QQ_GROUP_TYPE_PERMANENT = 0x01,
-	QQ_GROUP_TYPE_TEMPORARY
+	QQ_ROOM_TYPE_PERMANENT = 0x01,
+	QQ_ROOM_TYPE_TEMPORARY
 };
 
 enum {
-	QQ_GROUP_MEMBER_ADD = 0x01,
-	QQ_GROUP_MEMBER_DEL
+	QQ_ROOM_MEMBER_ADD = 0x01,
+	QQ_ROOM_MEMBER_DEL
 };
 
 void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new_members);
============================================================
--- libpurple/protocols/qq/group_search.c	fc5206710a5f2e134a0a97e1d59ae1a4f4be9a46
+++ libpurple/protocols/qq/group_search.c	07127dd246f2fbd0032f25f9b06f5abe91ec17f7
@@ -38,8 +38,8 @@ enum {
 #include "qq_network.h"
 
 enum {
-	QQ_GROUP_SEARCH_TYPE_BY_ID = 0x01,
-	QQ_GROUP_SEARCH_TYPE_DEMO = 0x02
+	QQ_ROOM_SEARCH_TYPE_BY_ID = 0x01,
+	QQ_ROOM_SEARCH_TYPE_DEMO = 0x02
 };
 
 /* send packet to search for qq_group */
@@ -49,7 +49,7 @@ void qq_send_cmd_group_search_group(Purp
 	gint bytes = 0;
 	guint8 type;
 
-	type = (ext_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID;
+	type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID;
 
 	bytes = 0;
 	bytes += qq_put8(raw_data + bytes, type);
@@ -63,21 +63,21 @@ static void _qq_setup_roomlist(qq_data *
 	PurpleRoomlistRoom *room;
 	gchar field[11];
 
-	room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, group->group_name_utf8, NULL);
+	room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, group->title_utf8, NULL);
 	g_snprintf(field, sizeof(field), "%d", group->ext_id);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
 	g_snprintf(field, sizeof(field), "%d", group->creator_uid);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	purple_roomlist_room_add_field(qd->roomlist, room, group->group_desc_utf8);
+	purple_roomlist_room_add_field(qd->roomlist, room, group->desc_utf8);
 	g_snprintf(field, sizeof(field), "%d", group->id);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
 	g_snprintf(field, sizeof(field), "%d", group->type8);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
 	g_snprintf(field, sizeof(field), "%d", group->auth_type);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	g_snprintf(field, sizeof(field), "%d", group->group_category);
+	g_snprintf(field, sizeof(field), "%d", group->category);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	purple_roomlist_room_add_field(qd->roomlist, room, group->group_name_utf8);
+	purple_roomlist_room_add_field(qd->roomlist, room, group->title_utf8);
 	purple_roomlist_room_add(qd->roomlist, room);
 
 	purple_roomlist_set_in_progress(qd->roomlist, FALSE);
@@ -109,14 +109,14 @@ void qq_process_group_cmd_search_group(g
 	bytes += qq_get16(&(unknown), data + bytes);
 	bytes += qq_get16(&(unknown), data + bytes);
 	bytes += qq_get16(&(unknown), data + bytes);
-	bytes += qq_get32(&(group.group_category), data + bytes);
-	bytes += convert_as_pascal_string(data + bytes, &(group.group_name_utf8), QQ_CHARSET_DEFAULT);
+	bytes += qq_get32(&(group.category), data + bytes);
+	bytes += convert_as_pascal_string(data + bytes, &(group.title_utf8), QQ_CHARSET_DEFAULT);
 	bytes += qq_get16(&(unknown), data + bytes);
 	bytes += qq_get8(&(group.auth_type), data + bytes);
-	bytes += convert_as_pascal_string(data + bytes, &(group.group_desc_utf8), QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &(group.desc_utf8), QQ_CHARSET_DEFAULT);
 	/* end of one qq_group */
 	if(bytes != len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+		purple_debug_error("QQ", 
 			"group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
@@ -125,7 +125,7 @@ void qq_process_group_cmd_search_group(g
 		qq_set_pending_id(&qd->joining_groups, group.ext_id, FALSE);
 		if (qq_room_search_id(gc, group.id) == NULL)
 			qq_group_create_internal_record(gc, 
-					group.id, group.ext_id, group.group_name_utf8);
+					group.id, group.ext_id, group.title_utf8);
 		qq_send_cmd_group_join_group(gc, &group);
 	} else {
 		_qq_setup_roomlist(qd, &group);
============================================================
--- libpurple/protocols/qq/header_info.c	3b81718aecc45aa7425e5da435eeab9b0096a99b
+++ libpurple/protocols/qq/header_info.c	f6c8d5882aa98e81f4f08d75d9e6f09d1e244bf9
@@ -122,7 +122,7 @@ const gchar *qq_get_ver_desc(gint source
 	case QQ_SERVER_0100:
 		return "QQ Server 0100";
 	default:
-		return "Unknown";
+		return "Unknown Version";
 	}
 }
 
@@ -146,8 +146,8 @@ const gchar *qq_get_cmd_desc(gint cmd)
 		return "QQ_CMD_DEL_BUDDY";
 	case QQ_CMD_BUDDY_AUTH:
 		return "QQ_CMD_BUDDY_AUTH";
-	case QQ_CMD_CHANGE_ONLINE_STATUS:
-		return "QQ_CMD_CHANGE_ONLINE_STATUS";
+	case QQ_CMD_CHANGE_STATUS:
+		return "QQ_CMD_CHANGE_STATUS";
 	case QQ_CMD_ACK_SYS_MSG:
 		return "QQ_CMD_ACK_SYS_MSG";
 	case QQ_CMD_SEND_IM:
@@ -172,10 +172,10 @@ const gchar *qq_get_cmd_desc(gint cmd)
 		return "QQ_CMD_TOKEN";
 	case QQ_CMD_RECV_MSG_SYS:
 		return "QQ_CMD_RECV_MSG_SYS";
-	case QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS:
-		return "QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS";
+	case QQ_CMD_BUDDY_CHANGE_STATUS:
+		return "QQ_CMD_BUDDY_CHANGE_STATUS";
 	default:
-		return "Unknown";
+		return "Unknown CMD";
 	}
 }
 
@@ -204,8 +204,8 @@ const gchar *qq_get_room_cmd_desc(gint r
 		return "QQ_ROOM_CMD_SEND_MSG";
 	case QQ_ROOM_CMD_GET_ONLINES:
 		return "QQ_ROOM_CMD_GET_ONLINES";
-	case QQ_ROOM_CMD_GET_MEMBER_INFO:
-		return "QQ_ROOM_CMD_GET_MEMBER_INFO";
+	case QQ_ROOM_CMD_GET_BUDDIES:
+		return "QQ_ROOM_CMD_GET_BUDDIES";
 	case QQ_ROOM_CMD_CHANGE_CARD:
 		return "QQ_ROOM_CMD_CHANGE_CARD";
 	case QQ_ROOM_CMD_GET_REALNAMES:
@@ -231,6 +231,6 @@ const gchar *qq_get_room_cmd_desc(gint r
 	case QQ_ROOM_CMD_TEMP_GET_MEMBERS:
 		return "QQ_ROOM_CMD_TEMP_GET_MEMBERS";
 	default:
-		return "Unknown QQ Room Command";
+		return "Unknown Room Command";
 	}
 }
============================================================
--- libpurple/protocols/qq/header_info.h	b6c6b91c69672f9605fd6dbed3c27a930319b774
+++ libpurple/protocols/qq/header_info.h	1fb677b46ef167f921799cfd63a850c36e429cc8
@@ -47,7 +47,7 @@ enum {
 	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_CHANGE_STATUS = 0x000d,		/* change my online status */
 	QQ_CMD_ACK_SYS_MSG = 0x0012,			/* ack system message */
 	QQ_CMD_SEND_IM = 0x0016,			/* send message */
 	QQ_CMD_RECV_IM = 0x0017,			/* receive message */
@@ -59,11 +59,11 @@ enum {
 	QQ_CMD_GET_BUDDIES_ONLINE = 0x0027,		/* get online buddies list */
 	QQ_CMD_CELL_PHONE_2 = 0x0029,			/* cell phone 2 */
 	QQ_CMD_ROOM = 0x0030,			/* room command */
-	QQ_CMD_GET_BUDDIES_AND_ROOMS = 0x0058,  
+	QQ_CMD_GET_BUDDIES_AND_ROOMS = 0x0058,
 	QQ_CMD_GET_LEVEL = 0x005C,			/* get level for one or more buddies */
 	QQ_CMD_TOKEN  = 0x0062, 		/* get login token */
 	QQ_CMD_RECV_MSG_SYS = 0x0080,			/* receive a system message */
-	QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS = 0x0081,	/* buddy change status */
+	QQ_CMD_BUDDY_CHANGE_STATUS = 0x0081,	/* buddy change status */
 };
 
 const gchar *qq_get_cmd_desc(gint type);
@@ -80,7 +80,7 @@ enum {
 	QQ_ROOM_CMD_QUIT = 0x09,
 	QQ_ROOM_CMD_SEND_MSG = 0x0a,
 	QQ_ROOM_CMD_GET_ONLINES = 0x0b,
-	QQ_ROOM_CMD_GET_MEMBER_INFO = 0x0c,
+	QQ_ROOM_CMD_GET_BUDDIES = 0x0c,
 
 	QQ_ROOM_CMD_CHANGE_CARD = 0x0E,
 	QQ_ROOM_CMD_GET_REALNAMES = 0x0F,
============================================================
--- libpurple/protocols/qq/im.c	90e409aba8c08748d11f8702874197fa1f8c6a3d
+++ libpurple/protocols/qq/im.c	5d3ec6ec0446db7b0d31d8c2613a1bcd42820909
@@ -209,21 +209,17 @@ static const gchar *qq_get_recv_im_type_
 			return "QQ_RECV_IM_TEMP_QUN_IM";
 		case QQ_RECV_IM_QUN_IM:
 			return "QQ_RECV_IM_QUN_IM";
+		case QQ_RECV_IM_NEWS:
+			return "QQ_RECV_IM_NEWS";
+		case QQ_RECV_IM_FROM_BUDDY_2006:
+			return "QQ_RECV_IM_FROM_BUDDY_2006";
+		case QQ_RECV_IM_FROM_UNKNOWN_2006:
+			return "QQ_RECV_IM_FROM_UNKNOWN_2006";
 		default:
 			return "QQ_RECV_IM_UNKNOWN";
 	}
 }
 
-/* when we receive a message,
- * we send an ACK which is the first 16 bytes of incoming packet */
-static void _qq_send_packet_recv_im_ack(PurpleConnection *gc, guint16 seq, guint8 *data)
-{
-	qq_data *qd;
-
-	qd = (qq_data *) gc->proto_data;
-	qq_send_cmd_detail(qd, QQ_CMD_RECV_IM, seq, FALSE, data, 16);
-}
-
 /* read the common parts of the normal_im,
  * returns the bytes read if succeed, or -1 if there is any error */
 static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common)
@@ -240,13 +236,61 @@ static gint _qq_normal_im_common_read(gu
 	bytes += qq_get16(&(common->normal_im_type), data + bytes);
 
 	if (bytes != 28) {	/* read common place fail */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Expect 28 bytes, read %d bytes\n", bytes);
+		purple_debug_error("QQ", "Expect 28 bytes, read %d bytes\n", bytes);
 		return -1;
 	}
 
 	return bytes;
 }
 
+static void _qq_process_recv_news(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+	qq_data *qd = (qq_data *) gc->proto_data;
+	gint bytes;
+	guint8 *temp;
+	guint8 temp_len;
+	gchar *title, *brief, *url;
+	gchar *content, *content_utf8;
+
+	g_return_if_fail(data != NULL && data_len != 0);
+
+#if 0
+	qq_show_packet("Rcv news", data, data_len);
+#endif
+
+	temp = g_newa(guint8, data_len);
+	bytes = 4;	// ignore unknown 4 bytes
+
+	bytes += qq_get8(&temp_len, data + bytes);
+	g_return_if_fail(bytes + temp_len <= data_len);
+	bytes += qq_getdata(temp, temp_len, data+bytes);
+	title = g_strndup((gchar *)temp, temp_len);
+
+	bytes += qq_get8(&temp_len, data + bytes);
+	g_return_if_fail(bytes + temp_len <= data_len);
+	bytes += qq_getdata(temp, temp_len, data+bytes);
+	brief = g_strndup((gchar *)temp, temp_len);
+
+	bytes += qq_get8(&temp_len, data + bytes);
+	g_return_if_fail(bytes + temp_len <= data_len);
+	bytes += qq_getdata(temp, temp_len, data+bytes);
+	url = g_strndup((gchar *)temp, temp_len);
+
+	content = g_strdup_printf(_("Title: %s\nBrief: %s\n\n%s"), title, brief, url);
+	content_utf8 = qq_to_utf8(content, QQ_CHARSET_DEFAULT);
+
+	if (qd->is_show_news) {
+		purple_notify_info(gc, NULL, _("QQ Server News"), content_utf8);
+	} else {
+		purple_debug_info("QQ", "QQ Server news:\n%s", content_utf8);
+	}
+	g_free(title);
+	g_free(brief);
+	g_free(url);
+	g_free(content);
+	g_free(content_utf8);
+}
+
 /* process received normal text IM */
 static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
 {
@@ -266,7 +310,7 @@ static void _qq_process_recv_normal_im_t
 	/* now it is QQ_NORMAL_IM_TEXT */
 	/*
 	   if (*cursor >= (data + len - 1)) {
-	   purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
+	   purple_debug_warning("QQ", "Received normal IM text is empty\n");
 	   return;
 	   } else
 	   */
@@ -313,9 +357,9 @@ static void _qq_process_recv_normal_im_t
 	}
 	qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 	if (qq_b != NULL) {
-		qq_b->client_version = common->sender_ver; 
+		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);
@@ -350,19 +394,18 @@ static void _qq_process_recv_normal_im(g
 
 	bytes = _qq_normal_im_common_read(data, len, common);
 	if (bytes < 0) {
-		purple_debug (PURPLE_DEBUG_ERROR, "QQ",
-				"Fail read the common part of normal IM\n");
+		purple_debug_error("QQ", "Fail read the common part of normal IM\n");
 		return;
 	}
 
 	switch (common->normal_im_type) {
 		case QQ_NORMAL_IM_TEXT:
-			purple_debug (PURPLE_DEBUG_INFO, "QQ",
+			purple_debug_info("QQ",
 					"Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n",
 					common->sender_uid, common->receiver_uid,
 					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");
+				purple_debug_warning("QQ", "Received normal IM text is empty\n");
 				return;
 			}
 			_qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
@@ -382,16 +425,50 @@ static void _qq_process_recv_normal_im(g
 		case QQ_NORMAL_IM_FILE_NOTIFY:
 			qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc);
 			break;
+		case QQ_NORMAL_IM_FILE_REQUEST_TCP:
+			/* Check ReceivedFileIM::parseContents in eva*/
+			/* some client use this function for detect invisable buddy*/
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n");
+			qq_show_packet ("Not support", data, len);
+			break;
+		case QQ_NORMAL_IM_FILE_APPROVE_TCP:
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_APPROVE_TCP\n");
+			qq_show_packet ("Not support", data, len);
+			break;
+		case QQ_NORMAL_IM_FILE_REJECT_TCP:
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REJECT_TCP\n");
+			qq_show_packet ("Not support", data, len);
+			break;
+		case QQ_NORMAL_IM_FILE_PASV:
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_PASV\n");
+			qq_show_packet ("Not support", data, len);
+			break;
+		case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP:
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n");
+			qq_show_packet ("QQ", data, len);
+			break;
+		case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT:
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT\n");
+			qq_show_packet ("QQ", data, len);
+			break;
+		case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL:
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL\n");
+			qq_show_packet ("Not support", data, len);
+			break;
+		case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP:
+			purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_NOTIFY_IP\n");
+			qq_show_packet ("Not support", data, len);
+			break;
 		default:
 			im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1);
 			im_unprocessed->common = common;
 			im_unprocessed->unknown = data + bytes;
 			im_unprocessed->length = len - bytes;
 			/* a simple process here, maybe more later */
-			purple_debug (PURPLE_DEBUG_WARNING, "QQ",
+			purple_debug_warning("QQ",
 					"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);
+			qq_show_packet ("QQ", im_unprocessed->unknown, im_unprocessed->length);
 			return;
 	}
 }
@@ -412,7 +489,7 @@ static void _qq_process_recv_sys_im(guin
 
 	reply = strtol(segments[0], NULL, 10);
 	if (reply == QQ_RECV_SYS_IM_KICK_OUT)
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "We are kicked out by QQ server\n");
+		purple_debug_warning("QQ", "We are kicked out by QQ server\n");
 	msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
 	purple_notify_warning(gc, NULL, _("System Message"), msg_utf8);
 }
@@ -475,7 +552,7 @@ void qq_send_packet_im(PurpleConnection 
 		g_datalist_clear(&attribs);
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_MESG", "send mesg: %s\n", msg);
+	purple_debug_info("QQ_MESG", "send mesg: %s\n", msg);
 	msg_filtered = purple_markup_strip_html(msg);
 	msg_len = strlen(msg_filtered);
 	now = time(NULL);
@@ -526,9 +603,9 @@ void qq_send_packet_im(PurpleConnection 
 	qq_show_packet("QQ_raw_data debug", raw_data, bytes);
 
 	if (bytes == raw_len)	/* create packet OK */
-		qq_send_cmd(qd, QQ_CMD_SEND_IM, raw_data, bytes);
+		qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 				"Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
 
 	if (font_color)
@@ -549,10 +626,10 @@ void qq_process_send_im_reply(guint8 *da
 	qd = gc->proto_data;
 
 	if (data[0] != QQ_SEND_IM_REPLY_OK) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n");
+		purple_debug_warning("QQ", "Send IM fail\n");
 		purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
 	}	else {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM ACK OK\n");
+		purple_debug_info("QQ", "IM ACK OK\n");
 	}
 }
 
@@ -569,16 +646,17 @@ void qq_process_recv_im(guint8 *data, gi
 	qd = (qq_data *) gc->proto_data;
 
 	if (data_len < 16) {	/* we need to ack with the first 16 bytes */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
+		purple_debug_error("QQ", "MSG is too short\n");
 		return;
 	} else {
-		_qq_send_packet_recv_im_ack(gc, seq, data);
+		/* when we receive a message,
+		 * we send an ACK which is the first 16 bytes of incoming packet */
+		qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16);
 	}
 
 	/* check len first */
 	if (data_len < 20) {	/* length of im_header */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				"Fail read recv IM header, len should longer than 20 bytes, read %d bytes\n", data_len);
+		purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len);
 		return;
 	}
 
@@ -594,77 +672,71 @@ void qq_process_recv_im(guint8 *data, gi
 	/* im_header prepared */
 
 	if (im_header->receiver_uid != qd->uid) {	/* should not happen */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid);
+		purple_debug_error("QQ", "MSG to [%d], NOT me\n", im_header->receiver_uid);
 		return;
 	}
 
 	/* check bytes */
 	if (bytes >= data_len - 1) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received IM is empty\n");
+		purple_debug_warning("QQ", "Empty MSG\n");
 		return;
 	}
 
 	switch (im_header->im_type) {
-		case QQ_RECV_IM_TO_BUDDY:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid);
-			_qq_process_recv_normal_im(data + bytes, data_len - bytes, gc); /* position and rest length */
+		case QQ_RECV_IM_NEWS:
+			_qq_process_recv_news(data + bytes, data_len - bytes, gc);
 			break;
+		case QQ_RECV_IM_FROM_BUDDY_2006:
+		case QQ_RECV_IM_FROM_UNKNOWN_2006:
 		case QQ_RECV_IM_TO_UNKNOWN:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid);
+		case QQ_RECV_IM_TO_BUDDY:
+			purple_debug_info("QQ", "MSG from buddy [%d]\n", im_header->sender_uid);
 			_qq_process_recv_normal_im(data + bytes, data_len - bytes, gc);
 			break;
 		case QQ_RECV_IM_UNKNOWN_QUN_IM:
 		case QQ_RECV_IM_TEMP_QUN_IM:
 		case QQ_RECV_IM_QUN_IM:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM from group, internal_id [%d]\n", im_header->sender_uid);
+			purple_debug_info("QQ", "MSG from room [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact id */
-			qq_process_recv_group_im(data + bytes, data_len - bytes, im_header->sender_uid, gc, im_header->im_type);
+			qq_process_room_msg_normal(data + bytes, data_len - bytes, im_header->sender_uid, gc, im_header->im_type);
 			break;
 		case QQ_RECV_IM_ADD_TO_QUN:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM from group, added by group internal_id [%d]\n", im_header->sender_uid);
+			purple_debug_info("QQ", "Notice from [%d], Added\n", im_header->sender_uid);
 			/* sender_uid is group id
 			 * we need this to create a dummy group and add to blist */
-			qq_process_recv_group_im_been_added(data + bytes, data_len - bytes, im_header->sender_uid, gc);
+			qq_process_room_msg_been_added(data + bytes, data_len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_DEL_FROM_QUN:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid);
+			purple_debug_info("QQ", "Notice from room [%d], Removed\n", im_header->sender_uid);
 			/* sender_uid is group id */
-			qq_process_recv_group_im_been_removed(data + bytes, data_len - bytes, im_header->sender_uid, gc);
+			qq_process_room_msg_been_removed(data + bytes, data_len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid);
+			purple_debug_info("QQ", "Notice from room [%d], Joined\n", im_header->sender_uid);
 			/* sender_uid is group id */
-			qq_process_recv_group_im_apply_join(data + bytes, data_len - bytes, im_header->sender_uid, gc);
+			qq_process_room_msg_apply_join(data + bytes, data_len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM for group system info, approved by group internal_id [%d]\n",
+			purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n",
 					im_header->sender_uid);
 			/* sender_uid is group id */
-			qq_process_recv_group_im_been_approved(data + bytes, data_len - bytes, im_header->sender_uid, gc);
+			qq_process_room_msg_been_approved(data + bytes, data_len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM for group system info, rejected by group internal_id [%d]\n",
+			purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n",
 					im_header->sender_uid);
 			/* sender_uid is group id */
-			qq_process_recv_group_im_been_rejected(data + bytes, data_len - bytes, im_header->sender_uid, gc);
+			qq_process_room_msg_been_rejected(data + bytes, data_len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_SYS_NOTIFICATION:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					"IM from [%d], should be a system administrator\n", im_header->sender_uid);
+			purple_debug_info("QQ", "Admin notice from [%d]\n", im_header->sender_uid);
 			_qq_process_recv_sys_im(data + bytes, data_len - bytes, gc);
 			break;
 		default:
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-					"IM from [%d], [0x%02x] %s is not processed\n",
-					im_header->sender_uid,
-					im_header->im_type, qq_get_recv_im_type_str(im_header->im_type));
+			purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%02x]\n",
+					im_header->sender_uid, qq_get_recv_im_type_str(im_header->im_type),
+					im_header->im_type);
+			qq_show_packet("Unknown MSG type", data, data_len);
 	}
 }
 
============================================================
--- libpurple/protocols/qq/im.h	9bb66bca43da19dfdcd526333228f8dd7648de75
+++ libpurple/protocols/qq/im.h	0dbb0b4261a59a829f78c7b75a43c416ee868329
@@ -41,6 +41,7 @@ enum {
 enum {
 	QQ_RECV_IM_TO_BUDDY = 0x0009,
 	QQ_RECV_IM_TO_UNKNOWN = 0x000a,
+	QQ_RECV_IM_NEWS = 0x0018,
 	QQ_RECV_IM_UNKNOWN_QUN_IM = 0x0020,
 	QQ_RECV_IM_ADD_TO_QUN = 0x0021,
 	QQ_RECV_IM_DEL_FROM_QUN = 0x0022,
@@ -50,7 +51,9 @@ enum {
 	QQ_RECV_IM_CREATE_QUN = 0x0026,
 	QQ_RECV_IM_TEMP_QUN_IM = 0x002A,
 	QQ_RECV_IM_QUN_IM = 0x002B,
-	QQ_RECV_IM_SYS_NOTIFICATION = 0x0030
+	QQ_RECV_IM_SYS_NOTIFICATION = 0x0030,
+	QQ_RECV_IM_FROM_BUDDY_2006 = 0x0084,
+	QQ_RECV_IM_FROM_UNKNOWN_2006 = 0x0085,
 };
 
 guint8 *qq_get_send_im_tail(const gchar *font_color,
============================================================
--- libpurple/protocols/qq/packet_parse.c	803b01df2d219f62afc4b1813faca0f67c82e5ae
+++ libpurple/protocols/qq/packet_parse.c	21732e9a4c9b39b77a94bd80073695ba1a12c103
@@ -46,8 +46,8 @@ gint qq_get8(guint8 *b, guint8 *buf)
 	memcpy(&b_dest, buf, sizeof(b_dest));
 	*b = b_dest;
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] buf %p\n", (void *)buf);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b);
+	purple_debug_info("QQ", "[DBG][get8] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b);
 #endif
 	return sizeof(b_dest);
 }
@@ -61,8 +61,8 @@ gint qq_get16(guint16 *w, guint8 *buf)
 	memcpy(&w_dest, buf, sizeof(w_dest));
 	*w = g_ntohs(w_dest);
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] buf %p\n", (void *)buf);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w);
+	purple_debug_info("QQ", "[DBG][get16] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w);
 #endif
 	return sizeof(w_dest);
 }
@@ -75,8 +75,8 @@ gint qq_get32(guint32 *dw, guint8 *buf)
 	memcpy(&dw_dest, buf, sizeof(dw_dest));
 	*dw = g_ntohl(dw_dest);
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] buf %p\n", (void *)buf);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw);
+	purple_debug_info("QQ", "[DBG][get32] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw);
 #endif
 	return sizeof(dw_dest);
 }
@@ -93,7 +93,7 @@ gint qq_getdata(guint8 *data, gint datal
 {
     memcpy(data, buf, datalen);
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getdata] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][getdata] buf %p\n", (void *)buf);
 #endif
     return datalen;
 }
@@ -107,12 +107,12 @@ gint qq_getime(time_t *t, guint8 *buf)
 	guint32 dw_dest;
 	memcpy(&dw_dest, buf, sizeof(dw_dest));
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] buf %p\n", (void *)buf);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest);
+	purple_debug_info("QQ", "[DBG][getime] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest);
 #endif
 	dw_dest = g_ntohl(dw_dest);
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest);
+	purple_debug_info("QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest);
 #endif
 	memcpy(t, &dw_dest, sizeof(dw_dest));
 	return sizeof(dw_dest);
@@ -125,8 +125,8 @@ gint qq_put8(guint8 *buf, guint8 b)
 {
     memcpy(buf, &b, sizeof(b));
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] buf %p\n", (void *)buf);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] b 0x%02x\n", b);
+	purple_debug_info("QQ", "[DBG][put8] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][put8] b 0x%02x\n", b);
 #endif
     return sizeof(b);
 }
@@ -139,8 +139,8 @@ gint qq_put16(guint8 *buf, guint16 w)
     guint16 w_porter;
     w_porter = g_htons(w);
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] buf %p\n", (void *)buf);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter);
+	purple_debug_info("QQ", "[DBG][put16] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter);
 #endif
     memcpy(buf, &w_porter, sizeof(w_porter));
     return sizeof(w_porter);
@@ -154,8 +154,8 @@ gint qq_put32(guint8 *buf, guint32 dw)
     guint32 dw_porter;
     dw_porter = g_htonl(dw);
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] buf %p\n", (void *)buf);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter);
+	purple_debug_info("QQ", "[DBG][put32] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter);
 #endif
     memcpy(buf, &dw_porter, sizeof(dw_porter));
     return sizeof(dw_porter);
@@ -173,7 +173,7 @@ gint qq_putdata(guint8 *buf, const guint
 {
     memcpy(buf, data, datalen);
 #ifdef PARSER_DEBUG
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][putdata] buf %p\n", (void *)buf);
+	purple_debug_info("QQ", "[DBG][putdata] buf %p\n", (void *)buf);
 #endif
     return datalen;
 }
============================================================
--- libpurple/protocols/qq/qq.c	c1ad87252bd96d8470a6d1768d68e67eb034416a
+++ libpurple/protocols/qq/qq.c	5a34bedb1748ea86e3abef9336083ad12ffa53e7
@@ -24,10 +24,6 @@
 
 #include "internal.h"
 
-#ifdef _WIN32
-#define random rand
-#endif
-
 #include "accountopt.h"
 #include "debug.h"
 #include "notify.h"
@@ -62,79 +58,72 @@
 #define OPENQ_AUTHOR            "Puzzlebird"
 #define OPENQ_WEBSITE            "http://openq.sourceforge.net"
 
-#define QQ_TCP_PORT       		8000
-#define QQ_UDP_PORT             	8000
+static GList *server_list_build(gchar select)
+{
+	GList *list = NULL;
 
-static void server_list_create(PurpleAccount *account) {
+	if ( select == 'T' || select == 'A') {
+		list = g_list_append(list, "tcpconn.tencent.com:8000");
+		list = g_list_append(list, "tcpconn2.tencent.com:8000");
+		list = g_list_append(list, "tcpconn3.tencent.com:8000");
+		list = g_list_append(list, "tcpconn4.tencent.com:8000");
+		list = g_list_append(list, "tcpconn5.tencent.com:8000");
+		list = g_list_append(list, "tcpconn6.tencent.com:8000");
+	}
+	if ( select == 'U' || select == 'A') {
+		list = g_list_append(list, "sz.tencent.com:8000");
+		list = g_list_append(list, "sz2.tencent.com:8000");
+		list = g_list_append(list, "sz3.tencent.com:8000");
+		list = g_list_append(list, "sz4.tencent.com:8000");
+		list = g_list_append(list, "sz5.tencent.com:8000");
+		list = g_list_append(list, "sz6.tencent.com:8000");
+		list = g_list_append(list, "sz7.tencent.com:8000");
+		list = g_list_append(list, "sz8.tencent.com:8000");
+		list = g_list_append(list, "sz9.tencent.com:8000");
+	}
+	return list;
+}
+
+static void server_list_create(PurpleAccount *account)
+{
 	PurpleConnection *gc;
 	qq_data *qd;
+	PurpleProxyInfo *gpi;
 	const gchar *user_server;
-	int port;
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create server list\n");
 	gc = purple_account_get_connection(account);
 	g_return_if_fail(gc != NULL  && gc->proto_data != NULL);
 	qd = gc->proto_data;
 
-	qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
-	port = purple_account_get_int(account, "port", 0);
-	if (port == 0) {
-		if (qd->use_tcp) {
-			port = QQ_TCP_PORT;
-		} else {
-			port = QQ_UDP_PORT;
-		}
-	}
-	qd->user_port = port;
+	gpi = purple_proxy_get_setup(account);
 
- 	g_return_if_fail(qd->user_server == NULL);
-	user_server = purple_account_get_string(account, "server", NULL);
-	if (user_server != NULL && strlen(user_server) > 0) {
-		qd->user_server = g_strdup(user_server);
+	qd->use_tcp  = TRUE;
+	if (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_UDP) {
+		qd->use_tcp  = FALSE;
 	}
 
-	if (qd->user_server != NULL) {
-		qd->servers = g_list_append(qd->servers, qd->user_server);
+	user_server = purple_account_get_string(account, "server", NULL);
+	purple_debug_info("QQ", "Select server '%s'\n", user_server);
+	if ( (user_server != NULL && strlen(user_server) > 0) && strcasecmp(user_server, "auto") != 0) {
+		qd->servers = g_list_append(qd->servers, g_strdup(user_server));
 		return;
 	}
+
 	if (qd->use_tcp) {
-		qd->servers = g_list_append(qd->servers, "tcpconn.tencent.com");
-		qd->servers = g_list_append(qd->servers, "tcpconn2.tencent.com");
-		qd->servers = g_list_append(qd->servers, "tcpconn3.tencent.com");
-		qd->servers = g_list_append(qd->servers, "tcpconn4.tencent.com");
-		qd->servers = g_list_append(qd->servers, "tcpconn5.tencent.com");
-		qd->servers = g_list_append(qd->servers, "tcpconn6.tencent.com");
+		qd->servers =	server_list_build('T');
 		return;
     }
-    
-	qd->servers = g_list_append(qd->servers, "sz.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz2.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz3.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz4.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz5.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz6.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz7.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz8.tencent.com");
-	qd->servers = g_list_append(qd->servers, "sz9.tencent.com");
+
+	qd->servers =	server_list_build('U');
 }
 
-static void server_list_remove_all(qq_data *qd) {
+static void server_list_remove_all(qq_data *qd)
+{
  	g_return_if_fail(qd != NULL);
 
-	if (qd->real_hostname) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
-		g_free(qd->real_hostname);
-		qd->real_hostname = NULL;
-	}
-	
-	if (qd->user_server != NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free user_server\n");
-		g_free(qd->user_server);
-		qd->user_server = NULL;
-	}
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "free server list\n");
+	purple_debug_info("QQ", "free server list\n");
  	g_list_free(qd->servers);
+	qd->curr_server = NULL;
 }
 
 static void qq_login(PurpleAccount *account)
@@ -151,6 +140,7 @@ static void qq_login(PurpleAccount *acco
 	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_AUTO_RESP;
 
 	qd = g_new0(qq_data, 1);
+	memset(qd, 0, sizeof(qq_data));
 	qd->gc = gc;
 	gc->proto_data = qd;
 
@@ -165,10 +155,31 @@ static void qq_login(PurpleAccount *acco
 	}
 
 	server_list_create(account);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ",
-		"Server list has %d\n", g_list_length(qd->servers));
+	purple_debug_info("QQ", "Server list has %d\n", g_list_length(qd->servers));
 
-	qq_connect(account);
+	qd->is_show_notice = purple_account_get_bool(account, "show_notice", TRUE);
+	qd->is_show_news = purple_account_get_bool(account, "show_news", TRUE);
+
+	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->connect_watcher = purple_timeout_add_seconds(0, qq_connect_later, gc);
 }
 
 /* clean up the given QQ connection and free all resources */
@@ -179,12 +190,20 @@ static void qq_close(PurpleConnection *g
 	g_return_if_fail(gc != NULL  && gc->proto_data);
 	qd = gc->proto_data;
 
-	qq_disconnect(gc);
+	if (qd->check_watcher > 0) {
+		purple_timeout_remove(qd->check_watcher);
+		qd->check_watcher = 0;
+	}
 
+	if (qd->connect_watcher > 0) {
+		purple_timeout_remove(qd->connect_watcher);
+		qd->connect_watcher = 0;
+	}
+
+	qq_disconnect(gc);
 	server_list_remove_all(qd);
-	
+
 	g_free(qd);
-
 	gc->proto_data = NULL;
 }
 
@@ -212,7 +231,7 @@ static gchar *_qq_status_text(PurpleBudd
 		g_string_append(status, _("Offline"));
 		break;
 	case QQ_BUDDY_ONLINE_NORMAL:
-		return NULL;
+		g_string_append(status, _("Online"));
 		break;
 	/* TODO What does this status mean? Labelling it as offline... */
 	case QQ_BUDDY_ONLINE_OFFLINE:
@@ -303,8 +322,8 @@ static void _qq_tooltip_text(PurpleBuddy
 		g_string_append( str, _(" Video") );
 	}
 
-	if (q_bud->ext_flag & QQ_EXT_FLAG_SPACE) {
-		g_string_append( str, _(" Space") );
+	if (q_bud->ext_flag & QQ_EXT_FLAG_ZONE) {
+		g_string_append( str, _(" Zone") );
 	}
 	purple_notify_user_info_add_pair(user_info, _("Flag"), str->str);
 
@@ -329,7 +348,7 @@ static const char *_qq_list_emblem(Purpl
 {
 	/* 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;
 	}
@@ -374,11 +393,11 @@ static GList *_qq_away_states(PurpleAcco
 }
 
 /* initiate QQ away with proper change_status packet */
-static void _qq_set_away(PurpleAccount *account, PurpleStatus *status)
+static void _qq_change_status(PurpleAccount *account, PurpleStatus *status)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
 
-	qq_send_packet_change_status(gc);
+	qq_request_change_status(gc, 0);
 }
 
 /* IMPORTANT: PurpleConvImFlags -> PurpleMessageFlags */
@@ -444,7 +463,7 @@ static void _qq_get_info(PurpleConnectio
 	uid = purple_name_to_uid(who);
 
 	if (uid <= 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Not valid QQid: %s\n", who);
+		purple_debug_error("QQ", "Not valid QQid: %s\n", who);
 		purple_notify_error(gc, NULL, _("Invalid name"), NULL);
 		return;
 	}
@@ -516,9 +535,8 @@ static void _qq_menu_show_login_info(Pur
 
 	g_string_append(info, "<hr>\n");
 
-	g_string_append_printf(info, _("<b>Server</b>: %s: %d<br>\n"), qd->server_name, qd->real_port);
+	g_string_append_printf(info, _("<b>Server</b>: %s<br>\n"), qd->curr_server);
 	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"), inet_ntoa(qd->my_ip));
 
 	g_string_append(info, "<hr>\n");
@@ -633,7 +651,7 @@ static GList *_qq_chat_menu(PurpleBlistN
 	PurpleMenuAction *act;
 
 	m = NULL;
-	act = purple_menu_action_new(_("Leave this QQ Qun"), PURPLE_CALLBACK(_qq_menu_unsubscribe_group), NULL, NULL);
+	act = purple_menu_action_new(_("Leave the QQ Qun"), PURPLE_CALLBACK(_qq_menu_unsubscribe_group), NULL, NULL);
 	m = g_list_append(m, act);
 
 	/* TODO: enable this
@@ -708,7 +726,7 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,							/* set_info */
 	NULL,							/* send_typing	*/
 	_qq_get_info,						/* get_info */
-	_qq_set_away,						/* set_away */
+	_qq_change_status,						/* change status */
 	NULL,							/* set_idle */
 	NULL,							/* change_passwd */
 	qq_add_buddy,						/* add_buddy */
@@ -800,19 +818,52 @@ static void init_plugin(PurplePlugin *pl
 static void init_plugin(PurplePlugin *plugin)
 {
 	PurpleAccountOption *option;
+	PurpleKeyValuePair *kvp;
+	GList *list = NULL;
+	GList *kvlist = NULL;
+	GList *entry;
 
+	list = server_list_build('A');
+
+	purple_prefs_add_string_list("/plugins/prpl/qq/serverlist", list);
+	list = purple_prefs_get_string_list("/plugins/prpl/qq/serverlist");
+
+	kvlist = NULL;
+	kvp = g_new0(PurpleKeyValuePair, 1);
+	kvp->key = g_strdup(_("Auto"));
+	kvp->value = g_strdup("auto");
+	kvlist = g_list_append(kvlist, kvp);
+
+	entry = list;
+	while(entry) {
+		if (entry->data != NULL && strlen(entry->data) > 0) {
+			kvp = g_new0(PurpleKeyValuePair, 1);
+			kvp->key = g_strdup(entry->data);
+			kvp->value = g_strdup(entry->data);
+			kvlist = g_list_append(kvlist, kvp);
+		}
+		entry = entry->next;
+	}
+
+	/*
 	option = purple_account_option_string_new(_("Server"), "server", NULL);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 	option = purple_account_option_int_new(_("Port"), "port", 0);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+	*/
+	option = purple_account_option_list_new(_("Server"), "server", kvlist);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
-	option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE);
+	option = purple_account_option_bool_new(_("Show server notice"), "show_notice", 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);
+	option = purple_account_option_bool_new(_("Show server news"), "show_news", 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);
 
@@ -823,6 +874,7 @@ static void init_plugin(PurplePlugin *pl
 	purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE);
 	purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE);
 	purple_prefs_add_bool("/plugins/prpl/qq/prompt_group_msg_on_recv", TRUE);
+
 }
 
 PURPLE_INIT_PLUGIN(qq, init_plugin, info);
============================================================
--- libpurple/protocols/qq/qq.h	17c7a1179cc82fcae2d3231ddf7883b78b7a9804
+++ libpurple/protocols/qq/qq.h	b59cc95c61437ae001fd4611c1bbca9f33edca83
@@ -36,6 +36,11 @@
 
 #define QQ_KEY_LENGTH       16
 
+#ifdef _WIN32
+const char *qq_win32_buddy_icon_dir(void);
+#define QQ_BUDDY_ICON_DIR qq_win32_buddy_icon_dir()
+#endif
+
 typedef struct _qq_data qq_data;
 typedef struct _qq_buddy qq_buddy;
 typedef struct _qq_interval qq_interval;
@@ -63,45 +68,46 @@ struct _qq_buddy {
 	guint16 timeRemainder;
 	time_t signon;
 	time_t idle;
-	time_t last_refresh;
+	time_t last_update;
 
 	gint8  role;		/* role in group, used only in group->members list */
 };
 
+typedef struct _qq_connection qq_connection;
+struct _qq_connection {
+	int fd;				/* socket file handler */
+	int input_handler;
+
+	/* tcp related */
+	int can_write_handler; 	/* use in tcp_send_out */
+	PurpleCircBuffer *tcp_txbuf;
+	guint8 *tcp_rxqueue;
+	int tcp_rxlen;
+};
+
 struct _qq_data {
 	PurpleConnection *gc;
 
-	/* common network resource */
-	GList *servers;
-	gchar *user_server;
-	gint user_port;
+	GSList *openconns;
 	gboolean use_tcp;		/* network in tcp or udp */
+	PurpleProxyConnectData *conn_data;
+	gint fd;							/* socket file handler */
+
+	GList *servers;
+	gchar *curr_server;		/* point to servers->data, do not free*/
 	
-	gchar *server_name;
-	gboolean is_redirect;
-	gchar *real_hostname;	/* from real connction */
-	guint16 real_port;
-	guint reconnect_timeout;
-	gint reconnect_times;
+	struct in_addr redirect_ip;
+	guint16 redirect_port;
+	guint check_watcher;
+	guint connect_watcher;
+	gint connect_retry;
 
-	PurpleProxyConnectData *connect_data;
-	gint fd;				/* socket file handler */
-	gint tx_handler; 	/* socket can_write handle, use in udp connecting and tcp send out */
-
 	qq_interval itv_config;
 	qq_interval itv_count;
-	guint network_timeout;
+	guint network_watcher;
 	
 	GList *transactions;	/* check ack packet and resend */
 
-	/* tcp related */
-	PurpleCircBuffer *tcp_txbuf;
-	guint8 *tcp_rxqueue;
-	int tcp_rxlen;
-	
-	/* udp related */
-	PurpleDnsQueryData *udp_query_data;
-
 	guint32 uid;			/* QQ number */
 	guint8 *token;		/* get from server*/
 	int token_len;
@@ -112,7 +118,8 @@ struct _qq_data {
 
 	guint16 send_seq;		/* send sequence number */
 	guint8 login_mode;		/* online of invisible */
-	gboolean logged_in;		/* used by qq-add_buddy */
+	gboolean is_login;		/* used by qq-add_buddy */
+	gboolean is_finish_update;
 
 	PurpleXfer *xfer;			/* file transfer handler */
 
@@ -143,6 +150,9 @@ struct _qq_data {
 	/* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */
 	gboolean modifying_info;
 	gboolean modifying_face;
+
+	gboolean is_show_notice;
+	gboolean is_show_news;
 };
 
 #endif
============================================================
--- libpurple/protocols/qq/qq_base.c	529b343ed51bb7740d680c11e73b2f473bca9d48
+++ libpurple/protocols/qq/qq_base.c	6ca17e28fe0a03df4a08018804b3c0c2a51f0b11
@@ -48,7 +48,7 @@
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
 	0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
-	0x95, 0x67, 0xda, 0x2c, 0x01 
+	0x95, 0x67, 0xda, 0x2c, 0x01
 }; */
 
 /* for QQ 2003iii 0304, fixed value */
@@ -139,7 +139,7 @@ static void get_session_md5(guint8 *sess
 {
 	guint8 src[QQ_KEY_LENGTH + QQ_KEY_LENGTH];
 	gint bytes = 0;
-	
+
 	bytes += qq_put32(src + bytes, uid);
 	bytes += qq_putdata(src + bytes, session_key, QQ_KEY_LENGTH);
 
@@ -161,7 +161,7 @@ static gint8 process_login_ok(PurpleConn
 	bytes += qq_get8(&lrop.result, data + bytes);
 	/* 001-016: session key */
 	bytes += qq_getdata(lrop.session_key, sizeof(lrop.session_key), data + bytes);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Got session_key\n");
+	purple_debug_info("QQ", "Got session_key\n");
 	/* 017-020: login uid */
 	bytes += qq_get32(&lrop.uid, data + bytes);
 	/* 021-024: server detected user public IP */
@@ -200,16 +200,16 @@ static gint8 process_login_ok(PurpleConn
 	bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) {	/* fail parsing login info */
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+		purple_debug_warning("QQ",
 			   "Fail parsing login info, expect %d bytes, read %d bytes\n",
 			   QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
 	}			/* but we still go on as login OK */
 
 	memcpy(qd->session_key, lrop.session_key, sizeof(qd->session_key));
 	get_session_md5(qd->session_md5, qd->uid, qd->session_key);
-	
+
 	qd->my_ip.s_addr = lrop.client_ip.s_addr;
-	
+
 	qd->my_port = lrop.client_port;
 	qd->login_time = lrop.login_time;
 	qd->last_login_time = lrop.last_login_time;
@@ -237,39 +237,19 @@ static gint8 process_login_redirect(Purp
 	bytes += qq_get16(&lrrp.new_server_port, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 			   "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
 			   QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
 		return QQ_LOGIN_REPLY_ERR_MISC;
 	}
-	
+
 	/* redirect to new server, do not disconnect or connect here
 	 * those connect should be called at packet_process */
-	if (qd->real_hostname) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
-		g_free(qd->real_hostname);
-		qd->real_hostname = NULL;
-	}
-	qd->real_hostname = g_strdup( inet_ntoa(lrrp.new_server_ip) );
-	qd->real_port = lrrp.new_server_port;
-
+	qd->redirect_ip.s_addr = lrrp.new_server_ip.s_addr;
+	qd->redirect_port = lrrp.new_server_port;
 	return QQ_LOGIN_REPLY_REDIRECT;
 }
 
-/* process login reply which says wrong password */
-static gint8 process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len)
-{
-	gchar *server_reply, *server_reply_utf8;
-	server_reply = g_new0(gchar, len);
-	g_memmove(server_reply, data + 1, len - 1);
-	server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Wrong password, server msg in UTF8: %s\n", server_reply_utf8);
-	g_free(server_reply);
-	g_free(server_reply_utf8);
-
-	return QQ_LOGIN_REPLY_ERR_PWD;
-}
-
 /* request before login */
 void qq_send_packet_token(PurpleConnection *gc)
 {
@@ -281,9 +261,9 @@ void qq_send_packet_token(PurpleConnecti
 	qd = (qq_data *) gc->proto_data;
 
 	bytes += qq_put8(buf + bytes, 0);
-	
+
 	qd->send_seq++;
-	qq_send_data(qd, QQ_CMD_TOKEN, qd->send_seq, TRUE, buf, bytes);
+	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN, qd->send_seq, buf, bytes, TRUE);
 }
 
 /* send login packet to QQ server */
@@ -312,14 +292,14 @@ void qq_send_packet_login(PurpleConnecti
 	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
 
 	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
-	
+
 	bytes = 0;
 	/* now generate the encrypted data
 	 * 000-015 use password_twice_md5 as key to encrypt empty string */
 	encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->password_twice_md5);
 	g_return_if_fail(encrypted_len == 16);
 	bytes += encrypted_len;
-	
+
 	/* 016-016 */
 	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 017-020, used to be IP, now zero */
@@ -349,14 +329,15 @@ void qq_send_packet_login(PurpleConnecti
 	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
 
 	qd->send_seq++;
-	qq_send_data(qd, QQ_CMD_LOGIN, qd->send_seq, TRUE, buf, bytes);
+	qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
 }
 
-guint8 qq_process_token_reply(PurpleConnection *gc, gchar *error_msg, guint8 *buf, gint buf_len)
+guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len)
 {
 	qq_data *qd;
 	guint8 ret;
 	int token_len;
+	gchar *error_msg;
 
 	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
 
@@ -364,30 +345,37 @@ guint8 qq_process_token_reply(PurpleConn
 	qd = (qq_data *) gc->proto_data;
 
 	ret = buf[0];
-	
+
 	if (ret != QQ_TOKEN_REPLY_OK) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]);
+		purple_debug_error("QQ", "Failed to request token: %d\n", buf[0]);
 		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
 				buf, buf_len,
 				">>> [default] decrypt and dump");
 		error_msg = try_dump_as_gbk(buf, buf_len);
+		if (error_msg == NULL) {
+				error_msg = g_strdup_printf( _("Invalid token reply code, 0x%02X"), ret);
+		}
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+		g_free(error_msg);
 		return ret;
 	}
-	
+
 	token_len = buf_len-2;
 	if (token_len <= 0) {
 		error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+		g_free(error_msg);
 		return -1;
 	}
-	
+
 	if (buf[1] != token_len) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		purple_debug_info("QQ",
 				"Invalid token len. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2);
 	}
 	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
 			buf+2, token_len,
 			"<<< got a token -> [default] decrypt and dump");
-			
+
 	qd->token = g_new0(guint8, token_len);
 	qd->token_len = token_len;
 	g_memmove(qd->token, buf + 2, qd->token_len);
@@ -402,48 +390,85 @@ void qq_send_packet_logout(PurpleConnect
 
 	qd = (qq_data *) gc->proto_data;
 	for (i = 0; i < 4; i++)
-		qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->password_twice_md5, QQ_KEY_LENGTH);
+		qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->password_twice_md5, QQ_KEY_LENGTH);
 
-	qd->logged_in = FALSE;	/* update login status AFTER sending logout packets */
+	qd->is_login = FALSE;	/* update login status AFTER sending logout packets */
 }
 
 /* process the login reply packet */
-guint8 qq_process_login_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	qq_data *qd;
-	gchar* error_msg;
+	guint8 ret = data[0];
+	gchar *server_reply, *server_reply_utf8;
+	gchar *error_msg;
 
 	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR_MISC);
 
 	qd = (qq_data *) gc->proto_data;
 
-	switch (data[0]) {
+	switch (ret) {
 		case QQ_LOGIN_REPLY_OK:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is OK\n");
+			purple_debug_info("QQ", "Login OK\n");
 			return process_login_ok(gc, data, data_len);
 		case QQ_LOGIN_REPLY_REDIRECT:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is redirect\n");
+			purple_debug_info("QQ", "Redirect new server\n");
 			return process_login_redirect(gc, data, data_len);
+
+		case QQ_LOGIN_REPLY_REDIRECT_EX:
+			purple_debug_error("QQ", "Extend redirect new server, not supported yet\n");
+			error_msg = g_strdup( _("Unable login for not support Redirect_EX now") );
+			return QQ_LOGIN_REPLY_REDIRECT_EX;
+
 		case QQ_LOGIN_REPLY_ERR_PWD:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is error password\n");
-			return process_login_wrong_pwd(gc, data, data_len);
+			server_reply = g_strndup((gchar *)data + 1, data_len - 1);
+			server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
+
+			purple_debug_error("QQ", "Error password: %s\n", server_reply_utf8);
+			error_msg = g_strdup_printf( _("Error password: %s"), server_reply_utf8);
+
+			g_free(server_reply);
+			g_free(server_reply_utf8);
+
+			if (!purple_account_get_remember_password(gc->account)) {
+				purple_account_set_password(gc->account, NULL);
+			}
+
+			purple_connection_error_reason(gc,
+				PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, error_msg);
+			g_free(error_msg);
+
+			return QQ_LOGIN_REPLY_ERR_PWD;
+
 		case QQ_LOGIN_REPLY_NEED_REACTIVE:
-		case QQ_LOGIN_REPLY_REDIRECT_EX:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is not actived or redirect extend\n");
-		default:
-		break;
-	}
+			server_reply = g_strndup((gchar *)data + 1, data_len - 1);
+			server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
 
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: 0x%02X\n", data[0]);
+			purple_debug_error("QQ", "Need active: %s\n", server_reply_utf8);
+			error_msg = g_strdup_printf( _("Need active: %s"), server_reply_utf8);
+
+			g_free(server_reply);
+			g_free(server_reply_utf8);
+			break;
+
+		default:
+			purple_debug_error("QQ",
+				"Unable login for unknow reply code 0x%02X\n", data[0]);
 			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
-			data, data_len,
-			">>> [default] decrypt and dump");
-	error_msg = try_dump_as_gbk(data, data_len);
-	if (error_msg)	{
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
-			g_free(error_msg);
+				data, data_len,
+				">>> [default] decrypt and dump");
+			error_msg = try_dump_as_gbk(data, data_len);
+			if (error_msg == NULL) {
+				error_msg = g_strdup_printf(
+					_("Unable login for unknow reply code 0x%02X"), data[0] );
+			}
+			break;
 	}
-	return QQ_LOGIN_REPLY_ERR_MISC;
+
+	purple_connection_error_reason(gc,
+		PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+	g_free(error_msg);
+	return ret;
 }
 
 /* send keep-alive packet to QQ server (it is a heart-beat) */
@@ -460,11 +485,11 @@ void qq_send_packet_keep_alive(PurpleCon
 	 * the amount of online QQ users, my ip and port */
 	bytes += qq_put32(raw_data + bytes, qd->uid);
 
-	qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4);
+	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, 4);
 }
 
 /* parse the return of keep-alive packet, it includes some system information */
-gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc) 
+gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gchar **segments;
@@ -478,7 +503,7 @@ gboolean qq_process_keep_alive(guint8 *d
 	/* the last one is 60, don't know what it is */
 	if (NULL == (segments = split_data(data, data_len, "\x1f", 6)))
 			return TRUE;
-			
+
 	/* segments[0] and segment[1] are all 0x30 ("0") */
 	qd->total_online = strtol(segments[2], NULL, 10);
 	if(0 == qd->total_online) {
@@ -488,9 +513,9 @@ gboolean qq_process_keep_alive(guint8 *d
 	qd->my_ip.s_addr = inet_addr(segments[3]);
 	qd->my_port = strtol(segments[4], NULL, 10);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "keep alive, %s:%d\n",
+	purple_debug_info("QQ", "keep alive, %s:%d\n",
 		inet_ntoa(qd->my_ip), qd->my_port);
-	
+
 	g_strfreev(segments);
 	return TRUE;
 }
============================================================
--- libpurple/protocols/qq/qq_base.h	6d6f1ef5450dc8bc5c06af0135bf2e1573835b04
+++ libpurple/protocols/qq/qq_base.h	1be7d89ee372ed31923b785525b7966caa34db3f
@@ -44,10 +44,10 @@ void qq_send_packet_token(PurpleConnecti
 #define QQ_UPDATE_ONLINE_INTERVAL   300	/* in sec */
 
 void qq_send_packet_token(PurpleConnection *gc);
-guint8 qq_process_token_reply(PurpleConnection *gc, gchar *error_msg, guint8 *buf, gint buf_len);
+guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len);
 
 void qq_send_packet_login(PurpleConnection *gc);
-guint8 qq_process_login_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len);
 
 void qq_send_packet_logout(PurpleConnection *gc);
 
============================================================
--- libpurple/protocols/qq/qq_network.c	4364dbec36ac54db6ce17b68698173e582d596de
+++ libpurple/protocols/qq/qq_network.c	3853b123926421d8dd7a315c551b2c20c3bcf71c
@@ -26,11 +26,6 @@
 #include "debug.h"
 #include "internal.h"
 
-#ifdef _WIN32
-#define random rand
-#define srandom srand
-#endif
-
 #include "buddy_info.h"
 #include "group_info.h"
 #include "group_free.h"
@@ -44,63 +39,100 @@
 #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
+#define QQ_DEFAULT_PORT					8000
 
+/* set QQ_CONNECT_MAX to 1, when test reconnecting */
+#define QQ_CONNECT_MAX						3
+#define QQ_CONNECT_INTERVAL			2
+#define QQ_CONNECT_CHECK					5
+#define QQ_KEEP_ALIVE_INTERVAL		60
+#define QQ_TRANS_INTERVAL				10
+
+gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port);
+
+static qq_connection *connection_find(qq_data *qd, int fd) {
+	qq_connection *ret = NULL;
+	GSList *entry = qd->openconns;
+	while(entry) {
+		ret = entry->data;
+		if(ret->fd == fd) return ret;
+		entry = entry->next;
+	}
+	return NULL;
+}
+
+static qq_connection *connection_create(qq_data *qd, int fd) {
+	qq_connection *ret = g_new0(qq_connection, 1);
+	ret->fd = fd;
+	qd->openconns = g_slist_append(qd->openconns, ret);
+	return ret;
+}
+
+static void connection_remove(qq_data *qd, int fd) {
+	qq_connection *conn = connection_find(qd, fd);
+	qd->openconns = g_slist_remove(qd->openconns, conn);
+
+	g_return_if_fail( conn != NULL );
+
+	purple_debug_info("QQ", "Close socket %d\n", conn->fd);
+	if(conn->input_handler > 0)	purple_input_remove(conn->input_handler);
+	if(conn->can_write_handler > 0)	purple_input_remove(conn->can_write_handler);
+
+	if (conn->fd >= 0)	close(conn->fd);
+	if(conn->tcp_txbuf != NULL) 	purple_circ_buffer_destroy(conn->tcp_txbuf);
+	if (conn->tcp_rxqueue != NULL)	g_free(conn->tcp_rxqueue);
+
+	g_free(conn);
+}
+
+static void connection_free_all(qq_data *qd) {
+	qq_connection *ret = NULL;
+	GSList *entry = qd->openconns;
+	while(entry) {
+		ret = entry->data;
+		connection_remove(qd, ret->fd);
+		entry = qd->openconns;
+	}
+}
 static gboolean set_new_server(qq_data *qd)
 {
 	gint count;
 	gint index;
 	GList *it = NULL;
-	
+
  	g_return_val_if_fail(qd != NULL, FALSE);
 
 	if (qd->servers == NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n");
+		purple_debug_info("QQ", "Server list is NULL\n");
 		return FALSE;
 	}
 
-	if (qd->real_hostname) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
-		g_free(qd->real_hostname);
-		qd->real_hostname = NULL;
-	}
-
 	/* remove server used before */
-	if (qd->server_name != NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-			"Remove previous server [%s]\n", qd->server_name);
-   		qd->servers = g_list_remove(qd->servers, qd->server_name);
-   		qd->server_name = NULL;
+	if (qd->curr_server != NULL) {
+		purple_debug_info("QQ",
+			"Remove current [%s] from server list\n", qd->curr_server);
+   		qd->servers = g_list_remove(qd->servers, qd->curr_server);
+   		qd->curr_server = NULL;
     }
-	
+
 	count = g_list_length(qd->servers);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count);
+	purple_debug_info("QQ", "Server list has %d\n", count);
 	if (count <= 0) {
 		/* no server left, disconnect when result is false */
 		qd->servers = NULL;
 		return FALSE;
 	}
-	
+
 	/* get new server */
-	index  = random() % count;
+	index  = rand() % count;
 	it = g_list_nth(qd->servers, index);
-    qd->server_name = it->data;		/* do not free server_name */
-    if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index);
+    qd->curr_server = it->data;		/* do not free server_name */
+    if (qd->curr_server == NULL || strlen(qd->curr_server) <= 0 ) {
+		purple_debug_info("QQ", "Server name at %d is empty\n", index);
 		return FALSE;
 	}
 
-	qd->real_hostname = g_strdup(qd->server_name);
-	qd->real_port = qd->user_port;
-	
- 	qd->reconnect_times = QQ_RECONNECT_MAX;
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ",
-		"set new server to %s:%d\n", qd->real_hostname, qd->real_port);
+	purple_debug_info("QQ", "set new server to %s\n", qd->curr_server);
 	return TRUE;
 }
 
@@ -115,152 +147,208 @@ static gint packet_get_header(guint8 *he
 	return bytes;
 }
 
-static gboolean reconnect_later_cb(gpointer data)
+static gboolean connect_check(gpointer data)
 {
-	PurpleConnection *gc;
+	PurpleConnection *gc = (PurpleConnection *) data;
 	qq_data *qd;
 
-	gc = (PurpleConnection *) data;
 	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
 	qd = (qq_data *) gc->proto_data;
 
-	qd->reconnect_timeout = 0;
+	if (qd->connect_watcher > 0) {
+		purple_timeout_remove(qd->connect_watcher);
+		qd->connect_watcher = 0;
+	}
 
-	qq_connect(gc->account);
-	return FALSE;	/* timeout callback stops */
+	if (qd->fd >= 0 && qd->token != NULL && qd->token_len >= 0) {
+		purple_debug_info("QQ", "Connect ok\n");
+		return FALSE;
+	}
+
+	qd->connect_watcher = purple_timeout_add_seconds(0, qq_connect_later, gc);
+	return FALSE;
 }
 
-static void reconnect_later(PurpleConnection *gc)
+/* Warning: qq_connect_later destory all connection
+ *  Any function should be care of use qq_data after call this function
+ *  Please conside tcp_pending and udp_pending */
+gboolean qq_connect_later(gpointer data)
 {
+	PurpleConnection *gc = (PurpleConnection *) data;
 	qq_data *qd;
+	char *server;
+	int port;
+	gchar **segments;
 
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
 	qd = (qq_data *) gc->proto_data;
 
-	qd->reconnect_times--;
-	if (qd->reconnect_times < 0) {
+	if (qd->check_watcher > 0) {
+		purple_timeout_remove(qd->check_watcher);
+		qd->check_watcher = 0;
+	}
+	qq_disconnect(gc);
+
+	if (qd->redirect_ip.s_addr != 0) {
+		/* redirect to new server */
+		server = g_strdup_printf("%s:%d", inet_ntoa(qd->redirect_ip), qd->redirect_port);
+		qd->servers = g_list_append(qd->servers, server);
+		qd->curr_server = server;
+
+		qd->redirect_ip.s_addr = 0;
+		qd->redirect_port = 0;
+		qd->connect_retry = QQ_CONNECT_MAX;
+	}
+
+	if (qd->curr_server == NULL || strlen (qd->curr_server) == 0 || qd->connect_retry <= 0) {
 		if ( set_new_server(qd) != TRUE) {
 			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-					_("Failed to connect server"));
-			return;
+					_("Failed to connect all servers"));
+			return FALSE;
 		}
+		qd->connect_retry = QQ_CONNECT_MAX;
 	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ",
-		"Reconnect to server %s:%d next retries %d in %d ms\n",
-		qd->real_hostname, qd->real_port,
-		qd->reconnect_times, QQ_RECONNECT_INTERVAL);
+	segments = g_strsplit_set(qd->curr_server, ":", 0);
+	server = g_strdup(segments[0]);
+	port = atoi(segments[1]);
+	if (port <= 0) {
+		purple_debug_info("QQ", "Port not define in %s\n", qd->curr_server);
+		port = QQ_DEFAULT_PORT;
+	}
+	g_strfreev(segments);
 
-	qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL,
-		reconnect_later_cb, gc);
+	qd->connect_retry--;
+	if ( !connect_to_server(gc, server, port) ) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Unable to connect."));
+	}
+
+	qd->check_watcher = purple_timeout_add_seconds(QQ_CONNECT_CHECK, connect_check, gc);
+	return FALSE;	/* timeout callback stops */
 }
 
 /* process the incoming packet from qq_pending */
-static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
+static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
 {
 	qq_data *qd;
 	gint bytes, bytes_not_read;
 
-	gboolean prev_login_status;
-	
+	gboolean prev_update_status;
+
 	guint8 header_tag;
 	guint16 source_tag;
 	guint16 cmd;
 	guint16 seq;		/* May be ack_seq or send_seq, depends on cmd */
-	
 	guint8 room_cmd;
 	guint32 room_id;
+	gint update_class;
+	guint32 ship32;
 
 	qq_transaction *trans;
 
-	g_return_if_fail(buf != NULL && buf_len > 0);
+	g_return_val_if_fail(buf != NULL && buf_len > 0, TRUE);
 
 	qd = (qq_data *) gc->proto_data;
 
-	prev_login_status = qd->logged_in;
-
 	/* Len, header and tail tag have been checked before */
 	bytes = 0;
 	bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
 
 #if 1
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"==> [%05d] 0x%04X %s, from (0x%04X %s) len %d\n",
-				seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag), buf_len);
-#endif	
+		purple_debug_info("QQ", "==> [%05d] 0x%04X %s, source tag 0x%04X len %d\n",
+				seq, cmd, qq_get_cmd_desc(cmd), source_tag, buf_len);
+#endif
+	/* this is the length of all the encrypted data (also remove tail tag) */
 	bytes_not_read = buf_len - bytes - 1;
 
 	/* ack packet, we need to update send tranactions */
 	/* we do not check duplication for server ack */
-	trans = qq_trans_find_rcved(qd, cmd, seq);
+	trans = qq_trans_find_rcved(gc, 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_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read);
+		if ( qd->is_finish_update ) {
 			qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
 		}
-		return;
+		return TRUE;
 	}
 
 	if (qq_trans_is_dup(trans)) {
-		purple_debug(PURPLE_DEBUG_WARNING,
-				"QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
-		return;
+		purple_debug_info("QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
+		return TRUE;
 	}
 
 	if (qq_trans_is_server(trans)) {
-		if ( qd->logged_in ) {
+		if ( qd->is_finish_update ) {
 			qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
 		}
-		return;
+		return TRUE;
 	}
 
-	/* this is the length of all the encrypted data (also remove tail tag */
-	if (cmd == QQ_CMD_ROOM) {
-		room_cmd = qq_trans_get_room_cmd(trans);
-		room_id = qq_trans_get_room_id(trans);
+	update_class = qq_trans_get_class(trans);
+	ship32 = qq_trans_get_ship(trans);
+
+	prev_update_status = qd->is_finish_update;
+	switch (cmd) {
+		case QQ_CMD_TOKEN:
+			if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) {
+				qq_send_packet_login(gc);
+			}
+			break;
+		case QQ_CMD_LOGIN:
+			qq_proc_cmd_login(gc, buf + bytes, bytes_not_read);
+			/* check is redirect or not, and do it now */
+			if (qd->redirect_ip.s_addr != 0) {
+				if (qd->check_watcher > 0) {
+					purple_timeout_remove(qd->check_watcher);
+					qd->check_watcher = 0;
+				}
+				if (qd->connect_watcher > 0)	purple_timeout_remove(qd->connect_watcher);
+				qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
+				return FALSE;	/* do nothing after this function and return now */
+			}
+			break;
+		case QQ_CMD_ROOM:
+			room_cmd = qq_trans_get_room_cmd(trans);
+			room_id = qq_trans_get_room_id(trans);
 #if 1
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"%s (0x%02X ) for room %d, len %d\n",
-				qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
-#endif	
-		qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read);
-	} else {
-		qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
+			purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n",
+					qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
+#endif
+			qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
+			break;
+		default:
+			qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
+			break;
 	}
-	
-	/* check is redirect or not, and do it now */
-	if (qd->is_redirect) {
-	 	/* free resource except real_hostname and port */
-		qq_disconnect(gc);
-	 	qd->reconnect_times = QQ_RECONNECT_MAX;
-		reconnect_later(gc);
-		return;
-	}
 
-	if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) {
-		/* logged_in, but we have packets before login */
-		qq_trans_process_before_login(qd);
+	if (prev_update_status != qd->is_finish_update && qd->is_finish_update == TRUE) {
+		/* is_login, but we have packets before login */
+		qq_trans_process_before_login(gc);
+		return TRUE;
 	}
+	return TRUE;
 }
 
 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
 {
-	PurpleConnection *gc;
+	PurpleConnection *gc = (PurpleConnection *) data;
 	qq_data *qd;
+	qq_connection *conn;
 	guint8 buf[1024];		/* set to 16 when test  tcp_rxqueue */
 	gint buf_len;
 	gint bytes;
-	
+
 	guint8 *pkt;
 	guint16 pkt_len;
-	
+
 	gchar *error_msg;
 	guint8 *jump;
 	gint jump_len;
 
-	gc = (PurpleConnection *) data;
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
 
 	if(cond != PURPLE_INPUT_READ) {
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -268,8 +356,9 @@ static void tcp_pending(gpointer data, g
 		return;
 	}
 
-	qd = (qq_data *) gc->proto_data;
-	
+	conn = connection_find(qd, source);
+	g_return_if_fail(conn != NULL);
+
 	/* test code, not using tcp_rxqueue
 	memset(pkt,0, sizeof(pkt));
 	buf_len = read(qd->fd, pkt, sizeof(pkt));
@@ -278,8 +367,8 @@ static void tcp_pending(gpointer data, g
 	}
 	return;
 	*/
-	
-	buf_len = read(qd->fd, buf, sizeof(buf));
+
+	buf_len = read(source, buf, sizeof(buf));
 	if (buf_len < 0) {
 		if (errno == EAGAIN)
 			/* No worries */
@@ -299,93 +388,93 @@ 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;
-	
+	/* purple_debug_info("TCP_PENDING", "Read %d bytes, rxlen is %d\n", buf_len, conn->tcp_rxlen); */
+	conn->tcp_rxqueue = g_realloc(conn->tcp_rxqueue, buf_len + conn->tcp_rxlen);
+	memcpy(conn->tcp_rxqueue + conn->tcp_rxlen, buf, buf_len);
+	conn->tcp_rxlen += buf_len;
+
 	pkt = g_newa(guint8, MAX_PACKET_SIZE);
-	while (1) {
-		if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) {
+	while (PURPLE_CONNECTION_IS_VALID(gc)) {
+		if (qd->openconns == NULL) {
 			break;
 		}
-		
+		if (conn->tcp_rxqueue == NULL) {
+			conn->tcp_rxlen = 0;
+			break;
+		}
+		if (conn->tcp_rxlen < QQ_TCP_HEADER_LENGTH) {
+			break;
+		}
+
 		bytes = 0;
-		bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes);
-		if (qd->tcp_rxlen < pkt_len) {
+		bytes += qq_get16(&pkt_len, conn->tcp_rxqueue + bytes);
+		if (conn->tcp_rxlen < pkt_len) {
 			break;
 		}
 
-		/* 
-		purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
-				   "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen);
-		*/
+		/* purple_debug_info("TCP_PENDING", "Packet len=%d, rxlen=%d\n", pkt_len, conn->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) {
+		    || *(conn->tcp_rxqueue + bytes) != QQ_PACKET_TAG
+			|| *(conn->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) {
 			/* HEY! This isn't even a QQ. What are you trying to pull? */
+			purple_debug_warning("TCP_PENDING", "Packet error, no header or tail tag\n");
 
-			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
-				 "Packet error, failed to check header and tail tag\n");
-
-			jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1);
+			jump = memchr(conn->tcp_rxqueue + 1, QQ_PACKET_TAIL, conn->tcp_rxlen - 1);
 			if ( !jump ) {
-				purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
-				 	"Failed to find next QQ_PACKET_TAIL, clear receive buffer\n");
-				g_free(qd->tcp_rxqueue);
-				qd->tcp_rxqueue = NULL;
-				qd->tcp_rxlen = 0;
+				purple_debug_warning("TCP_PENDING", "Failed to find next tail, clear receive buffer\n");
+				g_free(conn->tcp_rxqueue);
+				conn->tcp_rxqueue = NULL;
+				conn->tcp_rxlen = 0;
 				return;
 			}
 
 			/* jump and over QQ_PACKET_TAIL */
-			jump_len = (jump - qd->tcp_rxqueue) + 1;
-			purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
-				"Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1);
-			g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len);
-			qd->tcp_rxlen -= jump_len;
+			jump_len = (jump - conn->tcp_rxqueue) + 1;
+			purple_debug_warning("TCP_PENDING", "Find next tail at %d, jump %d\n", jump_len, jump_len + 1);
+			g_memmove(conn->tcp_rxqueue, jump, conn->tcp_rxlen - jump_len);
+			conn->tcp_rxlen -= jump_len;
 			continue;
 		}
 
 		memset(pkt, 0, MAX_PACKET_SIZE);
-		g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes);
-		
+		g_memmove(pkt, conn->tcp_rxqueue + bytes, pkt_len - bytes);
+
 		/* jump to next packet */
-		qd->tcp_rxlen -= pkt_len;
-		if (qd->tcp_rxlen) {
-			/*
-			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen);		
-			*/
-			jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen);
-			g_free(qd->tcp_rxqueue);
-			qd->tcp_rxqueue = jump;
+		conn->tcp_rxlen -= pkt_len;
+		if (conn->tcp_rxlen) {
+			/* purple_debug_info("TCP_PENDING", "shrink tcp_rxqueue to %d\n", conn->tcp_rxlen);	*/
+			jump = g_memdup(conn->tcp_rxqueue + pkt_len, conn->tcp_rxlen);
+			g_free(conn->tcp_rxqueue);
+			conn->tcp_rxqueue = jump;
 		} else {
-			/* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */
-			g_free(qd->tcp_rxqueue);
-			qd->tcp_rxqueue = NULL;
+			/* purple_debug_info("TCP_PENDING", "free tcp_rxqueue\n"); */
+			g_free(conn->tcp_rxqueue);
+			conn->tcp_rxqueue = NULL;
 		}
 
 		if (pkt == NULL) {
 			continue;
 		}
-		/* do not call packet_process before jump 
-		 * packet_process may call disconnect and destory tcp_rxqueue */
-		packet_process(gc, pkt, pkt_len - bytes);
+		/* packet_process may call disconnect and destory data like conn
+		 * do not call packet_process before jump,
+		 * break if packet_process return FALSE */
+		if (packet_process(gc, pkt, pkt_len - bytes) == FALSE) {
+			purple_debug_info("TCP_PENDING", "Connection has been destory\n");
+			break;
+		}
 	}
 }
 
 static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
 {
-	PurpleConnection *gc;
+	PurpleConnection *gc = (PurpleConnection *) data;
 	qq_data *qd;
 	guint8 *buf;
 	gint buf_len;
 
 	gc = (PurpleConnection *) data;
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
 
 	if(cond != PURPLE_INPUT_READ) {
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -393,13 +482,10 @@ static void udp_pending(gpointer data, g
 		return;
 	}
 
-	qd = (qq_data *) gc->proto_data;
-	g_return_if_fail(qd->fd >= 0);
-	
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 
 	/* here we have UDP proxy suppport */
-	buf_len = read(qd->fd, buf, MAX_PACKET_SIZE);
+	buf_len = read(source, buf, MAX_PACKET_SIZE);
 	if (buf_len <= 0) {
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Unable to read from socket"));
@@ -419,73 +505,95 @@ static void udp_pending(gpointer data, g
 			return;
 		}
 	}
-	
+
+	/* packet_process may call disconnect and destory data like conn
+	 * do not call packet_process before jump,
+	 * break if packet_process return FALSE */
 	packet_process(gc, buf, buf_len);
 }
 
-static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len)
+static gint udp_send_out(PurpleConnection *gc, guint8 *data, gint data_len)
 {
+	qq_data *qd;
 	gint ret;
 
-	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
-	/*
-	purple_debug(PURPLE_DEBUG_INFO, "UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
-	*/
-	
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+	qd = (qq_data *) gc->proto_data;
+
+#if 0
+	purple_debug_info("UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
+#endif
+
 	errno = 0;
 	ret = send(qd->fd, data, data_len, 0);
 	if (ret < 0 && errno == EAGAIN) {
 		return ret;
 	}
-	
+
 	if (ret < 0) {
 		/* TODO: what to do here - do we really have to disconnect? */
-		purple_debug(PURPLE_DEBUG_ERROR, "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));
+		purple_debug_error("UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
 	}
 	return ret;
 }
 
 static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond)
 {
-	qq_data *qd = data;
+	PurpleConnection *gc = (PurpleConnection *) data;
+	qq_data *qd;
+	qq_connection *conn;
 	int ret, writelen;
 
-	writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf);
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	conn = connection_find(qd, source);
+	g_return_if_fail(conn != NULL);
+
+	writelen = purple_circ_buffer_get_max_read(conn->tcp_txbuf);
 	if (writelen == 0) {
-		purple_input_remove(qd->tx_handler);
-		qd->tx_handler = 0;
+		purple_input_remove(conn->can_write_handler);
+		conn->can_write_handler = 0;
 		return;
 	}
 
-	ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen);
-	purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE",
-		"total %d bytes is sent %d\n", writelen, ret);
+	ret = write(source, conn->tcp_txbuf->outptr, writelen);
+	purple_debug_info("TCP_CAN_WRITE", "total %d bytes is sent %d\n", writelen, ret);
 
 	if (ret < 0 && errno == EAGAIN)
 		return;
 	else if (ret < 0) {
 		/* TODO: what to do here - do we really have to disconnect? */
-		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 		                               _("Write Error"));
 		return;
 	}
 
-	purple_circ_buffer_mark_read(qd->tcp_txbuf, ret);
+	purple_circ_buffer_mark_read(conn->tcp_txbuf, ret);
 }
 
-static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len)
+static gint tcp_send_out(PurpleConnection *gc, guint8 *data, gint data_len)
 {
+	qq_data *qd;
+	qq_connection *conn;
 	gint ret;
 
-	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+	g_return_val_if_fail(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);
-	 */
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+	qd = (qq_data *) gc->proto_data;
 
-	if (qd->tx_handler == 0) {
+	conn = connection_find(qd, qd->fd);
+	g_return_val_if_fail(conn, -1);
+
+#if 0
+	purple_debug_info("TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
+#endif
+
+	if (conn->can_write_handler == 0) {
 		ret = write(qd->fd, data, data_len);
 	} else {
 		ret = -1;
@@ -493,28 +601,28 @@ static gint tcp_send_out(qq_data *qd, gu
 	}
 
 	/*
-	purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+	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_info("TCP_SEND_OUT", "Socket is busy and send later\n");
 		ret = 0;
 	} else if (ret <= 0) {
 		/* TODO: what to do here - do we really have to disconnect? */
-		purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT",
+		purple_debug_error("TCP_SEND_OUT",
 			"Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
-		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
 		return ret;
 	}
 
 	if (ret < data_len) {
-		purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+		purple_debug_info("TCP_SEND_OUT",
 			"Add %d bytes to buffer\n", data_len - ret);
-		if (qd->tx_handler == 0) {
-			qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd);
+		if (conn->can_write_handler == 0) {
+			conn->can_write_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, gc);
 		}
-		purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret);
+		purple_circ_buffer_append(conn->tcp_txbuf, data + ret, data_len - ret);
 	}
 	return ret;
 }
@@ -528,17 +636,17 @@ static gboolean network_timeout(gpointer
 	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
 	qd = (qq_data *) gc->proto_data;
 
-	is_lost_conn = qq_trans_scan(qd);
+	is_lost_conn = qq_trans_scan(gc);
 	if (is_lost_conn) {
 		purple_connection_error_reason(gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
 		return TRUE;
 	}
 
-	if ( !qd->logged_in ) {
+	if ( !qd->is_login ) {
 		return TRUE;
 	}
-	
+
 	qd->itv_count.keep_alive--;
 	if (qd->itv_count.keep_alive <= 0) {
 		qd->itv_count.keep_alive = qd->itv_config.keep_alive;
@@ -553,55 +661,30 @@ static gboolean network_timeout(gpointer
 	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);
+		qq_update_online(gc, 0);
 		return TRUE;
 	}
 
 	return TRUE;		/* if return FALSE, timeout callback stops */
 }
 
-/* the callback function after socket is built
- * we setup the qq protocol related configuration here */
-static void qq_connect_cb(gpointer data, gint source, const gchar *error_message)
+static void do_request_token(PurpleConnection *gc)
 {
 	qq_data *qd;
-	PurpleConnection *gc;
 	gchar *conn_msg;
 	const gchar *passwd;
-	PurpleAccount *account ;
 
-	gc = (PurpleConnection *) data;
+	/* _qq_show_socket("Got login socket", source); */
 
-	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n");
-		close(source);
-		return;
-	}
-
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-
 	qd = (qq_data *) gc->proto_data;
-	account = purple_connection_get_account(gc);
 
-	/* Connect is now complete; clear the PurpleProxyConnectData */
-	qd->connect_data = NULL;
-
-	if (source < 0) {	/* socket returns -1 */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n");
-		qq_disconnect(gc);
-		reconnect_later(gc);
-		return;
-	}
-
-	/* _qq_show_socket("Got login socket", source); */
-
 	/* QQ use random seq, to minimize duplicated packets */
-	srandom(time(NULL));
-	qd->send_seq = random() & 0x0000ffff;
-	qd->fd = source;
-	qd->logged_in = FALSE;
+	srand(time(NULL));
+	qd->send_seq = rand() & 0xffff;
+
+	qd->is_login = FALSE;
+	qd->is_finish_update = FALSE;
 	qd->channel = 1;
 	qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
 
@@ -614,260 +697,94 @@ static void qq_connect_cb(gpointer data,
 	qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
 		qd->password_twice_md5, sizeof(qd->password_twice_md5));
 
-	g_return_if_fail(qd->network_timeout == 0);
-	qd->itv_config.resend = purple_account_get_int(account, "resend_interval", 10);
-	if (qd->itv_config.resend <= 0) qd->itv_config.resend = 10;
+	g_return_if_fail(qd->network_watcher == 0);
+	qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc);
 
-	qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60);
-	if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30;
-	qd->itv_config.keep_alive /= qd->itv_config.resend;
-	qd->itv_count.keep_alive = qd->itv_config.keep_alive;
-
-	qd->itv_config.update = purple_account_get_int(account, "update_interval", 300);
-	if (qd->itv_config.update > 0) {
-		if (qd->itv_config.update < qd->itv_config.keep_alive) {
-			qd->itv_config.update = qd->itv_config.keep_alive;
-		}
-		qd->itv_config.update /= qd->itv_config.resend;
-		qd->itv_count.update = qd->itv_config.update;
-	} else {
-		qd->itv_config.update = 0;
-	}
-
-	qd->network_timeout = purple_timeout_add(qd->itv_config.resend *1000, network_timeout, gc);
-	
-	if (qd->use_tcp)
-		gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc);
-	else
-		gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc);
-
 	/* Update the login progress status display */
-	conn_msg = g_strdup_printf("Login as %d", qd->uid);
-	purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
+	conn_msg = g_strdup_printf(_("Request token"));
+	purple_connection_update_progress(gc, conn_msg, 2, QQ_CONNECT_STEPS);
 	g_free(conn_msg);
 
 	qq_send_packet_token(gc);
 }
 
-static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+/* the callback function after socket is built
+ * we setup the qq protocol related configuration here */
+static void connect_cb(gpointer data, gint source, const gchar *error_message)
 {
 	PurpleConnection *gc;
 	qq_data *qd;
-	socklen_t len;
-	int error=0, ret;
+	PurpleAccount *account ;
+	qq_connection *conn;
 
 	gc = (PurpleConnection *) data;
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 
 	qd = (qq_data *) gc->proto_data;
+	account = purple_connection_get_account(gc);
 
+	/* conn_data will be destoryed */
+	qd->conn_data = NULL;
 
-	purple_debug_info("proxy", "Connected.\n");
-
-	/*
-	 * getsockopt after a non-blocking connect returns -1 if something is
-	 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
-	 * error holds what connect would have returned if it blocked until now.
-	 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
-	 * and anything else is a real error.
-	 *
-	 * (error == EINPROGRESS can happen after a select because the kernel can
-	 * be overly optimistic sometimes. select is just a hint that you might be
-	 * able to do something.)
-	 */
-	len = sizeof(error);
-	ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
-	if (ret == 0 && error == EINPROGRESS)
-		return; /* we'll be called again later */
-		
-	purple_input_remove(qd->tx_handler);
-	qd->tx_handler = 0;
-	if (ret < 0 || error != 0) {
-		if(ret != 0) 
-			error = errno;
-
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		purple_debug_info("QQ_CONN", "Invalid connection\n");
 		close(source);
-
-		purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
-
-		qq_connect_cb(gc, -1, _("Unable to connect"));
 		return;
 	}
 
-	qq_connect_cb(gc, source, NULL);
-}
-
-static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) {
-	PurpleConnection *gc;
-	qq_data *qd;
-	struct sockaddr server_addr;
-	int addr_size;
-	gint fd = -1;
-	int flags;
-
-	gc = (PurpleConnection *) data;
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-
-	qd = (qq_data *) gc->proto_data;
-
-	/* udp_query_data must be set as NULL.
-	 * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */
-	qd->udp_query_data = NULL;
-
-	if (!hosts || !hosts->data) {
-		purple_connection_error_reason(gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Couldn't resolve host"));
+	if (source < 0) {	/* socket returns -1 */
+		purple_debug_info("QQ_CONN",
+				"Could not establish a connection with the server:\n%s\n",
+				error_message);
+		if (qd->connect_watcher > 0)	purple_timeout_remove(qd->connect_watcher);
+		qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
 		return;
 	}
 
-	addr_size = GPOINTER_TO_INT(hosts->data);
-	hosts = g_slist_remove(hosts, hosts->data);
-	memcpy(&server_addr, hosts->data, addr_size);
-	g_free(hosts->data);
-	
-	hosts = g_slist_remove(hosts, hosts->data);
-	while(hosts) {
-		hosts = g_slist_remove(hosts, hosts->data);
-		g_free(hosts->data);
-		hosts = g_slist_remove(hosts, hosts->data);
+	/* _qq_show_socket("Got login socket", source); */
+	qd->fd = source;
+	conn = connection_create(qd, source);
+	if (qd->use_tcp) {
+		conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, tcp_pending, gc);
+	} else {
+		conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc);
 	}
 
-	fd = socket(PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-				"Unable to create socket: %s\n", g_strerror(errno));
-		return;
-	}
-
-	/* we use non-blocking mode to speed up connection */
-	flags = fcntl(fd, F_GETFL);
-	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
-	fcntl(fd, F_SETFD, FD_CLOEXEC);
-#endif
-
-	/* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
-	 *
-	 * If a UDP socket is unconnected, which is the normal state after a
-	 * bind() call, then send() or write() are not allowed, since no
-	 * destination is available; only sendto() can be used to send data.
-	 *   
-	 * Calling connect() on the socket simply records the specified address
-	 * and port number as being the desired communications partner. That
-	 * means that send() or write() are now allowed; they use the destination
-	 * address and port given on the connect call as the destination of packets.
-	 */
-	if (connect(fd, &server_addr, addr_size) >= 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
-		flags = fcntl(fd, F_GETFL);
-		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
-		qq_connect_cb(gc, fd, NULL);
-		return;
-	}
-	
-	/* [EINPROGRESS]
-	 *    The socket is marked as non-blocking and the connection cannot be 
-	 *    completed immediately. It is possible to select for completion by 
-	 *    selecting the socket for writing.
-	 * [EINTR]
-	 *    A signal interrupted the call. 
-	 *    The connection is established asynchronously.
-	 */
-	if ((errno == EINPROGRESS) || (errno == EINTR)) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
-			qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc);
-			return;
-		}
-
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %s\n", g_strerror(errno));
-	close(fd);
+	do_request_token( gc );
 }
 
-/* establish a generic QQ connection 
- * TCP/UDP, and direct/redirected */
-void qq_connect(PurpleAccount *account)
+gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port)
 {
-	PurpleConnection *gc;
+	PurpleAccount *account ;
 	qq_data *qd;
 	gchar *conn_msg;
 
-	gc = purple_account_get_connection(account);
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
+	account = purple_connection_get_account(gc);
 	qd = (qq_data *) gc->proto_data;
 
-
-	/* test set_new_server
-	while (set_new_server(qd)) {
-   		purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST",
-   			"New server %s:%d  Real server %s:%d\n",
-   			qd->server_name, qd->user_port, qd->real_hostname, qd->real_port);
-	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n",
- 			qd->servers);
- 	exit(1);
-	*/
-	if (qd->server_name == NULL) {
-		/* must be first call this function */
-		if ( set_new_server(qd) != TRUE) {
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-					_("Failed to connect server"));
-			return;
-		}
-	}
-
-	if (qd->real_hostname == NULL || qd->real_port == 0) {
+	if (server == NULL || strlen(server) == 0 || port == 0) {
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("hostname is NULL or port is 0"));
-		return;
+				_("Invalid server or port"));
+		return FALSE;
 	}
 
-	conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"),
-		qd->real_hostname, qd->reconnect_times);
+	conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), server, port);
 	purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS);
 	g_free(conn_msg);
 
-	if (qd->is_redirect) {
-   		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n",
-   			qd->real_hostname, qd->real_port);
-   	}
-	qd->is_redirect = FALSE;
+	purple_debug_info("QQ", "Connect to %s:%d\n", server, port);
 
-	qd->fd = -1;
-	qd->tx_handler = 0;
-	
-	/* QQ connection via UDP/TCP. 
-	* Now use Purple proxy function to provide TCP proxy support,
-	* and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
-	if(qd->use_tcp) {
-   		purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n",
-   			qd->real_hostname, qd->real_port);
-
-		/* TODO: is there a good default grow size? */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n");
-		qd->tcp_txbuf = purple_circ_buffer_new(0);
-
-		qd->connect_data = purple_proxy_connect(NULL, account,
-				qd->real_hostname, qd->real_port, qq_connect_cb, gc);
-		if (qd->connect_data == NULL) {
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Unable to connect."));
-		}
-		return;
+	if (qd->conn_data != NULL) {
+		purple_proxy_connect_cancel(qd->conn_data);
+		qd->conn_data = NULL;
 	}
-	
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n",
-		qd->real_hostname, qd->real_port);
-
-	g_return_if_fail(qd->udp_query_data == NULL);
-	qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port,
-		udp_host_resolved, gc);
-	if (qd->udp_query_data == NULL) {
-		purple_connection_error_reason(qd->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Could not resolve hostname"));
+	qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc);
+	if ( qd->conn_data == NULL ) {
+		purple_debug_error("QQ", _("Couldn't create socket"));
+		return FALSE;
 	}
+	return TRUE;
 }
 
 /* clean up qq_data structure and all its components
@@ -879,65 +796,32 @@ void qq_disconnect(PurpleConnection *gc)
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n");
+	purple_debug_info("QQ", "Disconnecting ...\n");
 
-	if (qd->network_timeout > 0) {
-		purple_timeout_remove(qd->network_timeout);
-		qd->network_timeout = 0;
+	if (qd->network_watcher > 0) {
+		purple_debug_info("QQ", "Remove network watcher\n");
+		purple_timeout_remove(qd->network_watcher);
+		qd->network_watcher = 0;
 	}
 
 	/* finish  all I/O */
-	if (qd->fd >= 0 && qd->logged_in) {
+	if (qd->fd >= 0 && qd->is_login) {
 		qq_send_packet_logout(gc);
 	}
 
-	if (gc->inpa > 0) {
-		purple_input_remove(gc->inpa);
-		gc->inpa = 0;
+	/* not connected */
+	if (qd->conn_data != NULL) {
+		purple_debug_info("QQ", "Connect cancel\n");
+		purple_proxy_connect_cancel(qd->conn_data);
+		qd->conn_data = NULL;
 	}
+	connection_free_all(qd);
+	qd->fd = -1;
 
-	if (qd->fd >= 0) {
-		close(qd->fd);
-		qd->fd = -1;
-	}
+	qq_trans_remove_all(gc);
 
-	if (qd->reconnect_timeout > 0) {
-		purple_timeout_remove(qd->reconnect_timeout);
-		qd->reconnect_timeout = 0;
-	}
-
-	if (qd->connect_data != NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n");
-		purple_proxy_connect_cancel(qd->connect_data);
-	}
-	
-	if(qd->tcp_txbuf != NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n");
-		purple_circ_buffer_destroy(qd->tcp_txbuf);
-		qd->tcp_txbuf = NULL;
-	}
-	
-	if (qd->tx_handler) {
-		purple_input_remove(qd->tx_handler);
-		qd->tx_handler = 0;
-	}
-	if (qd->tcp_rxqueue != NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n");
-		g_free(qd->tcp_rxqueue);
-		qd->tcp_rxqueue = NULL;
-		qd->tcp_rxlen = 0;
-	}
-	
-	if (qd->udp_query_data != NULL) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n");
-		purple_dnsquery_destroy(qd->udp_query_data);
-		qd->udp_query_data = NULL;
-	}
-
-	qq_trans_remove_all(qd);
-	
 	if (qd->token) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n");
+		purple_debug_info("QQ", "free token\n");
 		g_free(qd->token);
 		qd->token = NULL;
 		qd->token_len = 0;
@@ -955,13 +839,13 @@ void qq_disconnect(PurpleConnection *gc)
 	qq_buddies_list_free(gc->account, qd);
 }
 
-static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, 
+static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq,
 	guint8 *data, gint data_len)
 {
 	gint bytes = 0;
 	g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1);
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
-	
+
 	/* QQ TCP packet has two bytes in the begining defines packet length
 	 * so leave room here to store packet size */
 	if (qd->use_tcp) {
@@ -971,7 +855,7 @@ static gint encap(qq_data *qd, guint8 *b
 	bytes += qq_put8(buf + bytes, QQ_PACKET_TAG);
 	bytes += qq_put16(buf + bytes, QQ_CLIENT);
 	bytes += qq_put16(buf + bytes, cmd);
-	
+
 	bytes += qq_put16(buf + bytes, seq);
 
 	bytes += qq_put32(buf + bytes, qd->uid);
@@ -987,109 +871,144 @@ static gint encap(qq_data *qd, guint8 *b
 }
 
 /* data has been encrypted before */
-gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
-	guint8 *data, gint data_len)
+static gint packet_send_out(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
 {
+	qq_data *qd;
 	guint8 *buf;
 	gint buf_len;
 	gint bytes_sent;
 
-	g_return_val_if_fail(qd != NULL, -1);
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+	qd = (qq_data *)gc->proto_data;
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
-	buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
+	buf_len = packet_encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
 	if (buf_len <= 0) {
 		return -1;
 	}
 
 	if (qd->use_tcp) {
-		bytes_sent = tcp_send_out(qd, buf, buf_len);
+		bytes_sent = tcp_send_out(gc, buf, buf_len);
 	} else {
-		bytes_sent = udp_send_out(qd, buf, buf_len);
+		bytes_sent = udp_send_out(gc, buf, buf_len);
 	}
 
+	return bytes_sent;
+}
+
+gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq,
+	guint8 *data, gint data_len, gboolean need_ack)
+{
+	gint send_len;
+
+#if 1
+		purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n",
+				seq, qq_get_cmd_desc(cmd), cmd, data_len);
+#endif
+
+	send_len = packet_send_out(gc, cmd, seq, data, data_len);
 	if (need_ack)  {
-		qq_trans_add_client_cmd(qd, cmd, seq, data, data_len);
+		qq_trans_add_client_cmd(gc, cmd, seq, data, data_len, 0, 0);
 	}
-	
-#if 1
-		/* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"<== [%05d], 0x%04X %s, total %d bytes is sent %d\n", 
-				seq, cmd, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
-#endif
-	return bytes_sent;
+	return send_len;
 }
 
-/* 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)
+/* Encrypt data with session_key, and send packet out */
+static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq,
+	guint8 *data, gint data_len, gboolean need_ack, gint update_class, guint32 ship32)
 {
+	qq_data *qd;
 	guint8 *encrypted_data;
 	gint encrypted_len;
+	gint bytes_sent;
 
-	g_return_val_if_fail(qd != NULL, -1);
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+	qd = (qq_data *)gc->proto_data;
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
 	/* at most 16 bytes more */
 	encrypted_data = g_newa(guint8, data_len + 16);
-#if 0
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_ENCRYPT",
-			"Before %d: [%05d] 0x%04X %s\n",
-			data_len, seq, cmd, qq_get_cmd_desc(cmd));
-#endif
 	encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key);
 	if (encrypted_len < 16) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ_ENCRYPT",
-				"Error len %d: [%05d] 0x%04X %s\n",
+		purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n",
 				encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
 		return -1;
 	}
 
-#if 0
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_ENCRYPT",
-			"After %d: [%05d] 0x%04X %s\n",
-			encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
-#endif
-	return qq_send_data(qd, cmd, seq, need_ack, encrypted_data, encrypted_len);
+	bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
+
+	if (need_ack)  {
+		qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len, update_class, ship32);
+	}
+	return bytes_sent;
 }
 
-/* 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)
+gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len,
+		gint update_class, guint32 ship32)
 {
-	g_return_val_if_fail(qd != NULL, -1);
+	qq_data *qd;
+	guint16 seq;
+
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+	qd = (qq_data *) gc->proto_data;
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
-	qd->send_seq++;
-	return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len);
+	seq = ++qd->send_seq;
+#if 1
+		purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n",
+				seq, qq_get_cmd_desc(cmd), cmd, data_len);
+#endif
+	return send_cmd_detail(gc, cmd, seq, data, data_len, TRUE, update_class, ship32);
 }
 
-gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, 
-		guint8 *data, gint data_len)
+/* set seq and need_ack, then call send_cmd_detail */
+gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len)
 {
-	return qq_send_room_cmd(gc, room_cmd, 0, data, data_len);
+	qq_data *qd;
+	guint16 seq;
+	gboolean need_ack;
+
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+	qd = (qq_data *) gc->proto_data;
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+	if (cmd != QQ_CMD_LOGOUT) {
+		seq = ++qd->send_seq;
+		need_ack = TRUE;
+	} else {
+		seq = 0xFFFF;
+		need_ack = FALSE;
+	}
+#if 1
+		purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n",
+				seq, qq_get_cmd_desc(cmd), cmd, data_len);
+#endif
+	return send_cmd_detail(gc, cmd, seq, data, data_len, need_ack, 0, 0);
 }
 
-gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
+/* set seq and need_ack, then call send_cmd_detail */
+gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
 {
-	g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1);
-	return qq_send_room_cmd(gc, room_cmd, room_id, NULL, 0);
+#if 1
+		purple_debug_info("QQ", "<== [SRV-%05d], %s(0x%04X), datalen %d\n",
+				seq, qq_get_cmd_desc(cmd), cmd, data_len);
+#endif
+	return send_cmd_detail(gc, cmd, seq, data, data_len, FALSE, 0, 0);
 }
 
-gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
-		guint8 *data, gint data_len)
+static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
+		guint8 *data, gint data_len, gint update_class, guint32 ship32)
 {
 	qq_data *qd;
-
 	guint8 *buf;
 	gint buf_len;
 	guint8 *encrypted_data;
 	gint encrypted_len;
 	gint bytes_sent;
 	guint16 seq;
-	
+
 	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
 	qd = (qq_data *) gc->proto_data;
 
@@ -1106,6 +1025,7 @@ gint qq_send_room_cmd(PurpleConnection *
 	if (data != NULL && data_len > 0) {
 		buf_len += qq_putdata(buf + buf_len, data, data_len);
 	}
+
 	qd->send_seq++;
 	seq = qd->send_seq;
 
@@ -1114,32 +1034,43 @@ gint qq_send_room_cmd(PurpleConnection *
 	encrypted_data = g_newa(guint8, buf_len + 16);
 	encrypted_len = qq_encrypt(encrypted_data, buf, buf_len, qd->session_key);
 	if (encrypted_len < 16) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ_ENCRYPT",
-				"Error len %d: [%05d] QQ_CMD_ROOM.(0x%02X %s)\n",
-				encrypted_len, seq, room_cmd, qq_get_room_cmd_desc(room_cmd));
+		purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] %s (0x%02X)\n",
+				encrypted_len, seq, qq_get_room_cmd_desc(room_cmd), room_cmd);
 		return -1;
 	}
 
-	/* Encap header to buf */
-	buf_len = encap(qd, buf, MAX_PACKET_SIZE, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len);
-	if (buf_len <= 0) {
-		return -1;
-	}
-
-	if (qd->use_tcp) {
-		bytes_sent = tcp_send_out(qd, buf, buf_len);
-	} else {
-		bytes_sent = udp_send_out(qd, buf, buf_len);
-	}
-
-	qq_trans_add_room_cmd(qd, seq, room_cmd, room_id, buf, buf_len);
-	
+	bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len);
 #if 1
 		/* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"<== [%05d], QQ_CMD_ROOM.(0x%02X %s) to room %d, total %d bytes is sent %d\n", 
-				seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id,
-				buf_len, bytes_sent);
+		purple_debug_info("QQ",
+				"<== [%05d], %s (0x%02X) to room %d, datalen %d\n",
+				seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
 #endif
+
+	qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, buf, buf_len, update_class, ship32);
 	return bytes_sent;
 }
+
+gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
+		guint8 *data, gint data_len, gint update_class, guint32 ship32)
+{
+	return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32);
+}
+
+gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
+		guint8 *data, gint data_len)
+{
+	return send_room_cmd(gc, room_cmd, room_id, data, data_len, 0, 0);
+}
+
+gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd,
+		guint8 *data, gint data_len)
+{
+	return send_room_cmd(gc, room_cmd, 0, data, data_len, 0, 0);
+}
+
+gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
+{
+	g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1);
+	return send_room_cmd(gc, room_cmd, room_id, NULL, 0, 0, 0);
+}
============================================================
--- libpurple/protocols/qq/qq_network.h	2cdd0ad833eabce487e648a3199219fd25f6492e
+++ libpurple/protocols/qq/qq_network.h	d179aeabf6d58c136f1d465b18e23a4c64abcc60
@@ -32,19 +32,23 @@
 
 #define QQ_CONNECT_STEPS    3	/* steps in connection */
 
-void qq_connect(PurpleAccount *account);
+gboolean qq_connect_later(gpointer data);
 void qq_disconnect(PurpleConnection *gc);
-void qq_connect_later(PurpleConnection *gc);
 
-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);
+gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq,
+		guint8 *data, gint data_len, gboolean need_ack);
+gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len,
+		gint update_class, guint32 ship32);
 
+gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq,
+		guint8 *data, gint data_len);
+
 gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
 		guint8 *data, gint data_len);
+gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
+		guint8 *data, gint data_len, gint update_class, guint32 ship32);
 gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
-gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, 
+gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd,
 		guint8 *data, gint data_len);
 #endif
============================================================
--- libpurple/protocols/qq/qq_process.c	9dfd5fbbb3544fd5b8f71905085661c643d1a563
+++ libpurple/protocols/qq/qq_process.c	0cdaf6d2e2e957e9a0666ac0c2ccf0e25c9bfd5a
@@ -26,11 +26,6 @@
 #include "debug.h"
 #include "internal.h"
 
-#ifdef _WIN32
-#define random rand
-#define srandom srand
-#endif
-
 #include "buddy_info.h"
 #include "buddy_list.h"
 #include "buddy_opt.h"
@@ -88,8 +83,7 @@ static void process_cmd_unknow(PurpleCon
 	}
 }
 
-void qq_proc_cmd_server(PurpleConnection *gc,
-	guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
+void qq_proc_cmd_server(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
 {
 	qq_data *qd;
 
@@ -102,20 +96,20 @@ void qq_proc_cmd_server(PurpleConnection
 	data = g_newa(guint8, rcved_len);
 	data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
 	if (data_len < 0) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Can not decrypt server cmd by session key, [%05d], 0x%04X %s, len %d\n", 
+		purple_debug_warning("QQ",
+			"Can not decrypt server cmd by session key, [%05d], 0x%04X %s, len %d\n",
 			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
 		qq_show_packet("Can not decrypted", rcved, rcved_len);
 		return;
 	}
 
 	if (data_len <= 0) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Server cmd decrypted is empty, [%05d], 0x%04X %s, len %d\n", 
+		purple_debug_warning("QQ",
+			"Server cmd decrypted is empty, [%05d], 0x%04X %s, len %d\n",
 			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
 		return;
 	}
-	
+
 	/* now process the packet */
 	switch (cmd) {
 		case QQ_CMD_RECV_IM:
@@ -124,7 +118,7 @@ void qq_proc_cmd_server(PurpleConnection
 		case QQ_CMD_RECV_MSG_SYS:
 			qq_process_msg_sys(data, data_len, seq, gc);
 			break;
-		case QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS:
+		case QQ_CMD_BUDDY_CHANGE_STATUS:
 			qq_process_buddy_change_status(data, data_len, gc);
 			break;
 		default:
@@ -133,98 +127,198 @@ void qq_proc_cmd_server(PurpleConnection
 	}
 }
 
-static void process_cmd_login(PurpleConnection *gc, guint8 *data, gint data_len)
+static void process_room_cmd_notify(PurpleConnection *gc,
+	guint8 room_cmd, guint8 room_id, guint8 reply, guint8 *data, gint data_len)
 {
+	gchar *msg, *msg_utf8;
+	g_return_if_fail(data != NULL && data_len > 0);
+
+	msg = g_strndup((gchar *) data, data_len);	/* it will append 0x00 */
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+	g_free(msg);
+
+	msg = g_strdup_printf(_("Command %s(0x%02X) id %d, reply [0x%02X]:\n%s"),
+		qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, reply, msg_utf8);
+
+	purple_notify_error(gc, NULL, _("Invalid QQ Qun reply"), msg);
+	g_free(msg);
+	g_free(msg_utf8);
+}
+
+void qq_room_update(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
+{
 	qq_data *qd;
-	guint ret_8;
+	qq_group *group;
+	gint ret;
 
 	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	
 	qd = (qq_data *) gc->proto_data;
 
-	ret_8 = qq_process_login_reply(data, data_len, gc);
-	if (ret_8 == QQ_LOGIN_REPLY_OK) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login repliess OK; everything is fine\n");
+	group = qq_room_search_id(gc, room_id);
+	if (group == NULL && room_id <= 0) {
+		purple_debug_info("QQ", "No room, nothing update\n");
+		return;
+	}
+	if (group == NULL ) {
+		purple_debug_warning("QQ", "Failed search room id [%d]\n", room_id);
+		return;
+	}
 
-		purple_connection_set_state(gc, PURPLE_CONNECTED);
-		qd->logged_in = TRUE;	/* must be defined after sev_finish_login */
+	switch (room_cmd) {
+		case 0:
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, group->id, NULL, 0,
+					QQ_CMD_CLASS_UPDATE_ROOM, 0);
+			break;
+		case QQ_ROOM_CMD_GET_INFO:
+			ret = qq_request_room_get_buddies(gc, group, QQ_CMD_CLASS_UPDATE_ROOM);
+			if (ret <= 0) {
+				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0,
+						QQ_CMD_CLASS_UPDATE_ROOM, 0);
+			}
+			break;
+		case QQ_ROOM_CMD_GET_BUDDIES:
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0,
+					QQ_CMD_CLASS_UPDATE_ROOM, 0);
+			break;
+		case QQ_ROOM_CMD_GET_ONLINES:
+			/* last command */
+		default:
+			break;
+	}
+}
 
-		/* now initiate QQ Qun, do it first as it may take longer to finish */
-		qq_group_init(gc);
+static void update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
+{
+	qq_data *qd;
+	gboolean is_new_turn = FALSE;
+	qq_group *next_group;
 
-		/* Now goes on updating my icon/nickname, not showing info_window */
-		qd->modifying_face = FALSE;
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
 
-		qq_send_packet_get_info(gc, qd->uid, FALSE);
-		/* grab my level */
-		qq_send_packet_get_level(gc, qd->uid);
+	next_group = qq_room_get_next(gc, room_id);
+	if (next_group == NULL && room_id <= 0) {
+		purple_debug_info("QQ", "No room, nothing update\n");
+		qd->is_finish_update = TRUE;
+		return;
+	}
+	if (next_group == NULL ) {
+		is_new_turn = TRUE;
+		next_group = qq_room_get_next(gc, 0);
+		g_return_if_fail(next_group != NULL);
+	}
 
-		qq_send_packet_change_status(gc);
+	switch (room_cmd) {
+		case 0:
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0,
+					QQ_CMD_CLASS_UPDATE_ALL, 0);
+			break;
+		case QQ_ROOM_CMD_GET_INFO:
+			if (!is_new_turn) {
+				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0,
+						QQ_CMD_CLASS_UPDATE_ALL, 0);
+			} else {
+				qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL);
+			}
+			break;
+		case QQ_ROOM_CMD_GET_BUDDIES:
+			/* last command */
+			if (!is_new_turn) {
+				qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL);
+			} else {
+				qd->is_finish_update = TRUE;
+			}
+			break;
+		default:
+			break;
+	}
+}
 
-		/* refresh buddies */
-		qq_send_packet_get_buddies_list(gc, 0);
+void qq_update_all(PurpleConnection *gc, guint16 cmd)
+{
+	qq_data *qd;
 
-		/* refresh groups */
-		qq_send_packet_get_buddies_and_rooms(gc, 0);
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
 
-		return;
+	switch (cmd) {
+		case 0:
+			qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, QQ_BUDDY_INFO_UPDATE_ONLY);
+			break;
+		case QQ_CMD_GET_USER_INFO:
+			qq_request_change_status(gc, QQ_CMD_CLASS_UPDATE_ALL);
+			break;
+		case QQ_CMD_CHANGE_STATUS:
+			qq_request_get_buddies_list(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
+			break;
+		case QQ_CMD_GET_BUDDIES_LIST:
+			qq_request_get_buddies_and_rooms(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
+			break;
+		case QQ_CMD_GET_BUDDIES_AND_ROOMS:
+			qq_request_get_buddies_levels(gc, QQ_CMD_CLASS_UPDATE_ALL);
+			break;
+		case QQ_CMD_GET_LEVEL:
+			qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
+			break;
+		case QQ_CMD_GET_BUDDIES_ONLINE:
+			/* last command */
+			update_all_rooms(gc, 0, 0);
+			break;
+		default:
+			break;
 	}
+}
 
-	if (ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
-		qd->is_redirect = TRUE;
-		/*
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port);
-		*/
+static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
+{
+	qq_data *qd;
+	qq_group *next_group;
+
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	next_group = qq_room_get_next_conv(gc, room_id);
+	if (next_group == NULL && room_id <= 0) {
+		purple_debug_info("QQ", "No room, no update online buddies\n");
 		return;
 	}
-
-	if (ret_8 == QQ_LOGIN_REPLY_ERR_PWD) {
-		if (!purple_account_get_remember_password(gc->account)) {
-			purple_account_set_password(gc->account, NULL);
-		}
-		purple_connection_error_reason(gc,
-			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password."));
+	if (next_group == NULL ) {
+		purple_debug_info("QQ", "finished update online buddies\n");
 		return;
 	}
 
-	if (ret_8 == QQ_LOGIN_REPLY_ERR_MISC) {
-		if (purple_debug_is_enabled())
-			purple_connection_error_reason(gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login. Check debug log."));
-		else
-			purple_connection_error_reason(gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login"));
-		return;
+	switch (room_cmd) {
+		case 0:
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0,
+					QQ_CMD_CLASS_UPDATE_ALL, 0);
+			break;
+		case QQ_ROOM_CMD_GET_ONLINES:
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0,
+					QQ_CMD_CLASS_UPDATE_ALL, 0);
+			break;
+		default:
+			break;
 	}
 }
 
-static void process_room_cmd_notify(PurpleConnection *gc, 
-	guint8 room_cmd, guint8 room_id, guint8 reply_cmd, guint8 reply, guint8 *data, gint data_len)
+void qq_update_online(PurpleConnection *gc, guint16 cmd)
 {
-	gchar *msg, *msg_utf8;
-	g_return_if_fail(data != NULL && data_len > 0);
-
-	msg = g_strndup((gchar *) data, data_len);	/* it will append 0x00 */
-	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-	g_free(msg);
-	
-	msg = g_strdup_printf(_(
-		"Reply %s(0x%02X )\n"
-		"Sent %s(0x%02X )\n"
-		"Room id %d, reply [0x%02X]: \n"
-		"%s"), 
-		qq_get_room_cmd_desc(reply_cmd), reply_cmd, 
-		qq_get_room_cmd_desc(room_cmd), room_cmd, 
-		room_id, reply, msg_utf8);
-		
-	purple_notify_error(gc, NULL, _("Failed room reply"), msg);
-	g_free(msg);
-	g_free(msg_utf8);
+	switch (cmd) {
+		case 0:
+			qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ONLINE);
+			break;
+		case QQ_CMD_GET_BUDDIES_ONLINE:
+			/* last command */
+			update_all_rooms_online(gc, 0, 0);
+			break;
+		default:
+			break;
+	}
 }
 
-void qq_proc_room_cmd_reply(PurpleConnection *gc,
-	guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len)
+void qq_proc_room_cmd_reply(PurpleConnection *gc, guint16 seq,
+		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
+		gint update_class, guint32 ship32)
 {
 	qq_data *qd;
 	guint8 *data;
@@ -239,58 +333,57 @@ void qq_proc_room_cmd_reply(PurpleConnec
 	data = g_newa(guint8, rcved_len);
 	data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
 	if (data_len < 0) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Can not decrypt room cmd by session key, [%05d], 0x%02X %s for %d, len %d\n", 
+		purple_debug_warning("QQ",
+			"Can not decrypt room cmd by session key, [%05d], 0x%02X %s for %d, len %d\n",
 			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
 		qq_show_packet("Can not decrypted", rcved, rcved_len);
 		return;
 	}
 
 	if (room_id <= 0) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Invaild room id, [%05d], 0x%02X %s for %d, len %d\n", 
+		purple_debug_warning("QQ",
+			"Invaild room id, [%05d], 0x%02X %s for %d, len %d\n",
 			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
 		return;
 	}
 
 	if (data_len <= 2) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Invaild len of room cmd decrypted, [%05d], 0x%02X %s for %d, len %d\n", 
+		purple_debug_warning("QQ",
+			"Invaild len of room cmd decrypted, [%05d], 0x%02X %s for %d, len %d\n",
 			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
 		return;
 	}
-	
+
 	group = qq_room_search_id(gc, room_id);
 	if (group == NULL) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Missing room id in [%05d], 0x%02X %s for %d, len %d\n", 
+		purple_debug_warning("QQ",
+			"Missing room id in [%05d], 0x%02X %s for %d, len %d\n",
 			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
 	}
-	
+
 	bytes = 0;
 	bytes += qq_get8(&reply_cmd, data + bytes);
 	bytes += qq_get8(&reply, data + bytes);
 
 	if (reply_cmd != room_cmd) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Missing room cmd in reply 0x%02X %s, [%05d], 0x%02X %s for %d, len %d\n", 
+		purple_debug_warning("QQ",
+			"Missing room cmd in reply 0x%02X %s, [%05d], 0x%02X %s for %d, len %d\n",
 			reply_cmd, qq_get_room_cmd_desc(reply_cmd),
 			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
 	}
-	
+
 	/* now process the packet */
 	if (reply != QQ_ROOM_CMD_REPLY_OK) {
 		if (group != NULL) {
 			qq_set_pending_id(&qd->joining_groups, group->ext_id, FALSE);
 		}
-		
+
 		switch (reply) {	/* this should be all errors */
 		case QQ_ROOM_CMD_REPLY_NOT_MEMBER:
 			if (group != NULL) {
-				purple_debug(PURPLE_DEBUG_WARNING,
-					   "QQ",
-					   _("You are not a member of group \"%s\"\n"), group->group_name_utf8);
-				group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+				purple_debug_warning("QQ",
+					   _("You are not a member of QQ Qun \"%s\"\n"), group->title_utf8);
+				group->my_role = QQ_ROOM_ROLE_NO;
 				qq_group_refresh(gc, group);
 			}
 			break;
@@ -300,7 +393,7 @@ void qq_proc_room_cmd_reply(PurpleConnec
 					purple_roomlist_set_in_progress(qd->roomlist, FALSE);
 			}
 		default:
-			process_room_cmd_notify(gc, room_cmd, room_id, reply_cmd, reply, data + bytes, data_len - bytes);
+			process_room_cmd_notify(gc, reply_cmd, room_id, reply, data + bytes, data_len - bytes);
 		}
 		return;
 	}
@@ -309,10 +402,6 @@ void qq_proc_room_cmd_reply(PurpleConnec
 	switch (reply_cmd) {
 	case QQ_ROOM_CMD_GET_INFO:
 		qq_process_room_cmd_get_info(data + bytes, data_len - bytes, gc);
-		if (group != NULL) {
-			qq_send_cmd_group_get_members_info(gc, group);
-			qq_send_cmd_group_get_online_members(gc, group);
-		}
 		break;
 	case QQ_ROOM_CMD_CREATE:
 		qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc);
@@ -346,30 +435,90 @@ void qq_proc_room_cmd_reply(PurpleConnec
 		if (group != NULL)
 			qq_group_conv_refresh_online_member(gc, group);
 		break;
-	case QQ_ROOM_CMD_GET_MEMBER_INFO:
-		qq_process_room_cmd_get_members(data + bytes, data_len - bytes, gc);
+	case QQ_ROOM_CMD_GET_BUDDIES:
+		qq_process_room_cmd_get_buddies(data + bytes, data_len - bytes, gc);
 		if (group != NULL)
 			qq_group_conv_refresh_online_member(gc, group);
 		break;
 	default:
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Unknow room cmd 0x%02X %s\n", 
+		purple_debug_warning("QQ", "Unknow room cmd 0x%02X %s\n",
 			   reply_cmd, qq_get_room_cmd_desc(reply_cmd));
 	}
+
+	purple_debug_info("QQ", "Update class %d\n", update_class);
+	if (update_class == QQ_CMD_CLASS_UPDATE_ALL) {
+		update_all_rooms(gc, room_cmd, room_id);
+		return;
+	}
+	if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) {
+		update_all_rooms_online(gc, room_cmd, room_id);
+		return;
+	}
+	if (update_class == QQ_CMD_CLASS_UPDATE_ROOM) {
+		qq_room_update(gc, room_cmd, room_id);
+	}
 }
 
-void qq_proc_cmd_reply(PurpleConnection *gc,
-	guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
+void qq_proc_cmd_login(PurpleConnection *gc, guint8 *rcved, gint rcved_len)
 {
 	qq_data *qd;
+	guint8 *data;
+	gint data_len;
+	guint ret_8;
 
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	data = g_newa(guint8, rcved_len);
+	/* May use password_twice_md5 in the past version like QQ2005*/
+	data_len = qq_decrypt(data, rcved, rcved_len, qd->inikey);
+	if (data_len >= 0) {
+		purple_debug_warning("QQ",
+				"Decrypt login reply packet with inikey, %d bytes\n", data_len);
+	} else {
+		data_len = qq_decrypt(data, rcved, rcved_len, qd->password_twice_md5);
+		if (data_len >= 0) {
+			purple_debug_warning("QQ",
+				"Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len);
+		} else {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Can not decrypt login reply"));
+			return;
+		}
+	}
+
+	ret_8 = qq_process_login_reply(gc, data, data_len);
+	if (ret_8 != QQ_LOGIN_REPLY_OK) {
+		return;
+	}
+
+	purple_debug_info("QQ", "Login repliess OK; everything is fine\n");
+
+	purple_connection_set_state(gc, PURPLE_CONNECTED);
+	qd->is_login = TRUE;	/* must be defined after sev_finish_login */
+
+	/* now initiate QQ Qun, do it first as it may take longer to finish */
+	qq_group_init(gc);
+
+	/* Now goes on updating my icon/nickname, not showing info_window */
+	qd->modifying_face = FALSE;
+
+	qq_update_all(gc, 0);
+	return;
+}
+
+void qq_proc_cmd_reply(PurpleConnection *gc, guint16 cmd, guint16 seq,
+		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32)
+{
+	qq_data *qd;
+
 	guint8 *data;
 	gint data_len;
 
 	guint8 ret_8 = 0;
 	guint16 ret_16 = 0;
 	guint32 ret_32 = 0;
-	gchar *error_msg = NULL;
+	gboolean is_unknow = FALSE;
 
 	g_return_if_fail(rcved_len > 0);
 
@@ -377,61 +526,23 @@ void qq_proc_cmd_reply(PurpleConnection 
 	qd = (qq_data *) gc->proto_data;
 
 	data = g_newa(guint8, rcved_len);
-	if (cmd == QQ_CMD_TOKEN) {
-		g_memmove(data, rcved, rcved_len);
-		data_len = rcved_len;
-	} else if (cmd == QQ_CMD_LOGIN) {
-		/* May use password_twice_md5 in the past version like QQ2005*/
-		data_len = qq_decrypt(data, rcved, rcved_len, qd->inikey);
-		if (data_len >= 0) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"Decrypt login reply packet with inikey, %d bytes\n", data_len);
-		} else {
-			data_len = qq_decrypt(data, rcved, rcved_len, qd->password_twice_md5);
-			if (data_len >= 0) {
-				purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len);
-			} else {
-				purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, 
-					_("Can not decrypt login reply"));
-				return;
-			}
-		}
-	} else {
-		data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
-		if (data_len < 0) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				"Can not reply by session key, [%05d], 0x%04X %s, len %d\n", 
-				seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
-			qq_show_packet("Can not decrypted", rcved, rcved_len);
-			return;
-		}
+	data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
+	if (data_len < 0) {
+		purple_debug_warning("QQ",
+			"Reply can not be decrypted by session key, [%05d], 0x%04X %s, len %d\n",
+			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
+		qq_show_packet("Can not decrypted", rcved, rcved_len);
+		return;
 	}
-	
+
 	if (data_len <= 0) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			"Reply decrypted is empty, [%05d], 0x%04X %s, len %d\n", 
+		purple_debug_warning("QQ",
+			"Reply decrypted is empty, [%05d], 0x%04X %s, len %d\n",
 			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
 		return;
 	}
 
 	switch (cmd) {
-		case QQ_CMD_TOKEN:
-			ret_8 = qq_process_token_reply(gc, error_msg, data, data_len);
-			if (ret_8 != QQ_TOKEN_REPLY_OK) {
-				if (error_msg == NULL) {
-					error_msg = g_strdup_printf( _("Invalid token reply code, 0x%02X"), ret_8);
-				}
-				purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
-				g_free(error_msg);
-				return;
-			}
-			
-			qq_send_packet_login(gc);
-			break;
-		case QQ_CMD_LOGIN:
-			process_cmd_login(gc, data, data_len);
-			break;
 		case QQ_CMD_UPDATE_INFO:
 			qq_process_modify_info_reply(data, data_len, gc);
 			break;
@@ -450,7 +561,7 @@ void qq_proc_cmd_reply(PurpleConnection 
 		case QQ_CMD_GET_USER_INFO:
 			qq_process_get_info_reply(data, data_len, gc);
 			break;
-		case QQ_CMD_CHANGE_ONLINE_STATUS:
+		case QQ_CMD_CHANGE_STATUS:
 			qq_process_change_status_reply(data, data_len, gc);
 			break;
 		case QQ_CMD_SEND_IM:
@@ -462,41 +573,53 @@ void qq_proc_cmd_reply(PurpleConnection 
 		case QQ_CMD_GET_BUDDIES_ONLINE:
 			ret_8 = qq_process_get_buddies_online_reply(data, data_len, gc);
 			if (ret_8  > 0 && ret_8 < 0xff) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more online buddies\n"); 
-				qq_send_packet_get_buddies_online(gc, ret_8);
-			} else {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "All online buddies received\n"); 
-				/* Fixme: this should not be called once*/
-				qq_send_packet_get_buddies_levels(gc);
-
-				qq_refresh_all_buddy_status(gc);
+				purple_debug_info("QQ", "Requesting for more online buddies\n");
+				qq_request_get_buddies_online(gc, ret_8, update_class);
+				return;
 			}
+			purple_debug_info("QQ", "All online buddies received\n");
+			qq_refresh_all_buddy_status(gc);
 			break;
 		case QQ_CMD_GET_LEVEL:
 			qq_process_get_level_reply(data, data_len, gc);
 			break;
 		case QQ_CMD_GET_BUDDIES_LIST:
 			ret_16 = qq_process_get_buddies_list_reply(data, data_len, gc);
-			if (ret_16 > 0	&& ret_16 < 0xffff) { 
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies\n"); 
-				qq_send_packet_get_buddies_list(gc, ret_16);
-			} else {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies received. Requesting buddies' levels\n");
-				qq_send_packet_get_buddies_online(gc, 0);
+			if (ret_16 > 0	&& ret_16 < 0xffff) {
+				purple_debug_info("QQ", "Requesting for more buddies\n");
+				qq_request_get_buddies_list(gc, ret_16, update_class);
+				return;
 			}
+			purple_debug_info("QQ", "All buddies received. Requesting buddies' levels\n");
 			break;
 		case QQ_CMD_GET_BUDDIES_AND_ROOMS:
 			ret_32 = qq_process_get_buddies_and_rooms(data, data_len, gc);
 			if (ret_32 > 0 && ret_32 < 0xffffffff) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies and groups\n");
-				qq_send_packet_get_buddies_and_rooms(gc, ret_32);
-			} else {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies and groups received\n"); 
+				purple_debug_info("QQ", "Requesting for more buddies and groups\n");
+				qq_request_get_buddies_and_rooms(gc, ret_32, update_class);
+				return;
 			}
+			purple_debug_info("QQ", "All buddies and groups received\n");
 			break;
 		default:
 			process_cmd_unknow(gc, "Unknow reply CMD", data, data_len, cmd, seq);
+			is_unknow = TRUE;
 			break;
 	}
+	if (is_unknow)
+		return;
+
+	if (update_class == QQ_CMD_CLASS_NONE)
+		return;
+
+	purple_debug_info("QQ", "Update class %d\n", update_class);
+	if (update_class == QQ_CMD_CLASS_UPDATE_ALL) {
+		qq_update_all(gc, cmd);
+		return;
+	}
+	if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) {
+		qq_update_online(gc, cmd);
+		return;
+	}
 }
 
============================================================
--- libpurple/protocols/qq/qq_process.h	85481566d6e1810595242ecd79f2cdd00533b4c1
+++ libpurple/protocols/qq/qq_process.h	2f018e76532ac55f6c6241268da8606ff3ad811b
@@ -30,12 +30,24 @@
 
 #include "qq.h"
 
-void qq_proc_cmd_reply(PurpleConnection *gc,
-		guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len);
-void qq_proc_room_cmd_reply(PurpleConnection *gc,
-	guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len);
-	
-void qq_proc_cmd_server(PurpleConnection *gc,
-	guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len);
+enum {
+	QQ_CMD_CLASS_NONE = 0,
+	QQ_CMD_CLASS_UPDATE_ALL,
+	QQ_CMD_CLASS_UPDATE_ONLINE,
+	QQ_CMD_CLASS_UPDATE_ROOM,
+};
+
+void qq_proc_cmd_login(PurpleConnection *gc, guint8 *rcved, gint rcved_len);
+void qq_proc_cmd_reply(PurpleConnection *gc, guint16 cmd, guint16 seq,
+		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32);
+void qq_proc_room_cmd_reply(PurpleConnection *gc, guint16 seq,
+		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
+		gint update_class, guint32 ship32);
+
+void qq_proc_cmd_server(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len);
+
+void qq_update_all(PurpleConnection *gc, guint16 cmd);
+void qq_update_online(PurpleConnection *gc, guint16 cmd);
+void qq_room_update(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
 #endif
 
============================================================
--- libpurple/protocols/qq/qq_trans.c	5428a1f0f85c64a6e38cab4fc2777c98a2e4a853
+++ libpurple/protocols/qq/qq_trans.c	166bf6c83885256e73dfc43353cec519daa9a131
@@ -37,8 +37,35 @@
 
 #define QQ_RESEND_MAX               3	/* max resend per packet */
 
-qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq)
+enum {
+	QQ_TRANS_IS_SERVER = 0x01,			/* Is server command or client command */
+	QQ_TRANS_IS_IMPORT = 0x02,			/* Only notice if not get reply; or resend, disconn if reties get 0*/
+	QQ_TRANS_BEFORE_LOGIN = 0x04,		/* server command before login*/
+};
+
+struct _qq_transaction {
+	guint8 flag;
+	guint16 seq;
+	guint16 cmd;
+
+	guint8 room_cmd;
+	guint32 room_id;
+
+	guint8 *data;
+	gint data_len;
+
+	gint fd;
+	gint send_retries;
+	gint rcved_times;
+	gint scan_times;
+
+	gint update_class;
+	guint32 ship32;
+};
+
+qq_transaction *qq_trans_find_rcved(PurpleConnection *gc, guint16 cmd, guint16 seq)
 {
+	qq_data *qd = (qq_data *)gc->proto_data;
 	GList *curr;
 	GList *next;
 	qq_transaction *trans;
@@ -50,19 +77,21 @@ qq_transaction *qq_trans_find_rcved(qq_d
 	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++;
+			/* server may not get our confirm reply before, send reply again*/
+			/* only rcved buffer stored in transaction
 			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);
+					qq_send_cmd_encrypted(gc, trans->cmd, trans->seq, trans->data, trans->data_len, FALSE);
 				}
 			}
+			*/
 			return trans;
 		}
 	}
@@ -70,20 +99,20 @@ qq_transaction *qq_trans_find_rcved(qq_d
 	return NULL;
 }
 
-gboolean qq_trans_is_server(qq_transaction *trans) 
+gboolean qq_trans_is_server(qq_transaction *trans)
 {
 	g_return_val_if_fail(trans != NULL, FALSE);
-	
+
 	if (trans->flag & QQ_TRANS_IS_SERVER)
 		return TRUE;
 	else
 		return FALSE;
 }
 
-gboolean qq_trans_is_dup(qq_transaction *trans) 
+gboolean qq_trans_is_dup(qq_transaction *trans)
 {
 	g_return_val_if_fail(trans != NULL, TRUE);
-	
+
 	if (trans->rcved_times > 1)
 		return TRUE;
 	else
@@ -102,115 +131,122 @@ guint32 qq_trans_get_room_id(qq_transact
 	return trans->room_id;
 }
 
+gint qq_trans_get_class(qq_transaction *trans)
+{
+	g_return_val_if_fail(trans != NULL, QQ_CMD_CLASS_NONE);
+	return trans->update_class;
+}
+
+gint qq_trans_get_ship(qq_transaction *trans)
+{
+	g_return_val_if_fail(trans != NULL, 0);
+	return trans->ship32;
+}
+
+static qq_transaction *trans_create(PurpleConnection *gc, gint fd,
+	guint16 cmd, guint16 seq, guint8 *data, gint data_len, gint update_class, guint32 ship32)
+{
+	qq_data *qd;
+	qq_transaction *trans;
+
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	trans = g_new0(qq_transaction, 1);
+
+	memset(trans, 0, sizeof(qq_transaction));
+	trans->fd = fd;
+	trans->cmd = cmd;
+	trans->seq = seq;
+
+	trans->data = NULL;
+	trans->data_len = 0;
+	if (data != NULL && data_len > 0) {
+		/* don't use g_strdup, may have 0x00 */
+		trans->data = g_memdup(data, data_len);
+		trans->data_len = data_len;
+	}
+
+	trans->update_class = update_class;
+	return trans;
+}
+
 /* Remove a packet with seq from send trans */
-static void trans_remove(qq_data *qd, qq_transaction *trans) 
+static void trans_remove(PurpleConnection *gc, qq_transaction *trans)
 {
+	qq_data *qd = (qq_data *)gc->proto_data;
 	g_return_if_fail(qd != NULL && trans != NULL);
-	
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS",
+
+#if 0
+	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));
-
+#endif
 	if (trans->data)	g_free(trans->data);
 	qd->transactions = g_list_remove(qd->transactions, trans);
 	g_free(trans);
 }
 
-void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+void qq_trans_add_client_cmd(PurpleConnection *gc,
+	guint16 cmd, guint16 seq, guint8 *data, gint data_len, gint update_class, guint32 ship32)
 {
-	qq_transaction *trans = g_new0(qq_transaction, 1);
+	qq_data *qd = (qq_data *)gc->proto_data;
+	qq_transaction *trans = trans_create(gc, qd->fd, cmd, seq, data, data_len, update_class, ship32);
 
-	g_return_if_fail(trans != NULL);
-
-	trans->flag = 0;
 	if (cmd == QQ_CMD_TOKEN || cmd == QQ_CMD_LOGIN || cmd == QQ_CMD_KEEP_ALIVE) {
-		trans->flag |= QQ_TRANS_CLI_IMPORT;
+		trans->flag |= QQ_TRANS_IS_IMPORT;
 	}
-	trans->fd = qd->fd;
-	trans->cmd = cmd;
-	trans->seq = seq;
-	trans->room_cmd = 0;
-	trans->room_id = 0;
 	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_INFO, "QQ_TRANS",
-			"Add client cmd, seq = %d, data = %p, len = %d\n",
+#if 0
+	purple_debug_info("QQ_TRANS", "Add client cmd, seq %d, data %p, len %d\n",
 			trans->seq, trans->data, trans->data_len);
+#endif
 	qd->transactions = g_list_append(qd->transactions, trans);
 }
 
-void qq_trans_add_room_cmd(qq_data *qd, guint16 seq, guint8 room_cmd, guint32 room_id,
-		guint8 *data, gint data_len)
+void qq_trans_add_room_cmd(PurpleConnection *gc,
+		guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *data, gint data_len,
+		gint update_class, guint32 ship32)
 {
-	qq_transaction *trans = g_new0(qq_transaction, 1);
+	qq_data *qd = (qq_data *)gc->proto_data;
+	qq_transaction *trans = trans_create(gc, qd->fd, QQ_CMD_ROOM, seq, data, data_len,
+			update_class, ship32);
 
-	g_return_if_fail(trans != NULL);
-
-	trans->flag = 0;
-	trans->fd = qd->fd;
-	trans->seq = seq;
-	trans->cmd = QQ_CMD_ROOM;
 	trans->room_cmd = room_cmd;
 	trans->room_id = room_id;
 	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_INFO, "QQ_TRANS",
-			"Add room cmd, seq = %d, data = %p, len = %d\n",
+#if 0
+	purple_debug_info("QQ_TRANS", "Add room cmd, seq %d, data %p, len %d\n",
 			trans->seq, trans->data, trans->data_len);
+#endif
 	qd->transactions = g_list_append(qd->transactions, trans);
 }
 
-void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+void qq_trans_add_server_cmd(PurpleConnection *gc,
+	guint16 cmd, guint16 seq, guint8 *data, gint data_len)
 {
-	qq_transaction *trans = g_new0(qq_transaction, 1);
+	qq_data *qd = (qq_data *)gc->proto_data;
+	qq_transaction *trans = trans_create(gc, qd->fd, cmd, seq, data, data_len, QQ_CMD_CLASS_NONE, 0);
 
-	g_return_if_fail(trans != NULL);
-
 	trans->flag = QQ_TRANS_IS_SERVER;
-	if ( !qd->logged_in ) {
+	if ( !qd->is_login ) {
 		trans->flag |= QQ_TRANS_BEFORE_LOGIN;
 	}
-	trans->fd = qd->fd;
-	trans->cmd = cmd;
-	trans->seq = seq;
-	trans->room_cmd = 0;
-	trans->room_id = 0;
 	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_INFO, "QQ_TRANS",
-			"Add server cmd, seq = %d, data = %p, len = %d\n",
+#if 0
+	purple_debug_info("QQ_TRANS", "Add server cmd, seq %d, data %p, len %d\n",
 			trans->seq, trans->data, trans->data_len);
+#endif
 	qd->transactions = g_list_append(qd->transactions, trans);
 }
 
-void qq_trans_process_before_login(qq_data *qd)
+void qq_trans_process_before_login(PurpleConnection *gc)
 {
+	qq_data *qd = (qq_data *)gc->proto_data;
 	GList *curr;
 	GList *next;
 	qq_transaction *trans;
@@ -221,42 +257,44 @@ void qq_trans_process_before_login(qq_da
 	while( (curr = next) ) {
 		next = curr->next;
 		trans = (qq_transaction *) (curr->data);
-		/* purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
-		
+#if 0
+		purple_debug_info("QQ_TRANS", "Scan [%d]\n", trans->seq);
+#endif
 		if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) {
 			continue;
 		}
 		if ( !(trans->flag & QQ_TRANS_BEFORE_LOGIN) ) {
 			continue;
 		}
-		// set QQ_TRANS_BEFORE_LOGIN off
+		/* set QQ_TRANS_BEFORE_LOGIN off */
 		trans->flag &= ~QQ_TRANS_BEFORE_LOGIN;
 
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+		purple_debug_info("QQ_TRANS",
 				"Process server cmd before login, seq %d, data %p, len %d, send_retries %d\n",
 				trans->seq, trans->data, trans->data_len, trans->send_retries);
 
-		qq_proc_cmd_reply(qd->gc, trans->seq, trans->cmd, trans->data, trans->data_len);
+		qq_proc_cmd_reply(gc, trans->seq, trans->cmd, trans->data, trans->data_len, trans->update_class, trans->ship32);
 	}
 
-	/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+	/* purple_debug_info("QQ_TRANS", "Scan finished\n"); */
 	return;
 }
 
-gboolean qq_trans_scan(qq_data *qd)
+gboolean qq_trans_scan(PurpleConnection *gc)
 {
+	qq_data *qd = (qq_data *)gc->proto_data;
 	GList *curr;
 	GList *next;
 	qq_transaction *trans;
 
 	g_return_val_if_fail(qd != NULL, FALSE);
-	
+
 	next = qd->transactions;
 	while( (curr = next) ) {
 		next = curr->next;
 		trans = (qq_transaction *) (curr->data);
-		/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
-		
+		/* purple_debug_info("QQ_TRANS", "Scan [%d]\n", trans->seq); */
+
 		if (trans->flag & QQ_TRANS_BEFORE_LOGIN) {
 			/* keep server cmd before login*/
 			continue;
@@ -270,67 +308,60 @@ gboolean qq_trans_scan(qq_data *qd)
 
 		if (trans->rcved_times > 0) {
 			/* Has been received */
-			trans_remove(qd, trans);
+			trans_remove(gc, trans);
 			continue;
 		}
 
 		if (trans->flag & QQ_TRANS_IS_SERVER) {
 			continue;
 		}
-		
+
 		/* Never get reply */
 		trans->send_retries--;
 		if (trans->send_retries <= 0) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ_TRANS",
+			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) {
+			if (trans->flag & QQ_TRANS_IS_IMPORT) {
 				return TRUE;
 			}
 
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+			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);
+			trans_remove(gc, trans);
 			continue;
 		}
 
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+		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);
+		qq_send_cmd_encrypted(gc, trans->cmd, trans->seq, trans->data, trans->data_len, FALSE);
 	}
 
-	/* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+	/* purple_debug_info("QQ_TRANS", "Scan finished\n"); */
 	return FALSE;
 }
 
 /* clean up send trans and free all contents */
-void qq_trans_remove_all(qq_data *qd)
+void qq_trans_remove_all(PurpleConnection *gc)
 {
-	GList *curr;
-	GList *next;
+	qq_data *qd = (qq_data *)gc->proto_data;
 	qq_transaction *trans;
 	gint count = 0;
 
-	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);
+	while(qd->transactions != NULL) {
+		trans = (qq_transaction *) (qd->transactions->data);
+		qd->transactions = g_list_remove(qd->transactions, trans);
 
+		if (trans->data)	g_free(trans->data);
+		g_free(trans);
+
 		count++;
-		curr = next;
 	}
-	g_list_free(qd->transactions);
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Free all %d packets\n", count);
+	if (count > 0) {
+		purple_debug_info("QQ_TRANS", "Free all %d packets\n", count);
+	}
 }
============================================================
--- libpurple/protocols/qq/qq_trans.h	f4ace6ce706004259974c5198b24d9ac5daaa541
+++ libpurple/protocols/qq/qq_trans.h	f2aa20b1890c075f7279612c60de46c2565f10f2
@@ -28,44 +28,27 @@
 #include <glib.h>
 #include "qq.h"
 
-enum {
-	QQ_TRANS_IS_SERVER = 0x01,			/* Is server command or client command */
-	/* prefix QQ_TRANS_CLI is for client command*/
-	QQ_TRANS_CLI_EMERGE = 0x02,		/* send at once; or may wait for next reply*/
-	QQ_TRANS_CLI_IMPORT = 0x04,		/* Only notice if not get reply; or resend, disconn if reties get 0*/
-	QQ_TRANS_BEFORE_LOGIN = 0x08,	/* server command before login*/
-};
+typedef struct _qq_transaction qq_transaction;
 
-typedef struct _qq_transaction {
-	guint8 flag;
-	guint16 seq;
-	guint16 cmd;
-
-	guint8 room_cmd;
-	guint32 room_id;
-	
-	guint8 *data;
-	gint data_len;
-
-	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);
+qq_transaction *qq_trans_find_rcved(PurpleConnection *gc, guint16 cmd, guint16 seq);
 gboolean qq_trans_is_server(qq_transaction *trans) ;
 gboolean qq_trans_is_dup(qq_transaction *trans);
 guint8 qq_trans_get_room_cmd(qq_transaction *trans);
 guint32 qq_trans_get_room_id(qq_transaction *trans);
+gint qq_trans_get_class(qq_transaction *trans);
+gint qq_trans_get_ship(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_add_room_cmd(qq_data *qd, guint16 seq, guint8 room_cmd, guint32 room_id,
+void qq_trans_add_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
+		guint8 *data, gint data_len, gint update_class, guint32 ship32);
+void qq_trans_add_room_cmd(PurpleConnection *gc,
+		guint16 seq, guint8 room_cmd, guint32 room_id,
+		guint8 *data, gint data_len, gint update_class, guint32 ship32);
+
+void qq_trans_add_server_cmd(PurpleConnection *gc, 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);
+void qq_trans_process_before_login(PurpleConnection *gc);
+gboolean qq_trans_scan(PurpleConnection *gc);
+void qq_trans_remove_all(PurpleConnection *gc);
 
 #endif
============================================================
--- libpurple/protocols/qq/send_file.c	d8145843c01450a9064f4b31328ad9ca7761d1d7
+++ libpurple/protocols/qq/send_file.c	0377a21744db2dc5edfd2fb7d9a4c427a3e68250
@@ -54,7 +54,7 @@ static int _qq_in_same_lan(ft_info *info
 static int _qq_in_same_lan(ft_info *info)
 {
 	if (info->remote_internet_ip == info->local_internet_ip) return 1;
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+	purple_debug_info("QQ", 
 			"Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n",  
 			info->remote_internet_ip
 			, info->local_internet_ip);
@@ -87,7 +87,7 @@ static ssize_t _qq_xfer_udp_recv(guint8 
 	info = (ft_info *) xfer->data;
 	sinlen = sizeof(sin);
 	r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+	purple_debug_info("QQ", 
 			"==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n",
 			r, inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
 	return r;
@@ -121,12 +121,12 @@ static ssize_t _qq_xfer_udp_send(const g
 		sin.sin_port = g_htons(info->remote_minor_port);
 		sin.sin_addr.s_addr = g_htonl(info->remote_real_ip);
 	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending to channel: %d.%d.%d.%d:%d\n",
-			(int)sin.sin_addr.s_addr & 0xff,
-			(int)(sin.sin_addr.s_addr >> 8) & 0xff,
-			(int)(sin.sin_addr.s_addr >> 16) & 0xff,
-			(int)sin.sin_addr.s_addr >> 24,
-			(int)g_ntohs(sin.sin_port)
+	purple_debug_info("QQ", "sending to channel: %d.%d.%d.%d:%d\n",
+			sin.sin_addr.s_addr & 0xff,
+			(sin.sin_addr.s_addr >> 8) & 0xff,
+			(sin.sin_addr.s_addr >> 16) & 0xff,
+			sin.sin_addr.s_addr >> 24,
+			g_ntohs(sin.sin_port)
 		  );
 	return sendto(info->sender_fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(sin));
 }
@@ -207,20 +207,20 @@ static void _qq_xfer_end(PurpleXfer *xfe
 	qq_xfer_close_file(xfer);
 	if (info->dest_fp != NULL) {
 		fclose(info->dest_fp);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "file closed\n");
+		purple_debug_info("QQ", "file closed\n");
 	}
 	if (info->major_fd != 0) {
 		close(info->major_fd);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "major port closed\n");
+		purple_debug_info("QQ", "major port closed\n");
 	}
 	if (info->minor_fd != 0) {
 		close(info->minor_fd);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "minor port closed\n");
+		purple_debug_info("QQ", "minor port closed\n");
 	}
 	/*
 	if (info->buffer != NULL) {
 		munmap(info->buffer, purple_xfer_get_size(xfer));
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "file mapping buffer is freed.\n");
+		purple_debug_info("QQ", "file mapping buffer is freed.\n");
 	}
 	*/
 	g_free(info);
@@ -235,7 +235,7 @@ static void qq_show_conn_info(ft_info *i
 	real_ip_str = gen_ip_str((guint8 *) &ip);
 	ip = g_htonl(info->remote_internet_ip);
 	internet_ip_str = gen_ip_str((guint8 *) &ip);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "remote internet ip[%s:%d], major port[%d], real ip[%s], minor port[%d]\n",
+	purple_debug_info("QQ", "remote internet ip[%s:%d], major port[%d], real ip[%s], minor port[%d]\n",
 			internet_ip_str, info->remote_internet_port,
 			info->remote_major_port, real_ip_str, info->remote_minor_port
 		  );
@@ -393,7 +393,7 @@ static void _qq_xfer_init_socket(PurpleX
 	info->local_real_ip = 0x7f000001;
 	*/
 	info->local_real_ip = g_ntohl(inet_addr(purple_network_get_my_ip(-1)));
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "local real ip is %x", info->local_real_ip);
+	purple_debug_info("QQ", "local real ip is %x", info->local_real_ip);
 
 	for (i = 0; i < 2; i++) {
 		sockfd = socket(PF_INET, SOCK_DGRAM, 0);
@@ -412,13 +412,13 @@ static void _qq_xfer_init_socket(PurpleX
 			case 0:
 				info->local_major_port = listen_port;
 				info->major_fd = sockfd;
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Major Channel created on port[%d]\n",
+				purple_debug_info("QQ", "UDP Major Channel created on port[%d]\n",
 						info->local_major_port);
 				break;
 			case 1:
 				info->local_minor_port = listen_port;
 				info->minor_fd = sockfd;
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Minor Channel created on port[%d]\n",
+				purple_debug_info("QQ", "UDP Minor Channel created on port[%d]\n",
 						info->local_minor_port);
 				break;
 		}
@@ -475,9 +475,9 @@ static void _qq_send_packet_file_request
 	bytes += qq_putdata (raw_data + bytes, (guint8 *) filelen_str, filelen_strlen);
 
 	if (packet_len == bytes)
-		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
+		qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
-		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request",
+		purple_debug_info("qq_send_packet_file_request",
 			    "%d bytes expected but got %d bytes\n",
 			    packet_len, bytes);
 
@@ -497,7 +497,7 @@ static void _qq_send_packet_file_accept(
 	qd = (qq_data *) gc->proto_data;
 	info = (ft_info *) qd->xfer->data;
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "I've accepted the file transfer request from %d\n", to_uid);
+	purple_debug_info("QQ", "I've accepted the file transfer request from %d\n", to_uid);
 	_qq_xfer_init_socket(qd->xfer);
 
 	packet_len = 79;
@@ -516,9 +516,9 @@ static void _qq_send_packet_file_accept(
 	info->local_real_ip = real_ip;
 
 	if (packet_len == bytes)
-		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
+		qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
-		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept",
+		purple_debug_info("qq_send_packet_file_accept",
 			    "%d bytes expected but got %d bytes\n",
 			    packet_len, bytes);
 }
@@ -539,13 +539,13 @@ static void _qq_send_packet_file_notifyi
 	raw_data = g_newa (guint8, packet_len);
 	bytes = 0;
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== sending qq file notify ip packet\n");
+	purple_debug_info("QQ", "<== sending qq file notify ip packet\n");
 	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE);
 	bytes += qq_fill_conn_info(raw_data + bytes, info);
 	if (packet_len == bytes)
-		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
+		qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
-		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify",
+		purple_debug_info("qq_send_packet_file_notify",
 			    "%d bytes expected but got %d bytes\n",
 			    packet_len, bytes);
 
@@ -561,7 +561,7 @@ static void _qq_send_packet_file_reject 
 	guint8 *raw_data;
 	gint packet_len, bytes;
 
-	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start");
+	purple_debug_info("_qq_send_packet_file_reject", "start");
 	qd = (qq_data *) gc->proto_data;
 
 	packet_len = 64;
@@ -571,9 +571,9 @@ static void _qq_send_packet_file_reject 
 	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE);
 
 	if (packet_len == bytes)
-		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
+		qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
-		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
+		purple_debug_info("qq_send_packet_file",
 			    "%d bytes expected but got %d bytes\n",
 			    packet_len, bytes);
 }
@@ -585,27 +585,27 @@ static void _qq_send_packet_file_cancel 
 	guint8 *raw_data;
 	gint packet_len, bytes;
 
-	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n");
+	purple_debug_info("_qq_send_packet_file_cancel", "start\n");
 	qd = (qq_data *) gc->proto_data;
 
 	packet_len = 64;
 	raw_data = g_newa (guint8, packet_len);
 	bytes = 0;
 
-	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n");
+	purple_debug_info("_qq_send_packet_file_cancel", "before create header\n");
 	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE);
-	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n");
+	purple_debug_info("_qq_send_packet_file_cancel", "end create header\n");
 
 	if (packet_len == bytes) {
-		purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n");
-		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
+		purple_debug_info("_qq_send_packet_file_cancel", "before send cmd\n");
+		qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
 	}
 	else
-		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
+		purple_debug_info("qq_send_packet_file",
 			    "%d bytes expected but got %d bytes\n",
 			    packet_len, bytes);
 
-	purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_cancel", "end\n");
+	purple_debug_info("qq_send_packet_file_cancel", "end\n");
 }
 
 /* request to send a file */
@@ -694,7 +694,7 @@ void qq_process_recv_file_reject (guint8
 
 	/*	border has been checked before
 	if (*cursor >= (data + data_len - 1)) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
+		purple_debug_warning("QQ",
 			    "Received file reject message is empty\n");
 		return;
 	}
@@ -724,8 +724,7 @@ void qq_process_recv_file_cancel (guint8
 
 	/*	border has been checked before
 	if (*cursor >= (data + data_len - 1)) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received file reject message is empty\n");
+		purple_debug_warning("QQ", "Received file reject message is empty\n");
 		return;
 	}
 	*/
@@ -755,8 +754,7 @@ void qq_process_recv_file_accept(guint8 
 	info = (ft_info *) qd->xfer->data;
 
 	if (data_len <= 30 + QQ_CONN_INFO_LEN) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received file reject message is empty\n");
+		purple_debug_warning("QQ", "Received file reject message is empty\n");
 		return;
 	}
 
@@ -789,8 +787,7 @@ void qq_process_recv_file_request(guint8
 	info->to_uid = sender_uid;
 	
 	if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received file request message is empty\n");
+		purple_debug_warning("QQ", "Received file request message is empty\n");
 		return;
 	}
 	bytes = 0;
@@ -806,7 +803,7 @@ void qq_process_recv_file_request(guint8
 
 	/* FACE from IP detector, ignored by gfhuang */
 	if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+		purple_debug_warning("QQ",
 			    "Received a FACE ip detect from qq-%d, so he/she must be online :)\n", sender_uid);
 
 		b = purple_find_buddy(gc->account, sender_name);
@@ -826,11 +823,11 @@ void qq_process_recv_file_request(guint8
 				qq_update_buddy_contact(gc, q_bud);
 			}
 			else 
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "buddy %d is already online\n", sender_uid);
+				purple_debug_info("QQ", "buddy %d is already online\n", sender_uid);
 
 		}
 		else 
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in list\n", sender_uid);
+			purple_debug_warning("QQ", "buddy %d is not in list\n", sender_uid);
 
 		g_free(sender_name);	    
 		g_strfreev(fileinfo);
@@ -892,8 +889,7 @@ void qq_process_recv_file_notify(guint8 
 	xfer = qd->xfer;
 	info = (ft_info *) qd->xfer->data;
 	if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received file notify message is empty\n");
+		purple_debug_warning("QQ", "Received file notify message is empty\n");
 		return;
 	}
 	
============================================================
--- libpurple/protocols/qq/sys_msg.c	6e80379b474adc9cdf0823a8a8985f65678fdc0c
+++ libpurple/protocols/qq/sys_msg.c	b727ae40ad2e341625c4a13fa5405afc921bbe96
@@ -126,7 +126,7 @@ static void _qq_send_packet_ack_msg_sys(
 	gint ack_len, bytes;
 
 	qd = (qq_data *) gc->proto_data;
-	
+
 	str = g_strdup_printf("%d", from);
 	bar = 0x1e;
 	ack_len = 1 + 1 + strlen(str) + 1 + 2;
@@ -142,9 +142,9 @@ static void _qq_send_packet_ack_msg_sys(
 	g_free(str);
 
 	if (bytes == ack_len)	/* creation OK */
-		qq_send_cmd_detail(qd, QQ_CMD_ACK_SYS_MSG, 0, FALSE, ack, ack_len);
+		qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, ack, ack_len);
 	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+		purple_debug_error("QQ",
 			   "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
 }
 
@@ -194,8 +194,8 @@ static void _qq_process_msg_sys_add_cont
 
 	g_return_if_fail(from != NULL && to != NULL);
 
-	message = g_strdup_printf(_("User %s rejected your request"), from);
-	reason = g_strdup_printf(_("Reason: %s"), msg_utf8);
+	message = g_strdup_printf(_("Requestion rejected by %s"), from);
+	reason = g_strdup_printf(_("Message: %s"), msg_utf8);
 	_qq_sys_msg_log_write(gc, message, from);
 
 	purple_notify_info(gc, NULL, message, reason);
@@ -214,7 +214,7 @@ static void _qq_process_msg_sys_add_cont
 	qd = (qq_data *) gc->proto_data;
 	qq_add_buddy_by_recv_packet(gc, strtol(from, NULL, 10), TRUE, TRUE);
 
-	message = g_strdup_printf(_("User %s approved your request"), from);
+	message = g_strdup_printf(_("Requestion approved by %s"), from);
 	_qq_sys_msg_log_write(gc, message, from);
 	purple_notify_info(gc, NULL, message, NULL);
 
@@ -263,9 +263,9 @@ static void _qq_process_msg_sys_add_cont
 		g2 = g_new0(gc_and_uid, 1);
 		g2->gc = gc;
 		g2->uid = strtol(from, NULL, 10);
-		message = g_strdup_printf(_("%s is not in your buddy list"), from);
+		message = g_strdup_printf(_("%s is not in buddy list"), from);
 		purple_request_action(gc, NULL, message,
-				    _("Would you like to add him?"), PURPLE_DEFAULT_ACTION_NONE,
+				    _("Would you add?"), PURPLE_DEFAULT_ACTION_NONE,
 					purple_connection_get_account(gc), name, NULL,
 					g2, 3,
 					_("Cancel"), NULL,
@@ -279,14 +279,19 @@ static void _qq_process_msg_sys_notice(P
 
 static void _qq_process_msg_sys_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
 {
+	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar *title, *content;
 
 	g_return_if_fail(from != NULL && to != NULL);
 
-	title = g_strdup_printf(_("Notice from: %s"), from);
+	title = g_strdup_printf(_("QQ Server Notice from %s:"), from);
 	content = g_strdup_printf(_("%s"), msg_utf8);
 
-	purple_notify_info(gc, NULL, title, content);
+	if (qd->is_show_notice) {
+		purple_notify_info(gc, NULL, title, content);
+	} else {
+		purple_debug_info("QQ", "Server notice from %s:\n%s", from, msg_utf8);
+}
 	g_free(title);
 	g_free(content);
 }
@@ -310,12 +315,19 @@ void qq_process_msg_sys(guint8 *data, gi
 	_qq_send_packet_ack_msg_sys(gc, code[0], strtol(from, NULL, 10), seq);
 
 	if (strtol(to, NULL, 10) != qd->uid) {	/* not to me */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Recv sys msg to [%s], not me!, discard\n", to);
+		purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
 		g_strfreev(segments);
 		return;
 	}
 
 	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+	if (from == NULL && msg_utf8) {
+		purple_debug_error("QQ", "Recv NULL sys msg to [%s], discard\n", to);
+		g_strfreev(segments);
+		g_free(msg_utf8);
+		return;
+	}
+
 	switch (strtol(code, NULL, 10)) {
 	case QQ_MSG_SYS_BEING_ADDED:
 		_qq_process_msg_sys_being_added(gc, from, to, msg_utf8);
@@ -333,12 +345,12 @@ void qq_process_msg_sys(guint8 *data, gi
 		_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
 		break;
 	case QQ_MSG_SYS_NEW_VERSION:
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+		purple_debug_warning("QQ",
 			   "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);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "the msg is : %s\n", msg_utf8);
+		purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", code);
+		purple_debug_warning("QQ", "the msg is : %s\n", msg_utf8);
 	}
 	g_free(msg_utf8);
 	g_strfreev(segments);
============================================================
--- libpurple/protocols/qq/utils.c	034fd3fc8d7e12ad0491c8e5b6bc0799c1d2a10b
+++ libpurple/protocols/qq/utils.c	539d1c7f385eae718d39ad78e11d7367da291373
@@ -47,8 +47,8 @@
    struct sockaddr_in sin;
    socklen_t len = sizeof(sin);
    getsockname(fd, (struct sockaddr *)&sin, &len);
-   purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n",
-   inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
+   purple_debug_info(desc, "%s:%d\n",
+			inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
    }
    */
 
@@ -121,16 +121,16 @@ gchar **split_data(guint8 *data, gint le
 	for (i = 0; segments[i] != NULL; i++) {;
 	}
 	if (i < expected_fields) {	/* not enough fields */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid data, expect %d fields, found only %d, discard\n", expected_fields, i);
+		purple_debug_error("QQ", "Invalid data, expect %d fields, found only %d, discard\n",
+				expected_fields, i);
 		g_strfreev(segments);
 		return NULL;
 	} else if (i > expected_fields) {	/* more fields, OK */
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Dangerous data, expect %d fields, found %d, return all\n", expected_fields, i);
+		purple_debug_warning("QQ", "Dangerous data, expect %d fields, found %d, return all\n",
+				expected_fields, i);
 		/* free up those not used */
 		for (j = expected_fields; j < i; j++) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "field[%d] is %s\n", j, segments[j]);
+			purple_debug_warning("QQ", "field[%d] is %s\n", j, segments[j]);
 			g_free(segments[j]);
 		}
 
@@ -218,7 +218,7 @@ gchar* try_dump_as_gbk(const guint8 *con
 	msg_utf8 = i < len ? qq_to_utf8((gchar *) &incoming[i], QQ_CHARSET_DEFAULT) : NULL;
 
 	if (msg_utf8 != NULL) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8);
+		purple_debug_warning("QQ", "Try extract GB msg: %s\n", msg_utf8);
 	}
 	return msg_utf8;
 }
@@ -257,7 +257,7 @@ guint8 *hex_str_to_bytes(const gchar *co
 	hex_buffer = strstrip(buffer);
 
 	if (strlen(hex_buffer) % 2 != 0) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+		purple_debug_warning("QQ",
 			"Unable to convert an odd number of nibbles to a string of bytes!\n");
 		g_free(hex_buffer);
 		return NULL;
@@ -272,8 +272,8 @@ guint8 *hex_str_to_bytes(const gchar *co
 		} else if (g_ascii_isalpha(*cursor) && (gint) *cursor - 87 < 16) {
 			nibble1 = (gint) *cursor - 87;
 		} else {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				"Invalid char \'%c\' found in hex string!\n", *cursor);
+			purple_debug_warning("QQ", "Invalid char \'%c\' found in hex string!\n",
+					*cursor);
 			g_free(hex_str);
 			return NULL;
 		}
@@ -284,8 +284,7 @@ guint8 *hex_str_to_bytes(const gchar *co
 		} else if (g_ascii_isalpha(*cursor) && (gint) (*cursor - 87) < 16) {
 			nibble2 = (gint) *cursor - 87;
 		} else {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				"Invalid char found in hex string!\n");
+			purple_debug_warning("QQ", "Invalid char found in hex string!\n");
 			g_free(hex_str);
 			return NULL;
 		}
@@ -362,22 +361,7 @@ void qq_show_packet(const gchar *desc, c
 
 void qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
 {
-	/*
-	   char buf1[8*len+2], buf2[10];
-	   int i;
-	   buf1[0] = 0;
-	   for (i = 0; i < len; i++) {
-	   sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff);
-	   strcat(buf1, buf2);
-	   }
-	   strcat(buf1, "\n");
-	   purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1);
-	   */
-
-	/* modified by s3e, 20080424 */
-	qq_hex_dump(PURPLE_DEBUG_INFO, desc,
-		buf, len,
-		"");
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", buf, len, desc);
 }
 
 /* convert face num from packet (0-299) to local face (1-100) */
@@ -397,5 +381,16 @@ const char *qq_buddy_icon_dir(void)
 	if (purple_prefs_exists("/prpl/qq/buddy_icon_dir"))
 		return purple_prefs_get_string("/prpl/qq/buddy_icon_dir");
 	else
-		return NULL;
+		return QQ_BUDDY_ICON_DIR;
 }
+
+#ifdef _WIN32
+const char *qq_win32_buddy_icon_dir(void)
+{
+        static char *dir = NULL;
+        if (dir == NULL)
+                dir = g_build_filename(wpurple_install_dir(), "pixmaps",
+                        "purple", "buddy_icons", "qq", NULL);
+        return dir;
+}
+#endif
============================================================
--- libpurple/protocols/qq/utils.h	fe344b9710669a7efae22d9cc4180d2ae7423150
+++ libpurple/protocols/qq/utils.h	a391181cf3622cac8ce340dc75b39779c362456b
@@ -56,5 +56,6 @@ const gchar *qq_buddy_icon_dir(void);
 guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len);
 
 const gchar *qq_buddy_icon_dir(void);
+const gchar *qq_win32_buddy_icon_dir(void);
 
 #endif


More information about the Commits mailing list