changeset 24079:1bdf7b602684

2008.10.07 - ccpaging <ccpaging(at)gmail.com> * Update qun conversation * Drop group_conv.c and group_conv.h
author SHiNE CsyFeK <csyfek@gmail.com>
date Wed, 22 Oct 2008 14:58:32 +0000
parents 0a92aa673a33
children 818ab62006f5
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/Makefile.am libpurple/protocols/qq/Makefile.mingw libpurple/protocols/qq/buddy_info.c libpurple/protocols/qq/buddy_list.c libpurple/protocols/qq/buddy_list.h libpurple/protocols/qq/buddy_opt.c libpurple/protocols/qq/buddy_opt.h libpurple/protocols/qq/group_conv.c libpurple/protocols/qq/group_conv.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_internal.c libpurple/protocols/qq/group_join.c libpurple/protocols/qq/im.c libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq.h libpurple/protocols/qq/qq_define.c libpurple/protocols/qq/qq_define.h libpurple/protocols/qq/qq_network.c libpurple/protocols/qq/qq_process.c
diffstat 24 files changed, 521 insertions(+), 448 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Wed Oct 22 14:58:32 2008 +0000
@@ -1,3 +1,6 @@
+2008.10.07 - ccpaging <ccpaging(at)gmail.com>
+	* Update qun conversation
+
 2008.10.05 - lonicerae <lonicerae(at)gmail.com>
 	* Bug fix in 'About OpenQ' dialog
 
--- a/libpurple/protocols/qq/Makefile.am	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Wed Oct 22 14:58:32 2008 +0000
@@ -18,8 +18,6 @@
 	file_trans.h \
 	group.c \
 	group.h \
-	group_conv.c \
-	group_conv.h \
 	group_find.c \
 	group_find.h \
 	group_free.c \
--- a/libpurple/protocols/qq/Makefile.mingw	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Wed Oct 22 14:58:32 2008 +0000
@@ -46,7 +46,6 @@
 	qq_crypt.c \
 	file_trans.c \
 	group.c \
-	group_conv.c \
 	group_find.c \
 	group_free.c \
 	group_internal.c \
--- a/libpurple/protocols/qq/buddy_info.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Wed Oct 22 14:58:32 2008 +0000
@@ -245,7 +245,7 @@
 	bytes += qq_put8(raw_data + bytes, bar);
 
 	/* important! skip the first uid entry */
-	join = g_strjoinv("\x1f", segments +1);
+	join = g_strjoinv("\x1f", segments + 1);
 	bytes += qq_putdata(raw_data + bytes, (guint8 *)join, strlen(join));
 	g_free(join);
 
@@ -569,55 +569,56 @@
 }
 
 /* after getting info or modify myself, refresh the buddy list accordingly */
-static void qq_set_buddy_info(gchar **segments, PurpleConnection *gc)
+static void qq_update_buddy_info(PurpleConnection *gc, gchar **segments)
 {
-	PurpleBuddy *purple_buddy;
+	PurpleBuddy *buddy;
 	qq_data *qd;
+	qq_buddy *bd;
 	guint32 uid;
-	qq_buddy *buddy;
+	gchar *who;
 	gchar *alias_utf8;
-	gchar *purple_name;
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	qd = (qq_data *) gc->proto_data;
 
 	uid = strtol(segments[QQ_INFO_UID], NULL, 10);
-	purple_name = uid_to_purple_name(uid);
-
+	who = uid_to_purple_name(uid);
+	
 	alias_utf8 = qq_to_utf8(segments[QQ_INFO_NICK], QQ_CHARSET_DEFAULT);
-	if (qd->uid == strtol(segments[QQ_INFO_UID], NULL, 10)) {	/* it is me */
+	if (uid == qd->uid) {	/* it is me */
 		purple_debug_info("QQ", "Got my info\n");
 		qd->my_icon = strtol(segments[QQ_INFO_FACE], NULL, 10);
-		if (alias_utf8 != NULL)
+		if (alias_utf8 != NULL) {
 			purple_account_set_alias(account, alias_utf8);
-
+		}
 		/* add me to buddy list */
-		purple_buddy = purple_find_buddy(gc->account, purple_name);
-		if ( purple_buddy == NULL) {
-			purple_buddy = qq_create_buddy(gc, uid, TRUE, FALSE);
-		}
-		buddy = g_new0(qq_buddy, 1);
-		buddy->uid = uid;
-		buddy->status = QQ_BUDDY_ONLINE_NORMAL;
-		purple_buddy->proto_data = buddy;
-		qd->buddies = g_list_append(qd->buddies, buddy);
+		buddy = qq_buddy_find_or_new(gc, uid);
+	} else {
+		buddy = purple_find_buddy(gc->account, who);
+	}
+
+	if (buddy == NULL && buddy->proto_data == NULL) {
+		g_free(who);
+		g_free(alias_utf8);
+		return;
 	}
 
 	/* update buddy list (including myself, if myself is the buddy) */
-	purple_buddy = purple_find_buddy(gc->account, purple_name);
-	buddy = (purple_buddy == NULL) ? NULL : (qq_buddy *) purple_buddy->proto_data;
-	if (buddy != NULL) {	/* I have this buddy */
-		buddy->age = strtol(segments[QQ_INFO_AGE], NULL, 10);
-		buddy->gender = strtol(segments[QQ_INFO_GENDER], NULL, 10);
-		buddy->face = strtol(segments[QQ_INFO_FACE], NULL, 10);
-		if (alias_utf8 != NULL)
-			buddy->nickname = g_strdup(alias_utf8);
-		qq_update_buddy_contact(gc, buddy);
-		buddy_local_icon_upate(gc->account, purple_name, buddy->face);
-	} else {
-		purple_debug_info("QQ", "Can not find buddy data of %s\n", purple_name);
+	bd = (qq_buddy *)buddy->proto_data;
+
+	bd->age = strtol(segments[QQ_INFO_AGE], NULL, 10);
+	bd->gender = strtol(segments[QQ_INFO_GENDER], NULL, 10);
+	bd->face = strtol(segments[QQ_INFO_FACE], NULL, 10);
+	if (alias_utf8 != NULL) {
+		if (bd->nickname) g_free(bd->nickname);
+		bd->nickname = g_strdup(alias_utf8);
 	}
-	g_free(purple_name);
+	bd->last_update = time(NULL);
+
+	purple_blist_server_alias_buddy(buddy, bd->nickname);
+	buddy_local_icon_upate(gc->account, who, bd->face);
+
+	g_free(who);
 	g_free(alias_utf8);
 }
 
@@ -652,13 +653,13 @@
 			segments[QQ_INFO_FACE] = icon;
 
 			request_modify_info(gc, segments);
-			qq_set_buddy_info(segments, gc);
+			qq_update_buddy_info(gc, segments);
 		}
 		g_strfreev(segments);
 		return;
 	}
 
