changeset 23760:b00579ec01be

merge of '4f30170cef563ecf059bc5e798c3bd75d01247e7' and 'a9002d08e75602ce9bcf87e60a05f2b0c7862608'
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Sun, 10 Aug 2008 22:59:07 +0000
parents 019d5ae3ad72 (current diff) 80ce5ca985c2 (diff)
children 86bef5b06f70
files libpurple/protocols/qq/crypt.c libpurple/protocols/qq/crypt.h libpurple/protocols/qq/group_network.c libpurple/protocols/qq/group_network.h
diffstat 52 files changed, 1627 insertions(+), 1773 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Sun Aug 10 22:59:07 2008 +0000
@@ -1,3 +1,22 @@
+2008.08.10 - csyfek <csyfek(at)gmail.com>
+	* Commit to Pidgin
+
+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
+	* Canceled managing glist of group packet, add sub_cmdd and room_id  to transaction
+	* Fixed error of demo group:
+		If 'room list' and 'room infor' are not setup, response received from server will emits 'room_id = 0' packet.
+
+2008.08.04 - ccpaging <ecc_hy(at)hotmail.com>
+	* Use new crypt/decrypt functions
+	* Rename crypt.c/h to qq_crypt.c/h
+	* Clean code of decrypt functions
+	* Fixed decryption failure
+
+2008.08.04 - csyfek <csyfek(at)gmail.com>
+	* Update AUTHORS
+
 2008.08.03 - csyfek <csyfek(at)gmail.com>
 	* Commit lost files to Pidgin
 
--- a/libpurple/protocols/qq/Makefile.am	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Sun Aug 10 22:59:07 2008 +0000
@@ -12,8 +12,8 @@
 	buddy_opt.h \
 	char_conv.c \
 	char_conv.h \
-	crypt.c \
-	crypt.h \
+	qq_crypt.c \
+	qq_crypt.h \
 	file_trans.c \
 	file_trans.h \
 	group.c \
@@ -32,8 +32,6 @@
 	group_info.h \
 	group_join.c \
 	group_join.h \
-	group_network.c \
-	group_network.h \
 	group_opt.c \
 	group_opt.h \
 	group_search.c \
--- a/libpurple/protocols/qq/Makefile.mingw	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Sun Aug 10 22:59:07 2008 +0000
@@ -43,7 +43,7 @@
 	buddy_list.c \
 	buddy_opt.c \
 	char_conv.c \
-	crypt.c \
+	qq_crypt.c \
 	file_trans.c \
 	group.c \
 	group_conv.c \
@@ -53,7 +53,6 @@
 	group_im.c \
 	group_info.c \
 	group_join.c \
-	group_network.c \
 	group_opt.c \
 	group_search.c \
 	header_info.c \
--- a/libpurple/protocols/qq/buddy_info.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Sun Aug 10 22:59:07 2008 +0000
@@ -32,7 +32,6 @@
 #include "buddy_list.h"
 #include "buddy_info.h"
 #include "char_conv.h"
-#include "crypt.h"
 #include "header_info.h"
 #include "qq_base.h"
 #include "qq_network.h"
@@ -688,26 +687,18 @@
 }
 
 /* process the reply of modify_info packet */
-void qq_process_modify_info_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+void qq_process_modify_info_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len;
-	guint8 *data;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		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);
-		}
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt modify info reply\n");
+	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);
 	}
 }
 
@@ -858,10 +849,8 @@
 }
 
 /* process reply to get_info packet */
-void qq_process_get_info_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+void qq_process_get_info_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
-	gint len;
-	guint8 *data;
 	gchar **segments;
 	qq_info_query *query;
 	qq_data *qd;
@@ -869,52 +858,46 @@
 	GList *list, *query_list;
 	PurpleNotifyUserInfo *user_info;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
 	list = query_list = NULL;
-	len = buf_len;
-	data = g_newa(guint8, len);
 	info = NULL;
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		if (NULL == (segments = split_data(data, len, "\x1e", QQ_CONTACT_FIELDS)))
-			return;
+	if (NULL == (segments = split_data(data, data_len, "\x1e", QQ_CONTACT_FIELDS)))
+		return;
 
-		info = (contact_info *) segments;
-		if (qd->modifying_face && strtol(info->face, NULL, 10) != qd->my_icon) {
-			gchar *icon = g_strdup_printf("%d", qd->my_icon);
-			qd->modifying_face = FALSE;
-			g_free(info->face);
-			info->face = icon;
-			qq_send_packet_modify_info(gc, (contact_info *)segments);
-		}
+	info = (contact_info *) segments;
+	if (qd->modifying_face && strtol(info->face, NULL, 10) != qd->my_icon) {
+		gchar *icon = g_strdup_printf("%d", qd->my_icon);
+		qd->modifying_face = FALSE;
+		g_free(info->face);
+		info->face = icon;
+		qq_send_packet_modify_info(gc, (contact_info *)segments);
+	}
 
-		qq_refresh_buddy_and_myself(info, gc);
+	qq_refresh_buddy_and_myself(info, gc);
 
-		query_list = qd->info_query;
-		/* ensure we're processing the right query */
-		while (query_list) {
-			query = (qq_info_query *) query_list->data;
-			if (query->uid == atoi(info->uid)) {
-				if (query->show_window) {
-					user_info = info_to_notify_user_info(info);
-					purple_notify_userinfo(gc, info->uid, user_info, NULL, NULL);
-					purple_notify_user_info_destroy(user_info);
-				} else if (query->modify_info) {
-					create_modify_info_dialogue(gc, info);
-				}
-				qd->info_query = g_list_remove(qd->info_query, qd->info_query->data);
-				g_free(query);
-				break;
+	query_list = qd->info_query;
+	/* ensure we're processing the right query */
+	while (query_list) {
+		query = (qq_info_query *) query_list->data;
+		if (query->uid == atoi(info->uid)) {
+			if (query->show_window) {
+				user_info = info_to_notify_user_info(info);
+				purple_notify_userinfo(gc, info->uid, user_info, NULL, NULL);
+				purple_notify_user_info_destroy(user_info);
+			} else if (query->modify_info) {
+				create_modify_info_dialogue(gc, info);
 			}
-			query_list = query_list->next;
+			qd->info_query = g_list_remove(qd->info_query, qd->info_query->data);
+			g_free(query);
+			break;
 		}
+		query_list = query_list->next;
+	}
 
-		g_strfreev(segments);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt get info reply\n");
-	}
+	g_strfreev(segments);
 }
 
 void qq_info_query_free(qq_data *qd)
@@ -975,25 +958,18 @@
 	qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
 }
 
-void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+void qq_process_get_level_reply(guint8 *decr_buf, gint decr_len, PurpleConnection *gc)
 {
 	guint32 uid, onlineTime;
 	guint16 level, timeRemainder;
 	gchar *purple_name;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
-	gint decr_len, i;
-	guint8 *decr_buf;
+	gint i;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes = 0;
 
-	decr_len = buf_len;
-	decr_buf = g_new0(guint8, buf_len);
-	if (!qq_decrypt(buf, buf_len, qd->session_key, decr_buf, &decr_len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Couldn't decrypt get level packet\n");
-	}
-
 	decr_len--; 
 	if (decr_len % 12 != 0) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
@@ -1014,25 +990,34 @@
 		purple_debug(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);
+			continue;
+		}
+
 		purple_name = uid_to_purple_name(uid);
+		if (purple_name == NULL) {
+			continue;
+		}
+		
 		b = purple_find_buddy(account, purple_name);
-		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+		g_free(purple_name);
 
-		if (q_bud != NULL || uid == qd->uid) {
-			if (q_bud) {
-				q_bud->onlineTime = onlineTime;
-				q_bud->level = level;
-				q_bud->timeRemainder = timeRemainder;
-			}
-			if (uid == qd->uid) {
-				qd->my_level = level;
-			}
-		} else {
+		q_bud = NULL;
+		if (b != NULL) {
+			q_bud = (qq_buddy *) b->proto_data;
+		}
+		
+		if (q_bud == NULL) {
 			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"Got an online buddy %d, but not in my buddy list\n", uid);
+					"Got levels of %d not in my buddy list\n", uid);
+			continue;
 		}
-		g_free(purple_name);
+
+		q_bud->onlineTime = onlineTime;
+		q_bud->level = level;
+		q_bud->timeRemainder = timeRemainder;
 	}
-	g_free(decr_buf);
 }
 
--- a/libpurple/protocols/qq/buddy_info.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Sun Aug 10 22:59:07 2008 +0000
@@ -71,8 +71,8 @@
 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);
-void qq_process_modify_info_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_get_info_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+void qq_process_modify_info_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+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);
--- a/libpurple/protocols/qq/buddy_list.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Sun Aug 10 22:59:07 2008 +0000
@@ -34,7 +34,6 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
-#include "crypt.h"
 #include "header_info.h"
 #include "qq_base.h"
 #include "group.h"
@@ -103,7 +102,7 @@
 }
 
 /* get all list, buddies & Quns with groupsid support */
-void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position)
+void qq_send_packet_get_buddies_and_rooms(PurpleConnection *gc, guint32 position)
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
 	guint8 raw_data[16] = {0};
@@ -117,7 +116,7 @@
 	bytes += qq_put32(raw_data + bytes, 0x00000000);
 	bytes += qq_put32(raw_data + bytes, position);
 
-	qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes);
+	qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_AND_ROOMS, raw_data, bytes);
 }
 
 /* parse the data into qq_buddy_status */
@@ -158,28 +157,20 @@
 #define QQ_ONLINE_BUDDY_ENTRY_LEN       38
 
 /* process the reply packet for get_buddies_online packet */
-guint8 qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint8 qq_process_get_buddies_online_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len, bytes, bytes_buddy;
+	gint bytes, bytes_buddy;
 	gint count;
-	guint8 *data, position;
+	guint8  position;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	qq_buddy_online bo;
+	gchar *purple_name;
 
-	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+	g_return_val_if_fail(data != NULL && data_len != 0, -1);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n");
-
-	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
-		return -1;
-	}
 
 	/* qq_show_packet("Get buddies online reply packet", data, len); */
 
@@ -187,11 +178,11 @@
 	bytes += qq_get8(&position, data + bytes);
 
 	count = 0;
-	while (bytes < len) {
-		if (len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) {
+	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", 
-					(len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
+					(data_len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
 			break;
 		}
 		memset(&bo, 0 ,sizeof(bo));
@@ -219,9 +210,22 @@
 			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);
+			continue;
+		}
+
 		/* update buddy information */
-		b = purple_find_buddy(purple_connection_get_account(gc), 
-												uid_to_purple_name(bo.bs.uid) );
+		purple_name = uid_to_purple_name(bo.bs.uid);
+		if (purple_name == NULL) {
+			purple_debug(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", 
@@ -242,7 +246,7 @@
 		count++;
 	}
 
-	if(bytes > len) {
+	if(bytes > data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
 				"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
 	}
@@ -254,32 +258,31 @@
 
 
 /* process reply for get_buddies_list */
-guint16 qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	qq_buddy *q_bud;
-	gint len, bytes_expected, count;
+	gint bytes_expected, count;
 	gint bytes, buddy_bytes;
 	guint16 position, unknown;
-	guint8 *data, pascal_len;
+	guint8 pascal_len;
 	gchar *name;
 	PurpleBuddy *b;
 
-	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+	g_return_val_if_fail(data != NULL && data_len != 0, -1);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+	if (data_len <= 2) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "empty buddies list");
 		return -1;
 	}
+	/* qq_show_packet("QQ get buddies list", data, data_len); */
 	bytes = 0;
 	bytes += qq_get16(&position, data + bytes);
 	/* the following data is buddy list in this packet */
 	count = 0;
-	while (bytes < len) {
+	while (bytes < data_len) {
 		q_bud = g_new0(qq_buddy, 1);
 		/* set flag */
 		buddy_bytes = bytes;
@@ -311,11 +314,11 @@
 			count++;
 		}
 
-		if (QQ_DEBUG) {
-			purple_debug(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);
-		}
+#if 1
+		purple_debug(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
 
 		name = uid_to_purple_name(q_bud->uid);
 		b = purple_find_buddy(gc->account, name);
@@ -330,7 +333,7 @@
 		qq_update_buddy_contact(gc, q_bud);
 	}
 
-	if(bytes > len) {
+	if(bytes > data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
 				"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
 	}
@@ -340,36 +343,29 @@
 	return position;
 }
 
-guint32 qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len, i, j;
-	gint bytes = 0;
-	guint8 *data;
+	gint i, j;
+	gint bytes;
 	guint8 sub_cmd, reply_code;
 	guint32 unknown, position;
 	guint32 uid;
 	guint8 type, groupid;
 	qq_group *group;
 
-	g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+	g_return_val_if_fail(data != NULL && data_len != 0, -1);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
-		return -1;
-	}
-
+	bytes = 0;
 	bytes += qq_get8(&sub_cmd, data + bytes);
 	g_return_val_if_fail(sub_cmd == 0x01, -1);
 
 	bytes += qq_get8(&reply_code, data + bytes);
 	if(0 != reply_code) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-				"Get all list with group reply, reply_code(%d) is not zero", reply_code);
+				"qq_process_get_buddies_and_rooms, %d", reply_code);
 	}
 
 	bytes += qq_get32(&unknown, data + bytes);
@@ -377,7 +373,7 @@
 	/* the following data is all list in this packet */
 	i = 0;
 	j = 0;
-	while (bytes < len) {
+	while (bytes < data_len) {
 		/* 00-03: uid */
 		bytes += qq_get32(&uid, data + bytes);
 		/* 04: type 0x1:buddy 0x4:Qun */
@@ -398,24 +394,24 @@
 			 * qq_send_packet_get_buddies_list */
 			++i;
 		} else { /* a group */
-			group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID);
+			group = qq_room_search_id(gc, uid);
 			if(group == NULL) {
+				purple_debug(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);
-				group = g_newa(qq_group, 1);
-				group->internal_group_id = uid;
-				qq_send_cmd_group_get_group_info(gc, group);
+				qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, uid);
 			} else {
 				group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
 				qq_group_refresh(gc, group);
-				qq_send_cmd_group_get_group_info(gc, group);
+				qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
 			}
 			++j;
 		}
 	}
 
-	if(bytes > len) {
+	if(bytes > data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-				"qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
+				"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);
@@ -505,26 +501,19 @@
 }
 
 /* parse the reply packet for change_status */
-void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len, bytes;
-	guint8 *data, reply;
+	gint bytes;
+	guint8 reply;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	gchar *name;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
-		return;
-	}
-
+	
 	bytes = 0;
 	bytes = qq_get8(&reply, data + bytes);
 	if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
@@ -543,28 +532,19 @@
 }
 
 /* it is a server message indicating that one of my buddies has changed its status */
-void qq_process_buddy_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc) 
+void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc) 
 {
 	qq_data *qd;
 	gint bytes;
 	guint32 my_uid;
-	guint8 *data;
-	gint data_len;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	qq_buddy_status bs;
 	gchar *name;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	data_len = buf_len;
-	data = g_newa(guint8, data_len);
-
-	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len) ) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] Failed decrypt\n");
-		return;
-	}
 
 	if (data_len < 35) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] only %d, need 35 bytes\n", data_len);
@@ -606,18 +586,22 @@
 /*TODO: maybe this should be qq_update_buddy_status() ?*/
 void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud)
 {
-	gchar *name;
+	gchar *purple_name;
 	PurpleBuddy *bud;
 	gchar *status_id;
 	
 	g_return_if_fail(q_bud != NULL);
 
-	name = uid_to_purple_name(q_bud->uid);
-	bud = purple_find_buddy(gc->account, name);
+	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);
+		return;
+	}
 
+	bud = purple_find_buddy(gc->account, purple_name);
 	if (bud == NULL) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid);
-		g_free(name);
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Not find buddy: %d\n", q_bud->uid);
+		g_free(purple_name);
 		return;
 	}
 	
@@ -650,19 +634,19 @@
 		break;
 	}
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "buddy %d %s\n", q_bud->uid, status_id);
-	purple_prpl_got_user_status(gc->account, name, status_id, NULL);
+	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)
-		purple_prpl_got_user_status(gc->account, name, "mobile", NULL);
+		purple_prpl_got_user_status(gc->account, purple_name, "mobile", NULL);
 	else
-		purple_prpl_got_user_status_deactive(gc->account, name, "mobile");
+		purple_prpl_got_user_status_deactive(gc->account, purple_name, "mobile");
 
 	if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO && q_bud->status != QQ_BUDDY_OFFLINE)
-		purple_prpl_got_user_status(gc->account, name, "video", NULL);
+		purple_prpl_got_user_status(gc->account, purple_name, "video", NULL);
 	else
-		purple_prpl_got_user_status_deactive(gc->account, name, "video");
+		purple_prpl_got_user_status_deactive(gc->account, purple_name, "video");
 
-	g_free(name);
+	g_free(purple_name);
 }
 
 /* refresh all buddies online/offline,
--- a/libpurple/protocols/qq/buddy_list.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.h	Sun Aug 10 22:59:07 2008 +0000
@@ -49,13 +49,13 @@
 };
 
 void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position);
-guint8 qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+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);
-guint16 qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position);
-guint32 qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+void qq_send_packet_get_buddies_and_rooms(PurpleConnection *gc, guint32 position);
+guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc);
 
 void qq_refresh_all_buddy_status(PurpleConnection *gc);
 
@@ -64,8 +64,8 @@
 gint get_icon_offset(PurpleConnection *gc);
 
 void qq_send_packet_change_status(PurpleConnection *gc);
-void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_buddy_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc);
+void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
 
 void qq_refresh_all_buddy_status(PurpleConnection *gc);
 void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud);
--- a/libpurple/protocols/qq/buddy_opt.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Sun Aug 10 22:59:07 2008 +0000
@@ -31,7 +31,6 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
-#include "crypt.h"
 #include "header_info.h"
 #include "im.h"
 #include "qq_base.h"
@@ -252,29 +251,18 @@
 }
 
 /*  process reply to add_buddy_auth request */
-void qq_process_add_buddy_auth_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+void qq_process_add_buddy_auth_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len;
-	gint bytes = 0;
-	guint8 *data, reply;
 	gchar **segments, *msg_utf8;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
-	}
-
-	bytes += qq_get8(&reply, data + bytes);
-
-	if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
+	if (data[0] != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
-		if (NULL == (segments = split_data(data, len, "\x1f", 2))) {
+		if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) {
 			return;
 		}
 		msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
@@ -286,26 +274,15 @@
 }
 
 /* process the server reply for my request to remove a buddy */
