changeset 24088:1ee91ff0d5fe

2008.10.09 - ccpaging <ccpaging(at)gmail.com> * Update 'group' protocol * Functions of group_find, group_free, group_search merged into group_join and group_internal * Removed group_find.c/h, group_free.c/h, group_search.c/h
author SHiNE CsyFeK <csyfek@gmail.com>
date Tue, 28 Oct 2008 16:38:16 +0000
parents 147f1b17b6ce
children b6f09c1c79ce
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/Makefile.am libpurple/protocols/qq/Makefile.mingw libpurple/protocols/qq/buddy_list.c libpurple/protocols/qq/group.c libpurple/protocols/qq/group.h libpurple/protocols/qq/group_find.c libpurple/protocols/qq/group_find.h libpurple/protocols/qq/group_free.c libpurple/protocols/qq/group_free.h libpurple/protocols/qq/group_im.c libpurple/protocols/qq/group_im.h libpurple/protocols/qq/group_info.c libpurple/protocols/qq/group_info.h libpurple/protocols/qq/group_internal.c libpurple/protocols/qq/group_internal.h libpurple/protocols/qq/group_join.c libpurple/protocols/qq/group_join.h libpurple/protocols/qq/group_opt.c libpurple/protocols/qq/group_opt.h libpurple/protocols/qq/group_search.c libpurple/protocols/qq/group_search.h libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq_network.c libpurple/protocols/qq/qq_process.c
diffstat 25 files changed, 755 insertions(+), 1017 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:38:16 2008 +0000
@@ -1,3 +1,8 @@
+2008.10.09 - ccpaging <ccpaging(at)gmail.com>
+	* Update 'group' protocol
+	* Functions of group_find, group_free, group_search merged into group_join and group_internal 
+	* Removed group_find.c/h, group_free.c/h, group_search.c/h 
+
 2008.10.08 - ccpaging <ccpaging(at)gmail.com>
 	* Update 'group' protocol
 
--- a/libpurple/protocols/qq/Makefile.am	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Tue Oct 28 16:38:16 2008 +0000
@@ -18,10 +18,6 @@
 	file_trans.h \
 	group.c \
 	group.h \
-	group_find.c \
-	group_find.h \
-	group_free.c \
-	group_free.h \
 	group_internal.c \
 	group_internal.h \
 	group_im.c \
@@ -32,8 +28,6 @@
 	group_join.h \
 	group_opt.c \
 	group_opt.h \
-	group_search.c \
-	group_search.h \
 	qq_define.c \
 	qq_define.h \
 	im.c \
--- a/libpurple/protocols/qq/Makefile.mingw	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Tue Oct 28 16:38:16 2008 +0000
@@ -50,14 +50,11 @@
 	qq_crypt.c \
 	file_trans.c \
 	group.c \
-	group_find.c \
-	group_free.c \
 	group_internal.c \
 	group_im.c \
 	group_info.c \
 	group_join.c \
 	group_opt.c \
-	group_search.c \
 	qq_define.c \
 	im.c \
 	packet_parse.c \
--- a/libpurple/protocols/qq/buddy_list.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Tue Oct 28 16:38:16 2008 +0000
@@ -37,7 +37,6 @@
 #include "qq_define.h"
 #include "qq_base.h"
 #include "group.h"
-#include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 
@@ -236,13 +235,15 @@
 		if(0 != fe->s->client_tag)
 			q_bud->client_tag = fe->s->client_tag;
 		*/
+		if (bd->status != bs.status || bd->comm_flag != packet.comm_flag) {
+			bd->status = bs.status;
+			bd->comm_flag = packet.comm_flag;
+			qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
+		}
 		bd->ip.s_addr = bs.ip.s_addr;
 		bd->port = bs.port;
-		bd->status = bs.status;
 		bd->ext_flag = packet.ext_flag;
-		bd->comm_flag = packet.comm_flag;
 		bd->last_update = time(NULL);
-		qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
 		count++;
 	}
 
@@ -358,7 +359,7 @@
 	guint32 unknown, position;
 	guint32 uid;
 	guint8 type;
-	qq_group *group;
+	qq_room_data *rmd;
 
 	g_return_val_if_fail(data != NULL && data_len != 0, -1);
 
@@ -394,15 +395,12 @@
 			 * qq_request_get_buddies */
 			++i;
 		} else { /* a group */
-			group = qq_room_search_id(gc, uid);
-			if(group == NULL) {
-				purple_debug_info("QQ",
-					"Not find room id %d in qq_process_get_buddies_and_rooms\n", uid);
-				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, uid, NULL, 0,
-						0, QQ_ROOM_INFO_CREATE);
+			rmd = qq_room_data_find(gc, uid);
+			if(rmd == NULL) {
+				purple_debug_info("QQ", "Unknow room id %d", uid);
+				qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, uid);
 			} else {
-				group->my_role = QQ_ROOM_ROLE_YES;
-				qq_group_refresh(gc, group);
+				rmd->my_role = QQ_ROOM_ROLE_YES;
 			}
 			++j;
 		}
@@ -561,10 +559,11 @@
 		bd->ip.s_addr = bs.ip.s_addr;
 		bd->port = bs.port;
 	}
-	bd->status =bs.status;
-
+	if (bd->status != bs.status) {
+		bd->status = bs.status;
+		qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
+	}
 	bd->last_update = time(NULL);
-	qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
 
 	if (bd->status == QQ_BUDDY_ONLINE_NORMAL && bd->level <= 0) {
 		if (qd->client_version >= 2007) {
@@ -649,6 +648,7 @@
 		if (bd->uid == qd->uid) continue;	/* my status is always online in my buddy list */
 		if (tm_limit < bd->last_update) continue;
 		if (bd->status == QQ_BUDDY_ONLINE_INVISIBLE) continue;
+		if (bd->status == QQ_BUDDY_CHANGE_TO_OFFLINE) continue;
 
 		bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
 		bd->last_update = time(NULL);
--- a/libpurple/protocols/qq/group.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Tue Oct 28 16:38:16 2008 +0000
@@ -30,11 +30,10 @@
 
 #include "group_internal.h"
 #include "group_info.h"
-#include "group_search.h"
+#include "group_join.h"
 #include "utils.h"
 #include "qq_network.h"
 #include "qq_define.h"
-#include "group_free.h"
 
 static void _qq_group_search_callback(PurpleConnection *gc, const gchar *input)
 {
@@ -130,44 +129,3 @@
 	purple_roomlist_set_in_progress(list, FALSE);
 	purple_roomlist_unref(list);
 }
-
-/* this should be called upon signin, even when we did not open group chat window */
-void qq_group_init(PurpleConnection *gc)
-{
-	PurpleAccount *account;
-	PurpleChat *chat;
-	PurpleGroup *purple_group;
-	PurpleBlistNode *node;
-	qq_group *group;
-	gint count;
-
-	account = purple_connection_get_account(gc);
-
-	purple_debug_info("QQ", "Initial QQ Qun configurations\n");
-	purple_group = purple_find_group(PURPLE_GROUP_QQ_QUN);
-	if (purple_group == NULL) {
-		purple_debug_info("QQ", "We have no QQ Qun\n");
-		return;
-	}
-
-	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_room_data_new_by_hashtable(gc, chat->components);
-		if (group == NULL)
-			continue;
-
-		if (group->id <= 0)
-			continue;
-
-		count++;
-	}
-
-	purple_debug_info("QQ", "Load %d QQ Qun configurations\n", count);
-}
--- a/libpurple/protocols/qq/group.h	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group.h	Tue Oct 28 16:38:16 2008 +0000
@@ -40,7 +40,8 @@
 	QQ_ROOM_ROLE_ADMIN,
 } qq_room_role;
 
-typedef struct _qq_group {
+typedef struct _qq_room_data qq_room_data;
+struct _qq_room_data {
 	/* all these will be saved when we exit Purple */
 	qq_room_role my_role;	/* my role for this room */
 	guint32 id;
@@ -56,13 +57,11 @@
 
 	gboolean is_got_buddies;
 	GList *members;
-} qq_group;
+};
 
 GList *qq_chat_info(PurpleConnection *gc);
 GHashTable *qq_chat_info_defaults(PurpleConnection *gc, const gchar *chat_name);
 
-void qq_group_init(PurpleConnection *gc);
-
 PurpleRoomlist *qq_roomlist_get_list(PurpleConnection *gc);
 
 void qq_roomlist_cancel(PurpleRoomlist *list);
--- a/libpurple/protocols/qq/group_find.c	Tue Oct 28 16:35:06 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-/**
- * @file group_find.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 "qq.h"
-
-#include "conversation.h"
-#include "debug.h"
-#include "util.h"
-
-#include "group_find.h"
-#include "utils.h"
-
-/* find a qq_buddy_data by uid, called by im.c */
-qq_buddy_data *qq_group_find_member_by_uid(qq_group *group, guint32 uid)
-{
-	GList *list;
-	qq_buddy_data *bd;
-	g_return_val_if_fail(group != NULL && uid > 0, NULL);
-
-	list = group->members;
-	while (list != NULL) {
-		bd = (qq_buddy_data *) list->data;
-		if (bd->uid == uid)
-			return bd;
-		else
-			list = list->next;
-	}
-
-	return NULL;
-}
-
-/* remove a qq_buddy_data by uid, called by qq_group_opt.c */
-void qq_group_remove_member_by_uid(qq_group *group, guint32 uid)
-{
-	GList *list;
-	qq_buddy_data *bd;
-	g_return_if_fail(group != NULL && uid > 0);
-
-	list = group->members;
-	while (list != NULL) {
-		bd = (qq_buddy_data *) list->data;
-		if (bd->uid == uid) {
-			group->members = g_list_remove(group->members, bd);
-			return;
-		} else {
-			list = list->next;
-		}
-	}
-}
-
-qq_buddy_data *qq_group_find_or_add_member(PurpleConnection *gc, qq_group *group, guint32 member_uid)
-{
-	qq_buddy_data *member, *bd;
-	PurpleBuddy *buddy;
-	g_return_val_if_fail(group != NULL && member_uid > 0, NULL);
-
-	member = qq_group_find_member_by_uid(group, member_uid);
-	if (member == NULL) {	/* first appear during my session */
-		member = g_new0(qq_buddy_data, 1);
-		member->uid = member_uid;
-		buddy = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(member_uid));
-		if (buddy != NULL) {
-			bd = (qq_buddy_data *) buddy->proto_data;
-			if (bd != NULL && bd->nickname != NULL)
-				member->nickname = g_strdup(bd->nickname);
-			else if (buddy->alias != NULL)
-				member->nickname = g_strdup(buddy->alias);
-		}
-		group->members = g_list_append(group->members, member);
-	}
-
-	return member;
-}
-
-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;
-}
-
-qq_group *qq_room_get_next(PurpleConnection *gc, guint32 room_id)
-{
-	GList *list;
-	qq_group *group;
-	qq_data *qd;
-	gboolean is_find = FALSE;
-
-	qd = (qq_data *) gc->proto_data;
-
-	if (qd->groups == NULL) {
-		return NULL;
-	}
-
-	 if (room_id <= 0) {
-		return (qq_group *) qd->groups->data;
-	}
-
-	list = qd->groups;
-	while (list != NULL) {
-		group = (qq_group *) list->data;
-		list = list->next;
-		if (group->id == room_id) {
-			is_find = TRUE;
-			break;
-		}
-	}
-
-	if ( !is_find || list == NULL) {
-		return NULL;
-	}
-
-	return (qq_group *)list->data;
-}
-
-qq_group *qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id)
-{
-	GList *list;
-	qq_group *group;
-	qq_data *qd;
-	gboolean is_find;
-
-	qd = (qq_data *) gc->proto_data;
-
- 	list = qd->groups;
-	if (room_id > 0) {
-		/* search next room */
-		is_find = FALSE;
-		while (list != NULL) {
-			group = (qq_group *) list->data;
-			list = list->next;
-			if (group->id == room_id) {
-				is_find = TRUE;
-				break;
-			}
-		}
-		if ( !is_find || list == NULL) {
-			return NULL;
-		}
-	}
-
-	is_find = FALSE;
-	while (list != NULL) {
-		group = (qq_group *) list->data;
-		if (group->my_role == QQ_ROOM_ROLE_YES || group->my_role == QQ_ROOM_ROLE_ADMIN) {
-			if (NULL != purple_find_conversation_with_account(
-						PURPLE_CONV_TYPE_CHAT,group->title_utf8, purple_connection_get_account(gc))) {
-				/* In convseration*/
-				is_find = TRUE;
-				break;
-			}
-		}
-		list = list->next;
-	}
-
-	if ( !is_find) {
-		return NULL;
-	}
-	return group;
-}
--- a/libpurple/protocols/qq/group_find.h	Tue Oct 28 16:35:06 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/**
- * @file group_find.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_FIND_H_
-#define _QQ_GROUP_FIND_H_
-
-#include <glib.h>
-#include "connection.h"
-#include "group.h"
-
-qq_buddy_data *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_data *qq_group_find_or_add_member(PurpleConnection *gc, qq_group *group, guint32 member_uid);
-
-qq_group *qq_room_search_id(PurpleConnection *gc, guint32 room_id);
-
-qq_group *qq_room_get_next(PurpleConnection *gc, guint32 room_id);
-qq_group *qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id);
-
-#endif
--- a/libpurple/protocols/qq/group_free.c	Tue Oct 28 16:35:06 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/**
- * @file group_free.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 "buddy_opt.h"
-#include "group_free.h"
-
-/* gracefully free all members in a group */
-static void qq_group_free_member(qq_group *group)
-{
-	gint i;
-	GList *list;
-	qq_buddy_data *bd;
-
-	g_return_if_fail(group != NULL);
-	i = 0;
-	while (NULL != (list = group->members)) {
-		bd = (qq_buddy_data *) list->data;
-		i++;
-		group->members = g_list_remove(group->members, bd);
-		qq_buddy_data_free(bd);
-	}
-
-	group->members = NULL;
-}
-
-/* gracefully free the memory for one qq_group */
-void qq_group_free(qq_group *group)
-{
-	g_return_if_fail(group != NULL);
-	qq_group_free_member(group);
-	g_free(group->title_utf8);
-	g_free(group->desc_utf8);
-	g_free(group->notice_utf8);
-	g_free(group);
-}
-
-void qq_group_free_all(PurpleConnection *gc)
-{
-	qq_data *qd;
-	qq_group *group;
-	gint count;
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	count = 0;
-	while (qd->groups != NULL) {
-		group = (qq_group *) qd->groups->data;
-		qd->groups = g_list_remove(qd->groups, group);
-		qq_group_free(group);
-		count++;
-	}
-
-	if (count > 0) {
-		purple_debug_info("QQ", "%d rooms are freed\n", count);
-	}
-}
--- a/libpurple/protocols/qq/group_free.h	Tue Oct 28 16:35:06 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/**
- * @file group_free.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_FREE_H_
-#define _QQ_GROUP_FREE_H_
-
-#include <glib.h>
-#include "qq.h"
-#include "group.h"
-
-void qq_group_free(qq_group *group);
-void qq_group_free_all(PurpleConnection *gc);
-
-#endif
--- a/libpurple/protocols/qq/group_im.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Tue Oct 28 16:38:16 2008 +0000
@@ -32,7 +32,6 @@
 #include "util.h"
 
 #include "char_conv.h"
-#include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_im.h"
@@ -45,41 +44,41 @@
 #include "utils.h"
 
 /* show group conversation window */
-PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_group *group)
+PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_room_data *rmd)
 {
 	PurpleConversation *conv;
 	qq_data *qd;
 	gchar *topic_utf8;
 
-	g_return_val_if_fail(group != NULL, NULL);
+	g_return_val_if_fail(rmd != NULL, NULL);
 	qd = (qq_data *) gc->proto_data;
 
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-			group->title_utf8, purple_connection_get_account(gc));
+			rmd->title_utf8, purple_connection_get_account(gc));
 	if (conv != NULL)	{
-		/* show only one conversation per group */
+		/* show only one conversation per room */
 		return conv;
 	}
 
-	serv_got_joined_chat(gc, group->id, group->title_utf8);
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
+	serv_got_joined_chat(gc, rmd->id, rmd->title_utf8);
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, rmd->title_utf8, purple_connection_get_account(gc));
 	if (conv != NULL) {
-		topic_utf8 = g_strdup_printf("%d %s", group->ext_id, group->notice_utf8);
+		topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8);
 		purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
 		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
 		g_free(topic_utf8);
 
-		if (group->is_got_buddies)
-			qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, group->id);
+		if (rmd->is_got_buddies)
+			qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, rmd->id);
 		else
-			qq_update_room(gc, 0, group->id);
+			qq_update_room(gc, 0, rmd->id);
 		return conv;
 	}
 	return NULL;
 }
 
 /* refresh online member in group conversation window */
-void qq_room_conv_set_onlines(PurpleConnection *gc, qq_group *group)
+void qq_room_conv_set_onlines(PurpleConnection *gc, qq_room_data *rmd)
 {
 	GList *names, *list, *flags;
 	qq_buddy_data *bd;
@@ -88,20 +87,20 @@
 	gint flag;
 	gboolean is_find;
 
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-			group->title_utf8, purple_connection_get_account(gc));
+			rmd->title_utf8, purple_connection_get_account(gc));
 	if (conv == NULL) {
-		purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", group->title_utf8);
+		purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->title_utf8);
 		return;
 	}
-	g_return_if_fail(group->members != NULL);
+	g_return_if_fail(rmd->members != NULL);
 
 	names = NULL;
 	flags = NULL;
 
-	list = group->members;
+	list = rmd->members;
 	while (list != NULL) {
 		bd = (qq_buddy_data *) list->data;
 
@@ -116,7 +115,7 @@
 		/* TYPING to put online above OP and FOUNDER */
 		if (is_online(bd->status)) flag |= (PURPLE_CBFLAGS_TYPING | PURPLE_CBFLAGS_VOICE);
 		if(1 == (bd->role & 1)) flag |= PURPLE_CBFLAGS_OP;
-		if(bd->uid == group->creator_uid) flag |= PURPLE_CBFLAGS_FOUNDER;
+		if(bd->uid == rmd->creator_uid) flag |= PURPLE_CBFLAGS_FOUNDER;
 
 		is_find = TRUE;
 		if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_name))
@@ -251,19 +250,21 @@
 }
 
 void qq_room_got_chat_in(PurpleConnection *gc,
-		qq_group *group, guint32 uid_from, const gchar *msg, time_t in_time)
+		guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time)
 {
-	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleConversation *conv;
 	qq_buddy_data *bd;
+	qq_room_data *rmd;
 	gchar *from;
 
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(gc != NULL && room_id != 0);
 
-	conv = purple_find_conversation_with_account(
-			PURPLE_CONV_TYPE_CHAT, group->title_utf8, account);
+	conv = purple_find_chat(gc, room_id);
+	rmd = qq_room_data_find(gc, room_id);
+	g_return_if_fail(rmd != NULL);
+
 	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/show_room_when_newin")) {
-		conv = qq_room_conv_open(gc, group);
+		conv = qq_room_conv_open(gc, rmd);
 	}
 
 	if (conv == NULL) {
@@ -271,7 +272,8 @@
 	}
 
 	if (uid_from != 0) {
-		bd = qq_group_find_member_by_uid(group, uid_from);
+
+		bd = qq_room_buddy_find(rmd, uid_from);
 		if (bd == NULL || bd->nickname == NULL)
 			from = g_strdup_printf("%d", uid_from);
 		else
@@ -279,9 +281,7 @@
 	} else {
 		from = g_strdup("");
 	}
-	serv_got_chat_in(gc,
-			purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
-				from, 0, msg, in_time);
+	serv_got_chat_in(gc, room_id, from, 0, msg, in_time);
 	g_free(from);
 }
 
@@ -291,7 +291,7 @@
 	guint32 ext_id, admin_uid;
 	guint8 type8;
 	gchar *reason_utf8, *msg, *reason;
-	qq_group *group;
+	qq_room_data *rmd;
 	gint bytes;
 
 	g_return_if_fail(data != NULL && len > 0);
@@ -312,10 +312,10 @@
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
 
-	group = qq_room_search_id(gc, id);
-	if (group != NULL) {
-		group->my_role = QQ_ROOM_ROLE_NO;
-		qq_group_refresh(gc, group);
+	qq_room_find_or_new(gc, id, ext_id);
+	rmd = qq_room_data_find(gc, id);
+	if (rmd != NULL) {
+		rmd->my_role = QQ_ROOM_ROLE_NO;
 	}
 
 	g_free(msg);
@@ -329,7 +329,7 @@
 	guint32 ext_id, admin_uid;
 	guint8 type8;
 	gchar *msg, *reason;
-	qq_group *group;
+	qq_room_data *rmd;
 	gint bytes;
 	time_t now;
 
@@ -345,16 +345,16 @@
 	/* it is also a "æ— " here, so do not display */
 	bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
 
-	group = qq_room_search_id(gc, id);
-	if (group != NULL) {
-		group->my_role = QQ_ROOM_ROLE_YES;
-		qq_group_refresh(gc, group);
+	qq_room_find_or_new(gc, id, ext_id);
+	rmd = qq_room_data_find(gc, id);
+	if (rmd != NULL) {
+		rmd->my_role = QQ_ROOM_ROLE_YES;
 	}
 
 	msg = g_strdup_printf(_("<b>Joinning Qun %d is approved by Admin %d for %s</b>"),
 			ext_id, admin_uid, reason);
 	now = time(NULL);
-	qq_room_got_chat_in(gc, group, 0, msg, now);
+	qq_room_got_chat_in(gc, id, 0, msg, now);
 
 	g_free(msg);
 	g_free(reason);
@@ -366,7 +366,7 @@
 	guint32 ext_id, uid;
 	guint8 type8;
 	gchar *msg;
-	qq_group *group;
+	qq_room_data *rmd;
 	gint bytes = 0;
 	time_t now = time(NULL);
 
@@ -380,14 +380,14 @@
 
 	g_return_if_fail(ext_id > 0 && uid > 0);
 
-	group = qq_room_search_id(gc, id);
-	if (group != NULL) {
-		group->my_role = QQ_ROOM_ROLE_NO;
-		qq_group_refresh(gc, group);
+	qq_room_find_or_new(gc, id, ext_id);
+	rmd = qq_room_data_find(gc, id);
+	if (rmd != NULL) {
+		rmd->my_role = QQ_ROOM_ROLE_NO;
 	}
 
 	msg = g_strdup_printf(_("<b>Removed buddy %d.</b>"), uid);
-	qq_room_got_chat_in(gc, group, 0, msg, now);
+	qq_room_got_chat_in(gc, id, 0, msg, now);
 	g_free(msg);
 }
 
@@ -396,9 +396,9 @@
 {
 	guint32 ext_id, uid;
 	guint8 type8;
-	qq_group *group;
+	qq_room_data *rmd;
+	gint bytes;
 	gchar *msg;
-	gint bytes;
 	time_t now = time(NULL);
 
 	g_return_if_fail(data != NULL && len > 0);
@@ -409,22 +409,18 @@
 	bytes += qq_get8(&type8, data + bytes);
 	bytes += qq_get32(&uid, data + bytes);
 
-	g_return_if_fail(ext_id > 0 && uid > 0);
+	g_return_if_fail(ext_id > 0 && id > 0);
 
-	group = qq_room_search_id(gc, id);
-	if (group != NULL) {
-		group->my_role = QQ_ROOM_ROLE_YES;
-		qq_group_refresh(gc, group);
-	} else {		/* no such group, try to create a dummy first, and then update */
-		group = qq_group_create_internal_record(gc, id, ext_id, NULL);
-		group->my_role = QQ_ROOM_ROLE_YES;
-		qq_group_refresh(gc, group);
-		qq_update_room(gc, 0, group->id);
-		/* the return of this cmd will automatically update the group in blist */
-	}
+	qq_room_find_or_new(gc, id, ext_id);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail(rmd != NULL);
+
+	rmd->my_role = QQ_ROOM_ROLE_YES;
+
+	qq_update_room(gc, 0, rmd->id);
 
 	msg = g_strdup_printf(_("<b>Added new buddy %d.</b>"), uid);
-	qq_room_got_chat_in(gc, group, 0, msg, now);
+	qq_room_got_chat_in(gc, id, 0, msg, now);
 	g_free(msg);
 }
 
@@ -433,7 +429,6 @@
 {
 	gchar *msg_with_purple_smiley, *msg_utf8_encoded;
 	qq_data *qd;
-	qq_group *group;
 	gint skip_len;
 	gint bytes ;
 	struct {
@@ -458,7 +453,7 @@
 	qd = (qq_data *) gc->proto_data;
 
 #if 1
-	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump");
+	qq_show_packet("Room IM", data, data_len);
 #endif
 	memset(&packet, 0, sizeof(packet));
 	bytes = 0;
@@ -519,8 +514,7 @@
 	} else {
 		msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
 	}
-	group = qq_room_search_id(gc, id);
- 	qq_room_got_chat_in(gc, group, packet.member_uid, msg_utf8_encoded, packet.send_time);
+ 	qq_room_got_chat_in(gc, id, packet.member_uid, msg_utf8_encoded, packet.send_time);
 
 	g_free(msg_with_purple_smiley);
 	g_free(msg_utf8_encoded);
--- a/libpurple/protocols/qq/group_im.h	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Tue Oct 28 16:38:16 2008 +0000
@@ -30,11 +30,11 @@
 #include "conversation.h"
 #include "group.h"
 
-PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_group *group);
-void qq_room_conv_set_onlines(PurpleConnection *gc, qq_group *group);
+PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_room_data *rmd);
+void qq_room_conv_set_onlines(PurpleConnection *gc, qq_room_data *rmd);
 
 void qq_room_got_chat_in(PurpleConnection *gc,
-		qq_group *group, guint32 uid_from, const gchar *msg, time_t in_time);
+		guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time);
 
 void qq_send_packet_group_im(PurpleConnection *gc, guint32 room_id, const gchar *msg);
 