-	qq_set_buddy_info(segments, gc);
+	qq_update_buddy_info(gc, segments);
 	switch (action) {
 		case QQ_BUDDY_INFO_DISPLAY:
 			info_display_only(gc, segments);
@@ -712,36 +713,35 @@
 
 void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class)
 {
+	qq_data *qd = (qq_data *) gc->proto_data;
+	PurpleBuddy *buddy;
+	qq_buddy *buddy_data;
 	guint8 *buf;
-	guint16 size;
-	qq_buddy *q_bud;
-	qq_data *qd = (qq_data *) gc->proto_data;
-	GList *node = qd->buddies;
-	gint bytes = 0;
-
-	if ( qd->buddies == NULL) {
-		return;
-	}
+	GSList *buddies, *it;
+	gint bytes;
 
 	/* server only reply levels for online buddies */
-	size = 4 * g_list_length(qd->buddies) + 1 + 4;
-	buf = g_newa(guint8, size);
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+
+	bytes = 0;
 	bytes += qq_put8(buf + bytes, 0x00);
-	while (NULL != node) {
-		q_bud = (qq_buddy *) node->data;
-		if (NULL != q_bud) {
-			bytes += qq_put32(buf + bytes, q_bud->uid);
-		}
-		node = node->next;
+	buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
+	for (it = buddies; it; it = it->next) {
+		buddy = it->data;
+		if (buddy == NULL) continue;
+		if (buddy->proto_data == NULL) continue;
+		buddy_data = (qq_buddy *)buddy->proto_data;
+		if (buddy_data->uid == 0) continue;
+		if (buddy_data->uid == qd->uid) continue;
+		bytes += qq_put32(buf + bytes, buddy_data->uid);
 	}
-	/* my id should be the end if included */
+	/* my uid must be the end if included */
 	bytes += qq_put32(buf + bytes, qd->uid);
-	qq_send_cmd_mess(gc, QQ_CMD_GET_LEVEL, buf, size, update_class, 0);
+	qq_send_cmd_mess(gc, QQ_CMD_GET_LEVEL, buf, bytes, update_class, 0);
 }
 
 static void process_level(PurpleConnection *gc, guint8 *data, gint data_len)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes = 0;
 	guint32 uid, onlineTime;
 	guint16 level, timeRemainder;
@@ -754,12 +754,8 @@
 		bytes += qq_get16(&timeRemainder, data + bytes);
 		purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
 				uid, onlineTime, level, timeRemainder);
-		if (uid == qd->uid) {
-			qd->my_level = level;
-			purple_debug_warning("QQ", "Got my levels as %d\n", qd->my_level);
-		}
 
-		buddy = qq_get_buddy(gc, uid);
+		buddy = qq_buddy_find(gc, uid);
 		if (buddy == NULL) {
 			purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid);
 			continue;
@@ -778,7 +774,6 @@
 
 static void process_level_2007(PurpleConnection *gc, guint8 *data, gint data_len)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes;
 	guint32 uid, onlineTime;
 	guint16 level, timeRemainder;
@@ -795,12 +790,7 @@
 	purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
 			uid, onlineTime, level, timeRemainder);
 
-	if (uid == qd->uid) {
-		qd->my_level = level;
-		purple_debug_warning("QQ", "Got my levels as %d\n", qd->my_level);
-	}
-
-	buddy = qq_get_buddy(gc, uid);
+	buddy = qq_buddy_find(gc, uid);
 	if (buddy == NULL) {
 		purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid);
 		return;
--- a/libpurple/protocols/qq/buddy_list.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Wed Oct 22 14:58:32 2008 +0000
@@ -82,7 +82,7 @@
 
 /* position starts with 0x0000,
  * server may return a position tag if list is too long for one packet */
-void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class)
+void qq_request_get_buddies(PurpleConnection *gc, guint16 position, gint update_class)
 {
 	qq_data *qd;
 	guint8 raw_data[16] = {0};
@@ -158,16 +158,24 @@
 }
 
 /* process the reply packet for get_buddies_online packet */
-guint8 qq_process_get_buddies_online_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gint bytes, bytes_start;
 	gint count;
 	guint8  position;
 	qq_buddy *buddy;
-	qq_buddy_online bo;
 	int entry_len = 38;
 
+	qq_buddy_status bs;
+	struct {
+		guint16 unknown1;
+		guint8 ext_flag;
+		guint8 comm_flag;
+		guint16 unknown2;
+		guint8 ending;		/* 0x00 */
+	} packet;
+
 	g_return_val_if_fail(data != NULL && data_len != 0, -1);
 
 	qd = (qq_data *) gc->proto_data;
@@ -185,41 +193,42 @@
 					(data_len - bytes), entry_len);
 			break;
 		}
-		memset(&bo, 0 ,sizeof(bo));
+		memset(&bs, 0 ,sizeof(bs));
+		memset(&packet, 0 ,sizeof(packet));
 
 		/* set flag */
 		bytes_start = bytes;
 		/* based on one online buddy entry */
 		/* 000-030 qq_buddy_status */
-		bytes += get_buddy_status(&(bo.bs), data + bytes);
+		bytes += get_buddy_status(&bs, data + bytes);
 		/* 031-032: */
-		bytes += qq_get16(&bo.unknown1, data + bytes);
+		bytes += qq_get16(&packet.unknown1, data + bytes);
 		/* 033-033: ext_flag */
-		bytes += qq_get8(&bo.ext_flag, data + bytes);
+		bytes += qq_get8(&packet.ext_flag, data + bytes);
 		/* 034-034: comm_flag */
-		bytes += qq_get8(&bo.comm_flag, data + bytes);
+		bytes += qq_get8(&packet.comm_flag, data + bytes);
 		/* 035-036: */
-		bytes += qq_get16(&bo.unknown2, data + bytes);
+		bytes += qq_get16(&packet.unknown2, data + bytes);
 		/* 037-037: */
-		bytes += qq_get8(&bo.ending, data + bytes);	/* 0x00 */
+		bytes += qq_get8(&packet.ending, data + bytes);	/* 0x00 */
 		/* skip 4 bytes in qq2007 */
 		if (qd->client_version >= 2007)	bytes += 4;
 