-void qq_process_remove_buddy_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+void qq_process_remove_buddy_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len;
-	gint bytes = 0;
-	guint8 *data, reply;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove buddy reply\n");
-	}
-
-	bytes += qq_get8(&reply, data + bytes);
-
-	if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
+	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");
 	} else {		/* if reply */
@@ -316,26 +293,15 @@
 }
 
 /* process the server reply for my request to remove myself from a buddy */
-void qq_process_remove_self_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) 
+void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc) 
 {
 	qq_data *qd;
-	gint len;
-	gint bytes = 0;
-	guint8 *data, reply;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n");
-	}
-
-	bytes += qq_get8(&reply, data + bytes);
-
-	if (reply != QQ_REMOVE_SELF_REPLY_OK) {
+	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");
 	} else {		/* if reply */
@@ -345,23 +311,21 @@
 	}
 }
 
-void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
+void qq_process_add_buddy_reply(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len, for_uid;
+	gint for_uid;
 	gchar *msg, **segments, *uid, *reply;
-	guint8 *data;
 	GList *list;
 	PurpleBuddy *b;
 	gc_and_uid *g;
 	qq_add_buddy_request *req;
 	gchar *nombre;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	for_uid = 0;
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
 
 	list = qd->add_buddy_request;
 	while (list != NULL) {
@@ -382,50 +346,45 @@
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy reply [%d] is for id [%d]\n", seq, for_uid);
 	}
 
-	data = g_newa(guint8, len);
-
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		if (NULL == (segments = split_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);
-			g_strfreev(segments);
-			return;
-		}
+	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);
+		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");
-			nombre = uid_to_purple_name(for_uid);
-			b = purple_find_buddy(gc->account, nombre);
-			if (b != NULL)
-				purple_blist_remove_buddy(b);
-			g = g_new0(gc_and_uid, 1);
-			g->gc = gc;
-			g->uid = for_uid;
-			msg = g_strdup_printf(_("User %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?"),
-					TRUE, FALSE, NULL, _("Send"),
-					G_CALLBACK
-					(_qq_send_packet_add_buddy_auth_with_gc_and_uid),
-					_("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
-					purple_connection_get_account(gc), nombre, NULL,
-					g);
-			g_free(msg);
-			g_free(nombre);
-		} else {	/* add OK */
-			qq_add_buddy_by_recv_packet(gc, for_uid, TRUE, TRUE);
-			msg = g_strdup_printf(_("You have added %d to buddy list"), for_uid);
-			purple_notify_info(gc, NULL, msg, NULL);
-			g_free(msg);
-		}
-		g_strfreev(segments);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy reply\n");
+	if (strtol(reply, NULL, 10) > 0) {	/* need auth */
+		purple_debug(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)
+			purple_blist_remove_buddy(b);
+		g = g_new0(gc_and_uid, 1);
+		g->gc = gc;
+		g->uid = for_uid;
+		msg = g_strdup_printf(_("User %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?"),
+				TRUE, FALSE, NULL, _("Send"),
+				G_CALLBACK
+				(_qq_send_packet_add_buddy_auth_with_gc_and_uid),
+				_("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
+				purple_connection_get_account(gc), nombre, NULL,
+				g);
+		g_free(msg);
+		g_free(nombre);
+	} else {	/* add OK */
+		qq_add_buddy_by_recv_packet(gc, for_uid, TRUE, TRUE);
+		msg = g_strdup_printf(_("You have added %d to buddy list"), for_uid);
+		purple_notify_info(gc, NULL, msg, NULL);
+		g_free(msg);
 	}
+	g_strfreev(segments);
 }
 
 PurpleGroup *qq_get_purple_group(const gchar *group_name)
--- a/libpurple/protocols/qq/buddy_opt.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.h	Sun Aug 10 22:59:07 2008 +0000
@@ -46,9 +46,9 @@
 void qq_do_nothing_with_gc_and_uid(gc_and_uid *g, const gchar *msg);
 
 void qq_process_remove_buddy_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_remove_self_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc);
-void qq_process_add_buddy_auth_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_process_add_buddy_reply(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
+void qq_process_add_buddy_auth_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 PurpleBuddy *qq_add_buddy_by_recv_packet(PurpleConnection *gc, guint32 uid, gboolean is_known, gboolean create);
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 
--- a/libpurple/protocols/qq/crypt.c	Sun Aug 10 22:08:39 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,393 +0,0 @@
-/**
- * @file crypt.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *
- * QQ encryption algorithm
- * Convert from ASM code provided by PerlOICQ
- * 
- * Puzzlebird, Nov-Dec 2002
- */
-
-/* Notes: (QQ uses 16 rounds, and modified something...)
-
-IN : 64  bits of data in v[0] - v[1].
-OUT: 64  bits of data in w[0] - w[1].
-KEY: 128 bits of key  in k[0] - k[3].
-
-delta is chosen to be the real part of 
-the golden ratio: Sqrt(5/4) - 1/2 ~ 0.618034 multiplied by 2^32. 
-
-0x61C88647 is what we can track on the ASM codes.!!
-*/
-
-#include <string.h>
-
-#include "crypt.h"
-#include "debug.h"
-
-/* 1, fixed alignment problem, when compiled on different platform
- * 2, whether we need core debug
- * 20070717, s3e */
-#if 0 
-#define CORE_DEBUG
-#endif
-
-/********************************************************************
- * encryption 
- *******************************************************************/
-
-/* Tiny Encryption Algorithm (TEA) */
-static void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w)
-{
-	register guint32
-		y = g_ntohl(v[0]), 
-		 z = g_ntohl(v[1]), 
-		 a = g_ntohl(k[0]), 
-		 b = g_ntohl(k[1]), 
-		 c = g_ntohl(k[2]), 
-		 d = g_ntohl(k[3]), 
-		 n = 0x10, 
-		 sum = 0, 
-		 delta = 0x9E3779B9;	/*  0x9E3779B9 - 0x100000000 = -0x61C88647 */
-
-	while (n-- > 0) {
-		sum += delta;
-		y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
-		z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
-	}
-
-	w[0] = g_htonl(y);
-	w[1] = g_htonl(z);
-}
-
-/* it can be the real random seed function */
-/* override with number, convenient for debug */
-#ifdef DEBUG
-static gint rand(void) {	
-	return 0xdead; 
-}
-#else
-#include <stdlib.h>
-#endif
-
-/* 64-bit blocks and some kind of feedback mode of operation */
-static inline void encrypt_block(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted, 
-		guint8 **crypted_pre_8, const guint8 *const key, gint *count, 
-		gint *pos_in_block, gint *is_header) 
-{
-	/* loop it */
-	int j;
-	/* ships in encipher */
-	guint32 ptr_p[2];	/* 64 bits, guint32[2] */
-	guint32 ptr_k[4];	/* 128 bits, guint32[4] */
-	guint32 ptr_c[2];	/* 64 bits, guint32[2] */
-
-	/* prepare input text */
-#ifdef CORE_DEBUG
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ_CORE_DEBUG",
-		"!we are in encrypt_block! *pos_in_block comes: %d, *is_header comes: %d\n",
-		*pos_in_block, *is_header);
-#endif
-	for(j = 0; j < 8; j++) {
-#ifdef CORE_DEBUG
-		purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
-			"plain[%d]: 0x%02x, plain_pre_8[%d]: 0x%02x\n",
-			j, plain[j], j, plain_pre_8[j]);
-#endif
-		if (!*is_header) {
-#ifdef CORE_DEBUG
-			purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
-				"(*crypted_pre_8 + %d): 0x%02x\n",
-				j, *(*crypted_pre_8 + j));
-#endif
-			plain[j] ^= (*(*crypted_pre_8 + j));
-#ifdef CORE_DEBUG
-			purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
-				"NOW plain[%d]: 0x%02x\n",
-				j, plain[j]);
-#endif
-		} else {
-			plain[j] ^= plain_pre_8[j];
-#ifdef CORE_DEBUG
-			purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
-				"NOW plain[%d]: 0x%02x\n",
-				j, plain[j]);
-#endif
-		}
-	}
-
-	g_memmove(ptr_p, plain, 8);
-	g_memmove(ptr_k, key, 16);
-	g_memmove(ptr_c, *crypted, 8);
-
-	/* encrypt it */
-	qq_encipher(ptr_p, ptr_k, ptr_c);
-	
-	g_memmove(plain, ptr_p, 8);
-	g_memmove(*crypted, ptr_c, 8);
-
-	for(j = 0; j < 8; j++) {
-#ifdef CORE_DEBUG
-		purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
-			"j: %d, *(*crypted + %d): 0x%02x, plain_pre_8[%d]: 0x%02x\n",
-			j, j, *(*crypted + j), j, plain_pre_8[j]);
-#endif
-		(*(*crypted + j)) ^= plain_pre_8[j];
-#ifdef CORE_DEBUG
-		purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG",
-			"NOW *(*crypted + [%d]): 0x%02x\n",
-			j, *(*crypted + j));
-#endif
-	}
-	
-	memcpy(plain_pre_8, plain, 8);	/* prepare next */
-
-	*crypted_pre_8 = *crypted;	/* store position of previous 8 byte */
-	*crypted += 8;			/* prepare next output */
-	*count += 8;			/* outstrlen increase by 8 */
-	*pos_in_block = 0;		/* back to start */
-	*is_header = 0;			/* and exit header */
-}					/* encrypt_block */
-
-void qq_encrypt(const guint8 *const instr, gint instrlen, 
-		const guint8 *const key, 
-		guint8 *outstr, gint *outstrlen_ptr)
-{
-	guint8 plain[8],		/* plain text buffer */
-		plain_pre_8[8],		/* plain text buffer, previous 8 bytes */
-		*crypted,		/* crypted text */
-		*crypted_pre_8;		/* crypted text, previous 8 bytes */
-	const guint8 *inp;		/* current position in instr */
-	gint pos_in_block = 1,		/* loop in the byte */
-		is_header = 1,		/* header is one byte */
-		count = 0,		/* number of bytes being crypted */
-		padding = 0;		/* number of padding stuff */
-
-	pos_in_block = (instrlen + 0x0a) % 8;	/* header padding decided by instrlen */
-	if (pos_in_block)
-		pos_in_block = 8 - pos_in_block;
-
-	/* initialization vector */
-	plain[0] = (rand() & 0xf8) | pos_in_block;
-	memset(plain + 1, rand() & 0xff, pos_in_block++);
-
-	memset(plain_pre_8, 0x00, sizeof(plain_pre_8));
-
-	crypted = crypted_pre_8 = outstr;
-
-	padding = 1;		/* pad some stuff in header */
-	while (padding <= 2) {	/* at most two bytes */
-		if (pos_in_block < 8) {
-			plain[pos_in_block++] = rand() & 0xff;
-			padding++;
-		}
-		if (pos_in_block == 8) {
-			encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8, 
-					key, &count, &pos_in_block, &is_header);
-		}
-	}
-
-	inp = instr;
-	while (instrlen > 0) {
-		if (pos_in_block < 8) {
-			plain[pos_in_block++] = *(inp++);
-			instrlen--;
-		}
-		if (pos_in_block == 8) {
-			encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8, 
-					key, &count, &pos_in_block, &is_header);
-		}
-	}
-
-	padding = 1;		/* pad some stuff in tail */
-	while (padding <= 7) {	/* at most seven bytes */
-		if (pos_in_block < 8) {
-			plain[pos_in_block++] = 0x00;
-			padding++;
-		}
-		if (pos_in_block == 8) {
-			encrypt_block(plain, plain_pre_8, &crypted, &crypted_pre_8, 
-					key, &count, &pos_in_block, &is_header);
-		}
-	}
-
-	*outstrlen_ptr = count;
-}
-
-
-/******************************************************************** 
- * decryption 
- ********************************************************************/
-
-static void qq_decipher(guint32 *const v, const guint32 *const k, guint32 *const w)
-{
-	register guint32
-		y = g_ntohl(v[0]), 
-		z = g_ntohl(v[1]), 
-		a = g_ntohl(k[0]), 
-		b = g_ntohl(k[1]), 
-		c = g_ntohl(k[2]), 
-		d = g_ntohl(k[3]), 
-		n = 0x10, 
-		sum = 0xE3779B90,	/* why this ? must be related with n value */
-		delta = 0x9E3779B9;
-
-	/* sum = delta<<5, in general sum = delta * n */
-	while (n-- > 0) {
-		z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
-		y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
-		sum -= delta;
-	}
-
-	w[0] = g_htonl(y);
-	w[1] = g_htonl(z);
-}
-
-static gint decrypt_block(const guint8 **crypt_buff, const gint instrlen, 
-		const guint8 *const key, gint *context_start, 
-		guint8 *decrypted, gint *pos_in_block)
-{
-	/* loop */
-	int i;
-	/* ships in decipher */
-	guint32 ptr_v[2];
-	guint32 ptr_k[4];
-
-	if (*context_start == instrlen)
-		return 1;
-
-	for(i = 0; i < 8; i++) {
-		decrypted[i] ^= (*(*crypt_buff + i));
-	}
-	
-	g_memmove(ptr_v, decrypted, 8);
-	g_memmove(ptr_k, key, 16);
-
-	qq_decipher(ptr_v, ptr_k, ptr_v);
-
-	g_memmove(decrypted, ptr_v, 8);
-
-	*context_start += 8;
-	*crypt_buff += 8;
-	*pos_in_block = 0;
-
-	return 1;
-}
-
-/* return 0 if failed, 1 otherwise */
-gint qq_decrypt(const guint8 *const instr, gint instrlen, 
-		const guint8 *const key,
-		guint8 *outstr, gint *outstrlen_ptr)
-{
-	guint8 decrypted[8], m[8], *outp;
-	const guint8 *crypt_buff, *crypt_buff_pre_8;
-	gint count, context_start, pos_in_block, padding;
-	/* ships */
-	guint32 ptr_instr[2];
-	guint32 ptr_key[4];
-	guint32 ptr_decr[2];
-
-	/* at least 16 bytes and %8 == 0 */
-	if ((instrlen % 8) || (instrlen < 16)) { 
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-			"Ciphertext len is either too short or not a multiple of 8 bytes, read %d bytes\n", 
-			instrlen);
-		return 0;
-	}
-	g_memmove(ptr_instr, instr, 8);
-	g_memmove(ptr_key, key, 16);
-	g_memmove(ptr_decr, decrypted, 8);
-
-	qq_decipher(ptr_instr, ptr_key, ptr_decr);
-
-	g_memmove(decrypted, ptr_decr, 8);
-
-	pos_in_block = decrypted[0] & 0x7;
-	count = instrlen - pos_in_block - 10;	/* this is the plaintext length */
-	/* return if outstr buffer is not large enough or error plaintext length */
-	if (*outstrlen_ptr < count || count < 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Buffer len %d is less than real len %d", 
-			*outstrlen_ptr, count);
-		return 0;
-	}
-
-	memset(m, 0, 8);
-	crypt_buff_pre_8 = m;
-	*outstrlen_ptr = count;	/* everything is ok! set return string length */
-
-	crypt_buff = instr + 8;	/* address of real data start */
-	context_start = 8;	/* context is at the second block of 8 bytes */
-	pos_in_block++;		/* start of paddng stuff */
-
-	padding = 1;		/* at least one in header */
-	while (padding <= 2) {	/* there are 2 byte padding stuff in header */
-		if (pos_in_block < 8) {	/* bypass the padding stuff, it's nonsense data */
-			pos_in_block++;
-			padding++;
-		}
-		if (pos_in_block == 8) {
-			crypt_buff_pre_8 = instr;
-			if (!decrypt_block(&crypt_buff, instrlen, key, 
-						&context_start, decrypted, &pos_in_block)) {
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error A");
-				return 0;
-			}
-		}
-	}
-
-	outp = outstr;
-	while (count != 0) {
-		if (pos_in_block < 8) {
-			*outp = crypt_buff_pre_8[pos_in_block] ^ decrypted[pos_in_block];
-			outp++;
-			count--;
-			pos_in_block++;
-		}
-		if (pos_in_block == 8) {
-			crypt_buff_pre_8 = crypt_buff - 8;
-			if (!decrypt_block(&crypt_buff, instrlen, key, 
-						&context_start, decrypted, &pos_in_block)) {
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error B");
-				return 0;
-			}
-		}
-	}
-
-	for (padding = 1; padding < 8; padding++) {
-		if (pos_in_block < 8) {
-			if (crypt_buff_pre_8[pos_in_block] ^ decrypted[pos_in_block])
-				return 0;
-			pos_in_block++;
-		}
-		if (pos_in_block == 8) {
-			crypt_buff_pre_8 = crypt_buff;
-			if (!decrypt_block(&crypt_buff, instrlen, key, 
-						&context_start, decrypted, &pos_in_block)) {
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "decrypt every 8 bytes error C");
-				return 0;
-			}
-		}
-	}
-
-	return 1;
-}
--- a/libpurple/protocols/qq/crypt.h	Sun Aug 10 22:08:39 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
- /**
- * @file crypt.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef _QQ_CRYPT_H_
-#define _QQ_CRYPT_H_
-
-#include <glib.h>
-
-#define DECRYPT 0x00
-#define ENCRYPT 0x01
-
-void qq_encrypt(const guint8 *const instr, gint instrlen, 
-		const guint8 *const key, 
-		guint8 *outstr, gint *outstrlen_ptr);
-		
-gint qq_decrypt(const guint8 *const instr, gint instrlen, 
-		const guint8 *const key,
-		guint8 *outstr, gint *outstrlen_ptr);
-#endif
--- a/libpurple/protocols/qq/file_trans.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/file_trans.c	Sun Aug 10 22:59:07 2008 +0000
@@ -32,7 +32,7 @@
 #include "ft.h"
 #include "cipher.h"
 
-#include "crypt.h"
+#include "qq_crypt.h"
 #include "file_trans.h"
 #include "header_info.h"
 #include "im.h"
@@ -338,31 +338,30 @@
 		raw_data, bytes,
 		"sending packet[%s]:", qq_get_file_cmd_desc(packet_type));
 
-	encrypted_len = bytes + 16;
-	encrypted_data = g_newa(guint8, encrypted_len);
-	qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
+	encrypted_data = g_newa(guint8, bytes + 16);
+	encrypted_len = qq_encrypt(encrypted_data, raw_data, bytes, info->file_session_key);
 	/*debug: try to decrypt it */