--- a/libpurple/protocols/qq/group_info.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Tue Oct 28 16:38:16 2008 +0000
@@ -29,7 +29,6 @@
 
 #include "char_conv.h"
 #include "group_im.h"
-#include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 #include "buddy_list.h"
@@ -51,13 +50,13 @@
 
 /* this is done when we receive the reply to get_online_members sub_cmd
  * all member are set offline, and then only those in reply packets are online */
-static void set_all_offline(qq_group *group)
+static void set_all_offline(qq_room_data *rmd)
 {
 	GList *list;
 	qq_buddy_data *bd;
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 
-	list = group->members;
+	list = rmd->members;
 	while (list != NULL) {
 		bd = (qq_buddy_data *) list->data;
 		bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
@@ -66,15 +65,20 @@
 }
 
 /* send packet to get info for each group member */
-gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint update_class)
+gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, gint update_class)
 {
 	guint8 *raw_data;
 	gint bytes, num;
 	GList *list;
+	qq_room_data *rmd;
 	qq_buddy_data *bd;
 
-	g_return_val_if_fail(group != NULL, 0);
-	for (num = 0, list = group->members; list != NULL; list = list->next) {
+	g_return_val_if_fail(room_id > 0, 0);
+
+	rmd  = qq_room_data_find(gc, room_id);
+	g_return_val_if_fail(rmd != NULL, 0);
+
+	for (num = 0, list = rmd->members; list != NULL; list = list->next) {
 		bd = (qq_buddy_data *) list->data;
 		if (check_update_interval(bd))
 			num++;
@@ -89,7 +93,7 @@
 
 	bytes = 0;
 
-	list = group->members;
+	list = rmd->members;
 	while (list != NULL) {
 		bd = (qq_buddy_data *) list->data;
 		if (check_update_interval(bd))
@@ -97,7 +101,7 @@
 		list = list->next;
 	}
 
-	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_BUDDIES, group->id, raw_data, bytes,
+	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_BUDDIES, rmd->id, raw_data, bytes,
 			update_class, 0);
 	return num;
 }
@@ -125,38 +129,38 @@
 	return g_strdup(role_desc);
 }
 
-static void room_info_display(PurpleConnection *gc, qq_group *group)
+static void room_info_display(PurpleConnection *gc, qq_room_data *rmd)
 {
 	PurpleNotifyUserInfo *room_info;
 	gchar *utf8_value;
 
-	g_return_if_fail(group != NULL && group->id > 0);
+	g_return_if_fail(rmd != NULL && rmd->id > 0);
 
 	room_info = purple_notify_user_info_new();
 
-	purple_notify_user_info_add_pair(room_info, _("Title"), group->title_utf8);
-	purple_notify_user_info_add_pair(room_info, _("Notice"), group->notice_utf8);
-	purple_notify_user_info_add_pair(room_info, _("Detail"), group->desc_utf8);
+	purple_notify_user_info_add_pair(room_info, _("Title"), rmd->title_utf8);
+	purple_notify_user_info_add_pair(room_info, _("Notice"), rmd->notice_utf8);
+	purple_notify_user_info_add_pair(room_info, _("Detail"), rmd->desc_utf8);
 
 	purple_notify_user_info_add_section_break(room_info);
 
-	utf8_value = g_strdup_printf(("%d"), group->creator_uid);
+	utf8_value = g_strdup_printf(("%d"), rmd->creator_uid);
 	purple_notify_user_info_add_pair(room_info, _("Creator"), utf8_value);
 	g_free(utf8_value);
 
-	utf8_value = get_role_desc(group->my_role);
+	utf8_value = get_role_desc(rmd->my_role);
 	purple_notify_user_info_add_pair(room_info, _("About me"), utf8_value);
 	g_free(utf8_value);
 
-	utf8_value = g_strdup_printf(("%d"), group->category);
+	utf8_value = g_strdup_printf(("%d"), rmd->category);
 	purple_notify_user_info_add_pair(room_info, _("Category"), utf8_value);
 	g_free(utf8_value);
 
-	utf8_value = g_strdup_printf(("%d"), group->auth_type);
+	utf8_value = g_strdup_printf(("%d"), rmd->auth_type);
 	purple_notify_user_info_add_pair(room_info, _("Authorize"), utf8_value);
 	g_free(utf8_value);
 
-	utf8_value = g_strdup_printf(("%d"), group->ext_id);
+	utf8_value = g_strdup_printf(("%d"), rmd->ext_id);
 	purple_notify_userinfo(gc, utf8_value, room_info, NULL, NULL);
 	g_free(utf8_value);
 
@@ -165,9 +169,10 @@
 
 void qq_process_room_cmd_get_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc)
 {
-	qq_group *group;
+	qq_data *qd;
+	qq_room_data *rmd;
 	qq_buddy_data *bd;
-	qq_data *qd;
+	PurpleChat *chat;
 	PurpleConversation *conv;
 	guint8 organization, role;
 	guint16 unknown, max_members;
@@ -190,20 +195,18 @@
 	bytes += qq_get32(&ext_id, data + bytes);
 	g_return_if_fail(ext_id > 0);
 
-	if (action == QQ_ROOM_INFO_CREATE ) {
-		qq_group_create_internal_record(gc, id, ext_id, NULL);
-	}
+	chat = qq_room_find_or_new(gc, id, ext_id);
+	g_return_if_fail(chat != NULL);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail(rmd != NULL);
 
-	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
-
-	bytes += qq_get8(&(group->type8), data + bytes);
+	bytes += qq_get8(&(rmd->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);
+	bytes += qq_get32(&(rmd->creator_uid), data + bytes);
+	bytes += qq_get8(&(rmd->auth_type), data + bytes);
 	bytes += qq_get32(&unknown4, data + bytes);	/* oldCategory */
 	bytes += qq_get16(&unknown, data + bytes);
-	bytes += qq_get32(&(group->category), data + bytes);
+	bytes += qq_get32(&(rmd->category), data + bytes);
 	bytes += qq_get16(&max_members, data + bytes);
 	bytes += qq_get8(&unknown1, data + bytes);
 	/* the following, while Eva:
@@ -212,7 +215,7 @@
 	 * qunDestLen(qunDestcontent)) */
 	bytes += qq_get8(&unknown1, data + bytes);
 	purple_debug_info("QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
-			group->type8, group->creator_uid, group->category, max_members);
+			rmd->type8, rmd->creator_uid, rmd->category, max_members);
 
 	if (qd->client_version >= 2007) {
 		/* skip 7 bytes unknow in qq2007 0x(00 00 01 00 00 00 fc)*/
@@ -220,13 +223,13 @@
 	}
 	/* qq_show_packet("Room Info", data + bytes, data_len - bytes); */
 	/* strlen + <str content> */
-	bytes += qq_get_vstr(&(group->title_utf8), QQ_CHARSET_DEFAULT, data + bytes);
+	bytes += qq_get_vstr(&(rmd->title_utf8), QQ_CHARSET_DEFAULT, data + bytes);
 	bytes += qq_get16(&unknown, data + bytes);	/* 0x0000 */
 	bytes += qq_get_vstr(&notice, QQ_CHARSET_DEFAULT, data + bytes);
-	bytes += qq_get_vstr(&(group->desc_utf8), QQ_CHARSET_DEFAULT, data + bytes);
+	bytes += qq_get_vstr(&(rmd->desc_utf8), QQ_CHARSET_DEFAULT, data + bytes);
 
 	purple_debug_info("QQ", "room [%s] notice [%s] desc [%s] unknow 0x%04X\n",
-			group->title_utf8, notice, group->desc_utf8, unknown);
+			rmd->title_utf8, notice, rmd->desc_utf8, unknown);
 
 	num = 0;
 	/* now comes the member list separated by 0x00 */
@@ -242,7 +245,7 @@
 		}
 #endif
 
-		bd = qq_group_find_or_add_member(gc, group, member_uid);
+		bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
 		if (bd != NULL)
 			bd->role = role;
 	}
@@ -251,30 +254,30 @@
 			"group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
 	}
 
-	purple_debug_info("QQ", "group \"%s\" has %d members\n", group->title_utf8, num);
+	purple_debug_info("QQ", "group \"%s\" has %d members\n", rmd->title_utf8, num);
 
-	if (group->creator_uid == qd->uid)
-		group->my_role = QQ_ROOM_ROLE_ADMIN;
+	if (rmd->creator_uid == qd->uid)
+		rmd->my_role = QQ_ROOM_ROLE_ADMIN;
 
 	/* filter \r\n in notice */
 	qq_filter_str(notice);
-	group->notice_utf8 = strdup(notice);
+	rmd->notice_utf8 = strdup(notice);
 	g_free(notice);
 
-	qq_group_refresh(gc, group);
+	qq_room_update_chat_info(chat, rmd);
 
 	if (action == QQ_ROOM_INFO_DISPLAY) {
-		room_info_display(gc, group);
+		room_info_display(gc, rmd);
 	}
 
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-			group->title_utf8, purple_connection_get_account(gc));
+			rmd->title_utf8, purple_connection_get_account(gc));
 	if(NULL == conv) {
-		purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", group->title_utf8);
+		purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->title_utf8);
 		return;
 	}
 
-	topic_utf8 = g_strdup_printf("%d %s", group->ext_id, group->notice_utf8);
+	topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8);
 	purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
 	purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
 	g_free(topic_utf8);
@@ -285,7 +288,7 @@
 	guint32 id, member_uid;
 	guint8 unknown;
 	gint bytes, num;
-	qq_group *group;
+	qq_room_data *rmd;
 	qq_buddy_data *bd;
 
 	g_return_if_fail(data != NULL && len > 0);
@@ -300,19 +303,19 @@
 	bytes += qq_get8(&unknown, data + bytes);	/* 0x3c ?? */
 	g_return_if_fail(id > 0);
 