-		if (bo.bs.uid == 0 || (bytes - bytes_start) != entry_len) {
+		if (bs.uid == 0 || (bytes - bytes_start) != entry_len) {
 			purple_debug_error("QQ", "uid=0 or entry complete len(%d) != %d",
 					(bytes - bytes_start), entry_len);
 			continue;
 		}	/* check if it is a valid entry */
 
-		if (bo.bs.uid == qd->uid) {
-			purple_debug_warning("QQ", "I am in online list %d\n", bo.bs.uid);
+		if (bs.uid == qd->uid) {
+			purple_debug_warning("QQ", "I am in online list %d\n", bs.uid);
 		}
 
 		/* update buddy information */
-		buddy = qq_get_buddy(gc, bo.bs.uid);
+		buddy = qq_buddy_find(gc, bs.uid);
 		if (buddy == NULL) {
 			purple_debug_error("QQ",
-					"Got an online buddy %d, but not in my buddy list\n", bo.bs.uid);
+					"Got an online buddy %d, but not in my buddy list\n", bs.uid);
 			continue;
 		}
 		/* we find one and update qq_buddy */
@@ -227,18 +236,18 @@
 		if(0 != fe->s->client_tag)
 			q_bud->client_tag = fe->s->client_tag;
 		*/
-		buddy->ip.s_addr = bo.bs.ip.s_addr;
-		buddy->port = bo.bs.port;
-		buddy->status = bo.bs.status;
-		buddy->ext_flag = bo.ext_flag;
-		buddy->comm_flag = bo.comm_flag;
-		qq_update_buddy_contact(gc, buddy);
+		buddy->ip.s_addr = bs.ip.s_addr;
+		buddy->port = bs.port;
+		buddy->status = bs.status;
+		buddy->ext_flag = packet.ext_flag;
+		buddy->comm_flag = packet.comm_flag;
+		qq_update_buddy_status(gc, bs.uid, bs.status, packet.comm_flag);
 		count++;
 	}
 
 	if(bytes > data_len) {
 		purple_debug_error("QQ",
-				"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
+				"qq_process_get_buddies_online: Dangerous error! maybe protocol changed, notify developers!\n");
 	}
 
 	purple_debug_info("QQ", "Received %d online buddies, nextposition=%u\n",
@@ -248,16 +257,15 @@
 
 
 /* process reply for get_buddies_list */
-guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	qq_buddy *buddy;
+	qq_buddy bd;
 	gint bytes_expected, count;
 	gint bytes, buddy_bytes;
 	gint nickname_len;
 	guint16 position, unknown;
-	gchar *purple_name;
-	PurpleBuddy *purple_buddy;
+	PurpleBuddy *buddy;
 
 	g_return_val_if_fail(data != NULL && data_len != 0, -1);
 
@@ -273,26 +281,26 @@
 	/* the following data is buddy list in this packet */
 	count = 0;
 	while (bytes < data_len) {
-		buddy = g_new0(qq_buddy, 1);
+		memset(&bd, 0, sizeof(bd));
 		/* set flag */
 		buddy_bytes = bytes;
 		/* 000-003: uid */
-		bytes += qq_get32(&buddy->uid, data + bytes);
+		bytes += qq_get32(&bd.uid, data + bytes);
 		/* 004-005: icon index (1-255) */
-		bytes += qq_get16(&buddy->face, data + bytes);
+		bytes += qq_get16(&bd.face, data + bytes);
 		/* 006-006: age */
-		bytes += qq_get8(&buddy->age, data + bytes);
+		bytes += qq_get8(&bd.age, data + bytes);
 		/* 007-007: gender */
-		bytes += qq_get8(&buddy->gender, data + bytes);
+		bytes += qq_get8(&bd.gender, data + bytes);
 
-		nickname_len = qq_get_vstr(&buddy->nickname, QQ_CHARSET_DEFAULT, data + bytes);
+		nickname_len = qq_get_vstr(&bd.nickname, QQ_CHARSET_DEFAULT, data + bytes);
 		bytes += nickname_len;
-		qq_filter_str(buddy->nickname);
+		qq_filter_str(bd.nickname);
 
 		/* Fixme: merge following as 32bit flag */
 		bytes += qq_get16(&unknown, data + bytes);
-		bytes += qq_get8(&buddy->ext_flag, data + bytes);
-		bytes += qq_get8(&buddy->comm_flag, data + bytes);
+		bytes += qq_get8(&bd.ext_flag, data + bytes);
+		bytes += qq_get8(&bd.comm_flag, data + bytes);
 
 		if (qd->client_version >= 2007) {
 			bytes += 4;		/* skip 4 bytes */
@@ -301,11 +309,10 @@
 			bytes_expected = 12 + nickname_len;
 		}
 
-		if (buddy->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
+		if (bd.uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
 			purple_debug_info("QQ",
 					"Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes);
-			g_free(buddy->nickname);
-			g_free(buddy);
+			g_free(bd.nickname);
 			continue;
 		} else {
 			count++;
@@ -313,25 +320,27 @@
 
 #if 1
 		purple_debug_info("QQ", "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
-				buddy->uid, buddy->ext_flag, buddy->comm_flag, buddy->nickname);
+				bd.uid, bd.ext_flag, bd.comm_flag, bd.nickname);
 #endif
 
-		purple_name = uid_to_purple_name(buddy->uid);
-		purple_buddy = purple_find_buddy(gc->account, purple_name);
-		g_free(purple_name);
+		buddy = qq_buddy_find_or_new(gc, bd.uid);
+		if (buddy == NULL || buddy->proto_data == NULL) {
+			g_free(bd.nickname);
+			continue;
+		}
+		bd.last_update = time(NULL);
+		purple_blist_server_alias_buddy(buddy, bd.nickname);
+		qq_update_buddy_status(gc, bd.uid, bd.status, bd.comm_flag);
 
-		if (purple_buddy == NULL) {
-			purple_buddy = qq_create_buddy(gc, buddy->uid, TRUE, FALSE);
-		}
-
-		purple_buddy->proto_data = buddy;
-		qd->buddies = g_list_append(qd->buddies, buddy);
-		qq_update_buddy_contact(gc, buddy);
+		g_memmove(buddy->proto_data, &bd, sizeof(qq_buddy));
+		/* nickname has been copy to buddy_data do not free
+		   g_free(bd.nickname); 
+		*/
 	}
 
 	if(bytes > data_len) {
 		purple_debug_error("QQ",
-				"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
+				"qq_process_get_buddies: Dangerous error! maybe protocol changed, notify developers!");
 	}
 
 	purple_debug_info("QQ", "Received %d buddies, nextposition=%u\n",
@@ -385,7 +394,7 @@
 		}
 		if(0x1 == type) { /* a buddy */
 			/* don't do anything but count - buddies are handled by
-			 * qq_request_get_buddies_list */
+			 * qq_request_get_buddies */
 			++i;
 		} else { /* a group */
 			group = qq_room_search_id(gc, uid);
@@ -421,21 +430,6 @@
  * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy,
  * using different accounts to get info. */
 
-/* check if status means online or offline */
-gboolean is_online(guint8 status)
-{
-	switch(status) {
-		case QQ_BUDDY_ONLINE_NORMAL:
-		case QQ_BUDDY_ONLINE_AWAY:
-		case QQ_BUDDY_ONLINE_INVISIBLE:
-		case QQ_BUDDY_ONLINE_BUSY:
-			return TRUE;
-		case QQ_BUDDY_CHANGE_TO_OFFLINE:
-			return FALSE;
-	}
-	return FALSE;
-}
-
 /* Help calculate the correct icon index to tell the server. */
 gint get_icon_offset(PurpleConnection *gc)
 {
@@ -515,7 +509,7 @@
 }
 
 /* parse the reply packet for change_status */
-void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gint bytes;
@@ -534,7 +528,7 @@
 	}
 
 	/* purple_debug_info("QQ", "Change status OK\n"); */
-	buddy = qq_get_buddy(gc, qd->uid);
+	buddy = qq_buddy_find(gc, qd->uid);
 	if (buddy != NULL) {
 		qq_update_buddy_contact(gc, buddy);
 	}
@@ -567,7 +561,7 @@
 	 * QQ_BUDDY_ONLINE_INVISIBLE */
 	bytes += qq_get32(&my_uid, data + bytes);
 
-	buddy = qq_get_buddy(gc, bs.uid);
+	buddy = qq_buddy_find(gc, bs.uid);
 	if (buddy == NULL) {
 		purple_debug_warning("QQ", "Get status of unknown buddy %d\n", bs.uid);
 		return;
@@ -590,6 +584,55 @@
 }
 
 /*TODO: maybe this should be qq_update_buddy_status() ?*/
+void qq_update_buddy_status(PurpleConnection *gc, guint32 uid, guint8 status, guint8 flag)
+{
+	gchar *who;
+	gchar *status_id;
+
+	g_return_if_fail(uid != 0);
+
+	who = uid_to_purple_name(uid);
+	
+	/* purple supports signon and idle time
+	 * but it is not much use for QQ, I do not use them */
+	/* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
+	status_id = "available";
+	switch(status) {
+	case QQ_BUDDY_OFFLINE:
+		status_id = "offline";
+		break;
+	case QQ_BUDDY_ONLINE_NORMAL:
+		status_id = "available";
+		break;
+	case QQ_BUDDY_CHANGE_TO_OFFLINE:
+		status_id = "offline";
+		break;
+	case QQ_BUDDY_ONLINE_AWAY:
+		status_id = "away";
+		break;
+	case QQ_BUDDY_ONLINE_INVISIBLE:
+		status_id = "invisible";
+		break;
+	case QQ_BUDDY_ONLINE_BUSY:
+		status_id = "busy";
+		break;
+	default:
+		status_id = "invisible";
+		purple_debug_error("QQ", "unknown status: %x\n", status);
+		break;
+	}
+	purple_debug_info("QQ", "Update buddy %s status as %s\n", who, status_id);
+	purple_prpl_got_user_status(gc->account, who, status_id, NULL);
+
+	if (flag & QQ_COMM_FLAG_MOBILE && status != QQ_BUDDY_OFFLINE)
+		purple_prpl_got_user_status(gc->account, who, "mobile", NULL);
+	else
+		purple_prpl_got_user_status_deactive(gc->account, who, "mobile");
+		
+	g_free(who);
+}
+
+/*TODO: maybe this should be qq_update_buddy_status() ?*/
 void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *buddy)
 {
 	gchar *purple_name;
@@ -655,24 +698,58 @@
 
 /* refresh all buddies online/offline,
  * after receiving reply for get_buddies_online packet */
-void qq_refresh_all_buddy_status(PurpleConnection *gc)
+void qq_update_buddyies_status(PurpleConnection *gc)
 {
-	time_t now;
-	GList *list;
 	qq_data *qd;
-	qq_buddy *q_bud;
+	PurpleBuddy *buddy;
+	qq_buddy *bd;
+	GSList *buddies, *it;
+	time_t tm_limit = time(NULL);
 
 	qd = (qq_data *) (gc->proto_data);
-	now = time(NULL);
-	list = qd->buddies;
+	
+	tm_limit -= QQ_UPDATE_ONLINE_INTERVAL;
 
-	while (list != NULL) {
-		q_bud = (qq_buddy *) list->data;
-		if (q_bud != NULL && now > q_bud->last_update + QQ_UPDATE_ONLINE_INTERVAL
-				&& q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) {
-			q_bud->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
-			qq_update_buddy_contact(gc, q_bud);
-		}
-		list = list->next;
+	buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
+	for (it = buddies; it; it = it->next) {
+		buddy = it->data;
+		if (buddy == NULL) continue;
+		if (buddy->proto_data == NULL) continue;
+		
+		bd = (qq_buddy *)buddy->proto_data;
+		if (bd->uid == 0) continue;
+		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;
+		
+		bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
+		qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
 	}
 }
+
+void qq_buddy_data_free_all(PurpleConnection *gc)
+{
+	qq_data *qd;
+	PurpleBuddy *buddy;
+	GSList *buddies, *it;
+	gint count = 0;
+
+	qd = (qq_data *) (gc->proto_data);
+	
+	buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
+	for (it = buddies; it; it = it->next) {
+		buddy = it->data;
+		if (buddy == NULL) continue;
+		if (buddy->proto_data == NULL) continue;
+		
+		qq_buddy_data_free(buddy->proto_data);
+		buddy->proto_data = NULL;
+		
+		count++;
+	}
+
+	if (count > 0) {
+		purple_debug_info("QQ", "%d buddies' data are freed\n", count);
+	}
+}
+
--- a/libpurple/protocols/qq/buddy_list.h	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.h	Wed Oct 22 14:58:32 2008 +0000
@@ -40,34 +40,23 @@
 	guint8 unknown_key[QQ_KEY_LENGTH];
 } qq_buddy_status;
 
-enum {
-	QQ_BUDDY_OFFLINE = 0x00,
-	QQ_BUDDY_ONLINE_NORMAL = 10,
-	QQ_BUDDY_CHANGE_TO_OFFLINE = 20,
-	QQ_BUDDY_ONLINE_AWAY = 30,
-	QQ_BUDDY_ONLINE_INVISIBLE = 40,
-	QQ_BUDDY_ONLINE_BUSY = 50,
-};
+void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class);
+guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class);
-guint8 qq_process_get_buddies_online_reply(guint8 *data, gint data_len, PurpleConnection *gc);
-
-void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class);
-guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_request_get_buddies(PurpleConnection *gc, guint16 position, gint update_class);
+guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc);
 
 void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, gint update_class);
 guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_refresh_all_buddy_status(PurpleConnection *gc);