-	/*
-	   if (QQ_DEBUG) {
-	   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);
-	   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");
+
+#if 0
+	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);
+	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");
 	   if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
-	   purple_debug(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);
-	   g_free(hex_dump);
-	   } else {
-	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
-	   }
-	   }
-	   */
+			purple_debug(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);
+		g_free(hex_dump);
+	 } else {
+		purple_debug(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));
 	_qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
@@ -498,7 +497,7 @@
  */
 
 
-static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint len)
+static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	gint bytes ;
 	gint decryped_bytes;
@@ -514,10 +513,9 @@
 	bytes = 0;
 	bytes += _qq_get_file_header(&fh, data + bytes);
 
-	decrypted_data = g_newa(guint8, len);
-	decrypted_len = len;
-
-	if ( !qq_decrypt(data, len, qd->session_md5, decrypted_data, &decrypted_len) ) {
+	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");
 		return;
 	}
--- a/libpurple/protocols/qq/group.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Sun Aug 10 22:59:07 2008 +0000
@@ -32,17 +32,18 @@
 #include "group_info.h"
 #include "group_search.h"
 #include "utils.h"
-
+#include "qq_network.h"
+#include "header_info.h"
 #include "group.h"
 
 static void _qq_group_search_callback(PurpleConnection *gc, const gchar *input)
 {
-	guint32 external_group_id;
+	guint32 ext_id;
 
 	g_return_if_fail(input != NULL);
-	external_group_id = qq_string_to_dec_value(input);
+	ext_id = qq_string_to_dec_value(input);
 	/* 0x00000000 means search for demo group */
-	qq_send_cmd_group_search_group(gc, external_group_id);
+	qq_send_cmd_group_search_group(gc, ext_id);
 }
 
 static void _qq_group_search_cancel_callback(PurpleConnection *gc, const gchar *input)
@@ -104,7 +105,7 @@
 	fields = g_list_append(fields, f);
 	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_GROUP_KEY_INTERNAL_ID, TRUE);
 	fields = g_list_append(fields, f);
-	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_GROUP_KEY_GROUP_TYPE, TRUE);
+	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_GROUP_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);
 	fields = g_list_append(fields, f);
@@ -145,12 +146,12 @@
 /* this should be called upon signin, even when we did not open group chat window */
 void qq_group_init(PurpleConnection *gc)
 {
-	gint i;
 	PurpleAccount *account;
 	PurpleChat *chat;
 	PurpleGroup *purple_group;
 	PurpleBlistNode *node;
 	qq_group *group;
+	gint count;
 
 	account = purple_connection_get_account(gc);
 
@@ -160,18 +161,25 @@
 		return;
 	}
 
-	i = 0;
-	for (node = ((PurpleBlistNode *) purple_group)->child; node != NULL; node = node->next)
-		if (PURPLE_BLIST_NODE_IS_CHAT(node)) {	/* got one */
-			chat = (PurpleChat *) node;
-			if (account != chat->account)
-				continue;	/* very important here ! */
-			group = qq_group_from_hashtable(gc, chat->components);
-			if (group != NULL) {
-				i++;
-				qq_send_cmd_group_get_group_info(gc, group);	/* get group info and members */
-			}
+	count = 0;
+	for (node = ((PurpleBlistNode *) purple_group)->child; node != NULL; node = node->next) {
+		if ( !PURPLE_BLIST_NODE_IS_CHAT(node)) {
+			continue;
 		}
+		/* got one */
+		chat = (PurpleChat *) node;
+		if (account != chat->account)	/* not qq account*/
+			continue;
+		group = qq_group_from_hashtable(gc, chat->components);
+		if (group == NULL)
+			continue;
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Load %d QQ Qun configurations\n", i);
+		if (group->id <= 0)
+			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);
 }
--- a/libpurple/protocols/qq/group.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group.h	Sun Aug 10 22:59:07 2008 +0000
@@ -44,9 +44,9 @@
 	/* 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 */
-	guint32 internal_group_id;
-	guint32 external_group_id;
-	guint8 group_type;			/* permanent or temporory */
+	guint32 id;
+	guint32 ext_id;
+	guint8 type8;			/* permanent or temporory */
 	guint32 creator_uid;
 	guint32 group_category;
 	guint8 auth_type;
--- a/libpurple/protocols/qq/group_find.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_find.c	Sun Aug 10 22:59:07 2008 +0000
@@ -29,36 +29,8 @@
 #include "util.h"
 
 #include "group_find.h"
-#include "group_network.h"
 #include "utils.h"
 
-/* find the internal_group_id by the reply packet sequence
- * return TRUE if we have a record of it, return FALSE if not */
-gboolean qq_group_find_internal_group_id_by_seq(PurpleConnection *gc, guint16 seq, guint32 *internal_group_id)
-{
-	GList *list;
-	qq_data *qd;
-	group_packet *p;
-
-	if (internal_group_id == NULL)
-		return FALSE;
-	qd = (qq_data *) gc->proto_data;
-
-	list = qd->group_packets;
-	while (list != NULL) {
-		p = (group_packet *) (list->data);
-		if (p->send_seq == seq) {	/* found and remove */
-			*internal_group_id = p->internal_group_id;
-			qd->group_packets = g_list_remove(qd->group_packets, p);
-			g_free(p);
-			return TRUE;
-		}
-		list = list->next;
-	}
-
-	return FALSE;
-}
-
 /* find a qq_buddy by uid, called by im.c */
 qq_buddy *qq_group_find_member_by_uid(qq_group *group, guint32 uid)
 {
@@ -150,7 +122,7 @@
 }
 
 /* find a qq_group by its id, flag is QQ_INTERNAL_ID or QQ_EXTERNAL_ID */
-qq_group *qq_group_find_by_id(PurpleConnection *gc, guint32 id, gboolean flag)
+qq_group *qq_room_search_ext_id(PurpleConnection *gc, guint32 ext_id)
 {
 	GList *list;
 	qq_group *group;
@@ -158,17 +130,40 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	if (qd->groups == NULL || id <= 0)
+	if (qd->groups == NULL || ext_id <= 0)
 		return NULL;
 
 	list = qd->groups;
 	while (list != NULL) {
 		group = (qq_group *) list->data;
-		if (flag == QQ_INTERNAL_ID ? 
-				(group->internal_group_id == id) : (group->external_group_id == id))
+		if (group->ext_id == ext_id) {
 			return group;
+		}
 		list = list->next;
 	}
 
 	return NULL;
 }
+
+qq_group *qq_room_search_id(PurpleConnection *gc, guint32 room_id)
+{
+	GList *list;
+	qq_group *group;
+	qq_data *qd;
+
+	qd = (qq_data *) gc->proto_data;
+
+	if (qd->groups == NULL || room_id <= 0)
+		return NULL;
+
+	list = qd->groups;
+	while (list != NULL) {
+		group = (qq_group *) list->data;
+		if (group->id == room_id) {
+			return group;
+		}
+		list = list->next;
+	}
+
+	return NULL;
+}
--- a/libpurple/protocols/qq/group_find.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_find.h	Sun Aug 10 22:59:07 2008 +0000
@@ -29,14 +29,13 @@
 #include "connection.h"
 #include "group.h"
 
-#define QQ_INTERNAL_ID 0
-#define QQ_EXTERNAL_ID 1
-
 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_internal_group_id_by_seq(PurpleConnection *gc, guint16 seq, guint32 *internal_group_id);
+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_group_find_by_id(PurpleConnection *gc, guint32 id, gboolean flag);
+
+qq_group *qq_room_search_ext_id(PurpleConnection *gc, guint32 ext_id);
+qq_group *qq_room_search_id(PurpleConnection *gc, guint32 room_id);
 
 #endif
--- a/libpurple/protocols/qq/group_free.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_free.c	Sun Aug 10 22:59:07 2008 +0000
@@ -28,7 +28,6 @@
 
 #include "buddy_list.h"
 #include "group_free.h"
-#include "group_network.h"
 
 /* gracefully free all members in a group */
 static void qq_group_free_member(qq_group *group)
@@ -62,22 +61,6 @@
 	g_free(group);
 }
 
-/* clean up group_packets and free all contents */
-void qq_group_packets_free(qq_data *qd)
-{
-	group_packet *p;
-	gint i;
-
-	i = 0;
-	while (qd->group_packets != NULL) {
-		p = (group_packet *) (qd->group_packets->data);
-		qd->group_packets = g_list_remove(qd->group_packets, p);
-		g_free(p);
-		i++;
-	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d group packets are freed!\n", i);
-}
-
 void qq_group_free_all(qq_data *qd)
 {
 	qq_group *group;
--- a/libpurple/protocols/qq/group_free.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_free.h	Sun Aug 10 22:59:07 2008 +0000
@@ -29,8 +29,6 @@
 #include "qq.h"
 #include "group.h"
 
-void qq_group_packets_free(qq_data *qd);
-
 void qq_group_free(qq_group *group);
 void qq_group_free_all(qq_data *qd);
 
--- a/libpurple/protocols/qq/group_im.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Sun Aug 10 22:59:07 2008 +0000
@@ -36,15 +36,16 @@
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_im.h"
-#include "group_network.h"
 #include "group_opt.h"
 #include "im.h"
+#include "header_info.h"
 #include "packet_parse.h"
+#include "qq_network.h"
 #include "utils.h"
 
 typedef struct _qq_recv_group_im {
-	guint32 external_group_id;
-	guint8 group_type;
+	guint32 ext_id;
+	guint8 type8;
 	guint32 member_uid;
 	guint16 msg_seq;
 	time_t send_time;
@@ -68,12 +69,10 @@
 	purple_debug_info("QQ_MESG", "Send qun mesg filterd: %s\n", msg_filtered);
 	msg_len = strlen(msg_filtered);
 
-	data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
+	data_len = 2 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
 	raw_data = g_newa(guint8, data_len);
 
 	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEND_MSG);
-	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 	bytes += qq_put16(raw_data + bytes, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
 	bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
 	send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL,
@@ -84,7 +83,7 @@
 	g_free(msg_filtered);
 
 	if (bytes == data_len)	/* create OK */
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+		qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_MSG, group->id, raw_data, data_len);
 	else
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 				"Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
@@ -99,33 +98,33 @@
 }
 
 /* receive an application to join the group */
-void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
-	guint32 external_group_id, user_uid;
-	guint8 group_type;
+	guint32 ext_id, user_uid;
+	guint8 type8;
 	gchar *reason_utf8, *msg, *reason;
 	group_member_opt *g;
 	gchar *nombre;
 	gint bytes = 0;
 
-	g_return_if_fail(internal_group_id > 0 && data != NULL && len > 0);
+	g_return_if_fail(id > 0 && data != NULL && len > 0);
 
 	/* FIXME: check length here */
 
-	bytes += qq_get32(&external_group_id, data + bytes);
-	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&ext_id, data + bytes);
+	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&user_uid, data + bytes);
 
-	g_return_if_fail(external_group_id > 0 && user_uid > 0);
+	g_return_if_fail(ext_id > 0 && user_uid > 0);
 
 	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, external_group_id);
+	msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, ext_id);
 	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
 
 	g = g_new0(group_member_opt, 1);
 	g->gc = gc;
-	g->internal_group_id = internal_group_id;
+	g->id = id;
 	g->member = user_uid;
 
 	nombre = uid_to_purple_name(user_uid);
@@ -150,10 +149,10 @@
 }
 
 /* the request to join a group is rejected */
-void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
-	guint32 external_group_id, admin_uid;
-	guint8 group_type;
+	guint32 ext_id, admin_uid;
+	guint8 type8;
 	gchar *reason_utf8, *msg, *reason;
 	qq_group *group;
 	gint bytes = 0;
@@ -162,21 +161,21 @@
 
 	/* FIXME: check length here */
 
-	bytes += qq_get32(&external_group_id, data + bytes);
-	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&ext_id, data + bytes);
+	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&admin_uid, data + bytes);
 
-	g_return_if_fail(external_group_id > 0 && admin_uid > 0);
+	g_return_if_fail(ext_id > 0 && admin_uid > 0);
 
 	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"), external_group_id, admin_uid);
+		(_("Your request to join group %d has been rejected by admin %d"), ext_id, admin_uid);
 	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
 		qq_group_refresh(gc, group);
@@ -188,10 +187,10 @@
 }
 
 /* the request to join a group is approved */
-void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
-	guint32 external_group_id, admin_uid;
-	guint8 group_type;
+	guint32 ext_id, admin_uid;
+	guint8 type8;
 	gchar *reason_utf8, *msg;
 	qq_group *group;
 	gint bytes = 0;
@@ -200,20 +199,20 @@
 
 	/* FIXME: check length here */
 
-	bytes += qq_get32(&external_group_id, data + bytes);
-	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&ext_id, data + bytes);
+	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&admin_uid, data + bytes);
 
-	g_return_if_fail(external_group_id > 0 && admin_uid > 0);
+	g_return_if_fail(ext_id > 0 && admin_uid > 0);
 	/* it is also a "æ— " here, so do not display */
 	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"), external_group_id, admin_uid);
+		(_("Your request to join group %d has been approved by admin %d"), ext_id, admin_uid);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
 		qq_group_refresh(gc, group);
@@ -224,10 +223,10 @@
 }
 
 /* process the packet when removed from a group */
-void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
-	guint32 external_group_id, uid;
-	guint8 group_type;
+	guint32 ext_id, uid;
+	guint8 type8;
 	gchar *msg;
 	qq_group *group;
 	gint bytes = 0;
@@ -236,16 +235,16 @@
 
 	/* FIXME: check length here */
 
-	bytes += qq_get32(&external_group_id, data + bytes);
-	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&ext_id, data + bytes);
+	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&uid, data + bytes);
 
-	g_return_if_fail(external_group_id > 0 && uid > 0);
+	g_return_if_fail(ext_id > 0 && uid > 0);
 
-	msg = g_strdup_printf(_("You [%d] have left group \"%d\""), uid, external_group_id);
+	msg = g_strdup_printf(_("You [%d] have left group \"%d\""), uid, ext_id);
 	purple_notify_info(gc, _("QQ Qun Operation"), msg, NULL);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
 		qq_group_refresh(gc, group);
@@ -255,10 +254,10 @@
 }
 
 /* process the packet when added to a group */
-void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
 {
-	guint32 external_group_id, uid;
-	guint8 group_type;
+	guint32 ext_id, uid;
+	guint8 type8;
 	qq_group *group;
 	gchar *msg;
 	gint bytes = 0;
@@ -267,24 +266,24 @@
 
 	/* FIXME: check length here */
 
-	bytes += qq_get32(&external_group_id, data + bytes);
-	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&ext_id, data + bytes);
+	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&uid, data + bytes);
 
-	g_return_if_fail(external_group_id > 0 && uid > 0);
+	g_return_if_fail(ext_id > 0 && uid > 0);
 
-	msg = g_strdup_printf(_("You [%d] have been added to group \"%d\""), uid, external_group_id);
+	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"));
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
 		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, internal_group_id, external_group_id, NULL);
+		group = qq_group_create_internal_record(gc, id, ext_id, NULL);
 		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
 		qq_group_refresh(gc, group);
-		qq_send_cmd_group_get_group_info(gc, group);
+		qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
 		/* the return of this cmd will automatically update the group in blist */
 	}
 
@@ -292,7 +291,7 @@
 }
 
 /* recv an IM from a group chat */
-void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type)
+void qq_process_recv_group_im(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;
@@ -315,11 +314,11 @@
 
 	im_group = g_newa(qq_recv_group_im, 1);
 
-	bytes += qq_get32(&(im_group->external_group_id), data + bytes);
-	bytes += qq_get8(&(im_group->group_type), data + bytes);
+	bytes += qq_get32(&(im_group->ext_id), data + bytes);
+	bytes += qq_get8(&(im_group->type8), data + bytes);
 
 	if(QQ_RECV_IM_TEMP_QUN_IM == im_type) {
-		bytes += qq_get32(&(internal_group_id), data + bytes);
+		bytes += qq_get32(&(id), data + bytes);
 	}
 
 	bytes += qq_get32(&(im_group->member_uid), bytes + data);
@@ -372,13 +371,13 @@
 	else
 		msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	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));
 	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/prompt_group_msg_on_recv")) {
 		/* New conv should open, get group info*/
-		qq_send_cmd_group_get_group_info(gc, group);
+		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));
--- a/libpurple/protocols/qq/group_im.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Sun Aug 10 22:59:07 2008 +0000
@@ -35,26 +35,26 @@
 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 internal_group_id, PurpleConnection *gc, guint16 im_type); */
-void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type);
+ * 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_recv_group_im_apply_join(guint8 *data, guint8 **cursor, gint len, 
- * guint32 internal_group_id, PurpleConnection *gc); */
-void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+ * guint32 id, PurpleConnection *gc); */
+void qq_process_recv_group_im_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 internal_group_id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+ * guint32 id, PurpleConnection *gc); */
+void qq_process_recv_group_im_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 internal_group_id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+ * guint32 id, PurpleConnection *gc); */
+void qq_process_recv_group_im_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 internal_group_id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+ * guint32 id, PurpleConnection *gc); */
+void qq_process_recv_group_im_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 internal_group_id, PurpleConnection *gc); */
-void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+ * guint32 id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
 #endif
--- a/libpurple/protocols/qq/group_info.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Sun Aug 10 22:59:07 2008 +0000
@@ -32,7 +32,10 @@
 #include "group_internal.h"
 #include "group_info.h"
 #include "buddy_list.h"
-#include "group_network.h"
+#include "header_info.h"
+#include "packet_parse.h"
+#include "qq_network.h"
+#include "utils.h"
 
 /* we check who needs to update member info every minutes
  * this interval determines if their member info is outdated */
@@ -61,20 +64,6 @@
 	}
 }
 
-/* send packet to get detailed information of one group */
-void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group)
-{
-	guint8 raw_data[16] = {0};
-	gint bytes = 0;
-
-	g_return_if_fail(group != NULL);
-
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_GROUP_INFO);
-	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
-
-	qq_send_group_cmd(gc, group, raw_data, bytes);
-}
-
 /* send packet to get online group member, called by keep_alive */
 void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc)
 {
@@ -99,9 +88,6 @@
 
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group)
 {
-	guint8 raw_data[16] = {0};
-	gint bytes = 0;
-
 	g_return_if_fail(group != NULL);
 
 	/* only get online members when conversation window is on */
@@ -111,17 +97,14 @@
 		return;
 	}
 
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
-	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
-
-	qq_send_group_cmd(gc, group, raw_data, bytes);
+	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)
 {
 	guint8 *raw_data;
-	gint bytes, num, data_len;
+	gint bytes, num;
 	GList *list;
 	qq_buddy *member;
 
@@ -137,12 +120,9 @@
 		return;
 	}
 