-	group = qq_room_search_id(gc, id);
-	if (group == NULL) {
+	rmd = qq_room_data_find(gc, id);
+	if (rmd == NULL) {
 		purple_debug_error("QQ", "We have no group info for internal id [%d]\n", id);
 		return;
 	}
 
 	/* set all offline first, then update those online */
-	set_all_offline(group);
+	set_all_offline(rmd);
 	num = 0;
 	while (bytes < len) {
 		bytes += qq_get32(&member_uid, data + bytes);
 		num++;
-		bd = qq_group_find_or_add_member(gc, group, member_uid);
+		bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
 		if (bd != NULL)
 			bd->status = QQ_BUDDY_ONLINE_NORMAL;
 	}
@@ -321,8 +324,8 @@
 			"group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
-	purple_debug_info("QQ", "Group \"%s\" has %d online members\n", group->title_utf8, num);
-	qq_room_conv_set_onlines(gc, group);
+	purple_debug_info("QQ", "Group \"%s\" has %d online members\n", rmd->title_utf8, num);
+	qq_room_conv_set_onlines(gc, rmd);
 }
 
 /* process the reply to get_members_info packet */
@@ -332,7 +335,7 @@
 	gint num;
 	guint32 id, member_uid;
 	guint16 unknown;
-	qq_group *group;
+	qq_room_data *rmd;
 	qq_buddy_data *bd;
 	gchar *nick;
 
@@ -344,15 +347,15 @@
 	bytes += qq_get32(&id, data + bytes);
 	g_return_if_fail(id > 0);
 
-	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail(rmd != NULL);
 
 	num = 0;
 	/* now starts the member info, as get buddy list reply */
 	while (bytes < len) {
 		bytes += qq_get32(&member_uid, data + bytes);
 		g_return_if_fail(member_uid > 0);
-		bd = qq_group_find_member_by_uid(group, member_uid);
+		bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
 		g_return_if_fail(bd != NULL);
 
 		num++;
@@ -381,9 +384,9 @@
 		purple_debug_error("QQ",
 				"group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
 	}
-	purple_debug_info("QQ", "Group \"%s\" obtained %d member info\n", group->title_utf8, num);
+	purple_debug_info("QQ", "Group \"%s\" obtained %d member info\n", rmd->title_utf8, num);
 
-	group->is_got_buddies = TRUE;
-	qq_room_conv_set_onlines(gc, group);
+	rmd->is_got_buddies = TRUE;
+	qq_room_conv_set_onlines(gc, rmd);
 }
 
--- a/libpurple/protocols/qq/group_info.h	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_info.h	Tue Oct 28 16:38:16 2008 +0000
@@ -32,10 +32,9 @@
 enum {
 	QQ_ROOM_INFO_UPDATE_ONLY = 0,
 	QQ_ROOM_INFO_DISPLAY,
-	QQ_ROOM_INFO_CREATE,
 };
 
-gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint update_class);
+gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, gint update_class);
 
 void qq_process_room_cmd_get_info(guint8 *data, gint len, guint32 action, PurpleConnection *gc);
 void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc);
--- a/libpurple/protocols/qq/group_internal.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Tue Oct 28 16:38:16 2008 +0000
@@ -27,151 +27,395 @@
 #include "debug.h"
 
 #include "buddy_opt.h"
-#include "group_free.h"
 #include "group_internal.h"
 #include "utils.h"
 
-static void add_room_to_blist(PurpleConnection *gc, qq_group *group)
+static qq_room_data *room_data_new(guint32 id, guint32 ext_id, gchar *title)
+{
+	qq_room_data *rmd;
+
+	purple_debug_info("QQ", "Created room data: %s, ext id %d, id %d\n",
+			title, ext_id, id);
+	rmd = g_new0(qq_room_data, 1);
+	rmd->my_role = QQ_ROOM_ROLE_NO;
+	rmd->id = id;
+	rmd->ext_id = ext_id;
+	rmd->type8 = 0x01;       /* assume permanent Qun */
+	rmd->creator_uid = 10000;     /* assume by QQ admin */
+	rmd->category = 0x01;
+	rmd->auth_type = 0x02;        /* assume need auth */
+	rmd->title_utf8 = g_strdup(title == NULL ? "" : title);
+	rmd->desc_utf8 = g_strdup("");
+	rmd->notice_utf8 = g_strdup("");
+	rmd->members = NULL;
+	rmd->is_got_buddies = FALSE;
+	return rmd;
+}
+
+/* create a qq_room_data from hashtable */
+static qq_room_data *room_data_new_by_hashtable(PurpleConnection *gc, GHashTable *data)
+{
+	qq_room_data *rmd;
+	guint32 id, ext_id;
+	gchar *value;
+
+	value = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
+	id = value ? strtol(value, NULL, 10) : 0;
+	value= g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
+	ext_id = value ? strtol(value, NULL, 10) : 0;
+	value = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8));
+
+	rmd = room_data_new(id, ext_id, value);
+	rmd->my_role = QQ_ROOM_ROLE_YES;
+	return rmd;
+}
+
+/* gracefully free all members in a room */
+static void room_buddies_free(qq_room_data *rmd)
+{
+	gint i;
+	GList *list;
+	qq_buddy_data *bd;
+
+	g_return_if_fail(rmd != NULL);
+	i = 0;
+	while (NULL != (list = rmd->members)) {
+		bd = (qq_buddy_data *) list->data;
+		i++;
+		rmd->members = g_list_remove(rmd->members, bd);
+		qq_buddy_data_free(bd);
+	}
+
+	rmd->members = NULL;
+}
+
+/* gracefully free the memory for one qq_room_data */
+static void room_data_free(qq_room_data *rmd)
+{
+	g_return_if_fail(rmd != NULL);
+	room_buddies_free(rmd);
+	g_free(rmd->title_utf8);
+	g_free(rmd->desc_utf8);
+	g_free(rmd->notice_utf8);
+	g_free(rmd);
+}
+
+void qq_room_update_chat_info(PurpleChat *chat, qq_room_data *rmd)
+{
+	if (rmd->title_utf8 != NULL && strlen(rmd->title_utf8) > 0) {
+		purple_blist_alias_chat(chat, rmd->title_utf8);
+	}
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_INTERNAL_ID),
+		     g_strdup_printf("%d", rmd->id));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
+		     g_strdup_printf("%d", rmd->ext_id));
+	g_hash_table_replace(chat->components,
+		     g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8));
+}
+
+static PurpleChat *chat_new(PurpleConnection *gc, qq_room_data *rmd)
 {
 	GHashTable *components;
 	PurpleGroup *g;
 	PurpleChat *chat;
-	components = qq_group_to_hashtable(group);
-	chat = purple_chat_new(purple_connection_get_account(gc), group->title_utf8, components);
+
+	purple_debug_info("QQ", "Add new chat: id %d, ext id %d, title %s\n",
+		rmd->id, rmd->ext_id, rmd->title_utf8);
+
+	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	g_hash_table_insert(components,
+			    g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%d", rmd->id));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
+			    g_strdup_printf("%d", rmd->ext_id));
+	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8));
+
+	chat = purple_chat_new(purple_connection_get_account(gc), rmd->title_utf8, components);
 	g = qq_group_find_or_new(PURPLE_GROUP_QQ_QUN);
 	purple_blist_add_chat(chat, g, NULL);
-	purple_debug_info("QQ", "Added room \"%s\" to blist locally\n", group->title_utf8);
+
+	return chat;
+}
+
+PurpleChat *qq_room_find_or_new(PurpleConnection *gc, guint32 id, guint32 ext_id)
+{
+	qq_data *qd;
+	qq_room_data *rmd;
+	PurpleChat *chat;
+	gchar *num_str;
+
+	g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	g_return_val_if_fail(id != 0 && ext_id != 0, NULL);
+
+	purple_debug_info("QQ", "Find or add new room: id %d, ext id %d\n", id, ext_id);
+
+	rmd = qq_room_data_find(gc, id);
+	if (rmd == NULL) {
+		rmd = room_data_new(id, ext_id, NULL);
+		g_return_val_if_fail(rmd != NULL, NULL);
+		qd->groups = g_list_append(qd->groups, rmd);
+	}
+
+	num_str = g_strdup_printf("%d", ext_id);
+	chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
+	g_free(num_str);
+	if (chat) {
+		return chat;
+	}
+
+	return chat_new(gc, rmd);
 }
 
-/* Create a dummy qq_group, which includes only internal_id, ext_id,
- * and potentially title_utf8, in case we need to call group_conv_show_window
- * right after creation. All other attributes are set to empty.
- * We need to send a get_group_info to the QQ server to update it right away */
-qq_group *qq_group_create_internal_record(PurpleConnection *gc,
-                guint32 internal_id, guint32 ext_id, gchar *title_utf8)
+void qq_room_remove(PurpleConnection *gc, guint32 id)
 {
-        qq_group *group;
-        qq_data *qd;
+	qq_data *qd;
+	PurpleChat *chat;
+	qq_room_data *rmd;
+	gchar *num_str;
+	guint32 ext_id;
 
-        g_return_val_if_fail(internal_id > 0, NULL);
-        qd = (qq_data *) gc->proto_data;
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
 
-        group = g_new0(qq_group, 1);
-        group->my_role = QQ_ROOM_ROLE_NO;
-        group->id = internal_id;
-        group->ext_id = ext_id;
-        group->type8 = 0x01;       /* assume permanent Qun */
-        group->creator_uid = 10000;     /* assume by QQ admin */
-        group->category = 0x01;
-        group->auth_type = 0x02;        /* assume need auth */
-        group->title_utf8 = g_strdup(title_utf8 == NULL ? "" : title_utf8);
-        group->desc_utf8 = g_strdup("");
-        group->notice_utf8 = g_strdup("");
-        group->members = NULL;
+	purple_debug_info("QQ", "Find and remove room data, id %d", id);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail (rmd != NULL);
+
+	ext_id = rmd->ext_id;
+	qd->groups = g_list_remove(qd->groups, rmd);
+	room_data_free(rmd);
 
-        qd->groups = g_list_append(qd->groups, group);
-        add_room_to_blist(gc, group);
+	purple_debug_info("QQ", "Find and remove chat, ext_id %d", ext_id);
+	num_str = g_strdup_printf("%d", ext_id);
+	chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
+	g_free(num_str);
 
-        return group;
+	g_return_if_fail (chat != NULL);
+
+	purple_blist_remove_chat(chat);
 }
 
-void qq_group_delete_internal_record(qq_data *qd, guint32 id)
+/* find a qq_buddy_data by uid, called by im.c */
+qq_buddy_data *qq_room_buddy_find(qq_room_data *rmd, guint32 uid)
 {
-        qq_group *group;
-        GList *list;
+	GList *list;
+	qq_buddy_data *bd;
+	g_return_val_if_fail(rmd != NULL && uid > 0, NULL);
+
+	list = rmd->members;
+	while (list != NULL) {
+		bd = (qq_buddy_data *) list->data;
+		if (bd->uid == uid)
+			return bd;
+		else
+			list = list->next;
+	}
 
-        list = qd->groups;
-        while (list != NULL) {
-                group = (qq_group *) qd->groups->data;
-                if (id == group->id) {
-                        qd->groups = g_list_remove(qd->groups, group);
-                        qq_group_free(group);
-                        break;
-                } else {
-                        list = list->next;
-                }
-        }
+	return NULL;
+}
+
+/* remove a qq_buddy_data by uid, called by qq_group_opt.c */
+void qq_room_buddy_remove(qq_room_data *rmd, guint32 uid)
+{
+	GList *list;
+	qq_buddy_data *bd;
+	g_return_if_fail(rmd != NULL && uid > 0);
+
+	list = rmd->members;
+	while (list != NULL) {
+		bd = (qq_buddy_data *) list->data;
+		if (bd->uid == uid) {
+			rmd->members = g_list_remove(rmd->members, bd);
+			return;
+		} else {
+			list = list->next;
+		}
+	}
 }
 