-
-gboolean is_online(guint8 status);
-
 gint get_icon_offset(PurpleConnection *gc);
 
 void qq_request_change_status(PurpleConnection *gc, gint update_class);
-void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
 void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_refresh_all_buddy_status(PurpleConnection *gc);
+void qq_update_buddyies_status(PurpleConnection *gc);
+void qq_update_buddy_status(PurpleConnection *gc, guint32 uid, guint8 status, guint8 flag);
 void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud);
+void qq_buddy_data_free_all(PurpleConnection *gc);
 #endif
--- a/libpurple/protocols/qq/buddy_opt.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Wed Oct 22 14:58:32 2008 +0000
@@ -291,7 +291,6 @@
 	qq_data *qd;
 	gchar *purple_name;
 	PurpleBuddy *buddy;
-	qq_buddy *q_buddy;
 
 	g_return_if_fail(add_req != NULL);
 	if (add_req->gc == NULL || add_req->uid == 0) {
@@ -312,12 +311,13 @@
 		return;
 	}
 
-	q_buddy = (qq_buddy *) buddy->proto_data;
-	if (q_buddy != NULL)
-		qd->buddies = g_list_remove(qd->buddies, q_buddy);
-	else
+	if (buddy->proto_data != NULL) {
+		qq_buddy_data_free(buddy->proto_data);
+		buddy->proto_data = NULL;
+	} else {
 		purple_debug_warning("QQ", "We have no qq_buddy record for %s\n", buddy->name);
-
+	}
+	
 	purple_blist_remove_buddy(buddy);
 	g_free(add_req);
 }
@@ -476,6 +476,7 @@
 		g_free(nombre);
 	} else {	/* add OK */
 		qq_create_buddy(gc, uid, TRUE, TRUE);
+
 		msg = g_strdup_printf(_("Successed adding into %d's buddy list"), uid);
 		qq_got_attention(gc, msg);
 		g_free(msg);
@@ -483,7 +484,7 @@
 	g_strfreev(segments);
 }
 
-PurpleGroup *qq_create_group(const gchar *group_name)
+PurpleGroup *qq_group_find_or_new(const gchar *group_name)
 {
 	PurpleGroup *g;
 
@@ -520,7 +521,7 @@
 	 	group_name = g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
 	}
 