-	data_len = 5 + 4 * num;
-	raw_data = g_newa(guint8, data_len);
+	raw_data = g_newa(guint8, 4 * num);
 
 	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_MEMBER_INFO);
-	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
 	list = group->members;
 	while (list != NULL) {
@@ -152,16 +132,10 @@
 		list = list->next;
 	}
 
-	if (bytes != data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				"Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
-		return;
-	}
-
-	qq_send_group_cmd(gc, group, raw_data, bytes);
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_GET_MEMBER_INFO, group->id, raw_data, bytes);
 }
 
-void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc)
+void qq_process_room_cmd_get_info(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_group *group;
 	qq_buddy *member;
@@ -169,33 +143,33 @@
 	PurpleConversation *purple_conv;
 	guint8 organization, role;
 	guint16 unknown, max_members;
-	guint32 member_uid, internal_group_id, external_group_id;
+	guint32 member_uid, id, ext_id;
 	GSList *pending_id;
 	guint32 unknown4;
 	guint8 unknown1;
 	gint bytes, num;
 	gchar *notice;
 
-	g_return_if_fail(data != NULL && len > 0);
+	g_return_if_fail(data != NULL && data_len > 0);
 	qd = (qq_data *) gc->proto_data;
 
 	bytes = 0;
-	bytes += qq_get32(&(internal_group_id), data + bytes);
-	g_return_if_fail(internal_group_id > 0);
+	bytes += qq_get32(&id, data + bytes);
+	g_return_if_fail(id > 0);
 
-	bytes += qq_get32(&(external_group_id), data + bytes);
-	g_return_if_fail(external_group_id > 0);
+	bytes += qq_get32(&ext_id, data + bytes);
+	g_return_if_fail(ext_id > 0);
 
-	pending_id = qq_get_pending_id(qd->adding_groups_from_server, internal_group_id);
+	pending_id = qq_get_pending_id(qd->adding_groups_from_server, id);
 	if (pending_id != NULL) {
-		qq_set_pending_id(&qd->adding_groups_from_server, internal_group_id, FALSE);
-		qq_group_create_internal_record(gc, internal_group_id, external_group_id, NULL);
+		qq_set_pending_id(&qd->adding_groups_from_server, id, FALSE);
+		qq_group_create_internal_record(gc, id, ext_id, NULL);
 	}
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
-	bytes += qq_get8(&(group->group_type), data + bytes);
+	bytes += qq_get8(&(group->type8), data + bytes);
 	bytes += qq_get32(&unknown4, data + bytes);	/* unknown 4 bytes */
 	bytes += qq_get32(&(group->creator_uid), data + bytes);
 	bytes += qq_get8(&(group->auth_type), data + bytes);
@@ -210,7 +184,7 @@
 	 * qunDestLen(qunDestcontent)) */
 	bytes += qq_get8(&unknown1, data + bytes);
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
-			group->group_type, group->creator_uid, group->group_category, max_members);
+			group->type8, group->creator_uid, group->group_category, max_members);
 	
 	/* strlen + <str content> */
 	bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
@@ -223,23 +197,25 @@
 
 	num = 0;
 	/* now comes the member list separated by 0x00 */
-	while (bytes < len) {
+	while (bytes < data_len) {
 		bytes += qq_get32(&member_uid, data + bytes);
 		num++;
 		bytes += qq_get8(&organization, data + bytes);
 		bytes += qq_get8(&role, data + bytes);
 
-		/*
+#if 0
 		if(organization != 0 || role != 0) {
 			purple_debug(PURPLE_DEBUG_INFO, "QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role);
 		}
-		*/
+#endif
+
 		member = qq_group_find_or_add_member(gc, group, member_uid);
 		if (member != NULL)
 			member->role = role;
 	}
-	if(bytes > len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
+	if(bytes > data_len) {
+		purple_debug(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);
@@ -265,9 +241,9 @@
 	purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
 }
 
-void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc)
+void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc)
 {
-	guint32 internal_group_id, member_uid;
+	guint32 id, member_uid;
 	guint8 unknown;
 	gint bytes, num;
 	qq_group *group;
@@ -281,14 +257,14 @@
 	}
 
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get32(&id, data + bytes);
 	bytes += qq_get8(&unknown, data + bytes);	/* 0x3c ?? */
-	g_return_if_fail(internal_group_id > 0);
+	g_return_if_fail(id > 0);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	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", internal_group_id);
+				"We have no group info for internal id [%d]\n", id);
 		return;
 	}
 
@@ -311,11 +287,11 @@
 }
 
 /* process the reply to get_members_info packet */
-void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc)
+void qq_process_room_cmd_get_members(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
 	gint num;
-	guint32 internal_group_id, member_uid;
+	guint32 id, member_uid;
 	guint16 unknown;
 	qq_group *group;
 	qq_buddy *member;
@@ -323,11 +299,15 @@
 
 	g_return_if_fail(data != NULL && len > 0);
 
+#if 0
+	qq_show_packet("qq_process_room_cmd_get_members", data, len);
+#endif
+
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
-	g_return_if_fail(internal_group_id > 0);
+	bytes += qq_get32(&id, data + bytes);
+	g_return_if_fail(id > 0);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
 	num = 0;
@@ -352,19 +332,18 @@
 		member->nickname = g_strdup(nick);
 		g_free(nick);
 		
-		/*
-		if (QQ_DEBUG) {
-			purple_debug(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);
-		}
-		*/
+#if 0
+		purple_debug(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);
 	}
-	if(bytes > len) {
+	if (bytes > len) {
 		purple_debug(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);
 }
+
--- a/libpurple/protocols/qq/group_info.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_info.h	Sun Aug 10 22:59:07 2008 +0000
@@ -29,14 +29,12 @@
 #include "connection.h"
 #include "group.h"
 
-void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group);
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group);
 void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc);
 
 void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group);
 
-void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc);
-
+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);
 #endif
--- a/libpurple/protocols/qq/group_internal.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Sun Aug 10 22:59:07 2008 +0000
@@ -68,12 +68,12 @@
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "You have added group \"%s\" to blist locally\n", group->group_name_utf8);
 }
 
-/* Create a dummy qq_group, which includes only internal_id, external_id,
+/* 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
  * 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 external_id, gchar *group_name_utf8)
+                guint32 internal_id, guint32 ext_id, gchar *group_name_utf8)
 {
         qq_group *group;
         qq_data *qd;
@@ -84,9 +84,9 @@
         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->internal_group_id = internal_id;
-        group->external_group_id = external_id;
-        group->group_type = 0x01;       /* assume permanent Qun */
+        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->auth_type = 0x02;        /* assume need auth */
@@ -101,7 +101,7 @@
         return group;
 }
 
-void qq_group_delete_internal_record(qq_data *qd, guint32 internal_group_id)
+void qq_group_delete_internal_record(qq_data *qd, guint32 id)
 {
         qq_group *group;
         GList *list;
@@ -109,7 +109,7 @@
         list = qd->groups;
         while (list != NULL) {
                 group = (qq_group *) qd->groups->data;
-                if (internal_group_id == group->internal_group_id) {
+                if (id == group->id) {
                         qd->groups = g_list_remove(qd->groups, group);
                         qq_group_free(group);
                         break;
@@ -128,10 +128,10 @@
 	group->my_status_desc = _qq_group_set_my_status_desc(group);
 
 	g_hash_table_insert(components,
-			    g_strdup(QQ_GROUP_KEY_INTERNAL_ID), g_strdup_printf("%d", group->internal_group_id));
+			    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_printf("%d", group->external_group_id));
-	g_hash_table_insert(components, g_strdup(QQ_GROUP_KEY_GROUP_TYPE), g_strdup_printf("%d", group->group_type));
+			    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_GROUP_KEY_GROUP_CATEGORY), g_strdup_printf("%d", group->group_category));
@@ -157,12 +157,11 @@
 	    (NULL ==
 	     g_hash_table_lookup(data,
 				 QQ_GROUP_KEY_MEMBER_STATUS) ?
-	     g_strdup_printf("%d",
-			     QQ_GROUP_MEMBER_STATUS_NOT_MEMBER) :
+	     g_strdup_printf("%d", QQ_GROUP_MEMBER_STATUS_NOT_MEMBER) :
 	     g_hash_table_lookup(data, QQ_GROUP_KEY_MEMBER_STATUS));
-	group->internal_group_id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_INTERNAL_ID));
-	group->external_group_id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_EXTERNAL_ID));
-	group->group_type = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_GROUP_KEY_GROUP_TYPE));
+	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));
@@ -179,12 +178,12 @@
 void qq_group_refresh(PurpleConnection *gc, qq_group *group)
 {
 	PurpleChat *chat;
-	gchar *external_group_id;
+	gchar *ext_id;
 	g_return_if_fail(group != NULL);
 
-	external_group_id = g_strdup_printf("%d", group->external_group_id);
-	chat = purple_blist_find_chat(purple_connection_get_account(gc), external_group_id);
-	g_free(external_group_id);
+	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 */
@@ -198,12 +197,12 @@
 				     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->internal_group_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->external_group_id));
+				     g_strdup_printf("%d", group->ext_id));
 		g_hash_table_replace(chat->components,
-				     g_strdup(QQ_GROUP_KEY_GROUP_TYPE), g_strdup_printf("%d", group->group_type));
+				     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,
--- a/libpurple/protocols/qq/group_internal.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.h	Sun Aug 10 22:59:07 2008 +0000
@@ -28,20 +28,20 @@
 #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        "internal_group_id"
-#define QQ_GROUP_KEY_EXTERNAL_ID        "external_group_id"
-#define QQ_GROUP_KEY_GROUP_TYPE         "group_type"
-#define QQ_GROUP_KEY_CREATOR_UID        "creator_uid"
-#define QQ_GROUP_KEY_GROUP_CATEGORY     "group_category"
-#define QQ_GROUP_KEY_AUTH_TYPE          "auth_type"
-#define QQ_GROUP_KEY_GROUP_NAME_UTF8    "group_name_utf8"
-#define QQ_GROUP_KEY_GROUP_DESC_UTF8    "group_desc_utf8"
+#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"
 
 qq_group *qq_group_create_internal_record(PurpleConnection *gc, 
-		guint32 internal_id, guint32 external_id, gchar *group_name_utf8);
-void qq_group_delete_internal_record(qq_data *qd, guint32 internal_group_id);
+		guint32 internal_id, guint32 ext_id, gchar *group_name_utf8);
+void qq_group_delete_internal_record(qq_data *qd, guint32 id);
 
 GHashTable *qq_group_to_hashtable(qq_group *group);
 qq_group *qq_group_from_hashtable(PurpleConnection *gc, GHashTable *data);
--- a/libpurple/protocols/qq/group_join.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Sun Aug 10 22:59:07 2008 +0000
@@ -38,8 +38,10 @@
 #include "group_info.h"
 #include "group_join.h"
 #include "group_opt.h"
-#include "group_network.h"
 #include "group_search.h"
+#include "header_info.h"
+#include "packet_parse.h"
+#include "qq_network.h"
 
 enum {
 	QQ_GROUP_JOIN_OK = 0x01,
@@ -49,24 +51,21 @@
 static void _qq_group_exit_with_gc_and_id(gc_and_uid *g)
 {
 	PurpleConnection *gc;
-	guint32 internal_group_id;
+	guint32 id;
 	qq_group *group;
 
 	gc = g->gc;
-	internal_group_id = g->uid;
+	id = g->uid;
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
-	qq_send_cmd_group_exit_group(gc, group);
+	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, group->id);
 }
 
 /* send packet to join a group without auth */
 void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group)
 {
-	guint8 raw_data[16] = {0};
-	gint bytes = 0;
-
 	g_return_if_fail(group != NULL);
 
 	if (group->my_status == QQ_GROUP_MEMBER_STATUS_NOT_MEMBER) {
@@ -86,25 +85,21 @@
 		break;
 	}
 
-	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP);
-	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
-
-	qq_send_group_cmd(gc, group, raw_data, bytes);
+	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_JOIN, group->id);
 }
 
 static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8)
 {
 	PurpleConnection *gc;
 	qq_group *group;
-	guint32 internal_group_id;
+	guint32 id;
 
 	gc = g->gc;
-	internal_group_id = g->uid;
+	id = g->uid;
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	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", internal_group_id);
+		purple_debug(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);
@@ -118,12 +113,12 @@
 	g_return_if_fail(group != NULL);
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", 
-			"Group (internal id: %d) needs authentication\n", group->internal_group_id);
+			"Group (internal id: %d) needs authentication\n", group->id);
 
 	msg = g_strdup_printf("Group \"%s\" needs authentication\n", group->group_name_utf8);
 	g = g_new0(gc_and_uid, 1);
 	g->gc = gc;
-	g->uid = group->internal_group_id;
+	g->uid = group->id;
 	purple_request_input(gc, NULL, msg,
 			   _("Input request here"),
 			   _("Would you be my friend?"), TRUE, FALSE, NULL,
@@ -139,7 +134,7 @@
 {
 	guint8 *raw_data;
 	gchar *reason_qq;
-	gint bytes, data_len;
+	gint bytes;
 
 	g_return_if_fail(group != NULL);
 
@@ -154,45 +149,22 @@
 		uid = 0;
 	}
 
-	data_len = 10 + strlen(reason_qq) + 1;
-	raw_data = g_newa(guint8, data_len);
+	raw_data = g_newa(guint8, 6 + strlen(reason_qq));
 
 	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP_AUTH);
-	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 	bytes += qq_put8(raw_data + bytes, opt);
 	bytes += qq_put32(raw_data + bytes, uid);
 	bytes += qq_put8(raw_data + bytes, strlen(reason_qq));
 	bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq));
 
-	if (bytes != data_len) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP_AUTH));
-		return;
-	}
-
-	qq_send_group_cmd(gc, group, raw_data, data_len);
-}
-
-/* send a packet to exit a group */
-void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group)
-{
-	guint8 raw_data[16] = {0};
-	gint bytes = 0;
-
-	g_return_if_fail(group != NULL);
-
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_EXIT_GROUP);
-	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
-
-	qq_send_group_cmd(gc, group, raw_data, bytes);
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_AUTH, group->id, raw_data, bytes);
 }
 
 /* If comes here, cmd is OK already */
 void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
-	guint32 internal_group_id;
+	guint32 id;
 	PurpleChat *chat;
 	qq_group *group;
 	qq_data *qd;
@@ -207,15 +179,15 @@
 	}
 
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get32(&id, data + bytes);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	if (group != NULL) {
 		chat = purple_blist_find_chat
-			    (purple_connection_get_account(gc), g_strdup_printf("%d", group->external_group_id));
+			    (purple_connection_get_account(gc), g_strdup_printf("%d", group->ext_id));
 		if (chat != NULL)
 			purple_blist_remove_chat(chat);
-		qq_group_delete_internal_record(qd, internal_group_id);
+		qq_group_delete_internal_record(qd, id);
 	}
 	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
 }
@@ -224,7 +196,7 @@
 void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
-	guint32 internal_group_id;
+	guint32 id;
 	qq_data *qd;
 
 	g_return_if_fail(data != NULL && len > 0);
@@ -236,8 +208,8 @@
 		return;
 	}
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
-	g_return_if_fail(internal_group_id > 0);
+	bytes += qq_get32(&id, data + bytes);
+	g_return_if_fail(id > 0);
 
 	purple_notify_info(gc, _("QQ Group Auth"),
 		     _("Your authorization request has been accepted by the QQ server"), NULL);
@@ -247,7 +219,7 @@
 void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
-	guint32 internal_group_id;
+	guint32 id;
 	guint8 reply;
 	qq_group *group;
 
@@ -260,11 +232,11 @@
 	}
 	
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get32(&id, data + bytes);
 	bytes += qq_get8(&reply, data + bytes);
 
 	/* join group OK */
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	/* need to check if group is NULL or not. */
 	g_return_if_fail(group != NULL);
 	switch (reply) {
@@ -274,12 +246,12 @@
 		qq_group_refresh(gc, group);
 		/* this must be shown before getting online members */
 		qq_group_conv_show_window(gc, group);
-		qq_send_cmd_group_get_group_info(gc, group);
+		qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, group->id);
 		break;
 	case QQ_GROUP_JOIN_NEED_AUTH:
 		purple_debug(PURPLE_DEBUG_INFO, "QQ",
 			   "Fail joining group [%d] %s, needs authentication\n",
-			   group->external_group_id, group->group_name_utf8);
+			   group->ext_id, group->group_name_utf8);
 		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
 		qq_group_refresh(gc, group);
 		_qq_group_join_auth(gc, group);
@@ -287,7 +259,7 @@
 	default:
 		purple_debug(PURPLE_DEBUG_INFO, "QQ",
 			   "Error joining group [%d] %s, unknown reply: 0x%02x\n",
-			   group->external_group_id, group->group_name_utf8, reply);
+			   group->ext_id, group->group_name_utf8, reply);
 	}
 }
 
@@ -295,48 +267,48 @@
 void qq_group_join(PurpleConnection *gc, GHashTable *data)
 {
 	qq_data *qd;
-	gchar *external_group_id_ptr;
-	guint32 external_group_id;
+	gchar *ext_id_ptr;
+	guint32 ext_id;
 	qq_group *group;
 
 	g_return_if_fail(data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	external_group_id_ptr = g_hash_table_lookup(data, QQ_GROUP_KEY_EXTERNAL_ID);
-	g_return_if_fail(external_group_id_ptr != NULL);
+	ext_id_ptr = g_hash_table_lookup(data, QQ_GROUP_KEY_EXTERNAL_ID);
+	g_return_if_fail(ext_id_ptr != NULL);
 	errno = 0;
-	external_group_id = strtol(external_group_id_ptr, NULL, 10);
+	ext_id = strtol(ext_id_ptr, NULL, 10);
 	if (errno != 0) {
 		purple_notify_error(gc, _("Error"),
 				_("You entered a group ID outside the acceptable range"), NULL);
 		return;
 	}
 
-	group = qq_group_find_by_id(gc, external_group_id, QQ_EXTERNAL_ID);
+	group = qq_room_search_ext_id(gc, ext_id);
 	if (group) {
 		qq_send_cmd_group_join_group(gc, group);
 	} else {
-		qq_set_pending_id(&qd->joining_groups, external_group_id, TRUE);
-		qq_send_cmd_group_search_group(gc, external_group_id);
+		qq_set_pending_id(&qd->joining_groups, ext_id, TRUE);
+		qq_send_cmd_group_search_group(gc, ext_id);
 	}
 }
 
 void qq_group_exit(PurpleConnection *gc, GHashTable *data)
 {
-	gchar *internal_group_id_ptr;
-	guint32 internal_group_id;
+	gchar *id_ptr;
+	guint32 id;
 	gc_and_uid *g;
 
 	g_return_if_fail(data != NULL);
 
-	internal_group_id_ptr = g_hash_table_lookup(data, "internal_group_id");
-	internal_group_id = strtol(internal_group_id_ptr, NULL, 10);
+	id_ptr = g_hash_table_lookup(data, QQ_GROUP_KEY_INTERNAL_ID);
+	id = strtol(id_ptr, NULL, 10);
 
-	g_return_if_fail(internal_group_id > 0);
+	g_return_if_fail(id > 0);
 
 	g = g_new0(gc_and_uid, 1);
 	g->gc = gc;
-	g->uid = internal_group_id;
+	g->uid = id;
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("Are you sure you want to leave this Qun?"),
--- a/libpurple/protocols/qq/group_join.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_join.h	Sun Aug 10 22:59:07 2008 +0000
@@ -45,7 +45,6 @@
 void qq_group_join(PurpleConnection *gc, GHashTable *data);
 void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group);
 void qq_group_exit(PurpleConnection *gc, GHashTable *data);
-void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group);
 void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc);