-/* convert a qq_group to hash-table, which could be component of PurpleChat */
-GHashTable *qq_group_to_hashtable(qq_group *group)
+qq_buddy_data *qq_room_buddy_find_or_new(PurpleConnection *gc, qq_room_data *rmd, guint32 member_uid)
 {
-	GHashTable *components;
-	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	qq_buddy_data *member, *bd;
+	PurpleBuddy *buddy;
+	g_return_val_if_fail(rmd != NULL && member_uid > 0, NULL);
 
-	g_hash_table_insert(components,
-			    g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%d", group->id));
-	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
-			    g_strdup_printf("%d", group->ext_id));
-	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(group->title_utf8));
-	return components;
+	member = qq_room_buddy_find(rmd, member_uid);
+	if (member == NULL) {	/* first appear during my session */
+		member = g_new0(qq_buddy_data, 1);
+		member->uid = member_uid;
+		buddy = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(member_uid));
+		if (buddy != NULL) {
+			bd = (qq_buddy_data *) buddy->proto_data;
+			if (bd != NULL && bd->nickname != NULL)
+				member->nickname = g_strdup(bd->nickname);
+			else if (buddy->alias != NULL)
+				member->nickname = g_strdup(buddy->alias);
+		}
+		rmd->members = g_list_append(rmd->members, member);
+	}
+
+	return member;
 }
 
-static gint str2dec(const gchar *str)
+qq_room_data *qq_room_data_find(PurpleConnection *gc, guint32 room_id)
 {
-	g_return_val_if_fail(str != NULL, 0);
-	return strtol(str, NULL, 10);
+	GList *list;
+	qq_room_data *rmd;
+	qq_data *qd;
+
+	qd = (qq_data *) gc->proto_data;
+
+	if (qd->groups == NULL || room_id <= 0)
+		return 0;
+
+	list = qd->groups;
+	while (list != NULL) {
+		rmd = (qq_room_data *) list->data;
+		if (rmd->id == room_id) {
+			return rmd;
+		}
+		list = list->next;
+	}
+
+	return NULL;
 }
 
-/* create a qq_group from hashtable */
-qq_group *qq_room_data_new_by_hashtable(PurpleConnection *gc, GHashTable *data)
+guint32 qq_room_get_next(PurpleConnection *gc, guint32 room_id)
 {
+	GList *list;
+	qq_room_data *rmd;
 	qq_data *qd;
-	qq_group *group;
+	gboolean is_find = FALSE;
 
-	g_return_val_if_fail(data != NULL, NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	group = g_new0(qq_group, 1);
-	memset(group, 0, sizeof(qq_group));
-	group->my_role = QQ_ROOM_ROLE_YES;
-	group->id = str2dec(g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID));
-	group->ext_id = str2dec(g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID));
-	group->title_utf8 = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8));
-    group->type8 = 0x01;       /* assume permanent Qun */
-    group->creator_uid = 10000;     /* assume by QQ admin */
-    group->category = 0x01;
-    group->auth_type = 0x02;        /* assume need auth */
-    group->desc_utf8 = g_strdup("");
-    group->notice_utf8 = g_strdup("");
-    group->members = NULL;
-	group->is_got_buddies = FALSE;
+	if (qd->groups == NULL) {
+		return 0;
+	}
+
+	 if (room_id <= 0) {
+	 	rmd = (qq_room_data *) qd->groups->data;
+		return rmd->id;
+	}
 
-	purple_debug_info("QQ", "Created room info from hashtable: %s, %d, id %d\n",
-			group->title_utf8, group->ext_id, group->id);
-	qd->groups = g_list_append(qd->groups, group);
-	return group;
+	list = qd->groups;
+	while (list != NULL) {
+		rmd = (qq_room_data *) list->data;
+		list = list->next;
+		if (rmd->id == room_id) {
+			is_find = TRUE;
+			break;
+		}
+	}
+
+	g_return_val_if_fail(is_find, 0);
+	if (list == NULL) return 0;	/* be the end */
+ 	rmd = (qq_room_data *) list->data;
+	g_return_val_if_fail(rmd != NULL, 0);
+	return rmd->id;
 }
 
-/* refresh group local subscription */
-void qq_group_refresh(PurpleConnection *gc, qq_group *group)
+guint32 qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id)
 {
+	GList *list;
+	qq_room_data *rmd;
+	qq_data *qd;
+	gboolean is_find;
+
+	qd = (qq_data *) gc->proto_data;
+
+ 	list = qd->groups;
+	if (room_id > 0) {
+		/* search next room */
+		is_find = FALSE;
+		while (list != NULL) {
+			rmd = (qq_room_data *) list->data;
+			list = list->next;
+			if (rmd->id == room_id) {
+				is_find = TRUE;
+				break;
+			}
+		}
+		g_return_val_if_fail(is_find, 0);
+	}
+
+	while (list != NULL) {
+		rmd = (qq_room_data *) list->data;
+		g_return_val_if_fail(rmd != NULL, 0);
+
+		if (rmd->my_role == QQ_ROOM_ROLE_YES || rmd->my_role == QQ_ROOM_ROLE_ADMIN) {
+			if (NULL != purple_find_conversation_with_account(
+						PURPLE_CONV_TYPE_CHAT,rmd->title_utf8, purple_connection_get_account(gc))) {
+				/* In convseration*/
+				return rmd->id;
+			}
+		}
+		list = list->next;
+	}
+
+	return 0;
+}
+
+/* this should be called upon signin, even when we did not open group chat window */
+void qq_room_data_initial(PurpleConnection *gc)
+{
+	PurpleAccount *account;
 	PurpleChat *chat;
-	gchar *ext_id;
-	g_return_if_fail(group != NULL);
+	PurpleGroup *purple_group;
+	PurpleBlistNode *node;
+	qq_data *qd;
+	qq_room_data *rmd;
+	gint count;
 
-	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_role != QQ_ROOM_ROLE_NO) {
-		add_room_to_blist(gc, group);
+	account = purple_connection_get_account(gc);
+	qd = (qq_data *) gc->proto_data;
+
+	purple_debug_info("QQ", "Initial QQ Qun configurations\n");
+	purple_group = purple_find_group(PURPLE_GROUP_QQ_QUN);
+	if (purple_group == NULL) {
+		purple_debug_info("QQ", "We have no QQ Qun\n");
 		return;
 	}
 
-	if (chat == NULL) {
-		return;
+	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;
+
+		rmd = room_data_new_by_hashtable(gc, chat->components);
+		qd->groups = g_list_append(qd->groups, rmd);
+		count++;
 	}
 
-	/* we have a local record, update its info */
-	/* if there is title_utf8, we update the group name */
-	if (group->title_utf8 != NULL && strlen(group->title_utf8) > 0)
-		purple_blist_alias_chat(chat, group->title_utf8);
-	g_hash_table_replace(chat->components,
-		     g_strdup(QQ_ROOM_KEY_INTERNAL_ID),
-		     g_strdup_printf("%d", group->id));
-	g_hash_table_replace(chat->components,
-		     g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
-		     g_strdup_printf("%d", group->ext_id));
-	g_hash_table_replace(chat->components,
-		     g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(group->title_utf8));
+	purple_debug_info("QQ", "Load %d QQ Qun configurations\n", count);
 }
+
+void qq_room_data_free_all(PurpleConnection *gc)
+{
+	qq_data *qd;
+	qq_room_data *rmd;
+	gint count;
+
+	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	count = 0;
+	while (qd->groups != NULL) {
+		rmd = (qq_room_data *) qd->groups->data;
+		qd->groups = g_list_remove(qd->groups, rmd);
+		room_data_free(rmd);
+		count++;
+	}
+
+	if (count > 0) {
+		purple_debug_info("QQ", "%d rooms are freed\n", count);
+	}
+}
--- a/libpurple/protocols/qq/group_internal.h	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.h	Tue Oct 28 16:38:16 2008 +0000
@@ -32,13 +32,19 @@
 #define QQ_ROOM_KEY_EXTERNAL_ID					"ext_id"
 #define QQ_ROOM_KEY_TITLE_UTF8					"title_utf8"
 
-qq_group *qq_group_create_internal_record(PurpleConnection *gc,
-		guint32 internal_id, guint32 ext_id, gchar *group_name_utf8);
-void qq_group_delete_internal_record(qq_data *qd, guint32 id);
+PurpleChat *qq_room_find_or_new(PurpleConnection *gc, guint32 id, guint32 ext_id);
+void qq_room_remove(PurpleConnection *gc, guint32 id);
+void qq_room_update_chat_info(PurpleChat *chat, qq_room_data *rmd);
 
-GHashTable *qq_group_to_hashtable(qq_group *group);
-qq_group *qq_room_data_new_by_hashtable(PurpleConnection *gc, GHashTable *data);
+qq_buddy_data *qq_room_buddy_find(qq_room_data *rmd, guint32 uid);
+void qq_room_buddy_remove(qq_room_data *rmd, guint32 uid);
+qq_buddy_data *qq_room_buddy_find_or_new(PurpleConnection *gc, qq_room_data *rmd, guint32 member_uid);
 
-void qq_group_refresh(PurpleConnection *gc, qq_group *group);
+void qq_room_data_initial(PurpleConnection *gc);
+void qq_room_data_free_all(PurpleConnection *gc);
+qq_room_data *qq_room_data_find(PurpleConnection *gc, guint32 room_id);
+
+guint32 qq_room_get_next(PurpleConnection *gc, guint32 room_id);
+guint32 qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id);
 
 #endif
--- a/libpurple/protocols/qq/group_join.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Tue Oct 28 16:38:16 2008 +0000
@@ -31,12 +31,10 @@
 
 #include "char_conv.h"
 #include "im.h"
-#include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_join.h"
 #include "group_opt.h"
-#include "group_search.h"
 #include "group_im.h"
 #include "qq_define.h"
 #include "packet_parse.h"
@@ -49,11 +47,16 @@
 	QQ_ROOM_JOIN_DENIED = 0x03,
 };
 
+enum {
+	QQ_ROOM_SEARCH_TYPE_BY_ID = 0x01,
+	QQ_ROOM_SEARCH_TYPE_DEMO = 0x02
+};
+
 static void group_quit_cb(qq_add_request *add_req)
 {
 	PurpleConnection *gc;
 	guint32 id;
-	qq_group *group;
+	qq_room_data *rmd;
 
 	if (add_req->gc == NULL || add_req->uid == 0) {
 		g_free(add_req);
@@ -63,48 +66,47 @@
 	gc = add_req->gc;
 	id = add_req->uid;
 
-	group = qq_room_search_id(gc, id);
-	if (group == NULL) {
+	rmd = qq_room_data_find(gc, id);
+	if (rmd == NULL) {
 		g_free(add_req);
 		return;
 	}
 
-	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, group->id);
+	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, rmd->id);
 	g_free(add_req);
 }
 
 /* send packet to join a group without auth */