-	group = qq_create_group(group_name);
+	group = qq_group_find_or_new(group_name);
 
 	buddy_name = uid_to_purple_name(uid);
 	purple_buddy = purple_find_buddy(gc->account, buddy_name);
@@ -544,7 +545,6 @@
 		buddy = g_new0(qq_buddy, 1);
 		buddy->uid = uid;
 		purple_buddy->proto_data = buddy;
-		qd->buddies = g_list_append(qd->buddies, buddy);
 		qq_request_buddy_info(gc, uid, 0, 0);
 		qq_request_get_buddies_online(gc, 0, 0);
 		if (qd->client_version >= 2007) {
@@ -555,7 +555,7 @@
 	}
 
 	purple_blist_add_buddy(purple_buddy, NULL, group, NULL);
-	purple_debug_warning("QQ", "Add new buddy: [%s]\n", buddy_name);
+	purple_debug_info("QQ", "Add new buddy: [%s]\n", buddy_name);
 
 	g_free(buddy_name);
 	g_free(group_name);
@@ -563,7 +563,16 @@
 	return purple_buddy;
 }
 
-qq_buddy *qq_get_buddy(PurpleConnection *gc, guint32 uid)
+static qq_buddy *qq_buddy_data_new(guint32 uid)
+{
+	qq_buddy *buddy = g_new0(qq_buddy, 1);
+	memset(buddy, 0, sizeof(qq_buddy));
+	buddy->uid = uid;
+	buddy->status = QQ_BUDDY_ONLINE_NORMAL;
+	return buddy;
+}
+
+qq_buddy *qq_buddy_find(PurpleConnection *gc, guint32 uid)
 {
 	gchar *purple_name;
 	PurpleBuddy *purple_buddy;
@@ -574,20 +583,85 @@
 
 	purple_buddy = purple_find_buddy(purple_connection_get_account(gc), purple_name);
 	g_free(purple_name);
-	if (purple_buddy == NULL)	return NULL;
-
+	if (purple_buddy == NULL) {
+		purple_debug_error("QQ", "Can not find purple buddy of %d\n", uid);
+		return NULL;
+	}
 	if (purple_buddy->proto_data == NULL) {
 		purple_debug_error("QQ", "Can not find buddy data of %d\n", uid);
 		return NULL;
 	}
-	return (qq_buddy *) purple_buddy->proto_data;
+	return (qq_buddy *)purple_buddy->proto_data;
+}
+
+void qq_buddy_data_free(qq_buddy *bd)
+{
+	g_return_if_fail(bd != NULL);
+
+	if (bd->nickname) g_free(bd->nickname);
+	g_free(bd);
+}
+
+static PurpleBuddy *qq_buddy_new(PurpleConnection *gc, gchar *who)
+{
+	PurpleBuddy *buddy;
+	PurpleGroup *group;
+	gchar *group_name;
+
+	g_return_val_if_fail(gc->account != NULL && who != NULL, NULL);
+
+	group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT,
+			purple_account_get_username(gc->account));
+	group = qq_group_find_or_new(group_name);
+	if (group == NULL) {
+		purple_debug_error("QQ", "Failed creating group %s\n", group_name);
+		return NULL;
+	}
+
+	buddy = purple_buddy_new(gc->account, who, NULL);	/* alias is NULL */
+	buddy->proto_data = NULL;
+
+	purple_blist_add_buddy(buddy, NULL, group, NULL);
+	purple_debug_info("QQ", "Add new purple buddy: [%s]\n", who);
+
+	g_free(group_name);
+
+	return buddy;
+}
+
+PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, guint32 uid)
+{
+	PurpleBuddy *buddy;
+	gchar *who;
+
+	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+
+	who = uid_to_purple_name(uid);
+
+	buddy = purple_find_buddy(gc->account, who);
+	if (buddy == NULL) {
+		buddy = qq_buddy_new(gc, who);
+		if (buddy == NULL) {
+			g_free(who);
+			return NULL;
+		}
+	}
+	
+	if (buddy->proto_data != NULL) {
+		g_free(who);
+		return buddy;
+	}
+
+	buddy->proto_data = qq_buddy_data_new(uid);
+
+	g_free(who);
+	return buddy;
 }
 
 /* remove a buddy and send packet to QQ server accordingly */
 void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	qq_data *qd;
-	qq_buddy *q_bud;
 	guint32 uid;
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -602,44 +676,17 @@
 		request_buddy_remove(gc, uid);
 	}
 
-	q_bud = (qq_buddy *) buddy->proto_data;
-	if (q_bud != NULL)
-		qd->buddies = g_list_remove(qd->buddies, q_bud);
-	else
+	if (buddy->proto_data) {
+		qq_buddy_data_free(buddy->proto_data);
+		buddy->proto_data = NULL;
+	} else {
 		purple_debug_warning("QQ", "We have no qq_buddy record for %s\n", buddy->name);
-
+	}
+	
 	/* Do not call purple_blist_remove_buddy,
 	 * otherwise purple segmentation fault */
 }
 