--- a/libpurple/protocols/qq/group_network.c	Sun Aug 10 22:08:39 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/**
- * @file group_network.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "internal.h"
-
-#include "debug.h"
-#include "notify.h"
-
-#include "char_conv.h"
-#include "crypt.h"
-#include "group_conv.h"
-#include "group_find.h"
-#include "group_internal.h"
-#include "group_im.h"
-#include "group_info.h"
-#include "group_join.h"
-#include "group_network.h"
-#include "group_opt.h"
-#include "group_search.h"
-#include "header_info.h"
-#include "qq_network.h"
-#include "utils.h"
-
-enum {
-	QQ_GROUP_CMD_REPLY_OK = 0x00,
-	QQ_GROUP_CMD_REPLY_SEARCH_ERROR = 0x02,
-	QQ_GROUP_CMD_REPLY_NOT_MEMBER = 0x0a
-};
-
-const gchar *qq_group_cmd_get_desc(qq_group_cmd cmd)
-{
-	switch (cmd) {
-	case QQ_GROUP_CMD_CREATE_GROUP:
-		return "QQ_GROUP_CMD_CREATE_GROUP";
-	case QQ_GROUP_CMD_MEMBER_OPT:
-		return "QQ_GROUP_CMD_MEMBER_OPT";
-	case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
-		return "QQ_GROUP_CMD_MODIFY_GROUP_INFO";
-	case QQ_GROUP_CMD_GET_GROUP_INFO:
-		return "QQ_GROUP_CMD_GET_GROUP_INFO";
-	case QQ_GROUP_CMD_ACTIVATE_GROUP:
-		return "QQ_GROUP_CMD_ACTIVATE_GROUP";
-	case QQ_GROUP_CMD_SEARCH_GROUP:
-		return "QQ_GROUP_CMD_SEARCH_GROUP";
-	case QQ_GROUP_CMD_JOIN_GROUP:
-		return "QQ_GROUP_CMD_JOIN_GROUP";
-	case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
-		return "QQ_GROUP_CMD_JOIN_GROUP_AUTH";
-	case QQ_GROUP_CMD_EXIT_GROUP:
-		return "QQ_GROUP_CMD_EXIT_GROUP";
-	case QQ_GROUP_CMD_SEND_MSG:
-		return "QQ_GROUP_CMD_SEND_MSG";
-	case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
-		return "QQ_GROUP_CMD_GET_ONLINE_MEMBER";
-	case QQ_GROUP_CMD_GET_MEMBER_INFO:
-		return "QQ_GROUP_CMD_GET_MEMBER_INFO";
-	case QQ_GROUP_CMD_MODIFY_CARD:
-		return "QQ_GROUP_CMD_MODIFY_CARD";
-	case QQ_GROUP_CMD_REQUEST_ALL_REALNAMES:
-		return "QQ_GROUP_CMD_REQUEST_ALL_REALNAMES";
-	case QQ_GROUP_CMD_REQUEST_CARD:
-		return "QQ_GROUP_CMD_REQUEST_CARD";
-	case QQ_GROUP_CMD_SEND_IM_EX:
-		return "QQ_GROUP_CMD_SEND_IM_EX";
-	case QQ_GROUP_CMD_ADMIN:
-		return "QQ_GROUP_CMD_ADMIN";
-	case QQ_GROUP_CMD_TRANSFER:
-		return "QQ_GROUP_CMD_TRANSFER";
-	case QQ_GROUP_CMD_CREATE_TEMP_QUN:
-		return "QQ_GROUP_CMD_CREATE_TEMP_QUN";
-	case QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER:
-		return "QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER";
-	case QQ_GROUP_CMD_EXIT_TEMP_QUN:
-		return "QQ_GROUP_CMD_EXIT_TEMP_QUN";
-	case QQ_GROUP_CMD_GET_TEMP_QUN_INFO:
-		return "QQ_GROUP_CMD_GET_TEMP_QUN_INFO";
-	case QQ_GROUP_CMD_SEND_TEMP_QUN_IM:
-		return "QQ_GROUP_CMD_SEND_TEMP_QUN_IM";
-	case QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS:
-		return "QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS";
-	default:
-		return "Unknown QQ Group Command";
-	}
-}
-
-/* default process of reply error */
-static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *data, gint len, PurpleConnection *gc)
-{
-	gchar *msg, *msg_utf8;
-	g_return_if_fail(data != NULL && len > 0);
-
-	msg = g_strndup((gchar *) data, len);	/* it will append 0x00 */
-	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-	g_free(msg);
-	msg = g_strdup_printf(_("Code [0x%02X]: %s"), reply, msg_utf8);
-	purple_notify_error(gc, NULL, _("Group Operation Error"), msg);
-	g_free(msg);
-	g_free(msg_utf8);
-}
-
-/* default process, dump only */
-static void _qq_process_group_cmd_reply_default(guint8 *data, gint len, PurpleConnection *gc)
-{
-	g_return_if_fail(data != NULL && len > 0);
-
-	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
-		data, len, 
-		"Dump unprocessed group cmd reply:");
-}
-
-/* The lower layer command of send group cmd */
-void qq_send_group_cmd(PurpleConnection *gc, qq_group *group, guint8 *raw_data, gint data_len)
-{
-	qq_data *qd;
-	group_packet *p;
-
-	g_return_if_fail(raw_data != NULL && data_len > 0);
-
-	qd = (qq_data *) gc->proto_data;
-
-	qq_send_cmd(qd, QQ_CMD_GROUP_CMD, raw_data, data_len);
-
-	p = g_new0(group_packet, 1);
-
-	p->send_seq = qd->send_seq;
-	if (group == NULL)
-		p->internal_group_id = 0;
-	else
-		p->internal_group_id = group->internal_group_id;
-
-	qd->group_packets = g_list_append(qd->group_packets, p);
-}
-
-/* the main entry of group cmd processing, called by qq_recv_core.c */
-void qq_process_group_cmd_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
-{
-	qq_group *group;
-	qq_data *qd;
-	gint len, bytes;
-	guint32 internal_group_id;
-	guint8 *data, sub_cmd, reply;
-
-	g_return_if_fail(buf != NULL && buf_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	if (!qq_group_find_internal_group_id_by_seq(gc, seq, &internal_group_id)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "We have no record of group cmd, seq [%d]\n", seq);
-		return;
-	}
-
-	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n");
-		return;
-	}
-
-	if (len <= 2) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len);
-		return;
-	}
-
-	bytes = 0;
-	bytes += qq_get8(&sub_cmd, data + bytes);
-	bytes += qq_get8(&reply, data + bytes);
-
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
-
-	if (reply != QQ_GROUP_CMD_REPLY_OK) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd));
-
-		if (group != NULL)
-			qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
-
-		switch (reply) {	/* this should be all errors */
-		case QQ_GROUP_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;
-				qq_group_refresh(gc, group);
-			}
-			break;
-		case QQ_GROUP_CMD_REPLY_SEARCH_ERROR:
-			if (qd->roomlist != NULL) {
-				if (purple_roomlist_get_in_progress(qd->roomlist))
-					purple_roomlist_set_in_progress(qd->roomlist, FALSE);
-			}
-			_qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
-			break;
-		default:
-			_qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
-		}
-		return;
-	}
-
-	/* seems ok so far, so we process the reply according to sub_cmd */
-	switch (sub_cmd) {
-	case QQ_GROUP_CMD_GET_GROUP_INFO:
-		qq_process_group_cmd_get_group_info(data + bytes, 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_GROUP_CMD_CREATE_GROUP:
-		qq_group_process_create_group_reply(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
-		qq_group_process_modify_info_reply(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_MEMBER_OPT:
-		qq_group_process_modify_members_reply(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_ACTIVATE_GROUP:
-		qq_group_process_activate_group_reply(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_SEARCH_GROUP:
-		qq_process_group_cmd_search_group(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_JOIN_GROUP:
-		qq_process_group_cmd_join_group(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
-		qq_process_group_cmd_join_group_auth(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_EXIT_GROUP:
-		qq_process_group_cmd_exit_group(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_SEND_MSG:
-		qq_process_group_cmd_im(data + bytes, len - bytes, gc);
-		break;
-	case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
-		qq_process_group_cmd_get_online_members(data + bytes, len - bytes, gc);
-		if (group != NULL)
-			qq_group_conv_refresh_online_member(gc, group);
-		break;
-	case QQ_GROUP_CMD_GET_MEMBER_INFO:
-		qq_process_group_cmd_get_members_info(data + bytes, len - bytes, gc);
-		if (group != NULL)
-			qq_group_conv_refresh_online_member(gc, group);
-		break;
-	default:
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd));
-		_qq_process_group_cmd_reply_default(data + bytes, len, gc);
-	}
-}
--- a/libpurple/protocols/qq/group_network.h	Sun Aug 10 22:08:39 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/**
- * @file group_network.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#ifndef _QQ_GROUP_NETWORK_H_
-#define _QQ_GROUP_NETWORK_H_
-
-#include <glib.h>
-#include "connection.h"
-#include "group.h"
-#include "packet_parse.h"
-
-typedef enum {
-	QQ_GROUP_CMD_CREATE_GROUP = 0x01,
-	QQ_GROUP_CMD_MEMBER_OPT = 0x02,
-	QQ_GROUP_CMD_MODIFY_GROUP_INFO = 0x03,
-	QQ_GROUP_CMD_GET_GROUP_INFO = 0x04,
-	QQ_GROUP_CMD_ACTIVATE_GROUP = 0x05,
-	QQ_GROUP_CMD_SEARCH_GROUP = 0x06,
-	QQ_GROUP_CMD_JOIN_GROUP = 0x07,
-	QQ_GROUP_CMD_JOIN_GROUP_AUTH = 0x08,
-	QQ_GROUP_CMD_EXIT_GROUP = 0x09,
-	QQ_GROUP_CMD_SEND_MSG = 0x0a,
-	QQ_GROUP_CMD_GET_ONLINE_MEMBER = 0x0b,
-	QQ_GROUP_CMD_GET_MEMBER_INFO = 0x0c,
-
-	QQ_GROUP_CMD_MODIFY_CARD = 0x0E,
-	QQ_GROUP_CMD_REQUEST_ALL_REALNAMES = 0x0F,
-	QQ_GROUP_CMD_REQUEST_CARD = 0x10,
-	QQ_GROUP_CMD_SEND_IM_EX = 0x1A,
-	QQ_GROUP_CMD_ADMIN = 0x1B,
-	QQ_GROUP_CMD_TRANSFER = 0x1C,
-	QQ_GROUP_CMD_CREATE_TEMP_QUN = 0x30,
-	QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER = 0x31,
-	QQ_GROUP_CMD_EXIT_TEMP_QUN = 0x32,
-	QQ_GROUP_CMD_GET_TEMP_QUN_INFO = 0x33,
-	QQ_GROUP_CMD_SEND_TEMP_QUN_IM = 0x35,
-	QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS = 0x37,
-} qq_group_cmd;
-
-typedef struct _group_packet {
-	guint16 send_seq;
-	guint32 internal_group_id;
-} group_packet;
-
-const gchar *qq_group_cmd_get_desc(qq_group_cmd cmd);
-
-void qq_send_group_cmd(PurpleConnection *gc, qq_group *group, guint8 *raw_data, gint data_len);
-void qq_process_group_cmd_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc);
-
-#endif
--- a/libpurple/protocols/qq/group_opt.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Sun Aug 10 22:59:07 2008 +0000
@@ -34,9 +34,10 @@
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_join.h"
-#include "group_network.h"
 #include "group_opt.h"
+#include "header_info.h"
 #include "packet_parse.h"
+#include "qq_network.h"
 #include "utils.h"
 
 static int _compare_guint32(const void *a,
@@ -68,13 +69,11 @@
 	data = g_newa(guint8, data_len);
 	
 	bytes = 0;
-	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MEMBER_OPT);
-	bytes += qq_put32(data + bytes, group->internal_group_id);
 	bytes += qq_put8(data + bytes, operation);
 	for (i = 0; i < count; i++)
 		bytes += qq_put32(data + bytes, members[i]);
 
-	qq_send_group_cmd(gc, group, data, bytes);
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_MEMBER_OPT, group->id, data, bytes);
 }
 
 static void _qq_group_do_nothing_with_struct(group_member_opt *g)
@@ -86,8 +85,8 @@
 static void _qq_group_reject_application_real(group_member_opt *g, gchar *msg_utf8)
 {
 	qq_group *group;
-	g_return_if_fail(g != NULL && g->gc != NULL && g->internal_group_id > 0 && g->member > 0);
-	group = qq_group_find_by_id(g->gc, g->internal_group_id, QQ_INTERNAL_ID);
+	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);
 	g_free(g);
@@ -131,8 +130,8 @@
 void qq_group_approve_application_with_struct(group_member_opt *g)
 {
 	qq_group *group;
-	g_return_if_fail(g != NULL && g->gc != NULL && g->internal_group_id > 0 && g->member > 0);
-	group = qq_group_find_by_id(g->gc, g->internal_group_id, QQ_INTERNAL_ID);
+	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_group_find_or_add_member(g->gc, group, g->member);
@@ -198,24 +197,24 @@
 void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
-	guint32 internal_group_id;
+	guint32 id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
-	g_return_if_fail(internal_group_id > 0);
+	bytes += qq_get32(&id, data + bytes);
+	g_return_if_fail(id > 0);
 
 	/* we should have its info locally */
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	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->external_group_id);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in modify members for Qun %d\n", group->ext_id);
 
 	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully modified Qun member"), NULL);
 }
 
-void qq_group_modify_info(PurpleConnection *gc, qq_group *group)
+void qq_room_change_info(PurpleConnection *gc, qq_group *group)
 {
 	guint8 *data;
 	gint data_len;
@@ -228,16 +227,9 @@
 	group_desc = group->group_desc_utf8 == NULL ? "" : utf8_to_qq(group->group_desc_utf8, QQ_CHARSET_DEFAULT);
 	notice = group->notice_utf8 == NULL ? "" : utf8_to_qq(group->notice_utf8, QQ_CHARSET_DEFAULT);
 
-	data_len = 13 + 1 + strlen(group_name)
-	    + 1 + strlen(group_desc)
-	    + 1 + strlen(notice);
-
+	data_len = 64 + strlen(group_name) + strlen(group_desc) + strlen(notice);
 	data = g_newa(guint8, data_len);
 	bytes = 0;
-	/* 000-000 */
-	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MODIFY_GROUP_INFO);
-	/* 001-004 */
-	bytes += qq_put32(data + bytes, group->internal_group_id);
 	/* 005-005 */
 	bytes += qq_put8(data + bytes, 0x01);
 	/* 006-006 */
@@ -258,54 +250,52 @@
 	bytes += qq_put8(data + bytes, strlen(group_desc));
 	bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc));
 
-	if (bytes != data_len)	{
+	if (bytes > data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail to create group_modify_info packet, expect %d bytes, wrote %d bytes\n",
+			   "Overflow in qq_room_change_info, max %d bytes, now %d bytes\n",
 			   data_len, bytes);
 		return;
 	}
-
-	qq_send_group_cmd(gc, group, data, bytes);
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_CHANGE_INFO, group->id, data, bytes);
 }
 
 void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
-	guint32 internal_group_id;
+	guint32 id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
-	g_return_if_fail(internal_group_id > 0);
+	bytes += qq_get32(&id, data + bytes);
+	g_return_if_fail(id > 0);
 
 	/* we should have its info locally */
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	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->external_group_id);
+	purple_debug(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);
 }
 
 /* we create a very simple group first, and then let the user to modify */
-void qq_group_create_with_name(PurpleConnection *gc, const gchar *name)
+void qq_room_create_new(PurpleConnection *gc, const gchar *name)
 {
+	guint8 *data;
 	gint data_len;
-	guint8 *data;
 	gint bytes;
 	qq_data *qd;
 	g_return_if_fail(name != NULL);
 
 	qd = (qq_data *) gc->proto_data;
-	data_len = 7 + 1 + strlen(name) + 2 + 1 + 1 + 4;
+
+	data_len = 64 + strlen(name);
 	data = g_newa(guint8, data_len);
 
 	bytes = 0;
 	/* we create the simpleset group, only group name is given */
-	/* 000 */
-	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_CREATE_GROUP);
 	/* 001 */
 	bytes += qq_put8(data + bytes, QQ_GROUP_TYPE_PERMANENT);
 	/* 002 */
@@ -322,14 +312,13 @@
 	bytes += qq_put8(data + bytes, 0x00);	/* no group desc */
 	bytes += qq_put32(data + bytes, qd->uid);	/* I am member of coz */
 
-	if (bytes != data_len) {
+	if (bytes > data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create create_group packet, expect %d bytes, written %d bytes\n",
+			   "Overflow in qq_room_create, max %d bytes, now %d bytes\n",
 			   data_len, bytes);
 		return;
 	}
-
-	qq_send_group_cmd(gc, NULL, data, bytes);
+	qq_send_room_cmd_noid(gc, QQ_ROOM_CMD_CREATE, data, bytes);
 }
 
 static void qq_group_setup_with_gc_and_uid(gc_and_uid *g)