-void qq_request_room_join(PurpleConnection *gc, qq_group *group)
+void qq_request_room_join(PurpleConnection *gc, qq_room_data *rmd)
 {
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 
-	if (group->my_role == QQ_ROOM_ROLE_NO) {
-		group->my_role = QQ_ROOM_ROLE_REQUESTING;
-		qq_group_refresh(gc, group);
+	if (rmd->my_role == QQ_ROOM_ROLE_NO) {
+		rmd->my_role = QQ_ROOM_ROLE_REQUESTING;
 	}
 
-	switch (group->auth_type) {
+	switch (rmd->auth_type) {
 	case QQ_ROOM_AUTH_TYPE_NO_AUTH:
 	case QQ_ROOM_AUTH_TYPE_NEED_AUTH:
 		break;
 	case QQ_ROOM_AUTH_TYPE_NO_ADD:
-		if (group->my_role == QQ_ROOM_ROLE_NO
-				&& group->my_role == QQ_ROOM_ROLE_REQUESTING) {
+		if (rmd->my_role == QQ_ROOM_ROLE_NO
+				&& rmd->my_role == QQ_ROOM_ROLE_REQUESTING) {
 			purple_notify_warning(gc, NULL, _("The Qun does not allow others to join"), NULL);
 			return;
 		}
 		break;
 	default:
-		purple_debug_error("QQ", "Unknown room auth type: %d\n", group->auth_type);
+		purple_debug_error("QQ", "Unknown room auth type: %d\n", rmd->auth_type);
 		break;
 	}
 
-	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_JOIN, group->id);
+	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_JOIN, rmd->id);
 }
 
 static void group_join_cb(qq_add_request *add_req, const gchar *reason_utf8)
 {
-	qq_group *group;
+	qq_room_data *rmd;
 
 	g_return_if_fail(add_req != NULL);
 	if (add_req->gc == NULL || add_req->uid == 0) {
@@ -112,14 +114,14 @@
 		return;
 	}
 
-	group = qq_room_search_id(add_req->gc, add_req->uid);
-	if (group == NULL) {
-		purple_debug_error("QQ", "Can not find qq_group by internal_id: %d\n", add_req->uid);
+	rmd = qq_room_data_find(add_req->gc, add_req->uid);
+	if (rmd == NULL) {
+		purple_debug_error("QQ", "Can not find qq_room_data by internal_id: %d\n", add_req->uid);
 		g_free(add_req);
 		return;
 	}
 
-	qq_send_cmd_group_auth(add_req->gc, group, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
+	qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
 	g_free(add_req);
 }
 
@@ -129,36 +131,36 @@
 	g_free(add_req);
 }
 
-static void _qq_group_join_auth(PurpleConnection *gc, qq_group *group)
+static void _qq_group_join_auth(PurpleConnection *gc, qq_room_data *rmd)
 {
 	gchar *msg;
 	qq_add_request *add_req;
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 
-	purple_debug_info("QQ", "Group (internal id: %d) needs authentication\n", group->id);
+	purple_debug_info("QQ", "Group (internal id: %d) needs authentication\n", rmd->id);
 
-	msg = g_strdup_printf("Group \"%s\" needs authentication\n", group->title_utf8);
+	msg = g_strdup_printf("Group \"%s\" needs authentication\n", rmd->title_utf8);
 	add_req = g_new0(qq_add_request, 1);
 	add_req->gc = gc;
-	add_req->uid = group->id;
+	add_req->uid = rmd->id;
 	purple_request_input(gc, NULL, msg,
 			   _("Input request here"),
 			   _("Would you be my friend?"), TRUE, FALSE, NULL,
 			   _("Send"),
 			   G_CALLBACK(group_join_cb),
 			   _("Cancel"), G_CALLBACK(qq_group_cancel_cb),
-			   purple_connection_get_account(gc), group->title_utf8, NULL,
+			   purple_connection_get_account(gc), rmd->title_utf8, NULL,
 			   add_req);
 	g_free(msg);
 }
 
-void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8)
+void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, guint8 opt, guint32 uid, const gchar *reason_utf8)
 {
 	guint8 *raw_data;
 	gchar *reason_qq;
 	gint bytes;
 
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 
 	if (reason_utf8 == NULL || strlen(reason_utf8) == 0)
 		reason_qq = g_strdup("");
@@ -166,8 +168,7 @@
 		reason_qq = utf8_to_qq(reason_utf8, QQ_CHARSET_DEFAULT);
 
 	if (opt == QQ_ROOM_AUTH_REQUEST_APPLY) {
-		group->my_role = QQ_ROOM_ROLE_REQUESTING;
-		qq_group_refresh(gc, group);
+		rmd->my_role = QQ_ROOM_ROLE_REQUESTING;
 		uid = 0;
 	}
 
@@ -179,18 +180,15 @@
 	bytes += qq_put8(raw_data + bytes, strlen(reason_qq));
 	bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq));
 
-	qq_send_room_cmd(gc, QQ_ROOM_CMD_AUTH, group->id, raw_data, bytes);
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_AUTH, rmd->id, raw_data, bytes);
 }
 
 /* If comes here, cmd is OK already */
 void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc)
 {
+	qq_data *qd;
 	gint bytes;
 	guint32 id;
-	PurpleChat *chat;
-	qq_group *group;
-	qq_data *qd;
-	gchar *msg;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
@@ -203,20 +201,7 @@
 	bytes = 0;
 	bytes += qq_get32(&id, data + bytes);
 
-	group = qq_room_search_id(gc, id);
-	if (group != NULL) {
-		msg = g_strdup_printf(_("Successed quit Qun %s (%d)"),
-				group->title_utf8, group->ext_id);
-		chat = purple_blist_find_chat
-			    (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, id);
-	} else {
-		msg = g_strdup(_("Successed quit Qun"));
-	}
-	qq_got_attention(gc, msg);
-	g_free(msg);
+	qq_room_remove(gc, id);
 }
 
 /* Process the reply to group_auth subcmd */
@@ -225,7 +210,7 @@
 	gint bytes;
 	guint32 id;
 	qq_data *qd;
-	qq_group *group;
+	qq_room_data *rmd;
 	gchar *msg;
 
 	g_return_if_fail(data != NULL && len > 0);
@@ -240,9 +225,9 @@
 	bytes += qq_get32(&id, data + bytes);
 	g_return_if_fail(id > 0);
 
-	group = qq_room_search_id(gc, id);
-	if (group != NULL) {
-		msg = g_strdup_printf(_("Successed join to Qun %s (%d)"), group->title_utf8, group->ext_id);
+	rmd = qq_room_data_find(gc, id);
+	if (rmd != NULL) {
+		msg = g_strdup_printf(_("Successed join to Qun %s (%d)"), rmd->title_utf8, rmd->ext_id);
 		qq_got_attention(gc, msg);
 		g_free(msg);
 	} else {
@@ -256,14 +241,14 @@
 	gint bytes;
 	guint32 id;
 	guint8 reply;
-	qq_group *group;
+	qq_room_data *rmd;
 	gchar *msg;
 
 	g_return_if_fail(data != NULL && len > 0);
 
 	if (len < 5) {
 		purple_debug_error("QQ",
-			   "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len);
+			   "Invalid join room reply, expect %d bytes, read %d bytes\n", 5, len);
 		return;
 	}
 
@@ -272,34 +257,32 @@
 	bytes += qq_get8(&reply, data + bytes);
 
 	/* join group OK */
-	group = qq_room_search_id(gc, id);
+	rmd = qq_room_data_find(gc, id);
 	/* need to check if group is NULL or not. */
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 	switch (reply) {
 	case QQ_ROOM_JOIN_OK:
-		purple_debug_info("QQ", "Successed in joining group \"%s\"\n", group->title_utf8);
-		group->my_role = QQ_ROOM_ROLE_YES;
-		qq_group_refresh(gc, group);
+		purple_debug_info("QQ", "Successed in joining group \"%s\"\n", rmd->title_utf8);
+		rmd->my_role = QQ_ROOM_ROLE_YES;
 		/* this must be shown before getting online members */
-		qq_room_conv_open(gc, group);
+		qq_room_conv_open(gc, rmd);
 		break;
 	case QQ_ROOM_JOIN_NEED_AUTH:
 		purple_debug_info("QQ",
 			   "Fail joining group [%d] %s, needs authentication\n",
-			   group->ext_id, group->title_utf8);
-		group->my_role = QQ_ROOM_ROLE_NO;
-		qq_group_refresh(gc, group);
-		_qq_group_join_auth(gc, group);
+			   rmd->ext_id, rmd->title_utf8);
+		rmd->my_role = QQ_ROOM_ROLE_NO;
+		_qq_group_join_auth(gc, rmd);
 		break;
 	case QQ_ROOM_JOIN_DENIED:
-		msg = g_strdup_printf(_("Qun %d denied to join"), group->ext_id);
+		msg = g_strdup_printf(_("Qun %d denied to join"), rmd->ext_id);
 		purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), msg);
 		g_free(msg);
 		break;
 	default:
 		purple_debug_info("QQ",
 			   "Failed joining group [%d] %s, unknown reply: 0x%02x\n",
-			   group->ext_id, group->title_utf8, reply);
+			   rmd->ext_id, rmd->title_utf8, reply);
 
 		purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknow Reply"));
 	}
@@ -313,7 +296,7 @@
 	gchar *id_str;
 	guint32 ext_id;
 	guint32 id;
-	qq_group *group;
+	qq_room_data *rmd;
 
 	g_return_if_fail(data != NULL);
 	qd = (qq_data *) gc->proto_data;
@@ -325,9 +308,9 @@
 	if (id_str != NULL) {
 		id = strtol(id_str, NULL, 10);
 		if (id != 0) {
-			group = qq_room_search_id(gc, id);
-			if (group) {
-				qq_request_room_join(gc, group);
+			rmd = qq_room_data_find(gc, id);
+			if (rmd) {
+				qq_request_room_join(gc, rmd);
 				return;
 			}
 		}
@@ -362,3 +345,93 @@
 			    G_CALLBACK(qq_group_cancel_cb),
 			    _("Continue"), G_CALLBACK(group_quit_cb));
 }
+
+/* send packet to search for qq_group */
+void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action)
+{
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
+	guint8 type;
+
+	purple_debug_info("QQ", "Search QQ Qun %d\n", ext_id);
+	type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID;
+
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, type);
+	bytes += qq_put32(raw_data + bytes, ext_id);
+
+	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_SEARCH, 0, raw_data, bytes, 0, action);
+}
+
+static void add_to_roomlist(qq_data *qd, qq_room_data *rmd)
+{
+	PurpleRoomlistRoom *room;
+	gchar field[11];
+
+	room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, rmd->title_utf8, NULL);
+	g_snprintf(field, sizeof(field), "%d", rmd->ext_id);
+	purple_roomlist_room_add_field(qd->roomlist, room, field);
+	g_snprintf(field, sizeof(field), "%d", rmd->creator_uid);
+	purple_roomlist_room_add_field(qd->roomlist, room, field);
+	purple_roomlist_room_add_field(qd->roomlist, room, rmd->desc_utf8);
+	g_snprintf(field, sizeof(field), "%d", rmd->id);
+	purple_roomlist_room_add_field(qd->roomlist, room, field);
+	g_snprintf(field, sizeof(field), "%d", rmd->type8);
+	purple_roomlist_room_add_field(qd->roomlist, room, field);
+	g_snprintf(field, sizeof(field), "%d", rmd->auth_type);
+	purple_roomlist_room_add_field(qd->roomlist, room, field);
+	g_snprintf(field, sizeof(field), "%d", rmd->category);
+	purple_roomlist_room_add_field(qd->roomlist, room, field);
+	purple_roomlist_room_add_field(qd->roomlist, room, rmd->title_utf8);
+	purple_roomlist_room_add(qd->roomlist, room);
+
+	purple_roomlist_set_in_progress(qd->roomlist, FALSE);
+}
+
+/* process group cmd reply "search group" */
+void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32)
+{
+	qq_data *qd;
+	qq_room_data rmd;
+	PurpleChat *chat;
+	gint bytes;
+	guint8 search_type;
+	guint16 unknown;
+
+	g_return_if_fail(data != NULL && len > 0);
+	qd = (qq_data *) gc->proto_data;
+
+	bytes = 0;
+	bytes += qq_get8(&search_type, data + bytes);
+
+	/* now it starts with group_info_entry */
+	bytes += qq_get32(&(rmd.id), data + bytes);
+	bytes += qq_get32(&(rmd.ext_id), data + bytes);
+	bytes += qq_get8(&(rmd.type8), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get32(&(rmd.creator_uid), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get32(&(rmd.category), data + bytes);
+	bytes += qq_get_vstr(&(rmd.title_utf8), QQ_CHARSET_DEFAULT, data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get8(&(rmd.auth_type), data + bytes);
+	bytes += qq_get_vstr(&(rmd.desc_utf8), QQ_CHARSET_DEFAULT, data + bytes);
+	/* end of one qq_group */
+	if(bytes != len) {
+		purple_debug_error("QQ",
+			"group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
+	}
+
+	if (ship32 == QQ_ROOM_SEARCH_FOR_JOIN) {
+		chat = qq_room_find_or_new(gc, rmd.id, rmd.ext_id);
+		g_return_if_fail(chat != NULL);
+
+		qq_room_update_chat_info(gc, &rmd);
+		qq_request_room_join(gc, &rmd);
+	} else {
+		add_to_roomlist(qd, &rmd);
+	}
+}
--- a/libpurple/protocols/qq/group_join.h	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_join.h	Tue Oct 28 16:38:16 2008 +0000
@@ -41,9 +41,17 @@
 	QQ_ROOM_AUTH_REQUEST_REJECT = 0x03
 };
 
-void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8);
+enum {
+	QQ_ROOM_SEARCH_ONLY = 0,
+	QQ_ROOM_SEARCH_FOR_JOIN
+};
+
+void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action);
+void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32);
+
+void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, guint8 opt, guint32 uid, const gchar *reason_utf8);
 void qq_group_join(PurpleConnection *gc, GHashTable *data);