-/* free all qq_buddy */
-void qq_buddies_list_free(PurpleAccount *account, qq_data *qd)
-{
-	gint count;
-	qq_buddy *p;
-	gchar *name;
-	PurpleBuddy *b;
-
-	count = 0;
-	while (qd->buddies) {
-		p = (qq_buddy *) (qd->buddies->data);
-		qd->buddies = g_list_remove(qd->buddies, p);
-		name = uid_to_purple_name(p->uid);
-		b = purple_find_buddy(account, name);
-		if(b != NULL)
-			b->proto_data = NULL;
-		else
-			purple_debug_info("QQ", "qq_buddy %s not found in purple proto_data\n", name);
-		g_free(name);
-
-		g_free(p);
-		count++;
-	}
-	if (count > 0) {
-		purple_debug_info("QQ", "%d qq_buddy structures are freed!\n", count);
-	}
-}
-
 /* someone wants to add you to his buddy list */
 static void server_buddy_add_request(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
 {
--- a/libpurple/protocols/qq/buddy_opt.h	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.h	Wed Oct 22 14:58:32 2008 +0000
@@ -30,15 +30,16 @@
 
 #include "qq.h"
 
+void qq_buddy_data_free(qq_buddy *bd);
+
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
-qq_buddy *qq_get_buddy(PurpleConnection *gc, guint32 uid);
+qq_buddy *qq_buddy_find(PurpleConnection *gc, guint32 uid);
 void qq_change_buddys_group(PurpleConnection *gc, const char *who,
 		const char *old_group, const char *new_group);
 void qq_remove_buddy_and_me(PurpleBlistNode * node);
 void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 PurpleBuddy *qq_create_buddy(PurpleConnection *gc, guint32 uid,
 		gboolean is_known, gboolean is_create_data);
-void qq_buddies_list_free(PurpleAccount *account, qq_data *qd);
 
 void qq_process_buddy_remove(guint8 *buf, gint buf_len, PurpleConnection *gc);
 void qq_process_buddy_remove_me(guint8 *data, gint data_len, PurpleConnection *gc);
@@ -47,6 +48,7 @@
 void qq_process_buddy_from_server(PurpleConnection *gc, int funct,
 		gchar *from, gchar *to, gchar *msg_utf8);
 
-PurpleGroup *qq_create_group(const gchar *group_name);
+PurpleGroup *qq_group_find_or_new(const gchar *group_name);
+PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, guint32 uid);
 
 #endif
--- a/libpurple/protocols/qq/group_conv.c	Wed Oct 22 14:55:09 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-/**
- * @file group_conv.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 <glib.h>
-#include "qq.h"
-
-#include "group_conv.h"
-#include "buddy_list.h"
-#include "qq_define.h"
-#include "qq_network.h"
-#include "qq_process.h"
-#include "utils.h"
-
-/* show group conversation window */
-PurpleConversation *qq_room_conv_create(PurpleConnection *gc, qq_group *group)
-{
-	PurpleConversation *conv;
-	qq_data *qd;
-	gchar *topic_utf8;
-
-	g_return_val_if_fail(group != 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));
-	if (conv != NULL)	{
-		/* show only one conversation per group */
-		return conv;
-	}
-
-	serv_got_joined_chat(gc, qd->channel++, group->title_utf8);
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
-	if (conv != NULL) {
-		topic_utf8 = g_strdup_printf("%d %s", group->ext_id, group->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_info)
-			qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, group->id);
-		else
-			qq_update_room(gc, 0, group->id);
-		return conv;
-	}
-	return NULL;
-}
-
-/* refresh online member in group conversation window */
-void qq_group_conv_refresh_online_member(PurpleConnection *gc, qq_group *group)
-{
-	GList *names, *list, *flags;
-	qq_buddy *member;
-	gchar *member_name, *member_uid;
-	PurpleConversation *conv;
-	gint flag;
-	gboolean is_find;
-	g_return_if_fail(group != NULL);
-
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-			group->title_utf8, purple_connection_get_account(gc));
-
-	g_return_if_fail (conv != NULL && group->members != NULL);
-
-	names = NULL;
-	flags = NULL;
-
-	list = group->members;
-	while (list != NULL) {
-		member = (qq_buddy *) list->data;
-
-		/* we need unique identifiers for everyone in the chat or else we'll
-		 * run into problems with functions like get_cb_real_name from qq.c */
-		member_name =   (member->nickname != NULL && *(member->nickname) != '\0') ?
-				g_strdup_printf("%s (%u)", member->nickname, member->uid) :
-				g_strdup_printf("(%u)", member->uid);
-		member_uid = g_strdup_printf("(%u)", member->uid);
-
-		flag = 0;
-		/* TYPING to put online above OP and FOUNDER */
-		if (is_online(member->status)) flag |= (PURPLE_CBFLAGS_TYPING | PURPLE_CBFLAGS_VOICE);
-		if(1 == (member->role & 1)) flag |= PURPLE_CBFLAGS_OP;
-		if(member->uid == group->creator_uid) flag |= PURPLE_CBFLAGS_FOUNDER;
-
-		is_find = TRUE;
-		if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_name))
-		{
-			purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
-					member_name,
-					flag);
-		} else if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_uid))
-		{
-			purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
-					member_uid,
-					flag);
-			purple_conv_chat_rename_user(PURPLE_CONV_CHAT(conv), member_uid, member_name);
-		} else {
-			is_find = FALSE;
-		}
-		if (!is_find) {
-			/* always put it even offline */
-			names = g_list_append(names, member_name);
-			flags = g_list_append(flags, GINT_TO_POINTER(flag));
-		} else {
-			g_free(member_name);
-		}
-		g_free(member_uid);
-		list = list->next;
-	}
-
-	if (names != NULL && flags != NULL) {
-		purple_conv_chat_add_users(PURPLE_CONV_CHAT(conv), names, NULL, flags, FALSE);
-	}
-
-	/* clean up names */
-	while (names != NULL) {
-		member_name = (gchar *) names->data;
-		names = g_list_remove(names, member_name);
-		g_free(member_name);
-	}
-	g_list_free(flags);
-}
--- a/libpurple/protocols/qq/group_conv.h	Wed Oct 22 14:55:09 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/**
- * @file group_conv.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_CONV_H_
-#define _QQ_GROUP_CONV_H_
-
-#include "connection.h"
-#include "conversation.h"
-#include "group.h"
-
-PurpleConversation *qq_room_conv_create(PurpleConnection *gc, qq_group *group);
-void qq_group_conv_refresh_online_member(PurpleConnection *gc, qq_group *group);
-
-#endif
--- a/libpurple/protocols/qq/group_free.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/group_free.c	Wed Oct 22 14:58:32 2008 +0000
@@ -61,12 +61,15 @@
 	g_free(group);
 }
 
-void qq_group_free_all(qq_data *qd)
+void qq_group_free_all(PurpleConnection *gc)
 {
+	qq_data *qd;
 	qq_group *group;
 	gint count;
 
-	g_return_if_fail(qd != NULL);
+	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;
--- a/libpurple/protocols/qq/group_free.h	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/group_free.h	Wed Oct 22 14:58:32 2008 +0000
@@ -30,6 +30,6 @@
 #include "group.h"
 
 void qq_group_free(qq_group *group);
-void qq_group_free_all(qq_data *qd);
+void qq_group_free_all(PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/group_im.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Wed Oct 22 14:58:32 2008 +0000
@@ -37,7 +37,6 @@
 #include "group_info.h"
 #include "group_im.h"
 #include "group_opt.h"
-#include "group_conv.h"
 #include "im.h"
 #include "qq_define.h"
 #include "packet_parse.h"
@@ -45,6 +44,118 @@
 #include "qq_process.h"
 #include "utils.h"
 
+/* show group conversation window */
+PurpleConversation *qq_room_conv_new(PurpleConnection *gc, qq_group *group)
+{
+	PurpleConversation *conv;
+	qq_data *qd;
+	gchar *topic_utf8;
+
+	g_return_val_if_fail(group != 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));
+	if (conv != NULL)	{
+		/* show only one conversation per group */
+		return conv;
+	}
+
+	serv_got_joined_chat(gc, qd->channel++, group->title_utf8);
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
+	if (conv != NULL) {
+		topic_utf8 = g_strdup_printf("%d %s", group->ext_id, group->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_info)
+			qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, group->id);
+		else
+			qq_update_room(gc, 0, group->id);
+		return conv;
+	}
+	return NULL;
+}
+
+/* refresh online member in group conversation window */
+void qq_room_conv_set_onlines(PurpleConnection *gc, qq_group *group)
+{
+	GList *names, *list, *flags;
+	qq_buddy *member;
+	gchar *member_name, *member_uid;
+	PurpleConversation *conv;
+	gint flag;
+	gboolean is_find;
+	g_return_if_fail(group != NULL);
+
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+			group->title_utf8, purple_connection_get_account(gc));
+	if (conv == NULL) {
+		purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", group->title_utf8);
+		return;
+	}
+	g_return_if_fail(group->members != NULL);
+
+	names = NULL;
+	flags = NULL;
+
+	list = group->members;
+	while (list != NULL) {
+		member = (qq_buddy *) list->data;
+
+		/* we need unique identifiers for everyone in the chat or else we'll
+		 * run into problems with functions like get_cb_real_name from qq.c */
+		member_name =   (member->nickname != NULL && *(member->nickname) != '\0') ?
+				g_strdup_printf("%s (%u)", member->nickname, member->uid) :
+				g_strdup_printf("(%u)", member->uid);
+		member_uid = g_strdup_printf("(%u)", member->uid);
+
+		flag = 0;
+		/* TYPING to put online above OP and FOUNDER */
+		if (is_online(member->status)) flag |= (PURPLE_CBFLAGS_TYPING | PURPLE_CBFLAGS_VOICE);
+		if(1 == (member->role & 1)) flag |= PURPLE_CBFLAGS_OP;
+		if(member->uid == group->creator_uid) flag |= PURPLE_CBFLAGS_FOUNDER;
+
+		is_find = TRUE;
+		if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_name))
+		{
+			purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
+					member_name,
+					flag);
+		} else if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_uid))
+		{
+			purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
+					member_uid,
+					flag);
+			purple_conv_chat_rename_user(PURPLE_CONV_CHAT(conv), member_uid, member_name);
+		} else {
+			is_find = FALSE;
+		}
+		if (!is_find) {
+			/* always put it even offline */
+			names = g_list_append(names, member_name);
+			flags = g_list_append(flags, GINT_TO_POINTER(flag));
+		} else {
+			g_free(member_name);
+		}
+		g_free(member_uid);
+		list = list->next;
+	}
+
+	if (names != NULL && flags != NULL) {
+		purple_conv_chat_add_users(PURPLE_CONV_CHAT(conv), names, NULL, flags, FALSE);
+	}
+
+	/* clean up names */
+	while (names != NULL) {
+		member_name = (gchar *) names->data;
+		names = g_list_remove(names, member_name);
+		g_free(member_name);
+	}
+	g_list_free(flags);
+}
+
 /* send IM to a group */
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg)
 {
@@ -151,7 +262,7 @@
 	conv = purple_find_conversation_with_account(
 			PURPLE_CONV_TYPE_CHAT, group->title_utf8, account);
 	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/show_room_when_newin")) {
-		conv = qq_room_conv_create(gc, group);
+		conv = qq_room_conv_new(gc, group);
 	}
 
 	if (conv == NULL) {
--- a/libpurple/protocols/qq/group_im.h	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Wed Oct 22 14:58:32 2008 +0000
@@ -27,8 +27,12 @@
 
 #include <glib.h>
 #include "connection.h"
+#include "conversation.h"
 #include "group.h"
 
+PurpleConversation *qq_room_conv_new(PurpleConnection *gc, qq_group *group);
+void qq_room_conv_set_onlines(PurpleConnection *gc, qq_group *group);
+
 void qq_room_got_chat_in(PurpleConnection *gc,
 		qq_group *group, guint32 uid_from, const gchar *msg, time_t in_time);
 
--- a/libpurple/protocols/qq/group_info.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Wed Oct 22 14:58:32 2008 +0000
@@ -247,8 +247,7 @@
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
 			group->title_utf8, purple_connection_get_account(gc));
 	if(NULL == conv) {
-		purple_debug_warning("QQ",
-				"Conversation \"%s\" is not open, do not set topic\n", group->title_utf8);
+		purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", group->title_utf8);
 		return;
 	}
 