@@ -337,7 +326,7 @@
 	qq_group *group;
 	g_return_if_fail(g != NULL && g->gc != NULL && g->uid > 0);
 
-	group = qq_group_find_by_id(g->gc, g->uid, QQ_INTERNAL_ID);
+	group = qq_room_search_id(g->gc, g->uid);
 	g_return_if_fail(group != NULL);
 
 	/* TODO insert UI code here */
@@ -348,7 +337,7 @@
 void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
-	guint32 internal_group_id, external_group_id;
+	guint32 id, ext_id;
 	qq_group *group;
 	gc_and_uid *g;
 	qq_data *qd;
@@ -358,23 +347,23 @@
 	qd = (qq_data *) gc->proto_data;
 
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
-	bytes += qq_get32(&external_group_id, data + bytes);
-	g_return_if_fail(internal_group_id > 0 && external_group_id);
+	bytes += qq_get32(&id, data + bytes);
+	bytes += qq_get32(&ext_id, data + bytes);
+	g_return_if_fail(id > 0 && ext_id);
 
-	group = qq_group_create_internal_record(gc, internal_group_id, external_group_id, NULL);
+	group = qq_group_create_internal_record(gc, id, ext_id, NULL);
 	group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN;
 	group->creator_uid = qd->uid;
 	qq_group_refresh(gc, group);
 
-	qq_group_activate_group(gc, internal_group_id);
-	qq_send_cmd_group_get_group_info(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);
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in create Qun, external ID %d\n", group->external_group_id);
+	purple_debug(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;
-	g->uid = internal_group_id;
+	g->uid = id;
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("You have successfully created a Qun"),
@@ -387,54 +376,37 @@
 			    _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid));
 }
 
-/* we have to activate group after creation, otherwise the group can not be searched */
-void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id)
-{
-	guint8 data[16] = {0};
-	gint bytes = 0;
-	g_return_if_fail(internal_group_id > 0);
-
-	bytes = 0;
-	/* we create the simplest group, only group name is given */
-	/* 000 */
-	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_ACTIVATE_GROUP);
-	/* 001-005 */
-	bytes += qq_put32(data + bytes, internal_group_id);
-
-	qq_send_group_cmd(gc, NULL, data, bytes);
-}
-
 void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
-	guint32 internal_group_id;
+	guint32 id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
 	bytes = 0;
-	bytes += qq_get32(&internal_group_id, data + bytes);
-	g_return_if_fail(internal_group_id > 0);
+	bytes += qq_get32(&id, data + bytes);
+	g_return_if_fail(id > 0);
 
 	/* we should have its info locally */
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	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->external_group_id);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed in activate Qun %d\n", group->ext_id);
 }
 
 void qq_group_manage_group(PurpleConnection *gc, GHashTable *data)
 {
-	gchar *internal_group_id_ptr;
-	guint32 internal_group_id;
+	gchar *id_ptr;
+	guint32 id;
 	qq_group *group;
 
 	g_return_if_fail(data != NULL);
 
-	internal_group_id_ptr = g_hash_table_lookup(data, "internal_group_id");
-	internal_group_id = strtol(internal_group_id_ptr, NULL, 10);
-	g_return_if_fail(internal_group_id > 0);
+	id_ptr = g_hash_table_lookup(data, QQ_GROUP_KEY_INTERNAL_ID);
+	id = strtol(id_ptr, NULL, 10);
+	g_return_if_fail(id > 0);
 
-	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	group = qq_room_search_id(gc, id);
 	g_return_if_fail(group != NULL);
 
 	/* XXX insert UI code here */
--- a/libpurple/protocols/qq/group_opt.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.h	Sun Aug 10 22:59:07 2008 +0000
@@ -33,7 +33,7 @@
 
 typedef struct _group_member_opt {
 	PurpleConnection *gc;
-	guint32 internal_group_id;
+	guint32 id;
 	guint32 member;
 } group_member_opt;
 
@@ -48,7 +48,7 @@
 };
 
 void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new_members);
-void qq_group_modify_info(PurpleConnection *gc, qq_group *group);
+void qq_room_change_info(PurpleConnection *gc, qq_group *group);
 
 void qq_group_approve_application_with_struct(group_member_opt *g);
 void qq_group_reject_application_with_struct(group_member_opt *g);
@@ -57,8 +57,7 @@
 void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc);
 void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc);
 void qq_group_manage_group(PurpleConnection *gc, GHashTable *data);
-void qq_group_create_with_name(PurpleConnection *gc, const gchar *name);
-void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id);
+void qq_room_create_new(PurpleConnection *gc, const gchar *name);
 void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc);
 void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc);
 
--- a/libpurple/protocols/qq/group_search.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/group_search.c	Sun Aug 10 22:59:07 2008 +0000
@@ -31,9 +31,11 @@
 #include "group_free.h"
 #include "group_internal.h"
 #include "group_join.h"
-#include "group_network.h"
 #include "group_search.h"
 #include "utils.h"
+#include "header_info.h"
+#include "packet_parse.h"
+#include "qq_network.h"
 
 enum {
 	QQ_GROUP_SEARCH_TYPE_BY_ID = 0x01,
@@ -41,20 +43,19 @@
 };
 
 /* send packet to search for qq_group */
-void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id)
+void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 ext_id)
 {
 	guint8 raw_data[16] = {0};
 	gint bytes = 0;
 	guint8 type;
 
-	type = (external_group_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID;
+	type = (ext_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID;
 
 	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEARCH_GROUP);
 	bytes += qq_put8(raw_data + bytes, type);
-	bytes += qq_put32(raw_data + bytes, external_group_id);
+	bytes += qq_put32(raw_data + bytes, ext_id);
 
-	qq_send_group_cmd(gc, NULL, raw_data, bytes);
+	qq_send_room_cmd_noid(gc, QQ_ROOM_CMD_SEARCH, raw_data, bytes);
 }
 
 static void _qq_setup_roomlist(qq_data *qd, qq_group *group)
@@ -63,14 +64,14 @@
 	gchar field[11];
 
 	room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, group->group_name_utf8, NULL);
-	g_snprintf(field, sizeof(field), "%d", group->external_group_id);
+	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);
-	g_snprintf(field, sizeof(field), "%d", group->internal_group_id);
+	g_snprintf(field, sizeof(field), "%d", group->id);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	g_snprintf(field, sizeof(field), "%d", group->group_type);
+	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);
@@ -99,9 +100,9 @@
 	bytes += qq_get8(&search_type, data + bytes);
 
 	/* now it starts with group_info_entry */
-	bytes += qq_get32(&(group.internal_group_id), data + bytes);
-	bytes += qq_get32(&(group.external_group_id), data + bytes);
-	bytes += qq_get8(&(group.group_type), data + bytes);
+	bytes += qq_get32(&(group.id), data + bytes);
+	bytes += qq_get32(&(group.ext_id), data + bytes);
+	bytes += qq_get8(&(group.type8), data + bytes);
 	bytes += qq_get16(&(unknown), data + bytes);
 	bytes += qq_get16(&(unknown), data + bytes);
 	bytes += qq_get32(&(group.creator_uid), data + bytes);
@@ -119,12 +120,12 @@
 			"group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
-	pending_id = qq_get_pending_id(qd->joining_groups, group.external_group_id);
+	pending_id = qq_get_pending_id(qd->joining_groups, group.ext_id);
 	if (pending_id != NULL) {
-		qq_set_pending_id(&qd->joining_groups, group.external_group_id, FALSE);
-		if (qq_group_find_by_id(gc, group.internal_group_id, QQ_INTERNAL_ID) == NULL)
+		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.internal_group_id, group.external_group_id, group.group_name_utf8);
+					group.id, group.ext_id, group.group_name_utf8);
 		qq_send_cmd_group_join_group(gc, &group);
 	} else {
 		_qq_setup_roomlist(qd, &group);
--- a/libpurple/protocols/qq/header_info.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/header_info.c	Sun Aug 10 22:59:07 2008 +0000
@@ -61,58 +61,6 @@
 
 #define QQ_SERVER_0100 0x0100	/* server */
 
-/* given command alias, return the command name accordingly */
-const gchar *qq_get_cmd_desc(gint type)
-{
-	switch (type) {
-	case QQ_CMD_LOGOUT:
-		return "QQ_CMD_LOGOUT";
-	case QQ_CMD_KEEP_ALIVE:
-		return "QQ_CMD_KEEP_ALIVE";
-	case QQ_CMD_UPDATE_INFO:
-		return "QQ_CMD_UPDATE_INFO";
-	case QQ_CMD_SEARCH_USER:
-		return "QQ_CMD_SEARCH_USER";
-	case QQ_CMD_GET_USER_INFO:
-		return "QQ_CMD_GET_USER_INFO";
-	case QQ_CMD_ADD_BUDDY_WO_AUTH:
-		return "QQ_CMD_ADD_BUDDY_WO_AUTH";
-	case QQ_CMD_DEL_BUDDY:
-		return "QQ_CMD_DEL_BUDDY";
-	case QQ_CMD_BUDDY_AUTH:
-		return "QQ_CMD_BUDDY_AUTH";
-	case QQ_CMD_CHANGE_ONLINE_STATUS:
-		return "QQ_CMD_CHANGE_ONLINE_STATUS";
-	case QQ_CMD_ACK_SYS_MSG:
-		return "QQ_CMD_ACK_SYS_MSG";
-	case QQ_CMD_SEND_IM:
-		return "QQ_CMD_SEND_IM";
-	case QQ_CMD_RECV_IM:
-		return "QQ_CMD_RECV_IM";
-	case QQ_CMD_REMOVE_SELF:
-		return "QQ_CMD_REMOVE_SELF";
-	case QQ_CMD_LOGIN:
-		return "QQ_CMD_LOGIN";
-	case QQ_CMD_GET_BUDDIES_LIST:
-		return "QQ_CMD_GET_BUDDIES_LIST";
-	case QQ_CMD_GET_BUDDIES_ONLINE:
-		return "QQ_CMD_GET_BUDDIES_ONLINE";
-	case QQ_CMD_GROUP_CMD:
-		return "QQ_CMD_GROUP_CMD";
-	case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
-		return "QQ_CMD_GET_ALL_LIST_WITH_GROUP";
-	case QQ_CMD_GET_LEVEL:
-		return "QQ_CMD_GET_LEVEL";
-	case QQ_CMD_TOKEN:
-		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";
-	default:
-		return "Unknown";
-	}
-}
 
 /* given source tag, return its description accordingly */
 const gchar *qq_get_ver_desc(gint source)
@@ -177,3 +125,112 @@
 		return "Unknown";
 	}
 }
+
+/* given command alias, return the command name accordingly */
+const gchar *qq_get_cmd_desc(gint cmd)
+{
+	switch (cmd) {
+	case QQ_CMD_LOGOUT:
+		return "QQ_CMD_LOGOUT";
+	case QQ_CMD_KEEP_ALIVE:
+		return "QQ_CMD_KEEP_ALIVE";
+	case QQ_CMD_UPDATE_INFO:
+		return "QQ_CMD_UPDATE_INFO";
+	case QQ_CMD_SEARCH_USER:
+		return "QQ_CMD_SEARCH_USER";
+	case QQ_CMD_GET_USER_INFO:
+		return "QQ_CMD_GET_USER_INFO";
+	case QQ_CMD_ADD_BUDDY_WO_AUTH:
+		return "QQ_CMD_ADD_BUDDY_WO_AUTH";
+	case QQ_CMD_DEL_BUDDY:
+		return "QQ_CMD_DEL_BUDDY";
+	case QQ_CMD_BUDDY_AUTH:
+		return "QQ_CMD_BUDDY_AUTH";
+	case QQ_CMD_CHANGE_ONLINE_STATUS:
+		return "QQ_CMD_CHANGE_ONLINE_STATUS";
+	case QQ_CMD_ACK_SYS_MSG:
+		return "QQ_CMD_ACK_SYS_MSG";
+	case QQ_CMD_SEND_IM:
+		return "QQ_CMD_SEND_IM";
+	case QQ_CMD_RECV_IM:
+		return "QQ_CMD_RECV_IM";
+	case QQ_CMD_REMOVE_SELF:
+		return "QQ_CMD_REMOVE_SELF";
+	case QQ_CMD_LOGIN:
+		return "QQ_CMD_LOGIN";
+	case QQ_CMD_GET_BUDDIES_LIST:
+		return "QQ_CMD_GET_BUDDIES_LIST";
+	case QQ_CMD_GET_BUDDIES_ONLINE:
+		return "QQ_CMD_GET_BUDDIES_ONLINE";
+	case QQ_CMD_ROOM:
+		return "QQ_CMD_ROOM";
+	case QQ_CMD_GET_BUDDIES_AND_ROOMS:
+		return "QQ_CMD_GET_BUDDIES_AND_ROOMS";
+	case QQ_CMD_GET_LEVEL:
+		return "QQ_CMD_GET_LEVEL";
+	case QQ_CMD_TOKEN:
+		return "QQ_CMD_TOKEN";
+	case QQ_CMD_RECV_MSG_SYS:
+		return "QQ_CMD_RECV_MSG_SYS";
+	case QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS:
+		return "QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS";
+	default:
+		return "Unknown";
+	}
+}
+
+const gchar *qq_get_room_cmd_desc(gint room_cmd)
+{
+	switch (room_cmd) {
+	case QQ_ROOM_CMD_CREATE:
+		return "QQ_ROOM_CMD_CREATE";
+	case QQ_ROOM_CMD_MEMBER_OPT:
+		return "QQ_ROOM_CMD_MEMBER_OPT";
+	case QQ_ROOM_CMD_CHANGE_INFO:
+		return "QQ_ROOM_CMD_CHANGE_INFO";
+	case QQ_ROOM_CMD_GET_INFO:
+		return "QQ_ROOM_CMD_GET_INFO";
+	case QQ_ROOM_CMD_ACTIVATE:
+		return "QQ_ROOM_CMD_ACTIVATE";
+	case QQ_ROOM_CMD_SEARCH:
+		return "QQ_ROOM_CMD_SEARCH";
+	case QQ_ROOM_CMD_JOIN:
+		return "QQ_ROOM_CMD_JOIN";
+	case QQ_ROOM_CMD_AUTH:
+		return "QQ_ROOM_CMD_AUTH";
+	case QQ_ROOM_CMD_QUIT:
+		return "QQ_ROOM_CMD_QUIT";
+	case QQ_ROOM_CMD_SEND_MSG:
+		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_CHANGE_CARD:
+		return "QQ_ROOM_CMD_CHANGE_CARD";
+	case QQ_ROOM_CMD_GET_REALNAMES:
+		return "QQ_ROOM_CMD_GET_REALNAMES";
+	case QQ_ROOM_CMD_GET_CARD:
+		return "QQ_ROOM_CMD_GET_CARD";
+	case QQ_ROOM_CMD_SEND_IM_EX:
+		return "QQ_ROOM_CMD_SEND_IM_EX";
+	case QQ_ROOM_CMD_ADMIN:
+		return "QQ_ROOM_CMD_ADMIN";
+	case QQ_ROOM_CMD_TRANSFER:
+		return "QQ_ROOM_CMD_TRANSFER";
+	case QQ_ROOM_CMD_TEMP_CREATE:
+		return "QQ_ROOM_CMD_TEMP_CREATE";
+	case QQ_ROOM_CMD_TEMP_CHANGE_MEMBER:
+		return "QQ_ROOM_CMD_TEMP_CHANGE_MEMBER";
+	case QQ_ROOM_CMD_TEMP_QUIT:
+		return "QQ_ROOM_CMD_TEMP_QUIT";
+	case QQ_ROOM_CMD_TEMP_GET_INFO:
+		return "QQ_ROOM_CMD_TEMP_GET_INFO";
+	case QQ_ROOM_CMD_TEMP_SEND_IM:
+		return "QQ_ROOM_CMD_TEMP_SEND_IM";
+	case QQ_ROOM_CMD_TEMP_GET_MEMBERS:
+		return "QQ_ROOM_CMD_TEMP_GET_MEMBERS";
+	default:
+		return "Unknown QQ Room Command";
+	}
+}
--- a/libpurple/protocols/qq/header_info.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/header_info.h	Sun Aug 10 22:59:07 2008 +0000
@@ -35,6 +35,8 @@
 
 #define QQ_CLIENT       0x0d55
 
+const gchar *qq_get_ver_desc(gint source);
+
 /* list of known QQ commands */
 enum {
 	QQ_CMD_LOGOUT = 0x0001,				/* log out */
@@ -56,8 +58,8 @@
 	QQ_CMD_GET_BUDDIES_LIST = 0x0026,		/* get buddies list */
 	QQ_CMD_GET_BUDDIES_ONLINE = 0x0027,		/* get online buddies list */
 	QQ_CMD_CELL_PHONE_2 = 0x0029,			/* cell phone 2 */
-	QQ_CMD_GROUP_CMD = 0x0030,			/* group command */
-	QQ_CMD_GET_ALL_LIST_WITH_GROUP = 0x0058,  
+	QQ_CMD_ROOM = 0x0030,			/* room command */
+	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 */
@@ -66,6 +68,34 @@
 
 const gchar *qq_get_cmd_desc(gint type);
 
-const gchar *qq_get_ver_desc(gint source);
+enum {
+	QQ_ROOM_CMD_CREATE = 0x01,
+	QQ_ROOM_CMD_MEMBER_OPT = 0x02,
+	QQ_ROOM_CMD_CHANGE_INFO = 0x03,
+	QQ_ROOM_CMD_GET_INFO = 0x04,
+	QQ_ROOM_CMD_ACTIVATE = 0x05,
+	QQ_ROOM_CMD_SEARCH = 0x06,
+	QQ_ROOM_CMD_JOIN = 0x07,
+	QQ_ROOM_CMD_AUTH = 0x08,
+	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_CHANGE_CARD = 0x0E,
+	QQ_ROOM_CMD_GET_REALNAMES = 0x0F,
+	QQ_ROOM_CMD_GET_CARD = 0x10,
+	QQ_ROOM_CMD_SEND_IM_EX = 0x1A,
+	QQ_ROOM_CMD_ADMIN = 0x1B,
+	QQ_ROOM_CMD_TRANSFER = 0x1C,
+	QQ_ROOM_CMD_TEMP_CREATE = 0x30,
+	QQ_ROOM_CMD_TEMP_CHANGE_MEMBER = 0x31,
+	QQ_ROOM_CMD_TEMP_QUIT = 0x32,
+	QQ_ROOM_CMD_TEMP_GET_INFO = 0x33,
+	QQ_ROOM_CMD_TEMP_SEND_IM = 0x35,
+	QQ_ROOM_CMD_TEMP_GET_MEMBERS = 0x37,
+};
+
+const gchar *qq_get_room_cmd_desc(gint room_cmd);
 
 #endif
--- a/libpurple/protocols/qq/im.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Sun Aug 10 22:59:07 2008 +0000
@@ -35,7 +35,6 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
-#include "crypt.h"
 #include "group_im.h"
 #include "header_info.h"
 #include "im.h"
@@ -541,52 +540,35 @@
 }
 
 /* parse the reply to send_im */