-void qq_request_room_join(PurpleConnection *gc, qq_group *group);
+void qq_request_room_join(PurpleConnection *gc, qq_room_data *rmd);
 void qq_room_quit(PurpleConnection *gc, guint32 room_id);
 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);
--- a/libpurple/protocols/qq/group_opt.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Tue Oct 28 16:38:16 2008 +0000
@@ -30,7 +30,6 @@
 
 #include "buddy_info.h"
 #include "char_conv.h"
-#include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_join.h"
@@ -58,7 +57,7 @@
 	qsort (list, i, sizeof (guint32), _compare_guint32);
 }
 
-static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members)
+static void _qq_group_member_opt(PurpleConnection *gc, qq_room_data *rmd, gint operation, guint32 *members)
 {
 	guint8 *data;
 	gint i, count, data_len;
@@ -75,7 +74,7 @@
 	for (i = 0; i < count; i++)
 		bytes += qq_put32(data + bytes, members[i]);
 
-	qq_send_room_cmd(gc, QQ_ROOM_CMD_MEMBER_OPT, group->id, data, bytes);
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_MEMBER_OPT, rmd->id, data, bytes);
 }
 
 static void _qq_group_do_nothing_with_struct(group_member_opt *g)
@@ -86,11 +85,11 @@
 
 static void _qq_group_reject_application_real(group_member_opt *g, gchar *msg_utf8)
 {
-	qq_group *group;
+	qq_room_data *rmd;
 	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_ROOM_AUTH_REQUEST_REJECT, g->member, msg_utf8);
+	rmd = qq_room_data_find(g->gc, g->id);
+	g_return_if_fail(rmd != NULL);
+	qq_send_cmd_group_auth(g->gc, rmd, QQ_ROOM_AUTH_REQUEST_REJECT, g->member, msg_utf8);
 	g_free(g);
 }
 
@@ -131,16 +130,16 @@
 
 void qq_group_approve_application_with_struct(group_member_opt *g)
 {
-	qq_group *group;
+	qq_room_data *rmd;
 	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_ROOM_AUTH_REQUEST_APPROVE, g->member, "");
-	qq_group_find_or_add_member(g->gc, group, g->member);
+	rmd = qq_room_data_find(g->gc, g->id);
+	g_return_if_fail(rmd != NULL);
+	qq_send_cmd_group_auth(g->gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, g->member, "");
+	qq_room_buddy_find_or_new(g->gc, rmd, g->member);
 	g_free(g);
 }
 
-void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new_members)
+void qq_group_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *new_members)
 {
 	guint32 *old_members, *del_members, *add_members;
 	qq_buddy_data *bd;
@@ -148,7 +147,7 @@
 	gint i = 0, old = 0, new = 0, del = 0, add = 0;
 	GList *list;
 
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 	qd = (qq_data *) gc->proto_data;
 	if (new_members[0] == 0xffffffff)
 		return;
@@ -158,7 +157,7 @@
 	add_members = g_newa(guint32, QQ_QUN_MEMBER_MAX);
 
 	/* construct the old member list */
-	list = group->members;
+	list = rmd->members;
 	while (list != NULL) {
 		bd = (qq_buddy_data *) list->data;
 		if (bd != NULL)
@@ -186,14 +185,14 @@
 	del_members[del] = add_members[add] = 0xffffffff;
 
 	for (i = 0; i < del; i++)
-		qq_group_remove_member_by_uid(group, del_members[i]);
+		qq_room_buddy_remove(rmd, del_members[i]);
 	for (i = 0; i < add; i++)
-		qq_group_find_or_add_member(gc, group, add_members[i]);
+		qq_room_buddy_find_or_new(gc, rmd, add_members[i]);
 
 	if (del > 0)
-		_qq_group_member_opt(gc, group, QQ_ROOM_MEMBER_DEL, del_members);
+		_qq_group_member_opt(gc, rmd, QQ_ROOM_MEMBER_DEL, del_members);
 	if (add > 0)
-		_qq_group_member_opt(gc, group, QQ_ROOM_MEMBER_ADD, add_members);
+		_qq_group_member_opt(gc, rmd, QQ_ROOM_MEMBER_ADD, add_members);
 }
 
 void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc)
@@ -201,7 +200,7 @@
 	gint bytes;
 	guint32 id;
 	time_t now = time(NULL);
-	qq_group *group;
+	qq_room_data *rmd;
 	g_return_if_fail(data != NULL);
 
 	bytes = 0;
@@ -209,26 +208,26 @@
 	g_return_if_fail(id > 0);
 
 	/* we should have its info locally */
-	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail(rmd != NULL);
 
-	purple_debug_info("QQ", "Succeed in modify members for room %d\n", group->ext_id);
+	purple_debug_info("QQ", "Succeed in modify members for room %d\n", rmd->ext_id);
 
-	qq_room_got_chat_in(gc, group, 0, _("Successed changing Qun member"), now);
+	qq_room_got_chat_in(gc, id, 0, _("Successed changing Qun member"), now);
 }
 
-void qq_room_change_info(PurpleConnection *gc, qq_group *group)
+void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd)
 {
 	guint8 *data;
 	gint data_len;
 	gint bytes;
 	gchar *group_name, *group_desc, *notice;
 
-	g_return_if_fail(group != NULL);
+	g_return_if_fail(rmd != NULL);
 
-	group_name = group->title_utf8 == NULL ? "" : utf8_to_qq(group->title_utf8, QQ_CHARSET_DEFAULT);
-	group_desc = group->desc_utf8 == NULL ? "" : utf8_to_qq(group->desc_utf8, QQ_CHARSET_DEFAULT);
-	notice = group->notice_utf8 == NULL ? "" : utf8_to_qq(group->notice_utf8, QQ_CHARSET_DEFAULT);
+	group_name = rmd->title_utf8 == NULL ? "" : utf8_to_qq(rmd->title_utf8, QQ_CHARSET_DEFAULT);
+	group_desc = rmd->desc_utf8 == NULL ? "" : utf8_to_qq(rmd->desc_utf8, QQ_CHARSET_DEFAULT);
+	notice = rmd->notice_utf8 == NULL ? "" : utf8_to_qq(rmd->notice_utf8, QQ_CHARSET_DEFAULT);
 
 	data_len = 64 + strlen(group_name) + strlen(group_desc) + strlen(notice);
 	data = g_newa(guint8, data_len);
@@ -236,11 +235,11 @@
 	/* 005-005 */
 	bytes += qq_put8(data + bytes, 0x01);
 	/* 006-006 */
-	bytes += qq_put8(data + bytes, group->auth_type);
+	bytes += qq_put8(data + bytes, rmd->auth_type);
 	/* 007-008 */
 	bytes += qq_put16(data + bytes, 0x0000);
 	/* 009-010 */
-	bytes += qq_put16(data + bytes, group->category);
+	bytes += qq_put16(data + bytes, rmd->category);
 
 	bytes += qq_put8(data + bytes, strlen(group_name));
 	bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name));
@@ -259,14 +258,13 @@
 			   data_len, bytes);
 		return;
 	}
-	qq_send_room_cmd(gc, QQ_ROOM_CMD_CHANGE_INFO, group->id, data, bytes);
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_CHANGE_INFO, rmd->id, data, bytes);
 }
 
 void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
 	gint bytes;
 	guint32 id;
-	qq_group *group;
 	time_t now = time(NULL);
 
 	g_return_if_fail(data != NULL);
@@ -275,17 +273,12 @@
 	bytes += qq_get32(&id, data + bytes);
 	g_return_if_fail(id > 0);
 
-	/* we should have its info locally */
-	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
+	purple_debug_info("QQ", "Succeed modify room info of %d\n", id);
 
-	purple_debug_info("QQ", "Succeed in modify info for Qun %d\n", group->ext_id);
-	qq_group_refresh(gc, group);
-
-	qq_room_got_chat_in(gc, group, 0, _("Successed changing Qun information"), now);
+	qq_room_got_chat_in(gc, id, 0, _("Successed changing Qun information"), now);
 }
 
-/* we create a very simple group first, and then let the user to modify */
+/* we create a very simple room first, and then let the user to modify */
 void qq_room_create_new(PurpleConnection *gc, const gchar *name)
 {
 	guint8 *data;
@@ -328,21 +321,21 @@
 
 static void qq_group_setup_cb(qq_add_request *add_req)
 {
-	qq_group *group;
+	qq_room_data *rmd;
 	g_return_if_fail(add_req != NULL);
 	if (add_req->gc == NULL || add_req->uid == 0) {
 		g_free(add_req);
 		return;
 	}
 
-	group = qq_room_search_id(add_req->gc, add_req->uid);
-	if (group == NULL) {
+	rmd = qq_room_data_find(add_req->gc, add_req->uid);
+	if (rmd == NULL) {
 		g_free(add_req);
 		return;
 	}
 
 	/* TODO insert UI code here */
-	/* qq_group_detail_window_show(g->gc, group); */
+	/* qq_group_detail_window_show(g->gc, rmd); */
 	g_free(add_req);
 }
 
@@ -350,7 +343,7 @@
 {
 	gint bytes;
 	guint32 id, ext_id;
-	qq_group *group;
+	qq_room_data *rmd;
 	qq_add_request *add_req;
 	qq_data *qd;
 
@@ -363,15 +356,17 @@
 	bytes += qq_get32(&ext_id, data + bytes);
 	g_return_if_fail(id > 0 && ext_id);
 
-	group = qq_group_create_internal_record(gc, id, ext_id, NULL);
-	group->my_role = QQ_ROOM_ROLE_ADMIN;
-	group->creator_uid = qd->uid;
-	qq_group_refresh(gc, group);
+	qq_room_find_or_new(gc, id, ext_id);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail(rmd != NULL);
+
+	rmd->my_role = QQ_ROOM_ROLE_ADMIN;
+	rmd->creator_uid = qd->uid;
 
 	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_ACTIVATE, id);
-	qq_update_room(gc, 0, group->id);
+	qq_update_room(gc, 0, rmd->id);
 
-	purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", group->ext_id);
+	purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", rmd->ext_id);
 
 	add_req = g_new0(qq_add_request, 1);
 	add_req->gc = gc;
@@ -391,7 +386,7 @@
 {
 	gint bytes;
 	guint32 id;
-	qq_group *group;
+	qq_room_data *rmd;
 	g_return_if_fail(data != NULL);
 
 	bytes = 0;
@@ -399,17 +394,17 @@
 	g_return_if_fail(id > 0);
 
 	/* we should have its info locally */
-	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail(rmd != NULL);
 
-	purple_debug_info("QQ", "Succeed in activate Qun %d\n", group->ext_id);
+	purple_debug_info("QQ", "Succeed in activate Qun %d\n", rmd->ext_id);
 }
 
 void qq_group_manage_group(PurpleConnection *gc, GHashTable *data)
 {
 	gchar *id_ptr;
 	guint32 id;
-	qq_group *group;
+	qq_room_data *rmd;
 
 	g_return_if_fail(data != NULL);
 
@@ -417,9 +412,9 @@
 	id = strtol(id_ptr, NULL, 10);
 	g_return_if_fail(id > 0);
 
-	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
+	rmd = qq_room_data_find(gc, id);
+	g_return_if_fail(rmd != NULL);
 
 	/* XXX insert UI code here */
-	/* qq_group_detail_window_show(gc, group); */
+	/* qq_group_detail_window_show(gc, rmd); */
 }
--- a/libpurple/protocols/qq/group_opt.h	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.h	Tue Oct 28 16:38:16 2008 +0000
@@ -47,8 +47,8 @@
 	QQ_ROOM_MEMBER_DEL
 };
 
-void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new_members);
-void qq_room_change_info(PurpleConnection *gc, qq_group *group);
+void qq_group_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *new_members);
+void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd);
 
 void qq_group_approve_application_with_struct(group_member_opt *g);
 void qq_group_reject_application_with_struct(group_member_opt *g);