--- a/libpurple/protocols/qq/group_internal.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Wed Oct 22 14:58:32 2008 +0000
@@ -63,7 +63,7 @@
 	PurpleChat *chat;
 	components = qq_group_to_hashtable(group);
 	chat = purple_chat_new(purple_connection_get_account(gc), group->title_utf8, components);
-	g = qq_create_group(PURPLE_GROUP_QQ_QUN);
+	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);
 }
--- a/libpurple/protocols/qq/group_join.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Wed Oct 22 14:58:32 2008 +0000
@@ -31,13 +31,11 @@
 
 #include "char_conv.h"
 #include "im.h"
-#include "group_conv.h"
 #include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_join.h"
 #include "group_opt.h"
-#include "group_conv.h"
 #include "group_search.h"
 #include "group_im.h"
 #include "qq_define.h"
@@ -284,7 +282,7 @@
 		group->my_role = QQ_ROOM_ROLE_YES;
 		qq_group_refresh(gc, group);
 		/* this must be shown before getting online members */
-		qq_room_conv_create(gc, group);
+		qq_room_conv_new(gc, group);
 		break;
 	case QQ_ROOM_JOIN_NEED_AUTH:
 		purple_debug_info("QQ",
--- a/libpurple/protocols/qq/im.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Wed Oct 22 14:58:32 2008 +0000
@@ -163,19 +163,16 @@
 {
 	qq_data *qd;
 	gchar *from;
-	PurpleBuddy *buddy;
 	time_t now = time(NULL);
 
-	qd = (qq_data *) gc->proto_data;
+	g_return_if_fail(gc != NULL  && gc->proto_data != NULL);
+	qd = gc->proto_data;
 
 	g_return_if_fail(qd->uid > 0);
 
+	qq_buddy_find_or_new(gc, qd->uid);
+
 	from = uid_to_purple_name(qd->uid);
-	buddy = purple_find_buddy(gc->account, from);
-	if (buddy == NULL) {
-		qq_create_buddy(gc, qd->uid, TRUE, TRUE);
-	}
-
 	serv_got_im(gc, from, msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY, now);
 	g_free(from);
 }
--- a/libpurple/protocols/qq/qq.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Oct 22 14:58:32 2008 +0000
@@ -374,18 +374,25 @@
 /* we can show tiny icons on the four corners of buddy icon, */
 static const char *qq_list_emblem(PurpleBuddy *b)
 {
-	/* each char** are refering to a filename in pixmaps/purple/status/default/ */
-	qq_buddy *q_bud;
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	qq_data *qd;
+	qq_buddy *buddy;
 
-	if (!b || !(q_bud = b->proto_data)) {
+	if (!b || !(account = b->account) || 
+		!(gc = purple_account_get_connection(account)) || !(qd = gc->proto_data))
 		return NULL;
+
+	buddy = (qq_buddy *)b->proto_data;
+	if (!buddy) {
+		return "not-authorized";
 	}
 
-	if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE)
+	if (buddy->comm_flag & QQ_COMM_FLAG_MOBILE)
 		return "mobile";
-	if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO)
+	if (buddy->comm_flag & QQ_COMM_FLAG_VIDEO)
 		return "video";
-	if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER)
+	if (buddy->comm_flag & QQ_COMM_FLAG_QQ_MEMBER)
 		return "qq_member";
 
 	return NULL;
@@ -512,6 +519,8 @@
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
+
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
 	qd = (qq_data *) gc->proto_data;
 
 	if ( !qd->is_login ) {
@@ -526,6 +535,7 @@
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
 
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
 	qd = (qq_data *) gc->proto_data;
 	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_BASE);
 }
@@ -535,6 +545,7 @@
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
 
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
 	qd = (qq_data *) gc->proto_data;
 	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_EXT);
 }
@@ -544,6 +555,7 @@
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
 
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
 	qd = (qq_data *) gc->proto_data;
 	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_ADDR);
 }
@@ -553,12 +565,16 @@
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
 
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
 	qd = (qq_data *) gc->proto_data;
 	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_CONTACT);
 }
 
 static void action_change_password(PurplePluginAction *action)
 {
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
 	purple_notify_uri(NULL, "https://password.qq.com");
 }
 
@@ -571,6 +587,7 @@
 	struct tm *tm_local;
 	int index;
 
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
 	qd = (qq_data *) gc->proto_data;
 	info = g_string_new("<html><body>");
 