-void qq_process_send_im_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len;
-	guint8 *data, reply;
-	gint bytes = 0;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		bytes += qq_get8(&reply, data + bytes);
-		if (reply != QQ_SEND_IM_REPLY_OK) {
-			purple_debug(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");
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt send im reply\n");
+	if (data[0] != QQ_SEND_IM_REPLY_OK) {
+		purple_debug(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");
 	}
 }
 
 /* I receive a message, mainly it is text msg,
  * but we need to proess other types (group etc) */
-void qq_process_recv_im(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
+void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len, bytes;
-	guint8 *data;
+	gint bytes;
 	qq_recv_im_header *im_header;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
 
-	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n");
-	}
-
-	if (len < 16) {	/* we need to ack with the first 16 bytes */
+	if (data_len < 16) {	/* we need to ack with the first 16 bytes */
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
 		return;
 	} else {
@@ -594,9 +576,9 @@
 	}
 
 	/* check len first */
-	if (len < 20) {	/* length of im_header */
+	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", len);
+				"Fail read recv IM header, len should longer than 20 bytes, read %d bytes\n", data_len);
 		return;
 	}
 
@@ -617,7 +599,7 @@
 	}
 
 	/* check bytes */
-	if (bytes >= len - 1) {
+	if (bytes >= data_len - 1) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received IM is empty\n");
 		return;
 	}
@@ -626,57 +608,57 @@
 		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, len - bytes, gc); /* position and rest length */
+			_qq_process_recv_normal_im(data + bytes, data_len - bytes, gc); /* position and rest length */
 			break;
 		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);
-			_qq_process_recv_normal_im(data + bytes, len - bytes, gc);
+			_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);
-			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im(data + bytes, len - bytes, im_header->sender_uid, gc, im_header->im_type);
+			/* 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);
 			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);
-			/* sender_uid is in fact internal_group_id
+			/* 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, len - bytes, im_header->sender_uid, gc);
+			qq_process_recv_group_im_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);
-			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_removed(data + bytes, len - bytes, im_header->sender_uid, gc);
+			/* sender_uid is group id */
+			qq_process_recv_group_im_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);
-			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_apply_join(data + bytes, len - bytes, im_header->sender_uid, gc);
+			/* sender_uid is group id */
+			qq_process_recv_group_im_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",
 					im_header->sender_uid);
-			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_approved(data + bytes, len - bytes, im_header->sender_uid, gc);
+			/* sender_uid is group id */
+			qq_process_recv_group_im_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",
 					im_header->sender_uid);
-			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_rejected(data + bytes, len - bytes, im_header->sender_uid, gc);
+			/* sender_uid is group id */
+			qq_process_recv_group_im_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);
-			_qq_process_recv_sys_im(data + bytes, len - bytes, gc);
+			_qq_process_recv_sys_im(data + bytes, data_len - bytes, gc);
 			break;
 		default:
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
--- a/libpurple/protocols/qq/im.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/im.h	Sun Aug 10 22:59:07 2008 +0000
@@ -59,7 +59,7 @@
 			    gboolean is_bold, gboolean is_italic, gboolean is_underline, gint len);
 
 void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type);
-void qq_process_recv_im(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc);
-void qq_process_send_im_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
+void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/qq.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Sun Aug 10 22:59:07 2008 +0000
@@ -42,7 +42,6 @@
 #include "buddy_opt.h"
 #include "buddy_list.h"
 #include "char_conv.h"
-#include "crypt.h"
 #include "group.h"
 #include "group_find.h"
 #include "group_im.h"
@@ -551,7 +550,7 @@
 			   _("Input Qun name here"),
 			   _("Only QQ members can create permanent Qun"),
 			   "OpenQ", FALSE, FALSE, NULL,
-			   _("Create"), G_CALLBACK(qq_group_create_with_name), _("Cancel"), NULL, gc);
+			   _("Create"), G_CALLBACK(qq_room_create_new), _("Cancel"), NULL, gc);
 }
 */
 
--- a/libpurple/protocols/qq/qq.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Sun Aug 10 22:59:07 2008 +0000
@@ -35,7 +35,6 @@
 #include "roomlist.h"
 
 #define QQ_KEY_LENGTH       16
-#define QQ_DEBUG            1	/* whether we are doing DEBUG */
 
 #ifdef _WIN32
 const char *qq_win32_buddy_icon_dir(void);
@@ -138,7 +137,6 @@
 	gint channel;			/* the id for opened chat conversation */
 
 	GList *groups;
-	GList *group_packets;
 	GSList *joining_groups;
 	GSList *adding_groups_from_server; /* internal ids of groups the server wants in my blist */
 	GList *buddies;
--- a/libpurple/protocols/qq/qq_base.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Sun Aug 10 22:59:07 2008 +0000
@@ -30,7 +30,7 @@
 #include "buddy_info.h"
 #include "buddy_list.h"
 #include "char_conv.h"
-#include "crypt.h"
+#include "qq_crypt.h"
 #include "group.h"
 #include "header_info.h"
 #include "qq_base.h"
@@ -161,7 +161,7 @@
 	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", "Get session_key done\n");
+	purple_debug(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 */
@@ -300,23 +300,26 @@
 
 	g_return_if_fail(qd->token != NULL && qd->token_len > 0);
 
-	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
-	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
-
-	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
 #ifdef DEBUG
 	memset(qd->inikey, 0x01, sizeof(qd->inikey));
 #else
 	for (bytes = 0; bytes < sizeof(qd->inikey); bytes++)	{
-		qd->inikey[bytes] = (guint8) (g_random_int_range(0, 255) % 256);
+		qd->inikey[bytes] = (guint8) (rand() & 0xff);
 	}
 #endif
 
+	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
+	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
+
+	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
+	
 	bytes = 0;
 	/* now generate the encrypted data
 	 * 000-015 use password_twice_md5 as key to encrypt empty string */
-	qq_encrypt((guint8 *) "", 0, qd->password_twice_md5, raw_data + bytes, &encrypted_len);
-	bytes += 16;
+	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 */
@@ -337,7 +340,7 @@
 	bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
 	/* all zero left */
 
-	qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len);
+	encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey);
 
 	buf = g_newa(guint8, MAX_PACKET_SIZE);
 	memset(buf, 0, MAX_PACKET_SIZE);
@@ -405,36 +408,15 @@
 }
 
 /* process the login reply packet */
-guint8 qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint8 qq_process_login_reply(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *data;
-	gint data_len;
 	gchar* error_msg;
 
-	g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR_MISC);
+	g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR_MISC);
 
 	qd = (qq_data *) gc->proto_data;
 
-	data_len = buf_len;
-	data = g_newa(guint8, data_len);
-
-	if (qq_decrypt(buf, buf_len, qd->inikey, data, &data_len)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-				"Decrypt login reply packet with inikey, %d bytes\n", data_len);
-	} else {
-		/* reset data_len since it may changed */
-		data_len = buf_len;
-		if (qq_decrypt(buf, buf_len, qd->password_twice_md5, data, &data_len)) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len);
-		} else {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-					"No idea how to decrypt login reply\n");
-			return QQ_LOGIN_REPLY_ERR_MISC;
-		}
-	}
-	
 	switch (data[0]) {
 		case QQ_LOGIN_REPLY_OK:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is OK\n");
@@ -452,7 +434,7 @@
 		break;
 	}
 
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]);
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: 0x%02X\n", data[0]);
 			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
 			data, data_len,
 			">>> [default] decrypt and dump");
@@ -482,28 +464,19 @@
 }
 
 /* parse the return of keep-alive packet, it includes some system information */
-gboolean qq_process_keep_alive(guint8 *buf, gint buf_len, PurpleConnection *gc) 
+gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc) 
 {
 	qq_data *qd;
-	gint len;
 	gchar **segments;
-	guint8 *data;
 
-	g_return_val_if_fail(buf != NULL && buf_len != 0, FALSE);
+	g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-
-	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt keep alive reply\n");
-		return FALSE;
-	}
 
 	/* qq_show_packet("Keep alive reply packet", data, len); */
 
 	/* the last one is 60, don't know what it is */
-	if (NULL == (segments = split_data(data, len, "\x1f", 6)))
+	if (NULL == (segments = split_data(data, data_len, "\x1f", 6)))
 			return TRUE;
 			
 	/* segments[0] and segment[1] are all 0x30 ("0") */
--- a/libpurple/protocols/qq/qq_base.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.h	Sun Aug 10 22:59:07 2008 +0000
@@ -47,10 +47,10 @@
 guint8 qq_process_token_reply(PurpleConnection *gc, gchar *error_msg, guint8 *buf, gint buf_len);
 
 void qq_send_packet_login(PurpleConnection *gc);
-guint8 qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+guint8 qq_process_login_reply(guint8 *data, gint data_len, PurpleConnection *gc);
 
 void qq_send_packet_logout(PurpleConnection *gc);
 
 void qq_send_packet_keep_alive(PurpleConnection *gc);
-gboolean qq_process_keep_alive(guint8 *buf, gint buf_len, PurpleConnection *gc);
+gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc);
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_crypt.c	Sun Aug 10 22:59:07 2008 +0000
@@ -0,0 +1,330 @@
+/**
+ * @file qq_crypt.c
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * QQ encryption algorithm
+ * Convert from ASM code provided by PerlOICQ
+ * 
+ * Puzzlebird, Nov-Dec 2002
+ */
+
+/* Notes: (QQ uses 16 rounds, and modified something...)
+
+IN : 64  bits of data in v[0] - v[1].
+OUT: 64  bits of data in w[0] - w[1].
+KEY: 128 bits of key  in k[0] - k[3].
+
+delta is chosen to be the real part of 
+the golden ratio: Sqrt(5/4) - 1/2 ~ 0.618034 multiplied by 2^32. 
+
+0x61C88647 is what we can track on the ASM codes.!!
+*/
+
+#include <string.h>
+
+#include "debug.h"
+#include "qq_crypt.h"
+
+#if 0
+void show_binary(char *psztitle, const guint8 *const buffer, gint bytes)
+{
+	printf("== %s %d ==\r\n", psztitle, bytes);
+	gint i, j, ch;
+	for (i = 0; i < bytes; i += 16) {
+		/* length label */
+		printf("%07x: ", i);
+		
+		/* dump hex value */
+		for (j = 0; j < 16; j++) {
+			if (j == 8) {
+				printf(" -");
+			}
+			if ((i + j) < bytes)
+				printf(" %02x", buffer[i + j]);
+			else
+				printf("   ");
+		}
+		
+		printf("  ");
+		
+		
+		/* dump ascii value */
+		for (j = 0; j < 16 && (i + j) < bytes; j++) {
+			ch = buffer[i + j] & 127;
+			if (ch < ' ' || ch == 127)
+				printf(".");
+			else
+				printf("%c", ch);
+		}
+		printf("\r\n");
+	}
+	printf("========\r\n");
+}
+#else
+
+#define show_binary(args... )		/* nothing */
+
+#endif
+
+/********************************************************************
+ * encryption 
+ *******************************************************************/
+
+/* Tiny Encryption Algorithm (TEA) */
+static inline void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w)
+{
+	register guint32
+		y = g_ntohl(v[0]), 
+		 z = g_ntohl(v[1]), 
+		 a = g_ntohl(k[0]), 
+		 b = g_ntohl(k[1]), 
+		 c = g_ntohl(k[2]), 
+		 d = g_ntohl(k[3]), 
+		 n = 0x10, 
+		 sum = 0, 
+		 delta = 0x9E3779B9;	/*  0x9E3779B9 - 0x100000000 = -0x61C88647 */
+
+	while (n-- > 0) {
+		sum += delta;
+		y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
+		z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
+	}
+
+	w[0] = g_htonl(y);
+	w[1] = g_htonl(z);
+}
+
+/* it can be the real random seed function */
+/* override with number, convenient for debug */
+#ifdef DEBUG
+static gint crypt_rand(void) {	
+	return 0xdead; 
+}
+#else
+#include <stdlib.h>
+#define crypt_rand() rand()
+#endif
+
+/* 64-bit blocks and some kind of feedback mode of operation */
+static inline void encrypt_out(guint8 *crypted, const gint crypted_len, const guint8 *key) 
+{
+	/* ships in encipher */
+	guint32 plain32[2];
+	guint32 p32_prev[2];
+	guint32 key32[4];
+	guint32 crypted32[2];
+	guint32 c32_prev[2];
+	
+	guint8 *crypted_ptr;
+	gint count64;
+	
+	/* prepare at first */
+	crypted_ptr = crypted;
+	
+	memcpy(crypted32, crypted_ptr, sizeof(crypted32));
+	c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1];
+	
+	p32_prev[0] = 0; p32_prev[1] = 0;
+	plain32[0] = crypted32[0] ^ p32_prev[0]; plain32[1] = crypted32[1] ^ p32_prev[1];
+	
+	g_memmove(key32, key, 16);
+	count64 = crypted_len / 8;
+	while (count64-- > 0){
+		/* encrypt it */
+		qq_encipher(plain32, key32, crypted32);
+		
+		crypted32[0] ^= p32_prev[0]; crypted32[1] ^= p32_prev[1];
+		
+		/* store curr 64 bits crypted */
+		g_memmove(crypted_ptr, crypted32, sizeof(crypted32));
+		
+		/* set prev */
+		p32_prev[0] = plain32[0]; p32_prev[1] = plain32[1];
+		c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1];
+		
+		/* set next 64 bits want to crypt*/
+		crypted_ptr += 8;
+		memcpy(crypted32, crypted_ptr, sizeof(crypted32));
+		plain32[0] = crypted32[0] ^ c32_prev[0]; plain32[1] = crypted32[1] ^ c32_prev[1];
+	}
+}
+
+/* length of crypted buffer must be plain_len + 16*/
+gint qq_encrypt(guint8* crypted, const guint8* const plain, const gint plain_len, const guint8* const key)
+{
+	guint8 *crypted_ptr = crypted;		/* current position of dest */
+	gint pos, padding;
+	
+	padding = (plain_len + 10) % 8;
+	if (padding) {
+		padding = 8 - padding;
+	}
+
+	pos = 0;
+
+	/* set first byte as padding len */
+	crypted_ptr[pos] = (rand() & 0xf8) | padding;
+	pos++;
+
+	/* extra 2 bytes */
+	padding += 2;
+
+	/* faster a little
+	memset(crypted_ptr + pos, rand() & 0xff, padding);
+	pos += padding;
+	*/
+
+	/* more random */
+	while (padding--) {
+		crypted_ptr[pos++] = rand() & 0xff;
+	}
+
+	g_memmove(crypted_ptr + pos, plain, plain_len);
+	pos += plain_len;
+
+	/* header padding len + plain len must be multiple of 8
+	 * tail pading len is always 8 - (1st byte)
+	 */
+	memset(crypted_ptr + pos, 0x00, 7);
+	pos += 7;
+
+	show_binary("After padding", crypted, pos);
+
+	encrypt_out(crypted, pos, key);
+
+	show_binary("Encrypted", crypted, pos);
+	return pos;
+}
+
+/******************************************************************** 
+ * decryption 
+ ********************************************************************/
+
+static inline void qq_decipher(guint32 *const v, const guint32 *const k, guint32 *const w)
+{
+	register guint32
+		y = g_ntohl(v[0]), 
+		z = g_ntohl(v[1]), 
+		a = g_ntohl(k[0]), 
+		b = g_ntohl(k[1]), 
+		c = g_ntohl(k[2]), 
+		d = g_ntohl(k[3]), 
+		n = 0x10, 
+		sum = 0xE3779B90,	/* why this ? must be related with n value */
+		delta = 0x9E3779B9;
+
+	/* sum = delta<<5, in general sum = delta * n */
+	while (n-- > 0) {
+		z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d);
+		y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b);
+		sum -= delta;
+	}
+
+	w[0] = g_htonl(y);
+	w[1] = g_htonl(z);
+}
+
+static inline gint decrypt_out(guint8 *dest, gint crypted_len, const guint8* const key) 
+{
+	gint plain_len;
+	guint32 key32[4];
+	guint32 crypted32[2];
+	guint32 c32_prev[2];
+	guint32 plain32[2];
+	guint32 p32_prev[2];
+	gint count64;
+	gint padding;
+	guint8 *crypted_ptr = dest;
+
+	/* decrypt first 64 bit */
+	memcpy(key32, key, sizeof(key32));
+	memcpy(crypted32, crypted_ptr, sizeof(crypted32));
+	c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1];
+
+	qq_decipher(crypted32, key32, p32_prev);
+	memcpy(crypted_ptr, p32_prev, sizeof(p32_prev));
+
+	/* check padding len */
+	padding = 2 + (crypted_ptr[0] & 0x7);
+	if (padding < 2) {
+		padding += 8;
+	}
+	plain_len = crypted_len - 1 - padding - 7;
+	if( plain_len < 0 )	{
+		return -2;
+	}
+	
+	count64 = crypted_len / 8;
+	while (count64-- > 0){
+		c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1];
+		crypted_ptr += 8;
+
+		memcpy(crypted32, crypted_ptr, sizeof(crypted32));
+		p32_prev[0] ^= crypted32[0]; p32_prev[1] ^= crypted32[1];
+
+		qq_decipher(p32_prev, key32, p32_prev);
+		
+		plain32[0] = p32_prev[0] ^ c32_prev[0]; plain32[1] = p32_prev[1] ^ c32_prev[1];
+		memcpy(crypted_ptr, plain32, sizeof(plain32));
+	}
+
+	return plain_len;
+}
+
+/* length of plain buffer must be equal to crypted_len */
+gint qq_decrypt(guint8 *plain, const guint8* const crypted, const gint crypted_len, const guint8* const key)
+{
+	gint plain_len = 0;
+	gint hdr_padding;
+	gint pos;
+
+	/* at least 16 bytes and %8 == 0 */
+	if ((crypted_len % 8) || (crypted_len < 16)) { 
+		return -1;
+	}
+
+	memcpy(plain, crypted, crypted_len);
+
+	plain_len = decrypt_out(plain, crypted_len, key);
+	if (plain_len < 0) {
+		return plain_len;	/* invalid first 64 bits */
+	}
+
+	show_binary("Decrypted with padding", plain, crypted_len);
+
+	/* check last 7 bytes is zero or not? */
+	for (pos = crypted_len - 1; pos > crypted_len - 8; pos--) {
+		if (plain[pos] != 0) {
+			return -3;
+		}
+	}
+	if (plain_len == 0) {
+		return plain_len;
+	}
+
+	hdr_padding = crypted_len - plain_len - 7;
+	g_memmove(plain, plain + hdr_padding, plain_len);
+
+	return plain_len;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_crypt.h	Sun Aug 10 22:59:07 2008 +0000
@@ -0,0 +1,33 @@
+ /**
+ * @file qq_crypt.h
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _QQ_CRYPT_H_
+#define _QQ_CRYPT_H_
+
+#include <glib.h>
+
+gint qq_encrypt(guint8* crypted, const guint8* const plain, const gint plain_len, const guint8* const key);
+		
+gint qq_decrypt(guint8 *plain, const guint8* const crypted, const gint crypted_len, const guint8* const key);
+#endif
--- a/libpurple/protocols/qq/qq_network.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Sun Aug 10 22:59:07 2008 +0000
@@ -34,7 +34,7 @@
 #include "buddy_info.h"
 #include "group_info.h"
 #include "group_free.h"
-#include "crypt.h"
+#include "qq_crypt.h"
 #include "header_info.h"
 #include "qq_base.h"
 #include "buddy_list.h"
@@ -167,6 +167,9 @@
 	guint16 source_tag;
 	guint16 cmd;
 	guint16 seq;		/* May be ack_seq or send_seq, depends on cmd */
+	
+	guint8 room_cmd;
+	guint32 room_id;
 
 	qq_transaction *trans;
 
@@ -180,12 +183,11 @@
 	bytes = 0;
 	bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
 
-	if (QQ_DEBUG) {
+#if 1
 		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"==> [%05d] 0x%04X %s, from (0x%04X %s)\n",
-				seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag));
-	}
-	
+				"==> [%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	
 	bytes_not_read = buf_len - bytes - 1;
 
 	/* ack packet, we need to update send tranactions */