--- a/libpurple/protocols/qq/group_search.c	Tue Oct 28 16:35:06 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/**
- * @file group_search.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 "char_conv.h"
-#include "group_find.h"
-#include "group_free.h"
-#include "group_internal.h"
-#include "group_join.h"
-#include "group_search.h"
-#include "utils.h"
-#include "qq_define.h"
-#include "packet_parse.h"
-#include "qq_network.h"
-
-enum {
-	QQ_ROOM_SEARCH_TYPE_BY_ID = 0x01,
-	QQ_ROOM_SEARCH_TYPE_DEMO = 0x02
-};
-
-/* send packet to search for qq_group */
-void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action)
-{
-	guint8 raw_data[16] = {0};
-	gint bytes = 0;
-	guint8 type;
-
-	purple_debug_info("QQ", "Search QQ Qun %d\n", ext_id);
-	type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID;
-
-	bytes = 0;
-	bytes += qq_put8(raw_data + bytes, type);
-	bytes += qq_put32(raw_data + bytes, ext_id);
-
-	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_SEARCH, 0, raw_data, bytes, 0, action);
-}
-
-static void add_to_roomlist(qq_data *qd, qq_group *group)
-{
-	PurpleRoomlistRoom *room;
-	gchar field[11];
-
-	room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, group->title_utf8, NULL);
-	g_snprintf(field, sizeof(field), "%d", group->ext_id);
-	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	g_snprintf(field, sizeof(field), "%d", group->creator_uid);
-	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	purple_roomlist_room_add_field(qd->roomlist, room, group->desc_utf8);
-	g_snprintf(field, sizeof(field), "%d", group->id);
-	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	g_snprintf(field, sizeof(field), "%d", group->type8);
-	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	g_snprintf(field, sizeof(field), "%d", group->auth_type);
-	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	g_snprintf(field, sizeof(field), "%d", group->category);
-	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	purple_roomlist_room_add_field(qd->roomlist, room, group->title_utf8);
-	purple_roomlist_room_add(qd->roomlist, room);
-
-	purple_roomlist_set_in_progress(qd->roomlist, FALSE);
-}
-
-/* process group cmd reply "search group" */
-void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32)
-{
-	gint bytes;
-	guint8 search_type;
-	guint16 unknown;
-	qq_group group;
-	qq_data *qd;
-
-	g_return_if_fail(data != NULL && len > 0);
-	qd = (qq_data *) gc->proto_data;
-
-	bytes = 0;
-	bytes += qq_get8(&search_type, data + bytes);
-
-	/* now it starts with group_info_entry */
-	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);
-	bytes += qq_get16(&(unknown), data + bytes);
-	bytes += qq_get16(&(unknown), data + bytes);
-	bytes += qq_get16(&(unknown), data + bytes);
-	bytes += qq_get32(&(group.category), data + bytes);
-	bytes += qq_get_vstr(&(group.title_utf8), QQ_CHARSET_DEFAULT, data + bytes);
-	bytes += qq_get16(&(unknown), data + bytes);
-	bytes += qq_get8(&(group.auth_type), data + bytes);
-	bytes += qq_get_vstr(&(group.desc_utf8), QQ_CHARSET_DEFAULT, data + bytes);
-	/* end of one qq_group */
-	if(bytes != len) {
-		purple_debug_error("QQ",
-			"group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
-	}
-
-	if (ship32 == QQ_ROOM_SEARCH_FOR_JOIN) {
-		if (qq_room_search_id(gc, group.id) == NULL)
-			qq_group_create_internal_record(gc,
-					group.id, group.ext_id, group.title_utf8);
-		qq_request_room_join(gc, &group);
-	} else {
-		add_to_roomlist(qd, &group);
-	}
-}
--- a/libpurple/protocols/qq/group_search.h	Tue Oct 28 16:35:06 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/**
- * @file group_search.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_SEARCH_H_
-#define _QQ_GROUP_SEARCH_H_
-
-#include <glib.h>
-#include "connection.h"
-
-enum {
-	QQ_ROOM_SEARCH_ONLY = 0,
-	QQ_ROOM_SEARCH_FOR_JOIN
-};
-
-void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action);
-void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32);
-
-#endif
--- a/libpurple/protocols/qq/qq.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Tue Oct 28 16:38:16 2008 +0000
@@ -39,7 +39,6 @@
 #include "buddy_list.h"
 #include "char_conv.h"
 #include "group.h"
-#include "group_find.h"
 #include "group_im.h"
 #include "group_info.h"
 #include "group_join.h"
@@ -777,7 +776,7 @@
 	g_return_if_fail(room_id != 0);
 
 	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0,
-			QQ_CMD_CLASS_UPDATE_ROOM, QQ_ROOM_INFO_DISPLAY);
+			0, QQ_ROOM_INFO_DISPLAY);
 }
 
 #if 0
--- a/libpurple/protocols/qq/qq_network.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Tue Oct 28 16:38:16 2008 +0000
@@ -28,7 +28,7 @@
 
 #include "buddy_info.h"
 #include "group_info.h"
-#include "group_free.h"
+#include "group_internal.h"
 #include "qq_crypt.h"
 #include "qq_define.h"
 #include "qq_base.h"
@@ -1039,7 +1039,7 @@
 	qd->my_ip.s_addr = 0;
 	qd->my_port = 0;
 
-	qq_group_free_all(gc);
+	qq_room_data_free_all(gc);
 	qq_buddy_data_free_all(gc);
 }
 
--- a/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:35:06 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:38:16 2008 +0000
@@ -33,8 +33,6 @@
 #include "char_conv.h"
 #include "qq_crypt.h"
 
-#include "group_search.h"
-#include "group_find.h"
 #include "group_internal.h"
 #include "group_im.h"
 #include "group_info.h"
@@ -529,36 +527,25 @@
 void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
 {
 	qq_data *qd;
-	qq_group *group;
 	gint ret;
 
 	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	group = qq_room_search_id(gc, room_id);
-	if (group == NULL && room_id <= 0) {
-		purple_debug_info("QQ", "No room, nothing update\n");
-		return;
-	}
-	if (group == NULL ) {
-		purple_debug_warning("QQ", "Failed search room id [%d]\n", room_id);
-		return;
-	}
-
 	switch (room_cmd) {
 		case 0:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, group->id, NULL, 0,
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0,
 					QQ_CMD_CLASS_UPDATE_ROOM, 0);
 			break;
 		case QQ_ROOM_CMD_GET_INFO:
-			ret = qq_request_room_get_buddies(gc, group, QQ_CMD_CLASS_UPDATE_ROOM);
+			ret = qq_request_room_get_buddies(gc, room_id, QQ_CMD_CLASS_UPDATE_ROOM);
 			if (ret <= 0) {
-				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0,
+				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0,
 						QQ_CMD_CLASS_UPDATE_ROOM, 0);
 			}
 			break;
 		case QQ_ROOM_CMD_GET_BUDDIES:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0,
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0,
 					QQ_CMD_CLASS_UPDATE_ROOM, 0);
 			break;
 		case QQ_ROOM_CMD_GET_ONLINES:
@@ -572,39 +559,42 @@
 {
 	qq_data *qd;
 	gboolean is_new_turn = FALSE;
-	qq_group *next_group;
+	guint32 next_id;
 
 	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	next_group = qq_room_get_next(gc, room_id);
-	if (next_group == NULL && room_id <= 0) {
-		purple_debug_info("QQ", "No room. Finished update\n");
-		return;
-	}
-	if (next_group == NULL ) {
-		is_new_turn = TRUE;
-		next_group = qq_room_get_next(gc, 0);
-		g_return_if_fail(next_group != NULL);
+	next_id = qq_room_get_next(gc, room_id);
+	purple_debug_info("QQ", "Update rooms, next id %d, prev id %d\n", next_id, room_id);
+
+	if (next_id <= 0) {
+		if (room_id > 0) {
+			is_new_turn = TRUE;
+			next_id = qq_room_get_next(gc, 0);
+			purple_debug_info("QQ", "new turn, id %d\n", next_id);
+		} else {
+			purple_debug_info("QQ", "No room. Finished update\n");
+			return;
+		}
 	}
 
 	switch (room_cmd) {
 		case 0:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0,
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0,
 					QQ_CMD_CLASS_UPDATE_ALL, 0);
 			break;
 		case QQ_ROOM_CMD_GET_INFO:
 			if (!is_new_turn) {
-				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0,
+				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0,
 						QQ_CMD_CLASS_UPDATE_ALL, 0);
 			} else {
-				qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL);
+				qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL);
 			}
 			break;
 		case QQ_ROOM_CMD_GET_BUDDIES:
 			/* last command */
 			if (!is_new_turn) {
-				qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL);
+				qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL);
 			} else {
 				purple_debug_info("QQ", "Finished update\n");
 			}
@@ -658,28 +648,28 @@
 static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
 {
 	qq_data *qd;
-	qq_group *next_group;
+	guint32 next_id;
 
 	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	next_group = qq_room_get_next_conv(gc, room_id);
-	if (next_group == NULL && room_id <= 0) {
+	next_id = qq_room_get_next_conv(gc, room_id);
+	if (next_id <= 0 && room_id <= 0) {
 		purple_debug_info("QQ", "No room in conversation, no update online buddies\n");
 		return;
 	}
-	if (next_group == NULL ) {
+	if (next_id <= 0 ) {
 		purple_debug_info("QQ", "finished update rooms' online buddies\n");
 		return;
 	}
 
 	switch (room_cmd) {
 		case 0:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0,
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0,
 					QQ_CMD_CLASS_UPDATE_ALL, 0);
 			break;
 		case QQ_ROOM_CMD_GET_ONLINES:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0,
+			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0,
 					QQ_CMD_CLASS_UPDATE_ALL, 0);
 			break;
 		default:
@@ -714,7 +704,7 @@
 	qq_data *qd;
 	guint8 *data;
 	gint data_len;
-	qq_group *group;
+	qq_room_data *rmd;
 	gint bytes;
 	guint8 reply_cmd, reply;
 
@@ -760,16 +750,15 @@
 	if (reply != QQ_ROOM_CMD_REPLY_OK) {
 		switch (reply) {	/* this should be all errors */
 		case QQ_ROOM_CMD_REPLY_NOT_MEMBER:
-			group = qq_room_search_id(gc, room_id);
-			if (group == NULL) {
+			rmd = qq_room_data_find(gc, room_id);
+			if (rmd == NULL) {
 				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);
 			} else {
 				purple_debug_warning("QQ",
-					   _("You are not a member of QQ Qun \"%s\"\n"), group->title_utf8);
-				group->my_role = QQ_ROOM_ROLE_NO;
-				qq_group_refresh(gc, group);
+					   _("Not a member of room \"%s\"\n"), rmd->title_utf8);
+				rmd->my_role = QQ_ROOM_ROLE_NO;
 			}
 			break;
 		case QQ_ROOM_CMD_REPLY_SEARCH_ERROR:
@@ -972,7 +961,7 @@
 			qd->is_login = TRUE;	/* must be defined after sev_finish_login */
 
 			/* now initiate QQ Qun, do it first as it may take longer to finish */
-			qq_group_init(gc);
+			qq_room_data_initial(gc);
 
 			/* is_login, but we have packets before login */
 			qq_trans_process_remained(gc);