--- a/libpurple/protocols/qq/qq.h	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Wed Oct 22 14:58:32 2008 +0000
@@ -179,7 +179,6 @@
 	struct in_addr my_ip;			/* my ip address detected by server */
 	guint16 my_port;		/* my port detected by server */
 	guint16 my_icon;		/* my icon index */
-	guint16 my_level;		/* my level */
 	guint32 online_total;		/* the number of online QQ users */
 	time_t online_last_update;		/* last time send get_friends_online packet */
 
@@ -189,7 +188,6 @@
 	GList *groups;
 	GSList *joining_groups;
 	GSList *adding_groups_from_server; /* internal ids of groups the server wants in my blist */
-	GList *buddies;
 	GList *group_info_window;
 
 	gboolean is_show_notice;
--- a/libpurple/protocols/qq/qq_define.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Wed Oct 22 14:58:32 2008 +0000
@@ -245,3 +245,18 @@
 		return "Unknown Room Command";
 	}
 }
+
+/* check if status means online or offline */
+gboolean is_online(guint8 status)
+{
+	switch(status) {
+		case QQ_BUDDY_ONLINE_NORMAL:
+		case QQ_BUDDY_ONLINE_AWAY:
+		case QQ_BUDDY_ONLINE_INVISIBLE:
+		case QQ_BUDDY_ONLINE_BUSY:
+			return TRUE;
+		case QQ_BUDDY_CHANGE_TO_OFFLINE:
+			return FALSE;
+	}
+	return FALSE;
+}
--- a/libpurple/protocols/qq/qq_define.h	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Wed Oct 22 14:58:32 2008 +0000
@@ -115,4 +115,16 @@
 	QQ_SERVER_NOTICE= 0x06,
 	QQ_SERVER_NEW_CLIENT = 0x09
 };
+
+enum {
+	QQ_BUDDY_OFFLINE = 0x00,
+	QQ_BUDDY_ONLINE_NORMAL = 10,
+	QQ_BUDDY_CHANGE_TO_OFFLINE = 20,
+	QQ_BUDDY_ONLINE_AWAY = 30,
+	QQ_BUDDY_ONLINE_INVISIBLE = 40,
+	QQ_BUDDY_ONLINE_BUSY = 50,
+};
+
+gboolean is_online(guint8 status);
+
 #endif
--- a/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Wed Oct 22 14:58:32 2008 +0000
@@ -1040,8 +1040,8 @@
 	qd->my_ip.s_addr = 0;
 	qd->my_port = 0;
 
-	qq_group_free_all(qd);
-	qq_buddies_list_free(gc->account, qd);
+	qq_group_free_all(gc);
+	qq_buddy_data_free_all(gc);
 }
 
 static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq,
--- a/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:55:09 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Wed Oct 22 14:58:32 2008 +0000
@@ -34,7 +34,6 @@
 #include "qq_crypt.h"
 
 #include "group_search.h"
-#include "group_conv.h"
 #include "group_find.h"
 #include "group_internal.h"
 #include "group_im.h"
@@ -630,7 +629,7 @@
 			qq_request_change_status(gc, QQ_CMD_CLASS_UPDATE_ALL);
 			break;
 		case QQ_CMD_CHANGE_STATUS:
-			qq_request_get_buddies_list(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
+			qq_request_get_buddies(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
 			break;
 		case QQ_CMD_GET_BUDDIES_LIST:
 			qq_request_get_buddies_and_rooms(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
@@ -824,13 +823,13 @@
 	case QQ_ROOM_CMD_GET_ONLINES:
 		qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc);
 		if (group != NULL)
-			qq_group_conv_refresh_online_member(gc, group);
+			qq_room_conv_set_onlines(gc, group);
 		break;
 	case QQ_ROOM_CMD_GET_BUDDIES:
 		qq_process_room_cmd_get_buddies(data + bytes, data_len - bytes, gc);
 		if (group != NULL) {
 			group->is_got_info = TRUE;
-			qq_group_conv_refresh_online_member(gc, group);
+			qq_room_conv_set_onlines(gc, group);
 		}
 		break;
 	default:
@@ -898,23 +897,16 @@
 		case QQ_CMD_LOGIN:
 		default:
 			if (qd->client_version >= 2007) {
-				purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
 				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
 				if (data_len >= 0) {
-					purple_debug_warning("QQ", "Dpwd_twice_md5 *OK*\n");
-				}
-				else {
-					purple_debug_warning("QQ", "Dpwd_twice_md5 *FAILED*, try login_key, last data_len=%d\n", data_len);
+					purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
+				} else {
 					data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.login_key);
 					if (data_len >= 0) {
-						purple_debug_warning("QQ", "Dlogin_key *OK*\n");
-					}
-					else {
-						purple_debug_warning("QQ", "Dlogin_key *FAILED*\n");
+						purple_debug_warning("QQ", "Decrypt login packet by login_key\n");
 					}
 				}
-			}
-			else {
+			} else {
 				/* May use password_twice_md5 in the past version like QQ2005 */
 				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
 				if (data_len >= 0) {
@@ -999,7 +991,6 @@
 			qq_update_all(gc, 0);
 			break;
 		default:
-			purple_debug_warning("QQ", "UNKNOWN LOGIN CMD: %d\n", cmd);
 			process_unknow_cmd(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq);
 			return QQ_LOGIN_REPLY_ERR;
 	}
@@ -1061,7 +1052,7 @@
 			qq_process_get_buddy_info(data, data_len, ship32, gc);
 			break;
 		case QQ_CMD_CHANGE_STATUS:
-			qq_process_change_status_reply(data, data_len, gc);
+			qq_process_change_status(data, data_len, gc);
 			break;
 		case QQ_CMD_SEND_IM:
 			do_im_ack(data, data_len, gc);
@@ -1076,23 +1067,23 @@
 			}
 			break;
 		case QQ_CMD_GET_BUDDIES_ONLINE:
-			ret_8 = qq_process_get_buddies_online_reply(data, data_len, gc);
+			ret_8 = qq_process_get_buddies_online(data, data_len, gc);
 			if (ret_8  > 0 && ret_8 < 0xff) {
 				purple_debug_info("QQ", "Requesting for more online buddies\n");
 				qq_request_get_buddies_online(gc, ret_8, update_class);
 				return;
 			}
 			purple_debug_info("QQ", "All online buddies received\n");
-			qq_refresh_all_buddy_status(gc);
+			qq_update_buddyies_status(gc);
 			break;
 		case QQ_CMD_GET_LEVEL:
 			qq_process_get_level_reply(data, data_len, gc);
 			break;
 		case QQ_CMD_GET_BUDDIES_LIST:
-			ret_16 = qq_process_get_buddies_list_reply(data, data_len, gc);
+			ret_16 = qq_process_get_buddies(data, data_len, gc);
 			if (ret_16 > 0	&& ret_16 < 0xffff) {
 				purple_debug_info("QQ", "Requesting for more buddies\n");
-				qq_request_get_buddies_list(gc, ret_16, update_class);
+				qq_request_get_buddies(gc, ret_16, update_class);
 				return;
 			}
 			purple_debug_info("QQ", "All buddies received. Requesting buddies' levels\n");