@@ -214,8 +216,19 @@
 	}
 
 	/* this is the length of all the encrypted data (also remove tail tag */
-	qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
-
+	if (cmd == 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);
+	}
+	
 	/* check is redirect or not, and do it now */
 	if (qd->is_redirect) {
 	 	/* free resource except real_hostname and port */
@@ -933,7 +946,6 @@
 
 	qd->my_ip.s_addr = 0;
 
-	qq_group_packets_free(qd);
 	qq_group_free_all(qd);
 	qq_add_buddy_request_free(qd);
 	qq_info_query_free(qd);
@@ -945,16 +957,8 @@
 {
 	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);
 	
-	if (data == NULL) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n");
-		return -1;
-	}
-	if (data_len <= 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n");
-		return -1;
-	}
-
 	/* QQ TCP packet has two bytes in the begining defines packet length
 	 * so leave room here to store packet size */
 	if (qd->use_tcp) {
@@ -1007,12 +1011,12 @@
 		qq_trans_add_client_cmd(qd, cmd, seq, data, data_len);
 	}
 	
-	if (QQ_DEBUG) {
+#if 1
 		/* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */
 		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"<== [%05d], %s, total %d bytes is sent %d\n", 
-				seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
-	}
+				"<== [%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;
 }
 
@@ -1026,11 +1030,26 @@
 	g_return_val_if_fail(qd != NULL, -1);
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
-	encrypted_len = data_len + 16;	/* at most 16 bytes more */
-	encrypted_data = g_newa(guint8, encrypted_len);
+	/* 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",
+				encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
+		return -1;
+	}
 
-	qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len);
-
+#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);
 }
 
@@ -1043,3 +1062,81 @@
 	qd->send_seq++;
 	return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len);
 }
+
+gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, 
+		guint8 *data, gint data_len)
+{
+	return qq_send_room_cmd(gc, room_cmd, 0, data, data_len);
+}
+
+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 qq_send_room_cmd(gc, room_cmd, room_id, NULL, 0);
+}
+
+gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
+		guint8 *data, gint data_len)
+{
+	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;
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+
+	/* encap room_cmd and room id to buf*/
+	buf_len = 0;
+	buf_len += qq_put8(buf + buf_len, room_cmd);
+	if (room_id != 0) {
+		/* id 0 is for QQ Demo Group, now there are not existed*/
+		buf_len += qq_put32(buf + buf_len, room_id);
+	}
+	if (data != NULL && data_len > 0) {
+		buf_len += qq_putdata(buf + buf_len, data, data_len);
+	}
+	qd->send_seq++;
+	seq = qd->send_seq;
+
+	/* Encrypt to encrypted_data with session_key */
+	/* at most 16 bytes more */
+	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));
+		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);
+	
+#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);
+#endif
+	return bytes_sent;
+}
--- a/libpurple/protocols/qq/qq_network.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.h	Sun Aug 10 22:59:07 2008 +0000
@@ -42,4 +42,9 @@
 gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
 	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_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
+gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, 
+		guint8 *data, gint data_len);
 #endif
--- a/libpurple/protocols/qq/qq_process.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Sun Aug 10 22:59:07 2008 +0000
@@ -37,8 +37,17 @@
 #include "group_info.h"
 #include "group_free.h"
 #include "char_conv.h"
-#include "crypt.h"
-#include "group_network.h"
+#include "qq_crypt.h"
+
+#include "group_conv.h"
+#include "group_find.h"
+#include "group_internal.h"
+#include "group_im.h"
+#include "group_info.h"
+#include "group_join.h"
+#include "group_opt.h"
+#include "group_search.h"
+
 #include "header_info.h"
 #include "qq_base.h"
 #include "im.h"
@@ -49,28 +58,24 @@
 #include "sys_msg.h"
 #include "utils.h"
 
+enum {
+	QQ_ROOM_CMD_REPLY_OK = 0x00,
+	QQ_ROOM_CMD_REPLY_SEARCH_ERROR = 0x02,
+	QQ_ROOM_CMD_REPLY_NOT_MEMBER = 0x0a
+};
+
 /* default process, decrypt and dump */
-static void process_cmd_unknow(PurpleConnection *gc,gchar *title, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+static void process_cmd_unknow(PurpleConnection *gc,gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
 {
 	qq_data *qd;
-	guint8 *data;
-	gint data_len;
 	gchar *msg_utf8 = NULL;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
-	qq_show_packet(title, buf, buf_len);
+	qq_show_packet(title, data, data_len);
 
 	qd = (qq_data *) gc->proto_data;
 
-	data_len = buf_len;
-	data = g_newa(guint8, data_len);
-	memset(data, 0, data_len);
-	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
-		return;
-	}
-	
 	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
 			data, data_len,
 			">>> [%d] %s -> [default] decrypt and dump",
@@ -78,13 +83,39 @@
 
 	msg_utf8 = try_dump_as_gbk(data, data_len);
 	if (msg_utf8) {
+		purple_notify_info(gc, NULL, msg_utf8, NULL);
 		g_free(msg_utf8);
 	}
 }
 
 void qq_proc_cmd_server(PurpleConnection *gc,
-	guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+	guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
 {
+	qq_data *qd;
+
+	guint8 *data;
+	gint data_len;
+
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	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", 
+			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", 
+			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
+		return;
+	}
+	
 	/* now process the packet */
 	switch (cmd) {
 		case QQ_CMD_RECV_IM:
@@ -134,7 +165,7 @@
 		qq_send_packet_get_buddies_list(gc, 0);
 
 		/* refresh groups */
-		qq_send_packet_get_all_list_with_group(gc, 0);
+		qq_send_packet_get_buddies_and_rooms(gc, 0);
 
 		return;
 	}
@@ -168,14 +199,222 @@
 	}
 }
 
+static void process_room_cmd_notify(PurpleConnection *gc, 
+	guint8 room_cmd, guint8 room_id, guint8 reply_cmd, 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(_(
+		"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);
+}
+
+void qq_proc_room_cmd_reply(PurpleConnection *gc,
+	guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len)
+{
+	qq_data *qd;
+	guint8 *data;
+	gint data_len;
+	qq_group *group;
+	gint bytes;
+	guint8 reply_cmd, reply;
+
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	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", 
+			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", 
+			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", 
+			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", 
+			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", 
+			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;
+				qq_group_refresh(gc, group);
+			}
+			break;
+		case QQ_ROOM_CMD_REPLY_SEARCH_ERROR:
+			if (qd->roomlist != NULL) {
+				if (purple_roomlist_get_in_progress(qd->roomlist))
+					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);
+		}
+		return;
+	}
+
+	/* seems ok so far, so we process the reply according to sub_cmd */
+	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);
+		break;
+	case QQ_ROOM_CMD_CHANGE_INFO:
+		qq_group_process_modify_info_reply(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_MEMBER_OPT:
+		qq_group_process_modify_members_reply(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_ACTIVATE:
+		qq_group_process_activate_group_reply(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_SEARCH:
+		qq_process_group_cmd_search_group(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_JOIN:
+		qq_process_group_cmd_join_group(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_AUTH:
+		qq_process_group_cmd_join_group_auth(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_QUIT:
+		qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_SEND_MSG:
+		qq_process_group_cmd_im(data + bytes, data_len - bytes, gc);
+		break;
+	case QQ_ROOM_CMD_GET_ONLINES:
+		qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc);
+		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);
+		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", 
+			   reply_cmd, qq_get_room_cmd_desc(reply_cmd));
+	}
+}
+
 void qq_proc_cmd_reply(PurpleConnection *gc,
-	guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+	guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
 {
+	qq_data *qd;
+
+	guint8 *data;
+	gint data_len;
+
 	guint8 ret_8 = 0;
 	guint16 ret_16 = 0;
 	guint32 ret_32 = 0;
 	gchar *error_msg = NULL;
 
+	g_return_if_fail(rcved_len > 0);
+
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	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;
+		}
+	}
+	
+	if (data_len <= 0) {
+		purple_debug(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);
@@ -246,14 +485,11 @@
 				qq_send_packet_get_buddies_online(gc, 0);
 			}
 			break;
-		case QQ_CMD_GROUP_CMD:
-			qq_process_group_cmd_reply(data, data_len, seq, gc);
-			break;
-		case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
-			ret_32 = qq_process_get_all_list_with_group_reply(data, data_len, gc);
+		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_all_list_with_group(gc, ret_32);
+				qq_send_packet_get_buddies_and_rooms(gc, ret_32);
 			} else {
 				purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies and groups received\n"); 
 			}
--- a/libpurple/protocols/qq/qq_process.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.h	Sun Aug 10 22:59:07 2008 +0000
@@ -31,8 +31,11 @@
 #include "qq.h"
 
 void qq_proc_cmd_reply(PurpleConnection *gc,
-		guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+		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 *data, gint data_len);
+	guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len);
 #endif
 
--- a/libpurple/protocols/qq/qq_trans.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Sun Aug 10 22:59:07 2008 +0000
@@ -90,6 +90,18 @@
 		return FALSE;
 }
 
+guint8 qq_trans_get_room_cmd(qq_transaction *trans)
+{
+	g_return_val_if_fail(trans != NULL, 0);
+	return trans->room_cmd;
+}
+
+guint32 qq_trans_get_room_id(qq_transaction *trans)
+{
+	g_return_val_if_fail(trans != NULL, 0);
+	return trans->room_id;
+}
+
 /* Remove a packet with seq from send trans */
 static void trans_remove(qq_data *qd, qq_transaction *trans) 
 {
@@ -120,21 +132,53 @@
 	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_ERROR, "QQ_TRANS",
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS",
 			"Add client cmd, seq = %d, data = %p, len = %d\n",
 			trans->seq, trans->data, trans->data_len);
 	qd->transactions = g_list_append(qd->transactions, trans);
 }
 
+void qq_trans_add_room_cmd(qq_data *qd, guint16 seq, guint8 room_cmd, guint32 room_id,
+		guint8 *data, gint data_len)
+{
+	qq_transaction *trans = g_new0(qq_transaction, 1);
+
+	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",
+			trans->seq, trans->data, trans->data_len);
+	qd->transactions = g_list_append(qd->transactions, trans);
+}
+
 void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
 {
 	qq_transaction *trans = g_new0(qq_transaction, 1);
@@ -148,6 +192,8 @@
 	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;
@@ -157,7 +203,7 @@
 		trans->data = g_memdup(data, data_len);	/* don't use g_strdup, may have 0x00 */
 		trans->data_len = data_len;
 	}
-	purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS",
 			"Add server cmd, seq = %d, data = %p, len = %d\n",
 			trans->seq, trans->data, trans->data_len);
 	qd->transactions = g_list_append(qd->transactions, trans);
--- a/libpurple/protocols/qq/qq_trans.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/qq_trans.h	Sun Aug 10 22:59:07 2008 +0000
@@ -40,6 +40,10 @@
 	guint8 flag;
 	guint16 seq;
 	guint16 cmd;
+
+	guint8 room_cmd;
+	guint32 room_id;
+	
 	guint8 *data;
 	gint data_len;
 
@@ -52,8 +56,14 @@
 qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq);
 gboolean qq_trans_is_server(qq_transaction *trans) ;
 gboolean qq_trans_is_dup(qq_transaction *trans);
+guint8 qq_trans_get_room_cmd(qq_transaction *trans);
+guint32 qq_trans_get_room_id(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,
+	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);
--- a/libpurple/protocols/qq/send_file.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/send_file.c	Sun Aug 10 22:59:07 2008 +0000
@@ -30,7 +30,6 @@
 #include "notify.h"
 
 #include "buddy_list.h"
-#include "crypt.h"
 #include "file_trans.h"
 #include "header_info.h"
 #include "im.h"
--- a/libpurple/protocols/qq/sys_msg.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/sys_msg.c	Sun Aug 10 22:59:07 2008 +0000
@@ -31,7 +31,6 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
-#include "crypt.h"
 #include "header_info.h"
 #include "packet_parse.h"
 #include "qq.h"
@@ -292,64 +291,55 @@
 	g_free(content);
 }
 
-void qq_process_msg_sys(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
+void qq_process_msg_sys(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len;
-	guint8 *data;
 	gchar **segments, *code, *from, *to, *msg, *msg_utf8;
 
-	g_return_if_fail(buf != NULL && buf_len != 0);
+	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
+
+	if (NULL == (segments = split_data(data, data_len, "\x1f", 4)))
+		return;
+	code = segments[0];
+	from = segments[1];
+	to = segments[2];
+	msg = segments[3];
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		if (NULL == (segments = split_data(data, len, "\x1f", 4)))
-			return;
-		code = segments[0];
-		from = segments[1];
-		to = segments[2];
-		msg = segments[3];
+	_qq_send_packet_ack_msg_sys(gc, code[0], strtol(from, NULL, 10), seq);
 
-		_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);
-			g_strfreev(segments);
-			return;
-		}
+	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);
+		g_strfreev(segments);
+		return;
+	}
 
-		msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-		switch (strtol(code, NULL, 10)) {
-		case QQ_MSG_SYS_BEING_ADDED:
-			_qq_process_msg_sys_being_added(gc, from, to, msg_utf8);
-			break;
-		case QQ_MSG_SYS_ADD_CONTACT_REQUEST:
-			_qq_process_msg_sys_add_contact_request(gc, from, to, msg_utf8);
-			break;
-		case QQ_MSG_SYS_ADD_CONTACT_APPROVED:
-			_qq_process_msg_sys_add_contact_approved(gc, from, to, msg_utf8);
-			break;
-		case QQ_MSG_SYS_ADD_CONTACT_REJECTED:
-			_qq_process_msg_sys_add_contact_rejected(gc, from, to, msg_utf8);
-			break;
-		case QQ_MSG_SYS_NOTICE:
-			_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
-			break;
-		case QQ_MSG_SYS_NEW_VERSION:
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "QQ server says there is newer version than %s\n", qq_get_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);
-		}
-		g_free(msg_utf8);
-		g_strfreev(segments);
-
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt recv msg sys\n");
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+	switch (strtol(code, NULL, 10)) {
+	case QQ_MSG_SYS_BEING_ADDED:
+		_qq_process_msg_sys_being_added(gc, from, to, msg_utf8);
+		break;
+	case QQ_MSG_SYS_ADD_CONTACT_REQUEST:
+		_qq_process_msg_sys_add_contact_request(gc, from, to, msg_utf8);
+		break;
+	case QQ_MSG_SYS_ADD_CONTACT_APPROVED:
+		_qq_process_msg_sys_add_contact_approved(gc, from, to, msg_utf8);
+		break;
+	case QQ_MSG_SYS_ADD_CONTACT_REJECTED:
+		_qq_process_msg_sys_add_contact_rejected(gc, from, to, msg_utf8);
+		break;
+	case QQ_MSG_SYS_NOTICE:
+		_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
+		break;
+	case QQ_MSG_SYS_NEW_VERSION:
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+			   "QQ server says there is newer version than %s\n", qq_get_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);
 	}
+	g_free(msg_utf8);
+	g_strfreev(segments);
 }
--- a/libpurple/protocols/qq/sys_msg.h	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/sys_msg.h	Sun Aug 10 22:59:07 2008 +0000
@@ -28,6 +28,6 @@
 #include <glib.h>
 #include "connection.h"
 
-void qq_process_msg_sys(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc);
+void qq_process_msg_sys(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/utils.c	Sun Aug 10 22:08:39 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Sun Aug 10 22:59:07 2008 +0000
@@ -314,7 +314,7 @@
 			if ((i + j) < bytes)
 				g_string_append_printf(str, " %02x", buffer[i + j]);
 			else
-				g_string_append(str, "   ");
+				g_string_append(str, " --");
 		g_string_append(str, "  ");
 
 		/* dump ascii value */
--- a/po/POTFILES.in	Sun Aug 10 22:08:39 2008 +0000
+++ b/po/POTFILES.in	Sun Aug 10 22:59:07 2008 +0000
@@ -137,7 +137,6 @@
 libpurple/protocols/qq/group_im.c
 libpurple/protocols/qq/group_internal.c
 libpurple/protocols/qq/group_join.c
-libpurple/protocols/qq/group_network.c
 libpurple/protocols/qq/group_opt.c
 libpurple/protocols/qq/im.c
 libpurple/protocols/qq/qq.c