changeset 24132:11de3fd58c59

merge of '8c50cfe68a5cab6521acc54fedd6797e6c8b863f' and 'b5edbb5c5168486c2c8c4b6bd01af85cbe56dc3c'
author SHiNE CsyFeK <csyfek@gmail.com>
date Fri, 19 Sep 2008 14:48:00 +0000
parents d57928c9dd8f (diff) 8f30b1f32f46 (current diff)
children a3cd7c3d9da1
files libpurple/protocols/qq/sys_msg.c libpurple/protocols/qq/sys_msg.h
diffstat 25 files changed, 1117 insertions(+), 1436 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Fri Sep 19 14:48:00 2008 +0000
@@ -1,3 +1,7 @@
+2008.09.19 - ccpaging <ccpaging(at)gmail.com>
+	* Rewrite buddy modify info, there is a ticket for this problem
+	* Use ship32 to trans action code between request packet and reply packet process
+
 2008.09.15 - csyfek <csyfek(at)gmail.com>
 	* im.pidgin.pidgin.openq branch
 
--- a/libpurple/protocols/qq/Makefile.am	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Fri Sep 19 14:48:00 2008 +0000
@@ -54,8 +54,6 @@
 	send_file.h \
 	qq_trans.c \
 	qq_trans.h \
-	sys_msg.c \
-	sys_msg.h \
 	utils.c \
 	utils.h
 
--- a/libpurple/protocols/qq/Makefile.mingw	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Fri Sep 19 14:48:00 2008 +0000
@@ -64,7 +64,6 @@
 	qq_process.c \
 	qq_trans.c \
 	send_file.c \
-	sys_msg.c \
 	utils.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/qq/buddy_info.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Fri Sep 19 14:48:00 2008 +0000
@@ -36,29 +36,6 @@
 #include "qq_base.h"
 #include "qq_network.h"
 
-#define QQ_PRIMARY_INFORMATION _("Primary Information")
-#define QQ_ADDITIONAL_INFORMATION _("Additional Information")
-#define QQ_INTRO _("Personal Introduction")
-#define QQ_NUMBER _("QQ Number")
-#define QQ_NICKNAME _("Nickname")
-#define QQ_NAME _("Name")
-#define QQ_AGE _("Age")
-#define QQ_GENDER _("Gender")
-#define QQ_COUNTRY _("Country/Region")
-#define QQ_PROVINCE _("Province/State")
-#define QQ_CITY _("City")
-#define QQ_HOROSCOPE _("Horoscope Symbol")
-#define QQ_OCCUPATION _("Occupation")
-#define QQ_ZODIAC _("Zodiac Sign")
-#define QQ_BLOOD _("Blood Type")
-#define QQ_COLLEGE _("College")
-#define QQ_EMAIL _("Email")
-#define QQ_ADDRESS _("Address")
-#define QQ_ZIPCODE _("Zipcode")
-#define QQ_CELL _("Cellphone Number")
-#define QQ_TELEPHONE _("Phone Number")
-#define QQ_HOMEPAGE _("Homepage")
-
 #define QQ_HOROSCOPE_SIZE 13
 static const gchar *horoscope_names[] = {
 	"-", N_("Aquarius"), N_("Pisces"), N_("Aries"), N_("Taurus"),
@@ -78,223 +55,166 @@
 	"-", "A", "B", "O", "AB", N_("Other")
 };
 
-#define QQ_GENDER_SIZE 2
-static const gchar *genders[] = {
-	N_("Male"),
-	N_("Female")
+#define QQ_PUBLISH_SIZE 3
+static const gchar *publish_types[] = {
+	N_("Visible"), N_("Firend Only"), N_("Private")
 };
 
-#define QQ_CONTACT_FIELDS                               37
+#define QQ_GENDER_SIZE 3
+static const gchar *genders[] = {
+	N_("Private"),
+	N_("Male"),
+	N_("Female"),
+};
+
+static const gchar *genders_zh[] = {
+	N_("-"),
+	N_("\xc4\xd0"),
+	N_("\xc5\xae"),
+};
+
 #define QQ_FACES	    100
 
-/* There is no user id stored in the reply packet for information query
- * we have to manually store the query, so that we know the query source */
-typedef struct _qq_info_query {
-	guint32 uid;
-	gboolean show_window;
-	gboolean modify_info;
-} qq_info_query;
+enum {
+	QQ_INFO_UID = 0, QQ_INFO_NICK, QQ_INFO_COUNTRY, QQ_INFO_PROVINCE, QQ_INFO_ZIPCODE,
+	QQ_INFO_ADDR, QQ_INFO_TEL, QQ_INFO_AGE, QQ_INFO_GENDER, QQ_INFO_NAME, QQ_INFO_EMAIL,
+	QQ_INFO_PG_SN, QQ_INFO_PG_NUM, QQ_INFO_PG_SP, QQ_INFO_PG_BASE_NUM, QQ_INFO_PG_TYPE,
+	QQ_INFO_OCCU, QQ_INFO_HOME_PAGE, QQ_INFO_AUTH_TYPE, QQ_INFO_UNKNOW1, QQ_INFO_UNKNOW2,
+	QQ_INFO_FACE, QQ_INFO_MOBILE, QQ_INFO_MOBILE_TYPE, QQ_INFO_INTRO, QQ_INFO_CITY,
+	QQ_INFO_UNKNOW3, QQ_INFO_UNKNOW4, QQ_INFO_UNKNOW5,
+	QQ_INFO_IS_PUB_MOBILE, QQ_INFO_IS_PUB_CONTACT, QQ_INFO_COLLEGE, QQ_INFO_HOROSCOPE,
+	QQ_INFO_ZODIAC, QQ_INFO_BLOOD, QQ_INFO_SHOW, QQ_INFO_UNKNOW6,
+	QQ_INFO_LAST,
+};
 
-typedef struct _contact_info {
-	gchar *uid;
-	gchar *nick;
-	gchar *country;
-	gchar *province;
-	gchar *zipcode;
-	gchar *address;
-	gchar *tel;
-	gchar *age;
-	gchar *gender;
-	gchar *name;
-	gchar *email;
-	gchar *pager_sn;
-	gchar *pager_num;
-	gchar *pager_sp;
-	gchar *pager_base_num;
-	gchar *pager_type;
-	gchar *occupation;
-	gchar *homepage;
-	gchar *auth_type;
-	gchar *unknown1;
-	gchar *unknown2;
-	gchar *face;
-	gchar *hp_num;
-	gchar *hp_type;
-	gchar *intro;
-	gchar *city;
-	gchar *unknown3;
-	gchar *unknown4;
-	gchar *unknown5;
-	gchar *is_open_hp;
-	gchar *is_open_contact;
-	gchar *college;
-	gchar *horoscope;
-	gchar *zodiac;
-	gchar *blood;
-	gchar *qq_show;
-	gchar *unknown6;        /* always 0x2D */
-} contact_info;
+enum {
+	QQ_FIELD_UNUSED = 0, QQ_FIELD_BASE, QQ_FIELD_EXT, QQ_FIELD_CONTACT, QQ_FIELD_ADDR
+};
+
+enum {
+	QQ_FIELD_LABEL = 0, QQ_FIELD_STRING, QQ_FIELD_MULTI, QQ_FIELD_BOOL, QQ_FIELD_CHOICE,
+};
+
+typedef struct {
+	int iclass;
+	int type;
+	char *id;
+	char *text;
+	const gchar **choice;
+	int choice_size;
+} QQ_FIELD_INFO;
 
-/* We get an info packet on ourselves before we modify our information.
- * Even though not all of the information is modifiable, it still
- * all needs to be there when we send out the modify info packet */
-typedef struct _modify_info_data {
-	PurpleConnection *gc;
-	contact_info *info;
-} modify_info_data;
-
-/* return -1 as a sentinel */
-static gint choice_index(const gchar *value, const gchar **choice, gint choice_size)
-{
-	gint len, i;
+static const QQ_FIELD_INFO field_infos[] = {
+	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "uid", 			N_("QQ Number"), NULL, 0 },
+	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "nick", 			N_("Nickname"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "country", 	N_("Country/Region"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "province", 	N_("Province/State"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "zipcode", 	N_("Zipcode"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "address", 	N_("Address"), NULL, 0 },
+	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "tel", 				N_("Phone Number"), NULL, 0 },
+	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "age", 			N_("Age"), NULL, 0 },
+	{ QQ_FIELD_BASE, 		QQ_FIELD_CHOICE, "gender", 		N_("Gender"), genders, QQ_GENDER_SIZE },
+	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "name", 			N_("Name"), NULL, 0 },
+	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "email", 			N_("Email"), NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sn",		"Pager Serial Num", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_num",	"Pager Num", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sp",		"Pager Serivce Provider", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sta",		"Pager Station Num", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_type",	"Pager Type", NULL, 0 },
+	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "occupation", 	N_("Occupation"), NULL, 0 },
+	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "homepage", 		N_("Homepage"), NULL, 0 },
+	{ QQ_FIELD_BASE, 		QQ_FIELD_BOOL, 	"auth", 				N_("Authorize adding"), NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow1",	"Unknow1", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow2",	"Unknow2", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "face",				"Face", NULL, 0 },
+	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "mobile",		N_("Cellphone Number"), NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "mobile_type","Cellphone Type", NULL, 0 },
+	{ QQ_FIELD_BASE, 		QQ_FIELD_MULTI, 	"intro", 		N_("Personal Introduction"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "city",			N_("City/Area"), NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow3",	"Unknow3", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow4",	"Unknow4", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow5",	"Unknow5", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_CHOICE, "pub_mobile",	N_("Publish Mobile"), publish_types, QQ_PUBLISH_SIZE },
+	{ QQ_FIELD_CONTACT, QQ_FIELD_CHOICE, "pub_contact",	N_("Publish Contact"), publish_types, QQ_PUBLISH_SIZE },
+	{ QQ_FIELD_EXT, 		QQ_FIELD_STRING, "college",			N_("College"), NULL, 0 },
+	{ QQ_FIELD_EXT, 		QQ_FIELD_CHOICE, "horoscope",	N_("Horoscope"), horoscope_names, QQ_HOROSCOPE_SIZE },
+	{ QQ_FIELD_EXT, 		QQ_FIELD_CHOICE, "zodiac",		N_("Zodiac"), zodiac_names, QQ_ZODIAC_SIZE },
+	{ QQ_FIELD_EXT, 		QQ_FIELD_CHOICE, "blood",			N_("Blood"), blood_types, QQ_BLOOD_SIZE },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "qq_show",	"QQ Show", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow6",	"Unknow6", NULL, 0 }
+};
 
-	len = strlen(value);
-	if (len > 3 || len == 0) return -1;
-	for (i = 0; i < len; i++) {
-		if (!g_ascii_isdigit(value[i]))
-			return -1;
-	}
-	i = strtol(value, NULL, 10);
-	if (i >= choice_size)
-		return -1;
-
-	return i;
-}
+typedef struct _modify_info_request {
+	PurpleConnection *gc;
+	int iclass;
+	gchar **segments;
+} modify_info_request;
 
-/* return should be freed */
-static gchar *field_value(const gchar *field, const gchar **choice, gint choice_size)
+#ifdef DEBUG
+static void info_debug(gchar **segments)
 {
-	gint index, len;
+	int index;
+	gchar *utf8_str;
+	for (index = 0; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+		if (field_infos[index].type == QQ_FIELD_STRING
+				|| field_infos[index].type == QQ_FIELD_LABEL
+				|| field_infos[index].type == QQ_FIELD_MULTI
+				|| index == QQ_INFO_GENDER)  {
+			utf8_str = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT);
+			purple_debug_info("QQ_BUDDY_INFO", "%s: %s\n", field_infos[index]. text, utf8_str);
+			g_free(utf8_str);
+			continue;
+		}
+		purple_debug_info("QQ_BUDDY_INFO", "%s: %s\n", field_infos[index]. text, segments[index]);
+	}
+}
+#endif
+
+static void info_display_only(PurpleConnection *gc, gchar **segments)
+{
+	PurpleNotifyUserInfo *user_info;
+	gchar *utf8_value;
+	int index;
+	int choice_num;
 
-	len = strlen(field);
-	if (len == 0) {
-		return NULL;
-	} else if (choice != NULL) {
-		/* some choice fields are also customizable */
-		index = choice_index(field, choice, choice_size);
-		if (index == -1) {
-			if (strcmp(field, "-") != 0) {
-				return qq_to_utf8(field, QQ_CHARSET_DEFAULT);
-			} else {
-				return NULL;
-			}
-			/* else ASCIIized index */
-		} else {
-			if (strcmp(choice[index], "-") != 0)
-				return g_strdup(choice[index]);
-			else
-				return NULL;
+	user_info = purple_notify_user_info_new();
+
+	for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+		if (field_infos[index].iclass == QQ_FIELD_UNUSED) {
+			continue;
 		}
-	} else {
-		if (strcmp(field, "-") != 0) {
-			return qq_to_utf8(field, QQ_CHARSET_DEFAULT);
-		} else {
-			return NULL;
+		switch (field_infos[index].type) {
+			case QQ_FIELD_BOOL:
+				purple_notify_user_info_add_pair(user_info, field_infos[index].text,
+					strtol(segments[index], NULL, 10) ? _("True") : _("False"));
+				break;
+			case QQ_FIELD_CHOICE:
+				choice_num = strtol(segments[index], NULL, 10);
+				if (choice_num < 0 || choice_num >= field_infos[index].choice_size)	choice_num = 0;
+
+				purple_notify_user_info_add_pair(user_info, field_infos[index].text, field_infos[index].choice[choice_num]);
+				break;
+			case QQ_FIELD_LABEL:
+			case QQ_FIELD_STRING:
+			case QQ_FIELD_MULTI:
+			default:
+				if (strlen(segments[index]) != 0) {
+					utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT);
+					purple_notify_user_info_add_pair(user_info, field_infos[index].text, utf8_value);
+					g_free(utf8_value);
+				}
+				break;
 		}
 	}
-}
 
-static gboolean append_field_value(PurpleNotifyUserInfo *user_info, const gchar *field,
-		const gchar *title, const gchar **choice, gint choice_size)
-{
-	gchar *value = field_value(field, choice, choice_size);
-
-	if (value != NULL) {
-		purple_notify_user_info_add_pair(user_info, title, value);
-		g_free(value);
-
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static PurpleNotifyUserInfo *
-	info_to_notify_user_info(const contact_info *info)
-{
-	PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
-	const gchar *intro;
-	gboolean has_extra_info = FALSE;
-
-	purple_notify_user_info_add_pair(user_info, QQ_NUMBER, info->uid);
-
-	append_field_value(user_info, info->nick, QQ_NICKNAME, NULL, 0);
-	append_field_value(user_info, info->name, QQ_NAME, NULL, 0);
-	append_field_value(user_info, info->age, QQ_AGE, NULL, 0);
-	append_field_value(user_info, info->gender, QQ_GENDER, genders, QQ_GENDER_SIZE);
-	append_field_value(user_info, info->country, QQ_COUNTRY, NULL, 0);
-	append_field_value(user_info, info->province, QQ_PROVINCE, NULL, 0);
-	append_field_value(user_info, info->city, QQ_CITY, NULL, 0);
-
-	purple_notify_user_info_add_section_header(user_info, QQ_ADDITIONAL_INFORMATION);
-
-	has_extra_info |= append_field_value(user_info, info->horoscope, QQ_HOROSCOPE, horoscope_names, QQ_HOROSCOPE_SIZE);
-	has_extra_info |= append_field_value(user_info, info->occupation, QQ_OCCUPATION, NULL, 0);
-	has_extra_info |= append_field_value(user_info, info->zodiac, QQ_ZODIAC, zodiac_names, QQ_ZODIAC_SIZE);
-	has_extra_info |= append_field_value(user_info, info->blood, QQ_BLOOD, blood_types, QQ_BLOOD_SIZE);
-	has_extra_info |= append_field_value(user_info, info->college, QQ_COLLEGE, NULL, 0);
-	has_extra_info |= append_field_value(user_info, info->email, QQ_EMAIL, NULL, 0);
-	has_extra_info |= append_field_value(user_info, info->address, QQ_ADDRESS, NULL, 0);
-	has_extra_info |= append_field_value(user_info, info->zipcode, QQ_ZIPCODE, NULL, 0);
-	has_extra_info |= append_field_value(user_info, info->hp_num, QQ_CELL, NULL, 0);
-	has_extra_info |= append_field_value(user_info, info->tel, QQ_TELEPHONE, NULL, 0);
-	has_extra_info |= append_field_value(user_info, info->homepage, QQ_HOMEPAGE, NULL, 0);
+	purple_notify_userinfo(gc, segments[0], user_info, NULL, NULL);
 
-	if (!has_extra_info)
-		purple_notify_user_info_remove_last_item(user_info);
-
-	intro = field_value(info->intro, NULL, 0);
-	if (intro) {
-		purple_notify_user_info_add_pair(user_info, QQ_INTRO, intro);
-	}
-
-	/* for debugging */
-	/*
-	   g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
-	   append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
-	   append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
-	   append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
-	   append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
-	   append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
-	   append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
-	   append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
-	   append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
-	   append_field_value(info_text, info->face, "face", NULL, 0);
-	   append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
-	   append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
-	   append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
-	   append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
-	   append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
-	   append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
-	   append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
-	   append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
-	   */
-
-	return user_info;
-}
-
-/* send a packet to get detailed information of uid */
-void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window)
-{
-	qq_data *qd;
-	gchar uid_str[11];
-	qq_info_query *query;
-
-	g_return_if_fail(uid != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) uid_str, strlen(uid_str));
-
-	query = g_new0(qq_info_query, 1);
-	query->uid = uid;
-	query->show_window = show_window;
-	query->modify_info = FALSE;
-	qd->info_query = g_list_append(qd->info_query, query);
+	purple_notify_user_info_destroy(user_info);
+	g_strfreev(segments);
 }
 
 void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
-		gint update_class, guint32 ship32)
+		gint update_class, int action)
 {
 	qq_data *qd;
 	gchar raw_data[16] = {0};
@@ -304,399 +224,216 @@
 	qd = (qq_data *) gc->proto_data;
 	g_snprintf(raw_data, sizeof(raw_data), "%d", uid);
 	qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) raw_data, strlen(raw_data),
-			update_class, ship32);
-}
-
-/* set up the fields requesting personal information and send a get_info packet
- * for myself */
-void qq_prepare_modify_info(PurpleConnection *gc)
-{
-	qq_data *qd;
-	GList *ql;
-	qq_info_query *query;
-
-	qd = (qq_data *) gc->proto_data;
-	qq_send_packet_get_info(gc, qd->uid, FALSE);
-	/* traverse backwards so we get the most recent info_query */
-	for (ql = g_list_last(qd->info_query); ql != NULL; ql = g_list_previous(ql)) {
-		query = ql->data;
-		if (query->uid == qd->uid)
-			query->modify_info = TRUE;
-	}
+			update_class, action);
 }
 
 /* send packet to modify personal information */
-static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info)
+static void request_modify_info(PurpleConnection *gc, gchar **segments)
 {
 	gint bytes = 0;
 	guint8 raw_data[MAX_PACKET_SIZE - 128] = {0};
 	guint8 bar;
+	gchar *join;
 
-	g_return_if_fail(info != NULL);
+	g_return_if_fail(segments != NULL);
 
 	bar = 0x1f;
 
 	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_put8(raw_data + bytes, bar);
 
 	/* important! skip the first uid entry */
-	/*
-	   for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
-	   create_packet_b(raw_data, &cursor, bar);
-	   create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
-	   }
-	   */
-	/* uid */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->uid, strlen(info->uid));
-	/* nick */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->nick, strlen(info->nick));
-	/* country */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->country, strlen(info->country));
-	/* province */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->province, strlen(info->province));
-	/* zipcode */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zipcode, strlen(info->zipcode));
-	/* address */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->address, strlen(info->address));
-	/* tel */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->tel, strlen(info->tel));
-	/* age */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->age, strlen(info->age));
-	/* gender */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->gender, strlen(info->gender));
-	/* name */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->name, strlen(info->name));
-	/* email */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->email, strlen(info->email));
-	/* pager_sn */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sn, strlen(info->pager_sn));
-	/* pager_num */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_num, strlen(info->pager_num));
-	/* pager_sp */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sp, strlen(info->pager_sp));
-	/* pager_base_num */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_base_num, strlen(info->pager_base_num));
-	/* pager_type */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_type, strlen(info->pager_type));
-	/* occupation */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->occupation, strlen(info->occupation));
-	/* homepage */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->homepage, strlen(info->homepage));
-	/* auth_type */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->auth_type, strlen(info->auth_type));
-	/* unknown1 */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown1, strlen(info->unknown1));
-	/* unknown2 */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown2, strlen(info->unknown2));
-	/* face */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->face, strlen(info->face));
-	/* hp_num */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_num, strlen(info->hp_num));
-	/* hp_type */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_type, strlen(info->hp_type));
-	/* intro */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->intro, strlen(info->intro));
-	/* city */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->city, strlen(info->city));
-	/* unknown3 */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown3, strlen(info->unknown3));
-	/* unknown4 */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown4, strlen(info->unknown4));
-	/* unknown5 */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown5, strlen(info->unknown5));
-	/* is_open_hp */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_hp, strlen(info->is_open_hp));
-	/* is_open_contact */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_contact, strlen(info->is_open_contact));
-	/* college */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->college, strlen(info->college));
-	/* horoscope */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->horoscope, strlen(info->horoscope));
-	/* zodiac */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zodiac, strlen(info->zodiac));
-	/* blood */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->blood, strlen(info->blood));
-	/* qq_show */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->qq_show, strlen(info->qq_show));
-	/* unknown6 */
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown6, strlen(info->unknown6));
+	join = g_strjoinv("\x1f", segments +1);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)join, strlen(join));
+	g_free(join);
 
 	bytes += qq_put8(raw_data + bytes, bar);
 
+	/* qq_show_packet("request_modify_info", raw_data, bytes); */
 	qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, raw_data, bytes);
-
-}
-
-static void modify_info_cancel_cb(modify_info_data *mid)
-{
-	qq_data *qd;
-
-	qd = (qq_data *) mid->gc->proto_data;
-	qd->modifying_info = FALSE;
-
-	g_strfreev((gchar **) mid->info);
-	g_free(mid);
 }
 
-static gchar *parse_field(PurpleRequestField *field, gboolean choice)
+static void info_modify_cancel_cb(modify_info_request *info_request)
 {
-	gchar *value;
-
-	if (choice) {
-		value = g_strdup_printf("%d", purple_request_field_choice_get_value(field));
-	} else {
-		value = (gchar *) purple_request_field_string_get_value(field);
-		if (value == NULL)
-			value = g_strdup("-");
-		else
-			value = utf8_to_qq(value, QQ_CHARSET_DEFAULT);
-	}
-
-	return value;
+	g_strfreev(info_request->segments);
+	g_free(info_request);
 }
 
 /* parse fields and send info packet */
-static void modify_info_ok_cb(modify_info_data *mid, PurpleRequestFields *fields)
+static void info_modify_ok_cb(modify_info_request *info_request, PurpleRequestFields *fields)
 {
 	PurpleConnection *gc;
 	qq_data *qd;
-	GList *groups;
-	contact_info *info;
-
-	gc = mid->gc;
-	qd = (qq_data *) gc->proto_data;
-	qd->modifying_info = FALSE;
-
-	info = mid->info;
+	gchar **segments;
+	int index;
+	const char *utf8_str;
+	gchar *value;
+	int choice_num;
 
-	groups = purple_request_fields_get_groups(fields);
-	while (groups != NULL) {
-		PurpleRequestFieldGroup *group = groups->data;
-		const char *g_name = purple_request_field_group_get_title(group);
-		GList *fields = purple_request_field_group_get_fields(group);
-
-		if (g_name == NULL)
-			continue;
+	gc = info_request->gc;
+	g_return_if_fail(gc != NULL && info_request->gc);
+	qd = (qq_data *) gc->proto_data;
+	segments = info_request->segments;
+	g_return_if_fail(segments != NULL);
 
-		while (fields != NULL) {
-			PurpleRequestField *field = fields->data;
-			const char *f_id = purple_request_field_get_id(field);
-
-			if (!strcmp(QQ_PRIMARY_INFORMATION, g_name)) {
-
-				if (!strcmp(f_id, "uid"))
-					info->uid = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "nick"))
-					info->nick = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "name"))
-					info->name = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "age"))
-					info->age = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "gender"))
-					info->gender = parse_field(field, TRUE);
-				else if (!strcmp(f_id, "country"))
-					info->country = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "province"))
-					info->province = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "city"))
-					info->city = parse_field(field, FALSE);
-
-			} else if (!strcmp(QQ_ADDITIONAL_INFORMATION, g_name)) {
+	for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+		if (field_infos[index].iclass == QQ_FIELD_UNUSED) {
+			continue;
+		}
+		if (!purple_request_fields_exists(fields, field_infos[index].id)) {
+			continue;
+		}
+		switch (field_infos[index].type) {
+			case QQ_FIELD_BOOL:
+				value = purple_request_fields_get_bool(fields, field_infos[index].id)
+						? g_strdup("1") : g_strdup("0");
+				g_free(segments[index]);
+				segments[index] = value;
+				break;
+			case QQ_FIELD_CHOICE:
+				choice_num = purple_request_fields_get_choice(fields, field_infos[index].id);
+				if (choice_num < 0 || choice_num >= field_infos[index].choice_size)	choice_num = 0;
 
-				if (!strcmp(f_id, "horoscope"))
-					info->horoscope = parse_field(field, TRUE);
-				else if (!strcmp(f_id, "occupation"))
-					info->occupation = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "zodiac"))
-					info->zodiac = parse_field(field, TRUE);
-				else if (!strcmp(f_id, "blood"))
-					info->blood = parse_field(field, TRUE);
-				else if (!strcmp(f_id, "college"))
-					info->college = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "email"))
-					info->email = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "address"))
-					info->address = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "zipcode"))
-					info->zipcode = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "hp_num"))
-					info->hp_num = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "tel"))
-					info->tel = parse_field(field, FALSE);
-				else if (!strcmp(f_id, "homepage"))
-					info->homepage = parse_field(field, FALSE);
+				if (index == QQ_INFO_GENDER) {
+					/* QQ Server only recept gender in Chinese */
+					value = g_strdup(genders_zh[choice_num]);
+				} else {
+					value = g_strdup_printf("%d", choice_num);
+				}
+				g_free(segments[index]);
+				segments[index] = value;
+				break;
+			case QQ_FIELD_LABEL:
+			case QQ_FIELD_STRING:
+			case QQ_FIELD_MULTI:
+			default:
+				utf8_str = purple_request_fields_get_string(fields, field_infos[index].id);
+				if (utf8_str == NULL) {
+					value = g_strdup("-");
+				} else {
+					value = utf8_to_qq(utf8_str, QQ_CHARSET_DEFAULT);
+					if (value == NULL) value = g_strdup("-");
+				}
+				g_free(segments[index]);
+				segments[index] = value;
+				break;
+		}
+	}
+	request_modify_info(gc, segments);
 
-			} else if (!strcmp(QQ_INTRO, g_name)) {
-
-				if (!strcmp(f_id, "intro"))
-					info->intro = parse_field(field, FALSE);
-
-			}
-
-			fields = fields->next;
-		}
-
-		groups = groups->next;
-	}
-
-	/* This casting looks like a horrible idea to me -DAA
-	 * yes, rewritten -s3e
-	 * qq_send_packet_modify_info(gc, (gchar **) info);
-	 */
-	qq_send_packet_modify_info(gc, info);
-
-	g_strfreev((gchar **) mid->info);
-	g_free(mid);
+	g_strfreev(segments);
+	g_free(info_request);
 }
 
-static PurpleRequestFieldGroup *setup_field_group(PurpleRequestFields *fields, const gchar *title)
-{
-	PurpleRequestFieldGroup *group;
-
-	group = purple_request_field_group_new(title);
-	purple_request_fields_add_group(fields, group);
-
-	return group;
-}
-
-static void add_string_field_to_group(PurpleRequestFieldGroup *group,
-		const gchar *id, const gchar *title, const gchar *value)
+static void field_request_new(PurpleRequestFieldGroup *group, gint index, gchar **segments)
 {
 	PurpleRequestField *field;
 	gchar *utf8_value;
+	int choice_num;
+	int i;
 
-	utf8_value = qq_to_utf8(value, QQ_CHARSET_DEFAULT);
-	field = purple_request_field_string_new(id, title, utf8_value, FALSE);
-	purple_request_field_group_add_field(group, field);
-	g_free(utf8_value);
+	g_return_if_fail(index >=0 && segments[index] != NULL && index < QQ_INFO_LAST);
+
+	switch (field_infos[index].type) {
+		case QQ_FIELD_STRING:
+		case QQ_FIELD_MULTI:
+			utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT);
+			if (field_infos[index].type == QQ_FIELD_STRING) {
+				field = purple_request_field_string_new(
+						field_infos[index].id, field_infos[index].text, utf8_value, FALSE);
+			} else {
+				field = purple_request_field_string_new(
+						field_infos[index].id, field_infos[index].text, utf8_value, TRUE);
+			}
+			purple_request_field_group_add_field(group, field);
+			g_free(utf8_value);
+			break;
+		case QQ_FIELD_BOOL:
+			field = purple_request_field_bool_new(
+					field_infos[index].id, field_infos[index].text,
+					strtol(segments[index], NULL, 10) ? TRUE : FALSE);
+			purple_request_field_group_add_field(group, field);
+			break;
+		case QQ_FIELD_CHOICE:
+			choice_num = strtol(segments[index], NULL, 10);
+			if (choice_num < 0 || choice_num >= field_infos[index].choice_size)	choice_num = 0;
+
+			if (index == QQ_INFO_GENDER && strlen(segments[index]) != 0) {
+				for (i = 0; i < QQ_GENDER_SIZE; i++) {
+					if (strcmp(segments[index], genders_zh[i]) == 0) {
+						choice_num = i;
+					}
+				}
+			}
+			field = purple_request_field_choice_new(
+					field_infos[index].id, field_infos[index].text, choice_num);
+			for (i = 0; i < field_infos[index].choice_size; i++) {
+				purple_request_field_choice_add(field, field_infos[index].choice[i]);
+			}
+			purple_request_field_group_add_field(group, field);
+			break;
+		case QQ_FIELD_LABEL:
+		default:
+			field = purple_request_field_label_new(field_infos[index].id, segments[index]);
+			purple_request_field_group_add_field(group, field);
+			break;
+	}
 }
 
-static void add_choice_field_to_group(PurpleRequestFieldGroup *group,
-		const gchar *id, const gchar *title, const gchar *value,
-		const gchar **choice, gint choice_size)
-{
-	PurpleRequestField *field;
-	gint i, index;
-
-	index = choice_index(value, choice, choice_size);
-	field = purple_request_field_choice_new(id, title, index);
-	for (i = 0; i < choice_size; i++)
-		purple_request_field_choice_add(field, choice[i]);
-	purple_request_field_group_add_field(group, field);
-}
-
-/* take the info returned by a get_info packet for myself and set up a request form */
-static void create_modify_info_dialogue(PurpleConnection *gc, const contact_info *info)
+static void info_modify_dialogue(PurpleConnection *gc, gchar **segments, int iclass)
 {
 	qq_data *qd;
 	PurpleRequestFieldGroup *group;
 	PurpleRequestFields *fields;
-	PurpleRequestField *field;
-	modify_info_data *mid;
+	modify_info_request *info_request;
+	gchar *utf8_title, *utf8_prim;
+	int index;
 
-	/* so we only have one dialog open at a time */
 	qd = (qq_data *) gc->proto_data;
-	if (!qd->modifying_info) {
-		qd->modifying_info = TRUE;
-
-		fields = purple_request_fields_new();
+	/* Keep one dialog once a time */
+	purple_request_close_with_handle(gc);
 
-		group = setup_field_group(fields, QQ_PRIMARY_INFORMATION);
-		field = purple_request_field_string_new("uid", QQ_NUMBER, info->uid, FALSE);
-		purple_request_field_group_add_field(group, field);
-		purple_request_field_string_set_editable(field, FALSE);
-		add_string_field_to_group(group, "nick", QQ_NICKNAME, info->nick);
-		add_string_field_to_group(group, "name", QQ_NAME, info->name);
-		add_string_field_to_group(group, "age", QQ_AGE, info->age);
-		add_choice_field_to_group(group, "gender", QQ_GENDER, info->gender, genders, QQ_GENDER_SIZE);
-		add_string_field_to_group(group, "country", QQ_COUNTRY, info->country);
-		add_string_field_to_group(group, "province", QQ_PROVINCE, info->province);
-		add_string_field_to_group(group, "city", QQ_CITY, info->city);
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
 
-		group = setup_field_group(fields, QQ_ADDITIONAL_INFORMATION);
-		add_choice_field_to_group(group, "horoscope", QQ_HOROSCOPE, info->horoscope, horoscope_names, QQ_HOROSCOPE_SIZE);
-		add_string_field_to_group(group, "occupation", QQ_OCCUPATION, info->occupation);
-		add_choice_field_to_group(group, "zodiac", QQ_ZODIAC, info->zodiac, zodiac_names, QQ_ZODIAC_SIZE);
-		add_choice_field_to_group(group, "blood", QQ_BLOOD, info->blood, blood_types, QQ_BLOOD_SIZE);
-		add_string_field_to_group(group, "college", QQ_COLLEGE, info->college);
-		add_string_field_to_group(group, "email", QQ_EMAIL, info->email);
-		add_string_field_to_group(group, "address", QQ_ADDRESS, info->address);
-		add_string_field_to_group(group, "zipcode", QQ_ZIPCODE, info->zipcode);
-		add_string_field_to_group(group, "hp_num", QQ_CELL, info->hp_num);
-		add_string_field_to_group(group, "tel", QQ_TELEPHONE, info->tel);
-		add_string_field_to_group(group, "homepage", QQ_HOMEPAGE, info->homepage);
+	for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+		if (field_infos[index].iclass != iclass) {
+			continue;
+		}
+		field_request_new(group, index, segments);
+	}
 
-		group = setup_field_group(fields, QQ_INTRO);
-		field = purple_request_field_string_new("intro", QQ_INTRO, info->intro, TRUE);
-		purple_request_field_group_add_field(group, field);
+	switch (iclass) {
+		case QQ_FIELD_CONTACT:
+			utf8_title = g_strdup(_("Modify Contact"));
+			utf8_prim = g_strdup_printf("%s for %s", _("Modify Contact"), segments[0]);
+		case QQ_FIELD_ADDR:
+			utf8_title = g_strdup(_("Modify Address"));
+			utf8_prim = g_strdup_printf("%s for %s", _("Modify Address"), segments[0]);
+		case QQ_FIELD_EXT:
+			utf8_title = g_strdup(_("Modify Extend Information"));
+			utf8_prim = g_strdup_printf("%s for %s", _("Modify Extend Information"), segments[0]);
+			break;
+		case QQ_FIELD_BASE:
+		default:
+			utf8_title = g_strdup(_("Modify Information"));
+			utf8_prim = g_strdup_printf("%s for %s", _("Modify Information"), segments[0]);
+	}
 
-		/* prepare unmodifiable info */
-		mid = g_new0(modify_info_data, 1);
-		mid->gc = gc;
-		/* QQ_CONTACT_FIELDS+1 so that the array is NULL-terminated and can be g_strfreev()'ed later */
-		mid->info = (contact_info *) g_new0(gchar *, QQ_CONTACT_FIELDS+1);
-		mid->info->pager_sn = g_strdup(info->pager_sn);
-		mid->info->pager_num = g_strdup(info->pager_num);
-		mid->info->pager_sp = g_strdup(info->pager_sp);
-		mid->info->pager_base_num = g_strdup(info->pager_base_num);
-		mid->info->pager_type = g_strdup(info->pager_type);
-		mid->info->auth_type = g_strdup(info->auth_type);
-		mid->info->unknown1 = g_strdup(info->unknown1);
-		mid->info->unknown2 = g_strdup(info->unknown2);
-		mid->info->face = g_strdup(info->face);
-		mid->info->hp_type = g_strdup(info->hp_type);
-		mid->info->unknown3 = g_strdup(info->unknown3);
-		mid->info->unknown4 = g_strdup(info->unknown4);
-		mid->info->unknown5 = g_strdup(info->unknown5);
-		/* TODO stop hiding these 2 */
-		mid->info->is_open_hp = g_strdup(info->is_open_hp);
-		mid->info->is_open_contact = g_strdup(info->is_open_contact);
-		mid->info->qq_show = g_strdup(info->qq_show);
-		mid->info->unknown6 = g_strdup(info->unknown6);
+	info_request = g_new0(modify_info_request, 1);
+	info_request->gc = gc;
+	info_request->iclass = iclass;
+	info_request->segments = segments;
 
-		purple_request_fields(gc, _("Modify information"),
-				_("Modify information"), NULL, fields,
-				_("Update information"), G_CALLBACK(modify_info_ok_cb),
-				_("Cancel"), G_CALLBACK(modify_info_cancel_cb),
-				purple_connection_get_account(gc), NULL, NULL,
-				mid);
-	}
+	purple_request_fields(gc, utf8_title, utf8_prim, NULL,	fields,
+			_("Update"), G_CALLBACK(info_modify_ok_cb),
+			_("Cancel"), G_CALLBACK(info_modify_cancel_cb),
+			purple_connection_get_account(gc), NULL, NULL,
+			info_request);
+
+	g_free(utf8_title);
+	g_free(utf8_prim);
 }
 
 /* process the reply of modify_info packet */
@@ -715,7 +452,7 @@
 	}
 }
 
-static void _qq_send_packet_modify_face(PurpleConnection *gc, gint face_num)
+static void request_set_buddy_icon(PurpleConnection *gc, gint face_num)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurplePresence *presence = purple_account_get_presence(account);
@@ -732,11 +469,10 @@
 	}
 
 	qd->my_icon = 3 * (face_num - 1) + offset;
-	qd->modifying_face = TRUE;
-	qq_send_packet_get_info(gc, qd->uid, FALSE);
+	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_SET_ICON);
 }
 
-void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile)
+static void buddy_local_icon_set(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile)
 {
 	gchar *data;
 	gsize len;
@@ -749,7 +485,7 @@
 }
 
 /* TODO: custom faces for QQ members and users with level >= 16 */
-void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
+void qq_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	gchar *icon;
 	gint icon_num;
@@ -796,13 +532,13 @@
 	}
 	g_free(errmsg);
 	/* tell server my icon changed */
-	_qq_send_packet_modify_face(gc, icon_num);
+	request_set_buddy_icon(gc, icon_num);
 	/* display in blist */
-	qq_set_buddy_icon_for_user(account, account->username, icon, icon_path);
+	buddy_local_icon_set(account, account->username, icon, icon_path);
 }
 
 
-static void _qq_update_buddy_icon(PurpleAccount *account, const gchar *name, gint face)
+static void buddy_local_icon_upate(PurpleAccount *account, const gchar *name, gint face)
 {
 	PurpleBuddy *buddy;
 	gchar *icon_num_str = face_to_icon_str(face);
@@ -820,14 +556,14 @@
 				QQ_ICON_PREFIX, icon_num_str,
 				QQ_ICON_SUFFIX, NULL);
 
-		qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path);
+		buddy_local_icon_set(account, name, icon_num_str, icon_path);
 		g_free(icon_path);
 	}
 	g_free(icon_num_str);
 }
 
 /* after getting info or modify myself, refresh the buddy list accordingly */
-static void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
+static void qq_refresh_buddy_and_myself(gchar **segments, PurpleConnection *gc)
 {
 	PurpleBuddy *b;
 	qq_data *qd;
@@ -837,11 +573,11 @@
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	qd = (qq_data *) gc->proto_data;
-	purple_name = uid_to_purple_name(strtol(info->uid, NULL, 10));
+	purple_name = uid_to_purple_name(strtol(segments[QQ_INFO_UID], NULL, 10));
 
-	alias_utf8 = qq_to_utf8(info->nick, QQ_CHARSET_DEFAULT);
-	if (qd->uid == strtol(info->uid, NULL, 10)) {	/* it is me */
-		qd->my_icon = strtol(info->face, NULL, 10);
+	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 */
+		qd->my_icon = strtol(segments[QQ_INFO_FACE], NULL, 10);
 		if (alias_utf8 != NULL)
 			purple_account_set_alias(account, alias_utf8);
 	}
@@ -849,87 +585,73 @@
 	b = purple_find_buddy(gc->account, purple_name);
 	q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 	if (q_bud != NULL) {	/* I have this buddy */
-		q_bud->age = strtol(info->age, NULL, 10);
-		q_bud->gender = strtol(info->gender, NULL, 10);
-		q_bud->face = strtol(info->face, NULL, 10);
+		q_bud->age = strtol(segments[QQ_INFO_AGE], NULL, 10);
+		q_bud->gender = strtol(segments[QQ_INFO_GENDER], NULL, 10);
+		q_bud->face = strtol(segments[QQ_INFO_FACE], NULL, 10);
 		if (alias_utf8 != NULL)
 			q_bud->nickname = g_strdup(alias_utf8);
 		qq_update_buddy_contact(gc, q_bud);
-		_qq_update_buddy_icon(gc->account, purple_name, q_bud->face);
+		buddy_local_icon_upate(gc->account, purple_name, q_bud->face);
 	}
 	g_free(purple_name);
 	g_free(alias_utf8);
 }
 
 /* process reply to get_info packet */
-void qq_process_get_buddy_info(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_get_buddy_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc)
 {
+	qq_data *qd;
 	gchar **segments;
-	qq_info_query *query;
-	qq_data *qd;
-	contact_info *info;
-	GList *list, *query_list;
-	PurpleNotifyUserInfo *user_info;
 
 	g_return_if_fail(data != NULL && data_len != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	list = query_list = NULL;
-	info = NULL;
 
-	if (NULL == (segments = split_data(data, data_len, "\x1e", QQ_CONTACT_FIELDS)))
+	if (NULL == (segments = split_data(data, data_len, "\x1e", QQ_INFO_LAST)))
 		return;
 
-	info = (contact_info *) segments;
-	if (qd->modifying_face && strtol(info->face, NULL, 10) != qd->my_icon) {
-		gchar *icon = g_strdup_printf("%d", qd->my_icon);
-		qd->modifying_face = FALSE;
-		g_free(info->face);
-		info->face = icon;
-		qq_send_packet_modify_info(gc, (contact_info *)segments);
+#ifdef DEBUG
+	info_debug(segments);
+#endif
+
+	if (action == QQ_BUDDY_INFO_SET_ICON) {
+		if (strtol(segments[QQ_INFO_FACE], NULL, 10) != qd->my_icon) {
+			gchar *icon = g_strdup_printf("%d", qd->my_icon);
+
+			g_free(segments[QQ_INFO_FACE]);
+			segments[QQ_INFO_FACE] = icon;
+
+			request_modify_info(gc, segments);
+			qq_refresh_buddy_and_myself(segments, gc);
+		}
+		g_strfreev(segments);
+		return;
 	}
 
-	qq_refresh_buddy_and_myself(info, gc);
-
-	query_list = qd->info_query;
-	/* ensure we're processing the right query */
-	while (query_list) {
-		query = (qq_info_query *) query_list->data;
-		if (query->uid == atoi(info->uid)) {
-			if (query->show_window) {
-				user_info = info_to_notify_user_info(info);
-				purple_notify_userinfo(gc, info->uid, user_info, NULL, NULL);
-				purple_notify_user_info_destroy(user_info);
-			} else if (query->modify_info) {
-				create_modify_info_dialogue(gc, info);
-			}
-			qd->info_query = g_list_remove(qd->info_query, qd->info_query->data);
-			g_free(query);
+	qq_refresh_buddy_and_myself(segments, gc);
+	switch (action) {
+		case QQ_BUDDY_INFO_DISPLAY:
+			info_display_only(gc, segments);
+			break;
+		case QQ_BUDDY_INFO_SET_ICON:
+			break;
+		case QQ_BUDDY_INFO_MODIFY_BASE:
+			info_modify_dialogue(gc, segments, QQ_FIELD_BASE);
 			break;
-		}
-		query_list = query_list->next;
+		case QQ_BUDDY_INFO_MODIFY_EXT:
+			info_modify_dialogue(gc, segments, QQ_FIELD_EXT);
+			break;
+		case QQ_BUDDY_INFO_MODIFY_ADDR:
+			info_modify_dialogue(gc, segments, QQ_FIELD_ADDR);
+			break;
+		case QQ_BUDDY_INFO_MODIFY_CONTACT:
+			info_modify_dialogue(gc, segments, QQ_FIELD_CONTACT);
+			break;
+		default:
+			g_strfreev(segments);
+			break;
 	}
-
-	g_strfreev(segments);
-}
-
-void qq_info_query_free(qq_data *qd)
-{
-	gint count;
-	qq_info_query *p;
-
-	g_return_if_fail(qd != NULL);
-
-	count = 0;
-	while (qd->info_query != NULL) {
-		p = (qq_info_query *) (qd->info_query->data);
-		qd->info_query = g_list_remove(qd->info_query, p);
-		g_free(p);
-		count++;
-	}
-	if (count > 0) {
-		purple_debug_info("QQ", "%d info queries are freed!\n", count);
-	}
+	return;
 }
 
 void qq_request_get_level(PurpleConnection *gc, guint32 uid)
--- a/libpurple/protocols/qq/buddy_info.h	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Fri Sep 19 14:48:00 2008 +0000
@@ -70,18 +70,19 @@
 enum {
 	QQ_BUDDY_INFO_UPDATE_ONLY = 0,
 	QQ_BUDDY_INFO_DISPLAY,
-	QQ_BUDDY_INFO_MODIFY,
+	QQ_BUDDY_INFO_SET_ICON,
+	QQ_BUDDY_INFO_MODIFY_BASE,
+	QQ_BUDDY_INFO_MODIFY_EXT,
+	QQ_BUDDY_INFO_MODIFY_ADDR,
+	QQ_BUDDY_INFO_MODIFY_CONTACT,
 };
 
-void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window);
 void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
-		gint update_class, guint32 ship32);
-void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
-void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile);
-void qq_prepare_modify_info(PurpleConnection *gc);
+		gint update_class, int action);
+void qq_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
 void qq_process_modify_info_reply(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_process_get_buddy_info(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_info_query_free(qq_data *qd);
+void qq_process_get_buddy_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc);
+
 void qq_request_get_level(PurpleConnection *gc, guint32 uid);
 void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class);
 void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
--- a/libpurple/protocols/qq/buddy_list.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Fri Sep 19 14:48:00 2008 +0000
@@ -311,8 +311,7 @@
 		}
 
 #if 1
-		purple_debug_info("QQ",
-				"buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
+		purple_debug_info("QQ", "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
 				q_bud->uid, q_bud->ext_flag, q_bud->comm_flag, q_bud->nickname);
 #endif
 
@@ -321,7 +320,7 @@
 		g_free(name);
 
 		if (b == NULL) {
-			b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
+			b = qq_create_buddy(gc, q_bud->uid, TRUE, FALSE);
 		}
 
 		b->proto_data = q_bud;
--- a/libpurple/protocols/qq/buddy_opt.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Fri Sep 19 14:48:00 2008 +0000
@@ -26,6 +26,7 @@
 #include "internal.h"
 #include "notify.h"
 #include "request.h"
+#include "privacy.h"
 
 #include "buddy_info.h"
 #include "buddy_list.h"
@@ -52,24 +53,19 @@
 	QQ_MY_AUTH_REQUEST = 0x32,	/* ASCII value of "2" */
 };
 
-typedef struct _qq_add_buddy_request {
-	guint32 uid;
-	guint16 seq;
-} qq_add_buddy_request;
-
 /* send packet to remove a buddy from my buddy list */
-static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid)
+static void request_buddy_remove(PurpleConnection *gc, guint32 uid)
 {
 	gchar uid_str[11];
 
 	g_return_if_fail(uid > 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_DEL_BUDDY, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(gc, QQ_CMD_BUDDY_REMOVE, (guint8 *) uid_str, strlen(uid_str));
 }
 
 /* try to remove myself from someone's buddy list */
-static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid)
+static void request_buddy_remove_me(PurpleConnection *gc, guint32 uid)
 {
 	guint8 raw_data[16] = {0};
 	gint bytes = 0;
@@ -78,31 +74,24 @@
 
 	bytes += qq_put32(raw_data + bytes, uid);
 
-	qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, raw_data, bytes);
+	qq_send_cmd(gc, QQ_CMD_REMOVE_ME, raw_data, bytes);
 }
 
 /* try to add a buddy without authentication */
-static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid)
+static void request_buddy_add_no_auth(PurpleConnection *gc, guint32 uid)
 {
-	qq_data *qd = (qq_data *) gc->proto_data;
-	qq_add_buddy_request *req;
 	gchar uid_str[11];
 
 	g_return_if_fail(uid > 0);
 
 	/* we need to send the ascii code of this uid to qq server */
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
-
-	/* must be set after sending packet to get the correct send_seq */
-	req = g_new0(qq_add_buddy_request, 1);
-	req->seq = qd->send_seq;
-	req->uid = uid;
-	qd->add_buddy_request = g_list_append(qd->add_buddy_request, req);
+	qq_send_cmd_mess(gc, QQ_CMD_BUDDY_ADD_NO_AUTH,
+			(guint8 *) uid_str, strlen(uid_str), 0, uid);
 }
 
 /* this buddy needs authentication, text conversion is done at lowest level */
-static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
+static void request_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
 {
 	gchar *text_qq, uid_str[11];
 	guint8 bar, *raw_data;
@@ -125,130 +114,256 @@
 		g_free(text_qq);
 	}
 
-	qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, raw_data, bytes);
+	qq_send_cmd(gc, QQ_CMD_BUDDY_ADD_AUTH, raw_data, bytes);
 }
 
-static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text)
+static void request_buddy_add_auth_cb(qq_add_request *add_req, const gchar *text)
 {
-	PurpleConnection *gc;
-	guint32 uid;
-	g_return_if_fail(g != NULL);
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		g_free(add_req);
+		return;
+	}
 
-	gc = g->gc;
-	uid = g->uid;
-	g_return_if_fail(uid != 0);
-
-	_qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_REQUEST, text);
-	g_free(g);
+	request_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_REQUEST, text);
+	g_free(add_req);
 }
 
 /* the real packet to reject and request is sent from here */
-static void _qq_reject_add_request_real(gc_and_uid *g, const gchar *reason)
+static void buddy_add_deny_reason_cb(qq_add_request *add_req, const gchar *reason)
 {
-	gint uid;
-	PurpleConnection *gc;
-
-	g_return_if_fail(g != NULL);
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		g_free(add_req);
+		return;
+	}
 
-	gc = g->gc;
-	uid = g->uid;
-	g_return_if_fail(uid != 0);
-
-	_qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_REJECT, reason);
-	g_free(g);
+	request_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_REJECT, reason);
+	g_free(add_req);
 }
 
 /* we approve other's request of adding me as friend */
-void qq_approve_add_request_with_gc_and_uid(gc_and_uid *g)
+static void buddy_add_authorize_cb(qq_add_request *add_req)
 {
-	gint uid;
-	PurpleConnection *gc;
-
-	g_return_if_fail(g != NULL);
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid != 0) {
+		g_free(add_req);
+		return;
+	}
 
-	gc = g->gc;
-	uid = g->uid;
-	g_return_if_fail(uid != 0);
-
-	_qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_APPROVE, NULL);
-	g_free(g);
-}
-
-void qq_do_nothing_with_gc_and_uid(gc_and_uid *g, const gchar *msg)
-{
-	g_free(g);
+	request_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_APPROVE, NULL);
+	g_free(add_req);
 }
 
 /* we reject other's request of adding me as friend */
-void qq_reject_add_request_with_gc_and_uid(gc_and_uid *g)
+static void buddy_add_deny_cb(qq_add_request *add_req)
 {
 	gint uid;
 	gchar *msg1, *msg2;
 	PurpleConnection *gc;
-	gc_and_uid *g2;
-	gchar *nombre;
-
-	g_return_if_fail(g != NULL);
+	gchar *purple_name;
 
-	gc = g->gc;
-	uid = g->uid;
-	g_return_if_fail(uid != 0);
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		g_free(add_req);
+		return;
+	}
 
-	g_free(g);
-
-	g2 = g_new0(gc_and_uid, 1);
-	g2->gc = gc;
-	g2->uid = uid;
+	gc = add_req->gc;
+	uid = add_req->uid;
 
 	msg1 = g_strdup_printf(_("You rejected %d's request"), uid);
 	msg2 = g_strdup(_("Message:"));
 
-	nombre = uid_to_purple_name(uid);
+	purple_name = uid_to_purple_name(uid);
 	purple_request_input(gc, _("Reject request"), msg1, msg2,
 			_("Sorry, you are not my style..."), TRUE, FALSE,
-			NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
-			purple_connection_get_account(gc), nombre, NULL,
-			g2);
-	g_free(nombre);
+			NULL, _("Reject"), G_CALLBACK(buddy_add_deny_reason_cb), _("Cancel"), NULL,
+			purple_connection_get_account(gc), purple_name, NULL,
+			add_req);
+	g_free(purple_name);
+}
+
+/* suggested by rakescar@linuxsir, can still approve after search */
+static void buddy_add_check_info_cb(qq_add_request *add_req)
+{
+	PurpleConnection *gc;
+	guint32 uid;
+	gchar *purple_name;
+
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		g_free(add_req);
+		return;
+	}
+
+	gc = add_req->gc;
+	uid = add_req->uid;
+
+	qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY);
+
+	purple_name = uid_to_purple_name(uid);
+	purple_request_action
+	    (gc, NULL, _("Do you approve the requestion?"), "",
+		PURPLE_DEFAULT_ACTION_NONE,
+		 purple_connection_get_account(gc), purple_name, NULL,
+		 add_req, 2,
+	     _("Reject"), G_CALLBACK(buddy_add_deny_cb),
+	     _("Approve"), G_CALLBACK(buddy_add_authorize_cb));
+	g_free(purple_name);
 }
 
-void qq_add_buddy_with_gc_and_uid(gc_and_uid *g)
+/* add a buddy and send packet to QQ server
+ * note that when purple load local cached buddy list into its blist
+ * it also calls this funtion, so we have to
+ * define qd->is_login=TRUE AFTER serv_finish_login(gc) */
+void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+{
+	qq_data *qd;
+	guint32 uid;
+	PurpleBuddy *b;
+
+	qd = (qq_data *) gc->proto_data;
+	if (!qd->is_login)
+		return;		/* IMPORTANT ! */
+
+	uid = purple_name_to_uid(buddy->name);
+	if (uid > 0) {
+		request_buddy_add_no_auth(gc, uid);
+		return;
+	}
+
+	b = purple_find_buddy(gc->account, buddy->name);
+	if (b != NULL) {
+		purple_blist_remove_buddy(b);
+	}
+	purple_notify_error(gc, NULL, _("QQ Number Error"), _("Invalid QQ Number"));
+}
+
+void qq_change_buddys_group(PurpleConnection *gc, const char *who,
+		const char *old_group, const char *new_group)
 {
 	gint uid;
-	PurpleConnection *gc;
+	g_return_if_fail(who != NULL);
 
-	g_return_if_fail(g != NULL);
+	if (strcmp(new_group, PURPLE_GROUP_QQ_UNKNOWN) == 0) {
+		if (purple_privacy_check(gc->account, who)) {
+			purple_privacy_deny(gc->account, who, TRUE, FALSE);
+		} else {
+			purple_privacy_deny_add(gc->account, who, TRUE);
+		}
+		return;
+	}
 
-	gc = g->gc;
-	uid = g->uid;
+	if (strcmp(old_group, PURPLE_GROUP_QQ_UNKNOWN) != 0) {
+		return;
+	}
+
+	uid = purple_name_to_uid(who);
 	g_return_if_fail(uid != 0);
 
-	_qq_send_packet_add_buddy(gc, uid);
-	g_free(g);
+	purple_privacy_deny_remove(gc->account, who, TRUE);
+
+	purple_debug_info("QQ", "Add unknow buddy %d\n", uid);
+	request_buddy_add_no_auth(gc, uid);
+}
+
+static void buddy_cancel_cb(qq_add_request *add_req, const gchar *msg)
+{
+	g_return_if_fail(add_req != NULL);
+	g_free(add_req);
+}
+
+static void buddy_add_no_auth_cb(qq_add_request *add_req)
+{
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		g_free(add_req);
+		return;
+	}
+
+	request_buddy_add_no_auth(add_req->gc, add_req->uid);
+	g_free(add_req);
 }
 
-void qq_block_buddy_with_gc_and_uid(gc_and_uid *g)
+static void buddy_remove_both_cb(qq_add_request *add_req)
 {
-	guint32 uid;
 	PurpleConnection *gc;
-	PurpleBuddy buddy;
-	PurpleGroup group;
+	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) {
+		g_free(add_req);
+		return;
+	}
+
+	gc = add_req->gc;
+	qd = (qq_data *) gc->proto_data;
+
+	request_buddy_remove(gc, add_req->uid);
+	request_buddy_remove_me(gc, add_req->uid);
+
+	purple_name = uid_to_purple_name(add_req->uid);
+	buddy = purple_find_buddy(gc->account, purple_name);
+	if (buddy == NULL) {
+		g_free(add_req);
+		return;
+	}
 
-	g_return_if_fail(g != NULL);
+	q_buddy = (qq_buddy *) buddy->proto_data;
+	if (q_buddy != NULL)
+		qd->buddies = g_list_remove(qd->buddies, q_buddy);
+	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);
+}
 
-	gc = g->gc;
-	uid = g->uid;
+/* remove a buddy from my list and remove myself from his list */
+/* TODO: re-enable this */
+void qq_remove_buddy_and_me(PurpleBlistNode * node)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	guint32 uid;
+	qq_add_request *add_req;
+	PurpleBuddy *buddy;
+	const gchar *who;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *) node;
+	gc = purple_account_get_connection(buddy->account);
+	qd = (qq_data *) gc->proto_data;
+	if (!qd->is_login)
+		return;
+
+	who = buddy->name;
+	g_return_if_fail(who != NULL);
+
+	uid = purple_name_to_uid(who);
 	g_return_if_fail(uid > 0);
 
-	buddy.name = uid_to_purple_name(uid);
-	group.name = PURPLE_GROUP_QQ_BLOCKED;
+	add_req = g_new0(qq_add_request, 1);
+	add_req->gc = gc;
+	add_req->uid = uid;
 
-	qq_remove_buddy(gc, &buddy, &group);
-	_qq_send_packet_remove_self_from(gc, uid);
+	purple_request_action(gc, _("Block Buddy"),
+			    "Are you sure you want to block this buddy?",
+			    NULL,
+			    1,
+				purple_connection_get_account(gc), NULL, NULL,
+				add_req, 2,
+			    _("Cancel"), G_CALLBACK(buddy_cancel_cb),
+				_("Block"), G_CALLBACK(buddy_remove_both_cb));
 }
 
 /*  process reply to add_buddy_auth request */
-void qq_process_add_buddy_auth_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_buddy_add_auth(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gchar **segments, *msg_utf8;
@@ -271,7 +386,7 @@
 }
 
 /* process the server reply for my request to remove a buddy */
-void qq_process_remove_buddy_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_buddy_remove(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 
@@ -291,7 +406,7 @@
 }
 
 /* process the server reply for my request to remove myself from a buddy */
-void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_buddy_remove_me(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 
@@ -302,91 +417,75 @@
 	if (data[0] != QQ_REMOVE_SELF_REPLY_OK) {
 		/* there is no reason return from server */
 		purple_debug_warning("QQ", "Remove self fails\n");
-		purple_notify_info(gc, _("QQ Buddy"), _("Failed:"), _("Remove from other's buddy list"));
+		purple_notify_info(gc, _("QQ Buddy"), _("Failed:"), _("Remove me from other's buddy list"));
 	} else {		/* if reply */
 		purple_debug_info("QQ", "Remove from a buddy OK\n");
-		/* TODO: Does the user really need to be notified about this? */
+#if 0
 		purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), _("Remove from other's buddy list"));
+#endif
 	}
 }
 
-void qq_process_add_buddy_reply(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
+void qq_process_buddy_add_no_auth(guint8 *data, gint data_len, guint32 uid, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint for_uid;
-	gchar *msg, **segments, *uid, *reply;
-	GList *list;
+	gchar *msg, **segments, *dest_uid, *reply;
 	PurpleBuddy *b;
-	gc_and_uid *g;
-	qq_add_buddy_request *req;
+	qq_add_request *add_req;
 	gchar *nombre;
 
 	g_return_if_fail(data != NULL && data_len != 0);
 
-	for_uid = 0;
 	qd = (qq_data *) gc->proto_data;
 
-	list = qd->add_buddy_request;
-	while (list != NULL) {
-		req = (qq_add_buddy_request *) list->data;
-		if (req->seq == seq) {	/* reply to this */
-			for_uid = req->uid;
-			qd->add_buddy_request = g_list_remove(qd->add_buddy_request, qd->add_buddy_request->data);
-			g_free(req);
-			break;
-		}
-		list = list->next;
-	}
-
-	if (for_uid == 0) {	/* we have no record for this */
-		purple_debug_error("QQ", "We have no record for add buddy reply [%d], discard\n", seq);
+	if (uid == 0) {	/* we have no record for this */
+		purple_debug_error("QQ", "Process buddy add, unknow id\n");
 		return;
 	} else {
-		purple_debug_info("QQ", "Add buddy reply [%d] is for id [%d]\n", seq, for_uid);
+		purple_debug_info("QQ", "Process buddy add for id [%d]\n", uid);
 	}
 
 	if (NULL == (segments = split_data(data, data_len, "\x1f", 2)))
 		return;
 
-	uid = segments[0];
+	dest_uid = segments[0];
 	reply = segments[1];
-	if (strtol(uid, NULL, 10) != qd->uid) {	/* should not happen */
-		purple_debug_error("QQ", "Add buddy reply is to [%s], not me!", uid);
+	if (strtol(dest_uid, NULL, 10) != qd->uid) {	/* should not happen */
+		purple_debug_error("QQ", "Add buddy reply is to [%s], not me!", dest_uid);
 		g_strfreev(segments);
 		return;
 	}
 
 	if (strtol(reply, NULL, 10) > 0) {	/* need auth */
 		purple_debug_warning("QQ", "Add buddy attempt fails, need authentication\n");
-		nombre = uid_to_purple_name(for_uid);
+		nombre = uid_to_purple_name(uid);
 		b = purple_find_buddy(gc->account, nombre);
 		if (b != NULL)
 			purple_blist_remove_buddy(b);
-		g = g_new0(gc_and_uid, 1);
-		g->gc = gc;
-		g->uid = for_uid;
-		msg = g_strdup_printf(_("%d needs authentication"), for_uid);
+		add_req = g_new0(qq_add_request, 1);
+		add_req->gc = gc;
+		add_req->uid = uid;
+		msg = g_strdup_printf(_("%d needs authentication"), uid);
 		purple_request_input(gc, NULL, msg,
 				_("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
 				_("Would you be my friend?"),
 				TRUE, FALSE, NULL, _("Send"),
-				G_CALLBACK
-				(_qq_send_packet_add_buddy_auth_with_gc_and_uid),
-				_("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
+				G_CALLBACK(request_buddy_add_auth_cb),
+				_("Cancel"), G_CALLBACK(buddy_cancel_cb),
 				purple_connection_get_account(gc), nombre, NULL,
-				g);
+				add_req);
 		g_free(msg);
 		g_free(nombre);
 	} else {	/* add OK */
-		qq_add_buddy_by_recv_packet(gc, for_uid, TRUE, TRUE);
-		msg = g_strdup_printf(_("Add into %d's buddy list"), for_uid);
+		qq_create_buddy(gc, uid, TRUE, TRUE);
+		msg = g_strdup_printf(_("Add into %d's buddy list"), uid);
 		purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), msg);
 		g_free(msg);
 	}
 	g_strfreev(segments);
 }
 
-PurpleGroup *qq_get_purple_group(const gchar *group_name)
+PurpleGroup *qq_create_group(const gchar *group_name)
 {
 	PurpleGroup *g;
 
@@ -404,131 +503,93 @@
 
 /* we add new buddy, if the received packet is from someone not in my list
  * return the PurpleBuddy that is just created */
-PurpleBuddy *qq_add_buddy_by_recv_packet(PurpleConnection *gc, guint32 uid, gboolean is_known, gboolean create)
+PurpleBuddy *qq_create_buddy(PurpleConnection *gc, guint32 uid, gboolean is_known, gboolean create)
 {
-	PurpleAccount *a;
-	PurpleBuddy *b;
-	PurpleGroup *g;
+	PurpleBuddy *buddy;
+	PurpleGroup *group;
 	qq_data *qd;
 	qq_buddy *q_bud;
-	gchar *name, *group_name;
+	gchar *buddy_name, *group_name;
 
-	a = gc->account;
+	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
 	qd = (qq_data *) gc->proto_data;
-	g_return_val_if_fail(a != NULL && uid != 0, NULL);
 
-	group_name = is_known ?
-		g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
+	if (is_known) {
+		group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT,
+				purple_account_get_username(gc->account));
+	 } else {
+	 	group_name = g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
+	}
 
-	g = qq_get_purple_group(group_name);
+	group = qq_create_group(group_name);
 
-	name = uid_to_purple_name(uid);
-	b = purple_find_buddy(gc->account, name);
+	buddy_name = uid_to_purple_name(uid);
+	buddy = purple_find_buddy(gc->account, buddy_name);
 	/* remove old, we can not simply return here
 	 * because there might be old local copy of this buddy */
-	if (b != NULL)
-		purple_blist_remove_buddy(b);
+	if (buddy != NULL)
+		purple_blist_remove_buddy(buddy);
 
-	b = purple_buddy_new(a, name, NULL);
+	buddy = purple_buddy_new(gc->account, buddy_name, NULL);
+	if ( !is_known ) {
+		if (purple_privacy_check(gc->account, buddy_name)) {
+			purple_privacy_deny(gc->account, buddy_name, TRUE, FALSE);
+		} else {
+			purple_privacy_deny_add(gc->account, buddy_name, TRUE);
+		}
+	}
 
 	if (!create)
-		b->proto_data = NULL;
+		buddy->proto_data = NULL;
 	else {
 		q_bud = g_new0(qq_buddy, 1);
 		q_bud->uid = uid;
-		b->proto_data = q_bud;
+		buddy->proto_data = q_bud;
 		qd->buddies = g_list_append(qd->buddies, q_bud);
-		qq_send_packet_get_info(gc, q_bud->uid, FALSE);
+		qq_request_buddy_info(gc, qd->uid, 0, 0);
 		qq_request_get_buddies_online(gc, 0, 0);
+		qq_request_get_level(gc, qd->uid);
 	}
 
-	purple_blist_add_buddy(b, NULL, g, NULL);
-	purple_debug_warning("QQ", "Add new buddy: [%s]\n", name);
+	purple_blist_add_buddy(buddy, NULL, group, NULL);
+	purple_debug_warning("QQ", "Add new buddy: [%s]\n", buddy_name);
 
-	g_free(name);
+	g_free(buddy_name);
 	g_free(group_name);
 
-	return b;
-}
-
-/* add a buddy and send packet to QQ server
- * note that when purple load local cached buddy list into its blist
- * it also calls this funtion, so we have to
- * define qd->is_login=TRUE AFTER serv_finish_login(gc) */
-void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
-{
-	qq_data *qd;
-	guint32 uid;
-	PurpleBuddy *b;
-
-	qd = (qq_data *) gc->proto_data;
-	if (!qd->is_login)
-		return;		/* IMPORTANT ! */
-
-	uid = purple_name_to_uid(buddy->name);
-	if (uid > 0)
-		_qq_send_packet_add_buddy(gc, uid);
-	else {
-		b = purple_find_buddy(gc->account, buddy->name);
-		if (b != NULL)
-			purple_blist_remove_buddy(b);
-		purple_notify_error(gc, NULL,
-				_("QQ Number Error"),
-				_("Invalid QQ Number"));
-	}
+	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;
-	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	guint32 uid;
 
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	g_return_if_fail(buddy != NULL);
 	qd = (qq_data *) gc->proto_data;
+
 	uid = purple_name_to_uid(buddy->name);
-
 	if (!qd->is_login)
 		return;
 
-	if (uid > 0)
-		_qq_send_packet_remove_buddy(gc, uid);
+	if (uid > 0) {
+		request_buddy_remove(gc, uid);
+	}
 
-	b = purple_find_buddy(gc->account, buddy->name);
-	if (b != NULL) {
-		q_bud = (qq_buddy *) b->proto_data;
-		if (q_bud != NULL)
-			qd->buddies = g_list_remove(qd->buddies, q_bud);
-		else
-			purple_debug_warning("QQ", "We have no qq_buddy record for %s\n", buddy->name);
-		/* remove buddy on blist, this does not trigger qq_remove_buddy again
-		 * do this only if the request comes from block request,
-		 * otherwise purple segmentation fault */
-		if (g_ascii_strcasecmp(group->name, PURPLE_GROUP_QQ_BLOCKED) == 0)
-			purple_blist_remove_buddy(b);
-	}
+	q_bud = (qq_buddy *) buddy->proto_data;
+	if (q_bud != NULL)
+		qd->buddies = g_list_remove(qd->buddies, q_bud);
+	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 add buddy request queue */
-void qq_add_buddy_request_free(qq_data *qd)
-{
-	gint count;
-	qq_add_buddy_request *p;
-
-	count = 0;
-	while (qd->add_buddy_request != NULL) {
-		p = (qq_add_buddy_request *) (qd->add_buddy_request->data);
-		qd->add_buddy_request = g_list_remove(qd->add_buddy_request, p);
-		g_free(p);
-		count++;
-	}
-	if (count > 0) {
-		purple_debug_info("QQ", "%d add buddy requests are freed!\n", count);
-	}
-}
-
-/* free up all qq_buddy */
+/* free all qq_buddy */
 void qq_buddies_list_free(PurpleAccount *account, qq_data *qd)
 {
 	gint count;
@@ -555,3 +616,149 @@
 		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)
+{
+	gchar *message, *reason;
+	guint32 uid;
+	qq_add_request *g, *g2;
+	PurpleBuddy *b;
+	gchar *name;
+
+	g_return_if_fail(from != NULL && to != NULL);
+
+	uid = strtol(from, NULL, 10);
+	g = g_new0(qq_add_request, 1);
+	g->gc = gc;
+	g->uid = uid;
+
+	name = uid_to_purple_name(uid);
+
+	/* TODO: this should go through purple_account_request_authorization() */
+	message = g_strdup_printf(_("%s wants to add you [%s] as a friend"), from, to);
+	reason = g_strdup_printf(_("Message: %s"), msg_utf8);
+
+	purple_request_action
+	    (gc, NULL, message, reason, PURPLE_DEFAULT_ACTION_NONE,
+		purple_connection_get_account(gc), name, NULL,
+		 g, 3,
+	     _("Reject"),
+	     G_CALLBACK(buddy_add_deny_cb),
+	     _("Approve"),
+	     G_CALLBACK(buddy_add_authorize_cb),
+	     _("Search"), G_CALLBACK(buddy_add_check_info_cb));
+
+	g_free(message);
+	g_free(reason);
+
+	/* XXX: Is this needed once the above goes through purple_account_request_authorization()? */
+	b = purple_find_buddy(gc->account, name);
+	if (b == NULL) {	/* the person is not in my list  */
+		g2 = g_new0(qq_add_request, 1);
+		g2->gc = gc;
+		g2->uid = strtol(from, NULL, 10);
+		message = g_strdup_printf(_("%s is not in buddy list"), from);
+		purple_request_action(gc, NULL, message,
+				    _("Would you add?"), PURPLE_DEFAULT_ACTION_NONE,
+					purple_connection_get_account(gc), name, NULL,
+					g2, 3,
+					_("Cancel"), NULL,
+					_("Add"), G_CALLBACK(buddy_add_no_auth_cb),
+				    _("Search"), G_CALLBACK(buddy_add_check_info_cb));
+		g_free(message);
+	}
+
+	g_free(name);
+}
+
+/* when you are added by a person, QQ server will send sys message */
+static void server_buddy_added(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+{
+	gchar *message;
+	PurpleBuddy *b;
+	guint32 uid;
+	qq_add_request *add_req;
+	gchar *name;
+
+	g_return_if_fail(from != NULL && to != NULL);
+
+	uid = strtol(from, NULL, 10);
+	name = uid_to_purple_name(uid);
+	b = purple_find_buddy(gc->account, name);
+
+	if (b == NULL) {	/* the person is not in my list */
+		add_req = g_new0(qq_add_request, 1);
+		add_req->gc = gc;
+		add_req->uid = uid;	/* only need to get value */
+		message = g_strdup_printf(_("You have been added by %s"), from);
+		purple_request_action(gc, NULL, message,
+				    _("Would you like to add him?"),
+					PURPLE_DEFAULT_ACTION_NONE,
+					purple_connection_get_account(gc), name, NULL,
+					add_req, 3,
+				    _("Cancel"), G_CALLBACK(buddy_cancel_cb),
+					_("Add"), G_CALLBACK(buddy_add_no_auth_cb),
+				    _("Search"), G_CALLBACK(buddy_add_check_info_cb));
+	} else {
+		message = g_strdup_printf(_("%s added you [%s] to buddy list"), from, to);
+		purple_notify_info(gc, _("QQ Budy"), _("Successed:"), message);
+	}
+
+	g_free(name);
+	g_free(message);
+}
+
+/* the buddy approves your request of adding him/her as your friend */
+static void server_buddy_added_me(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+{
+	gchar *message;
+	qq_data *qd;
+
+	g_return_if_fail(from != NULL && to != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+	qq_create_buddy(gc, strtol(from, NULL, 10), TRUE, TRUE);
+
+	message = g_strdup_printf(_("Requestion approved by %s"), from);
+	purple_notify_info(gc, _("QQ Buddy"), _("Notice:"), message);
+
+	g_free(message);
+}
+
+/* you are rejected by the person */
+static void server_buddy_rejected_me(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+{
+	gchar *message, *reason;
+
+	g_return_if_fail(from != NULL && to != NULL);
+
+	message = g_strdup_printf(_("Requestion rejected by %s"), from);
+	reason = g_strdup_printf(_("Message: %s"), msg_utf8);
+
+	purple_notify_info(gc, _("QQ Buddy"), message, reason);
+	g_free(message);
+	g_free(reason);
+}
+
+void qq_process_buddy_from_server(PurpleConnection *gc, int funct,
+		gchar *from, gchar *to, gchar *msg_utf8)
+{
+	switch (funct) {
+	case QQ_SERVER_BUDDY_ADDED:
+		server_buddy_added(gc, from, to, msg_utf8);
+		break;
+	case QQ_SERVER_BUDDY_ADD_REQUEST:
+		server_buddy_add_request(gc, from, to, msg_utf8);
+		break;
+	case QQ_SERVER_BUDDY_ADDED_ME:
+		server_buddy_added_me(gc, from, to, msg_utf8);
+		break;
+	case QQ_SERVER_BUDDY_REJECTED_ME:
+		server_buddy_rejected_me(gc, from, to, msg_utf8);
+		break;
+	default:
+		purple_debug_warning("QQ", "Unknow buddy operate (%d) from server\n", funct);
+		break;
+	}
+}
--- a/libpurple/protocols/qq/buddy_opt.h	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.h	Fri Sep 19 14:48:00 2008 +0000
@@ -30,33 +30,21 @@
 
 #include "qq.h"
 
-typedef struct _gc_and_uid gc_and_uid;
-
-struct _gc_and_uid {
-	guint32 uid;
-	PurpleConnection *gc;
-};
-
-void qq_approve_add_request_with_gc_and_uid(gc_and_uid *g);
-void qq_reject_add_request_with_gc_and_uid(gc_and_uid *g);
-
-void qq_add_buddy_with_gc_and_uid(gc_and_uid *g);
-void qq_block_buddy_with_gc_and_uid(gc_and_uid *g);
-
-void qq_do_nothing_with_gc_and_uid(gc_and_uid *g, const gchar *msg);
-
-void qq_process_remove_buddy_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_process_add_buddy_reply(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
-void qq_process_add_buddy_auth_reply(guint8 *data, gint data_len, PurpleConnection *gc);
-PurpleBuddy *qq_add_buddy_by_recv_packet(PurpleConnection *gc, guint32 uid, gboolean is_known, gboolean create);
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
-
-PurpleGroup *qq_get_purple_group(const gchar *group_name);
-
+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);
-void qq_add_buddy_request_free(qq_data *qd);
-
+PurpleBuddy *qq_create_buddy(PurpleConnection *gc, guint32 uid, gboolean is_known, gboolean create);
 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);
+void qq_process_buddy_add_no_auth(guint8 *data, gint data_len, guint32 uid, PurpleConnection *gc);
+void qq_process_buddy_add_auth(guint8 *data, gint data_len, PurpleConnection *gc);
+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);
+
 #endif
--- a/libpurple/protocols/qq/group.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Fri Sep 19 14:48:00 2008 +0000
@@ -34,7 +34,7 @@
 #include "utils.h"
 #include "qq_network.h"
 #include "header_info.h"
-#include "group.h"
+#include "group_free.h"
 
 static void _qq_group_search_callback(PurpleConnection *gc, const gchar *input)
 {
--- a/libpurple/protocols/qq/group_internal.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Fri Sep 19 14:48:00 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_get_purple_group(PURPLE_GROUP_QQ_QUN);
+	g = qq_create_group(PURPLE_GROUP_QQ_QUN);
 	purple_blist_add_chat(chat, g, NULL);
 	purple_debug_info("QQ", "You have added group \"%s\" to blist locally\n", group->title_utf8);
 }
--- a/libpurple/protocols/qq/group_join.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Fri Sep 19 14:48:00 2008 +0000
@@ -29,11 +29,9 @@
 #include "request.h"
 #include "server.h"
 
-#include "buddy_opt.h"
 #include "char_conv.h"
 #include "group_conv.h"
 #include "group_find.h"
-#include "group_free.h"
 #include "group_internal.h"
 #include "group_info.h"
 #include "group_join.h"
@@ -51,19 +49,28 @@
 	QQ_ROOM_JOIN_DENIED = 0x03,
 };
 
-static void _qq_group_exit_with_gc_and_id(gc_and_uid *g)
+static void group_quit_cb(qq_add_request *add_req)
 {
 	PurpleConnection *gc;
 	guint32 id;
 	qq_group *group;
 
-	gc = g->gc;
-	id = g->uid;
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		g_free(add_req);
+		return;
+	}
+
+	gc = add_req->gc;
+	id = add_req->uid;
 
 	group = qq_room_search_id(gc, id);
-	g_return_if_fail(group != NULL);
+	if (group == NULL) {
+		g_free(add_req);
+		return;
+	}
 
 	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, group->id);
+	g_free(add_req);
 }
 
 /* send packet to join a group without auth */
@@ -95,44 +102,53 @@
 	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_JOIN, group->id);
 }
 
-static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8)
+static void group_join_cb(qq_add_request *add_req, const gchar *reason_utf8)
 {
-	PurpleConnection *gc;
 	qq_group *group;
-	guint32 id;
+
+	g_return_if_fail(add_req != NULL);
+	if (add_req->gc == NULL || add_req->uid == 0) {
+		g_free(add_req);
+		return;
+	}
 
-	gc = g->gc;
-	id = g->uid;
-
-	group = qq_room_search_id(gc, id);
+	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", id);
+		purple_debug_error("QQ", "Can not find qq_group by internal_id: %d\n", add_req->uid);
+		g_free(add_req);
 		return;
-	} else {		/* everything is OK */
-		qq_send_cmd_group_auth(gc, group, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
 	}
+
+	qq_send_cmd_group_auth(add_req->gc, group, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
+	g_free(add_req);
+}
+
+void qq_group_cancel_cb(qq_add_request *add_req, const gchar *msg)
+{
+	g_return_if_fail(add_req != NULL);
+	g_free(add_req);
 }
 
 static void _qq_group_join_auth(PurpleConnection *gc, qq_group *group)
 {
 	gchar *msg;
-	gc_and_uid *g;
+	qq_add_request *add_req;
 	g_return_if_fail(group != NULL);
 
 	purple_debug_info("QQ", "Group (internal id: %d) needs authentication\n", group->id);
 
 	msg = g_strdup_printf("Group \"%s\" needs authentication\n", group->title_utf8);
-	g = g_new0(gc_and_uid, 1);
-	g->gc = gc;
-	g->uid = group->id;
+	add_req = g_new0(qq_add_request, 1);
+	add_req->gc = gc;
+	add_req->uid = group->id;
 	purple_request_input(gc, NULL, msg,
 			   _("Input request here"),
 			   _("Would you be my friend?"), TRUE, FALSE, NULL,
 			   _("Send"),
-			   G_CALLBACK(_qq_group_join_auth_with_gc_and_id),
-			   _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
+			   G_CALLBACK(group_join_cb),
+			   _("Cancel"), G_CALLBACK(qq_group_cancel_cb),
 			   purple_connection_get_account(gc), group->title_utf8, NULL,
-			   g);
+			   add_req);
 	g_free(msg);
 }
 
@@ -308,7 +324,7 @@
 {
 	gchar *id_ptr;
 	guint32 id;
-	gc_and_uid *g;
+	qq_add_request *add_req;
 
 	g_return_if_fail(data != NULL);
 
@@ -317,16 +333,16 @@
 
 	g_return_if_fail(id > 0);
 
-	g = g_new0(gc_and_uid, 1);
-	g->gc = gc;
-	g->uid = id;
+	add_req = g_new0(qq_add_request, 1);
+	add_req->gc = gc;
+	add_req->uid = id;
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("Are you sure you want to leave this Qun?"),
 			    _("Note, if you are the creator, \nthis operation will eventually remove this Qun."),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
-			    g, 2, _("Cancel"),
-			    G_CALLBACK(qq_do_nothing_with_gc_and_uid),
-			    _("Continue"), G_CALLBACK(_qq_group_exit_with_gc_and_id));
+			    add_req, 2, _("Cancel"),
+			    G_CALLBACK(qq_group_cancel_cb),
+			    _("Continue"), G_CALLBACK(group_quit_cb));
 }
--- a/libpurple/protocols/qq/group_join.h	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/group_join.h	Fri Sep 19 14:48:00 2008 +0000
@@ -49,4 +49,5 @@
 void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc);
 void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc);
 
+void qq_group_cancel_cb(qq_add_request *add_req, const gchar *msg);
 #endif
--- a/libpurple/protocols/qq/group_opt.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Fri Sep 19 14:48:00 2008 +0000
@@ -97,7 +97,7 @@
 {
 	g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0);
 
-	qq_send_packet_get_info(g->gc, g->member, TRUE);	/* we want to see window */
+	qq_request_buddy_info(g->gc, g->member, 0, QQ_BUDDY_INFO_DISPLAY);
 	purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "",
 				PURPLE_DEFAULT_ACTION_NONE,
 				purple_connection_get_account(g->gc), NULL, NULL,
@@ -322,17 +322,24 @@
 	qq_send_room_cmd_noid(gc, QQ_ROOM_CMD_CREATE, data, bytes);
 }
 
-static void qq_group_setup_with_gc_and_uid(gc_and_uid *g)
+static void qq_group_setup_cb(qq_add_request *add_req)
 {
 	qq_group *group;
-	g_return_if_fail(g != NULL && g->gc != NULL && g->uid > 0);
+	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(g->gc, g->uid);
-	g_return_if_fail(group != NULL);
+	group = qq_room_search_id(add_req->gc, add_req->uid);
+	if (group == NULL) {
+		g_free(add_req);
+		return;
+	}
 
 	/* TODO insert UI code here */
 	/* qq_group_detail_window_show(g->gc, group); */
-	g_free(g);
+	g_free(add_req);
 }
 
 void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc)
@@ -340,7 +347,7 @@
 	gint bytes;
 	guint32 id, ext_id;
 	qq_group *group;
-	gc_and_uid *g;
+	qq_add_request *add_req;
 	qq_data *qd;
 
 	g_return_if_fail(data != NULL);
@@ -362,19 +369,18 @@
 
 	purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", group->ext_id);
 
-	g = g_new0(gc_and_uid, 1);
-	g->gc = gc;
-	g->uid = id;
+	add_req = g_new0(qq_add_request, 1);
+	add_req->gc = gc;
+	add_req->uid = id;
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("You have successfully created a Qun"),
-			    _
-			    ("Would you like to set up the detail information now?"),
+			    _("Would you like to set up the detail information now?"),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
-				g, 2,
-				_("Setup"), G_CALLBACK(qq_group_setup_with_gc_and_uid),
-			    _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid));
+				add_req, 2,
+				_("Setup"), G_CALLBACK(qq_group_setup_cb),
+			    _("Cancel"), G_CALLBACK(qq_group_cancel_cb));
 }
 
 void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc)
--- a/libpurple/protocols/qq/header_info.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/header_info.c	Fri Sep 19 14:48:00 2008 +0000
@@ -140,12 +140,12 @@
 		return "QQ_CMD_SEARCH_USER";
 	case QQ_CMD_GET_BUDDY_INFO:
 		return "QQ_CMD_GET_BUDDY_INFO";
-	case QQ_CMD_ADD_BUDDY_WO_AUTH:
-		return "QQ_CMD_ADD_BUDDY_WO_AUTH";
-	case QQ_CMD_DEL_BUDDY:
-		return "QQ_CMD_DEL_BUDDY";
-	case QQ_CMD_BUDDY_AUTH:
-		return "QQ_CMD_BUDDY_AUTH";
+	case QQ_CMD_BUDDY_ADD_NO_AUTH:
+		return "QQ_CMD_BUDDY_ADD_NO_AUTH";
+	case QQ_CMD_BUDDY_REMOVE:
+		return "QQ_CMD_BUDDY_REMOVE";
+	case QQ_CMD_BUDDY_ADD_AUTH:
+		return "QQ_CMD_BUDDY_ADD_AUTH";
 	case QQ_CMD_CHANGE_STATUS:
 		return "QQ_CMD_CHANGE_STATUS";
 	case QQ_CMD_ACK_SYS_MSG:
@@ -154,8 +154,8 @@
 		return "QQ_CMD_SEND_IM";
 	case QQ_CMD_RECV_IM:
 		return "QQ_CMD_RECV_IM";
-	case QQ_CMD_REMOVE_SELF:
-		return "QQ_CMD_REMOVE_SELF";
+	case QQ_CMD_REMOVE_ME:
+		return "QQ_CMD_REMOVE_ME";
 	case QQ_CMD_LOGIN:
 		return "QQ_CMD_LOGIN";
 	case QQ_CMD_GET_BUDDIES_LIST:
--- a/libpurple/protocols/qq/header_info.h	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/header_info.h	Fri Sep 19 14:48:00 2008 +0000
@@ -44,14 +44,14 @@
 	QQ_CMD_UPDATE_INFO = 0x0004,			/* update information */
 	QQ_CMD_SEARCH_USER = 0x0005,			/* search for user */
 	QQ_CMD_GET_BUDDY_INFO = 0x0006,			/* get user information */
-	QQ_CMD_ADD_BUDDY_WO_AUTH = 0x0009,		/* add buddy without auth */
-	QQ_CMD_DEL_BUDDY = 0x000a,			/* delete a buddy  */
-	QQ_CMD_BUDDY_AUTH = 0x000b,			/* buddy authentication */
+	QQ_CMD_BUDDY_ADD_NO_AUTH = 0x0009,		/* add buddy without auth */
+	QQ_CMD_BUDDY_REMOVE = 0x000a,			/* delete a buddy  */
+	QQ_CMD_BUDDY_ADD_AUTH = 0x000b,			/* buddy authentication */
 	QQ_CMD_CHANGE_STATUS = 0x000d,		/* change my online status */
 	QQ_CMD_ACK_SYS_MSG = 0x0012,			/* ack system message */
 	QQ_CMD_SEND_IM = 0x0016,			/* send message */
 	QQ_CMD_RECV_IM = 0x0017,			/* receive message */
-	QQ_CMD_REMOVE_SELF = 0x001c,			/* remove self */
+	QQ_CMD_REMOVE_ME = 0x001c,			/* remove self */
 	QQ_CMD_REQUEST_KEY = 0x001d,			/* request key for file transfer */
 	QQ_CMD_CELL_PHONE_1 = 0x0021,			/* cell phone 1 */
 	QQ_CMD_LOGIN = 0x0022,				/* login */
@@ -98,4 +98,12 @@
 
 const gchar *qq_get_room_cmd_desc(gint room_cmd);
 
+enum {
+	QQ_SERVER_BUDDY_ADDED = 0x01,
+	QQ_SERVER_BUDDY_ADD_REQUEST = 0x02,
+	QQ_SERVER_BUDDY_ADDED_ME = 0x03,
+	QQ_SERVER_BUDDY_REJECTED_ME = 0x04,
+	QQ_SERVER_NOTICE= 0x06,
+	QQ_SERVER_NEW_CLIENT = 0x09
+};
 #endif
--- a/libpurple/protocols/qq/im.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Fri Sep 19 14:48:00 2008 +0000
@@ -355,7 +355,7 @@
 	name = uid_to_purple_name(common->sender_uid);
 	b = purple_find_buddy(gc->account, name);
 	if (b == NULL) {
-		qq_add_buddy_by_recv_packet(gc, common->sender_uid, FALSE, TRUE);
+		qq_create_buddy(gc, common->sender_uid, FALSE, TRUE);
 		b = purple_find_buddy(gc->account, name);
 	}
 	qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
--- a/libpurple/protocols/qq/packet_parse.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.c	Fri Sep 19 14:48:00 2008 +0000
@@ -38,7 +38,7 @@
 #define PARSER_DEBUG
 #endif
 
-/* read one byte from buf, 
+/* read one byte from buf,
  * return the number of bytes read if succeeds, otherwise return -1 */
 gint qq_get8(guint8 *b, guint8 *buf)
 {
@@ -53,7 +53,7 @@
 }
 
 
-/* read two bytes as "guint16" from buf, 
+/* read two bytes as "guint16" from buf,
  * return the number of bytes read if succeeds, otherwise return -1 */
 gint qq_get16(guint16 *w, guint8 *buf)
 {
@@ -67,7 +67,7 @@
 	return sizeof(w_dest);
 }
 
-/* read four bytes as "guint32" from buf, 
+/* read four bytes as "guint32" from buf,
  * return the number of bytes read if succeeds, otherwise return -1 */
 gint qq_get32(guint32 *dw, guint8 *buf)
 {
@@ -87,7 +87,7 @@
 	return sizeof(struct in_addr);
 }
 
-/* read datalen bytes from buf, 
+/* read datalen bytes from buf,
  * return the number of bytes read if succeeds, otherwise return -1 */
 gint qq_getdata(guint8 *data, gint datalen, guint8 *buf)
 {
@@ -171,7 +171,7 @@
  * return the number of bytes packed, otherwise return -1 */
 gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen)
 {
-    memcpy(buf, data, datalen);
+   	memcpy(buf, data, datalen);
 #ifdef PARSER_DEBUG
 	purple_debug_info("QQ", "[DBG][putdata] buf %p\n", (void *)buf);
 #endif
--- a/libpurple/protocols/qq/qq.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Fri Sep 19 14:48:00 2008 +0000
@@ -456,7 +456,7 @@
 }
 
 /* send packet to get who's detailed information */
-static void _qq_get_info(PurpleConnection *gc, const gchar *who)
+static void qq_show_buddy_info(PurpleConnection *gc, const gchar *who)
 {
 	guint32 uid;
 	qq_data *qd;
@@ -471,59 +471,65 @@
 	}
 
 	qq_request_get_level(gc, uid);
-	qq_send_packet_get_info(gc, uid, TRUE);
+	qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY);
 }
 
-/* get my own information */
-static void _qq_menu_modify_my_info(PurplePluginAction *action)
+static void action_update_all_rooms(PurplePluginAction *action)
+{
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	qq_data *qd;
+	qd = (qq_data *) gc->proto_data;
+
+	if ( !qd->is_login ) {
+		return;
+	}
+
+	qq_update_all_rooms(gc, 0, 0);
+}
+
+static void action_modify_info_base(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
 
 	qd = (qq_data *) gc->proto_data;
-	qq_prepare_modify_info(gc);
+	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_BASE);
+}
+
+static void action_modify_info_ext(PurplePluginAction *action)
+{
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	qq_data *qd;
+
+	qd = (qq_data *) gc->proto_data;
+	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_EXT);
 }
 
-static void _qq_menu_change_password(PurplePluginAction *action)
+static void action_modify_info_addr(PurplePluginAction *action)
+{
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	qq_data *qd;
+
+	qd = (qq_data *) gc->proto_data;
+	qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_ADDR);
+}
+
+static void action_modify_info_contact(PurplePluginAction *action)
+{
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	qq_data *qd;
+
+	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)
 {
 	purple_notify_uri(NULL, "https://password.qq.com");
 }
 
-/* remove a buddy from my list and remove myself from his list */
-/* TODO: re-enable this
-static void _qq_menu_block_buddy(PurpleBlistNode * node)
-{
-	guint32 uid;
-	gc_and_uid *g;
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-	const gchar *who;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	who = buddy->name;
-	g_return_if_fail(who != NULL);
-
-	uid = purple_name_to_uid(who);
-	g_return_if_fail(uid > 0);
-
-	g = g_new0(gc_and_uid, 1);
-	g->gc = gc;
-	g->uid = uid;
-
-	purple_request_action(gc, _("Block Buddy"),
-			    _("Are you sure you want to block this buddy?"), NULL,
-			    1, g, 2,
-			    _("Cancel"),
-			    G_CALLBACK(qq_do_nothing_with_gc_and_uid),
-			    _("Block"), G_CALLBACK(qq_block_buddy_with_gc_and_uid));
-}
-*/
-
 /* show a brief summary of what we get from login packet */
-static void _qq_menu_account_info(PurplePluginAction *action)
+static void action_show_account_info(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	qq_data *qd;
@@ -628,19 +634,31 @@
 #endif
 
 /* protocol related menus */
-static GList *_qq_actions(PurplePlugin *plugin, gpointer context)
+static GList *qq_actions(PurplePlugin *plugin, gpointer context)
 {
 	GList *m;
 	PurplePluginAction *act;
 
 	m = NULL;
-	act = purple_plugin_action_new(_("Set My Information"), _qq_menu_modify_my_info);
+	act = purple_plugin_action_new(_("Modify Information"), action_modify_info_base);
+	m = g_list_append(m, act);
+
+	act = purple_plugin_action_new(_("Modify Extend Information"), action_modify_info_ext);
+	m = g_list_append(m, act);
+
+	act = purple_plugin_action_new(_("Modify Address"), action_modify_info_addr);
 	m = g_list_append(m, act);
 
-	act = purple_plugin_action_new(_("Change Password"), _qq_menu_change_password);
+	act = purple_plugin_action_new(_("Modify Contact"), action_modify_info_contact);
 	m = g_list_append(m, act);
 
-	act = purple_plugin_action_new(_("Account Information"), _qq_menu_account_info);
+	act = purple_plugin_action_new(_("Change Password"), action_change_password);
+	m = g_list_append(m, act);
+
+	act = purple_plugin_action_new(_("Account Information"), action_show_account_info);
+	m = g_list_append(m, act);
+
+	act = purple_plugin_action_new(_("Update all QQ Quns"), action_update_all_rooms);
 	m = g_list_append(m, act);
 
 	/*
@@ -676,17 +694,18 @@
 static GList *_qq_buddy_menu(PurpleBlistNode * node)
 {
 	GList *m;
+	PurpleMenuAction *act;
 
 	if(PURPLE_BLIST_NODE_IS_CHAT(node))
 		return _qq_chat_menu(node);
 
 	m = NULL;
 
+	act = purple_menu_action_new(_("Remove both side"), PURPLE_CALLBACK(qq_remove_buddy_and_me), NULL, NULL); /* add NULL by gfhuang */
+	m = g_list_append(m, act);
+
 /* TODO : not working, temp commented out by gfhuang */
 #if 0
-
-	act = purple_menu_action_new(_("Block this buddy"), PURPLE_CALLBACK(_qq_menu_block_buddy), NULL, NULL); /* add NULL by gfhuang */
-	m = g_list_append(m, act);
 /*	if (q_bud && is_online(q_bud->status)) { */
 		act = purple_menu_action_new(_("Send File"), PURPLE_CALLBACK(_qq_menu_send_file), NULL, NULL); /* add NULL by gfhuang */
 		m = g_list_append(m, act);
@@ -705,7 +724,7 @@
 
 	purple_name = chat_name_to_purple_name(who);
 	if (purple_name != NULL)
-		_qq_get_info(gc, purple_name);
+		qq_show_buddy_info(gc, purple_name);
 }
 
 /* convert chat nickname to qq-uid to invite individual IM to buddy */
@@ -735,7 +754,7 @@
 	_qq_send_im,						/* send_im */
 	NULL,							/* set_info */
 	NULL,							/* send_typing	*/
-	_qq_get_info,						/* get_info */
+	qq_show_buddy_info,						/* get_info */
 	_qq_change_status,						/* change status */
 	NULL,							/* set_idle */
 	NULL,							/* change_passwd */
@@ -760,12 +779,12 @@
 	_qq_get_chat_buddy_info,				/* get_cb_info	*/
 	NULL,							/* get_cb_away	*/
 	NULL,							/* alias_buddy	*/
-	NULL,							/* group_buddy	*/
+	qq_change_buddys_group,							/* group_buddy	*/
 	NULL,							/* rename_group */
 	NULL,							/* buddy_free */
 	NULL,							/* convo_closed */
 	NULL,							/* normalize */
-	qq_set_my_buddy_icon,					/* set_buddy_icon */
+	qq_set_buddy_icon,					/* set_buddy_icon */
 	NULL,							/* remove_group */
 	_qq_get_chat_buddy_real_name,				/* get_cb_real_name */
 	NULL,							/* set_chat_topic */
@@ -815,7 +834,7 @@
 	NULL,				/**< ui_info		*/
 	&prpl_info,			/**< extra_info		*/
 	NULL,				/**< prefs_info		*/
-	_qq_actions,
+	qq_actions,
 
 	/* padding */
 	NULL,
@@ -878,7 +897,7 @@
 	purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE);
 	purple_prefs_add_bool("/plugins/prpl/qq/show_room_when_newin", TRUE);
 	purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3);
-	purple_prefs_add_int("/plugins/prpl/qq/resend_times", 4);
+	purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10);
 }
 
 PURPLE_INIT_PLUGIN(qq, init_plugin, info);
--- a/libpurple/protocols/qq/qq.h	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Fri Sep 19 14:48:00 2008 +0000
@@ -40,6 +40,12 @@
 typedef struct _qq_buddy qq_buddy;
 typedef struct _qq_interval qq_interval;
 typedef struct _qq_net_stat qq_net_stat;
+typedef struct _qq_add_request qq_add_request;
+
+struct _qq_add_request {
+	guint32 uid;
+	PurpleConnection *gc;
+};
 
 struct _qq_interval {
 	gint resend;
@@ -151,14 +157,7 @@
 	GSList *joining_groups;
 	GSList *adding_groups_from_server; /* internal ids of groups the server wants in my blist */
 	GList *buddies;
-	GList *contact_info_window;
 	GList *group_info_window;
-	GList *info_query;
-	GList *add_buddy_request;
-
-	/* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */
-	gboolean modifying_info;
-	gboolean modifying_face;
 
 	gboolean is_show_notice;
 	gboolean is_show_news;
--- a/libpurple/protocols/qq/qq_network.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Fri Sep 19 14:48:00 2008 +0000
@@ -285,6 +285,10 @@
 
 	update_class = qq_trans_get_class(trans);
 	ship32 = qq_trans_get_ship(trans);
+	if (update_class != 0 || ship32 != 0) {
+		purple_debug_info("QQ", "Process in Update class %d, ship32 %d\n",
+				update_class, ship32);
+	}
 
 	switch (cmd) {
 		case QQ_CMD_TOKEN:
@@ -998,8 +1002,6 @@
 	qd->my_ip.s_addr = 0;
 
 	qq_group_free_all(qd);
-	qq_add_buddy_request_free(qd);
-	qq_info_query_free(qd);
 	qq_buddies_list_free(gc->account, qd);
 }
 
--- a/libpurple/protocols/qq/qq_process.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Fri Sep 19 14:48:00 2008 +0000
@@ -30,10 +30,10 @@
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "group_info.h"
-#include "group_free.h"
 #include "char_conv.h"
 #include "qq_crypt.h"
 
+#include "group_search.h"
 #include "group_conv.h"
 #include "group_find.h"
 #include "group_internal.h"
@@ -41,7 +41,6 @@
 #include "group_info.h"
 #include "group_join.h"
 #include "group_opt.h"
-#include "group_search.h"
 
 #include "header_info.h"
 #include "qq_base.h"
@@ -50,7 +49,6 @@
 #include "packet_parse.h"
 #include "qq_network.h"
 #include "qq_trans.h"
-#include "sys_msg.h"
 #include "utils.h"
 
 enum {
@@ -83,6 +81,111 @@
 	}
 }
 
+/* Send ACK if the sys message needs an ACK */
+static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
+{
+	qq_data *qd;
+	guint8 bar, *ack;
+	gchar *str;
+	gint ack_len, bytes;
+
+	qd = (qq_data *) gc->proto_data;
+
+	str = g_strdup_printf("%d", from);
+	bar = 0x1e;
+	ack_len = 1 + 1 + strlen(str) + 1 + 2;
+	ack = g_newa(guint8, ack_len);
+
+	bytes = 0;
+	bytes += qq_put8(ack + bytes, code);
+	bytes += qq_put8(ack + bytes, bar);
+	bytes += qq_putdata(ack + bytes, (guint8 *) str, strlen(str));
+	bytes += qq_put8(ack + bytes, bar);
+	bytes += qq_put16(ack + bytes, seq);
+
+	g_free(str);
+
+	if (bytes == ack_len)	/* creation OK */
+		qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, ack, ack_len);
+	else
+		purple_debug_error("QQ",
+			   "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
+}
+
+static void _qq_process_msg_sys_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+{
+	qq_data *qd = (qq_data *) gc->proto_data;
+	gchar *title, *content;
+
+	g_return_if_fail(from != NULL && to != NULL);
+
+	title = g_strdup_printf(_("From %s:"), from);
+	content = g_strdup_printf(_("%s"), msg_utf8);
+
+	if (qd->is_show_notice) {
+		purple_notify_info(gc, _("QQ Server Notice"), title, content);
+	} else {
+		purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8);
+}
+	g_free(title);
+	g_free(content);
+}
+
+static void process_server_msg(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
+{
+	qq_data *qd;
+	gchar **segments, *code, *from, *to, *msg, *msg_utf8;
+	int funct;
+
+	g_return_if_fail(data != NULL && data_len != 0);
+
+	qd = (qq_data *) gc->proto_data;
+
+	if (NULL == (segments = split_data(data, data_len, "\x1f", 4)))
+		return;
+	code = segments[0];
+	from = segments[1];
+	to = segments[2];
+	msg = segments[3];
+
+	_qq_send_packet_ack_msg_sys(gc, code[0], strtol(from, NULL, 10), seq);
+
+	if (strtol(to, NULL, 10) != qd->uid) {	/* not to me */
+		purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
+		g_strfreev(segments);
+		return;
+	}
+
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+	if (from == NULL && msg_utf8) {
+		purple_debug_error("QQ", "Recv NULL sys msg to [%s], discard\n", to);
+		g_strfreev(segments);
+		g_free(msg_utf8);
+		return;
+	}
+
+	funct = strtol(code, NULL, 10);
+	switch (funct) {
+		case QQ_SERVER_BUDDY_ADDED:
+		case QQ_SERVER_BUDDY_ADD_REQUEST:
+		case QQ_SERVER_BUDDY_ADDED_ME:
+		case QQ_SERVER_BUDDY_REJECTED_ME:
+			qq_process_buddy_from_server(gc,  funct, from, to, msg_utf8);
+			break;
+		case QQ_SERVER_NOTICE:
+			_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
+			break;
+		case QQ_SERVER_NEW_CLIENT:
+			purple_debug_warning("QQ",
+				   "QQ Server has newer client than %s\n", qq_get_ver_desc(QQ_CLIENT));
+			break;
+		default:
+			purple_debug_warning("QQ", "Recv unknown sys msg code: %s\nMsg: %s\n", code, msg_utf8);
+	}
+	g_free(msg_utf8);
+	g_strfreev(segments);
+}
+
 void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
 {
 	qq_data *qd;
@@ -116,7 +219,7 @@
 			qq_process_recv_im(data, data_len, seq, gc);
 			break;
 		case QQ_CMD_RECV_MSG_SYS:
-			qq_process_msg_sys(data, data_len, seq, gc);
+			process_server_msg(data, data_len, seq, gc);
 			break;
 		case QQ_CMD_BUDDY_CHANGE_STATUS:
 			qq_process_buddy_change_status(data, data_len, gc);
@@ -189,7 +292,7 @@
 	}
 }
 
-static void update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
+void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
 {
 	qq_data *qd;
 	gboolean is_new_turn = FALSE;
@@ -244,7 +347,7 @@
 
 	switch (cmd) {
 		case 0:
-			qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, QQ_BUDDY_INFO_UPDATE_ONLY);
+			qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, 0);
 			break;
 		case QQ_CMD_GET_BUDDY_INFO:
 			qq_request_change_status(gc, QQ_CMD_CLASS_UPDATE_ALL);
@@ -263,7 +366,7 @@
 			break;
 		case QQ_CMD_GET_BUDDIES_ONLINE:
 			/* last command */
-			update_all_rooms(gc, 0, 0);
+			qq_update_all_rooms(gc, 0, 0);
 			break;
 		default:
 			break;
@@ -451,9 +554,8 @@
 	if (update_class == QQ_CMD_CLASS_NONE)
 		return;
 
-	purple_debug_info("QQ", "Update class %d\n", update_class);
 	if (update_class == QQ_CMD_CLASS_UPDATE_ALL) {
-		update_all_rooms(gc, room_cmd, room_id);
+		qq_update_all_rooms(gc, room_cmd, room_id);
 		return;
 	}
 	if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) {
@@ -506,9 +608,6 @@
 	/* now initiate QQ Qun, do it first as it may take longer to finish */
 	qq_group_init(gc);
 
-	/* Now goes on updating my icon/nickname, not showing info_window */
-	qd->modifying_face = FALSE;
-
 	/* is_login, but we have packets before login */
 	qq_trans_process_remained(gc);
 
@@ -555,20 +654,20 @@
 		case QQ_CMD_UPDATE_INFO:
 			qq_process_modify_info_reply(data, data_len, gc);
 			break;
-		case QQ_CMD_ADD_BUDDY_WO_AUTH:
-			qq_process_add_buddy_reply(data, data_len, seq, gc);
+		case QQ_CMD_BUDDY_ADD_NO_AUTH:
+			qq_process_buddy_add_no_auth(data, data_len, ship32, gc);
 			break;
-		case QQ_CMD_DEL_BUDDY:
-			qq_process_remove_buddy_reply(data, data_len, gc);
+		case QQ_CMD_BUDDY_REMOVE:
+			qq_process_buddy_remove(data, data_len, gc);
 			break;
-		case QQ_CMD_REMOVE_SELF:
-			qq_process_remove_self_reply(data, data_len, gc);
+		case QQ_CMD_REMOVE_ME:
+			qq_process_buddy_remove_me(data, data_len, gc);
 			break;
-		case QQ_CMD_BUDDY_AUTH:
-			qq_process_add_buddy_auth_reply(data, data_len, gc);
+		case QQ_CMD_BUDDY_ADD_AUTH:
+			qq_process_buddy_add_auth(data, data_len, gc);
 			break;
 		case QQ_CMD_GET_BUDDY_INFO:
-			qq_process_get_buddy_info(data, data_len, gc);
+			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);
--- a/libpurple/protocols/qq/qq_process.h	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.h	Fri Sep 19 14:48:00 2008 +0000
@@ -34,6 +34,7 @@
 	QQ_CMD_CLASS_NONE = 0,
 	QQ_CMD_CLASS_UPDATE_ALL,
 	QQ_CMD_CLASS_UPDATE_ONLINE,
+	QQ_CMD_CLASS_UPDATE_BUDDY,
 	QQ_CMD_CLASS_UPDATE_ROOM,
 };
 
@@ -49,5 +50,6 @@
 void qq_update_all(PurpleConnection *gc, guint16 cmd);
 void qq_update_online(PurpleConnection *gc, guint16 cmd);
 void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
+void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
 #endif
 
--- a/libpurple/protocols/qq/qq_trans.c	Fri Sep 19 14:43:21 2008 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Fri Sep 19 14:48:00 2008 +0000
@@ -131,6 +131,7 @@
 	}
 
 	trans->update_class = update_class;
+	trans->ship32 = ship32;
 	return trans;
 }
 
--- a/libpurple/protocols/qq/sys_msg.c	Fri Sep 19 14:43:21 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,357 +0,0 @@
-/**
- * @file sys_msg.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 "debug.h"
-#include "internal.h"
-#include "notify.h"
-#include "request.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "char_conv.h"
-#include "header_info.h"
-#include "packet_parse.h"
-#include "qq.h"
-#include "qq_network.h"
-#include "sys_msg.h"
-#include "utils.h"
-
-enum {
-	QQ_MSG_SYS_BEING_ADDED = 0x01,
-	QQ_MSG_SYS_ADD_CONTACT_REQUEST = 0x02,
-	QQ_MSG_SYS_ADD_CONTACT_APPROVED = 0x03,
-	QQ_MSG_SYS_ADD_CONTACT_REJECTED = 0x04,
-	QQ_MSG_SYS_NOTICE= 0x06,
-	QQ_MSG_SYS_NEW_VERSION = 0x09
-};
-
-/* Henry: private function for reading/writing of system log */
-static void _qq_sys_msg_log_write(PurpleConnection *gc, gchar *msg, gchar *from)
-{
-	PurpleLog *log;
-	PurpleAccount *account;
-
-	account = purple_connection_get_account(gc);
-
-	log = purple_log_new(PURPLE_LOG_IM,
-			"systemim",
-			account,
-			NULL,
-			time(NULL),
-			NULL
-			);
-	purple_log_write(log, PURPLE_MESSAGE_SYSTEM, from,
-			time(NULL), msg);
-	purple_log_free(log);
-}
-
-/* suggested by rakescar@linuxsir, can still approve after search */
-static void _qq_search_before_auth_with_gc_and_uid(gc_and_uid *g)
-{
-	PurpleConnection *gc;
-	guint32 uid;
-	gchar *nombre;
-
-	g_return_if_fail(g != NULL);
-
-	gc = g->gc;
-	uid = g->uid;
-	g_return_if_fail(gc != 0 && uid != 0);
-
-	qq_send_packet_get_info(gc, uid, TRUE);	/* we want to see window */
-
-	nombre = uid_to_purple_name(uid);
-	purple_request_action
-	    (gc, NULL, _("Do you approve the requestion?"), "",
-		PURPLE_DEFAULT_ACTION_NONE,
-		 purple_connection_get_account(gc), nombre, NULL,
-		 g, 2,
-	     _("Reject"), G_CALLBACK(qq_reject_add_request_with_gc_and_uid),
-	     _("Approve"), G_CALLBACK(qq_approve_add_request_with_gc_and_uid));
-	g_free(nombre);
-}
-
-static void _qq_search_before_add_with_gc_and_uid(gc_and_uid *g)
-{
-	PurpleConnection *gc;
-	guint32 uid;
-	gchar *nombre;
-
-	g_return_if_fail(g != NULL);
-
-	gc = g->gc;
-	uid = g->uid;
-	g_return_if_fail(gc != 0 && uid != 0);
-
-	qq_send_packet_get_info(gc, uid, TRUE);	/* we want to see window */
-	nombre = uid_to_purple_name(uid);
-	purple_request_action
-	    (gc, NULL, _("Do you add the buddy?"), "",
-		PURPLE_DEFAULT_ACTION_NONE,
-		 purple_connection_get_account(gc), nombre, NULL,
-		 g, 2,
-	     _("Cancel"), NULL,
-		 _("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid));
-	g_free(nombre);
-}
-
-/* Send ACK if the sys message needs an ACK */
-static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
-{
-	qq_data *qd;
-	guint8 bar, *ack;
-	gchar *str;
-	gint ack_len, bytes;
-
-	qd = (qq_data *) gc->proto_data;
-
-	str = g_strdup_printf("%d", from);
-	bar = 0x1e;
-	ack_len = 1 + 1 + strlen(str) + 1 + 2;
-	ack = g_newa(guint8, ack_len);
-
-	bytes = 0;
-	bytes += qq_put8(ack + bytes, code);
-	bytes += qq_put8(ack + bytes, bar);
-	bytes += qq_putdata(ack + bytes, (guint8 *) str, strlen(str));
-	bytes += qq_put8(ack + bytes, bar);
-	bytes += qq_put16(ack + bytes, seq);
-
-	g_free(str);
-
-	if (bytes == ack_len)	/* creation OK */
-		qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, ack, ack_len);
-	else
-		purple_debug_error("QQ",
-			   "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
-}
-
-/* when you are added by a person, QQ server will send sys message */
-static void _qq_process_msg_sys_being_added(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
-	gchar *message;
-	PurpleBuddy *b;
-	guint32 uid;
-	gc_and_uid *g;
-	gchar *name;
-
-	g_return_if_fail(from != NULL && to != NULL);
-
-	uid = strtol(from, NULL, 10);
-	name = uid_to_purple_name(uid);
-	b = purple_find_buddy(gc->account, name);
-
-	if (b == NULL) {	/* the person is not in my list */
-		g = g_new0(gc_and_uid, 1);
-		g->gc = gc;
-		g->uid = uid;	/* only need to get value */
-		message = g_strdup_printf(_("You have been added by %s"), from);
-		_qq_sys_msg_log_write(gc, message, from);
-		purple_request_action(gc, NULL, message,
-				    _("Would you like to add him?"),
-					PURPLE_DEFAULT_ACTION_NONE,
-					purple_connection_get_account(gc), name, NULL,
-					g, 3,
-				    _("Cancel"), NULL,
-					_("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid),
-				    _("Search"), G_CALLBACK(_qq_search_before_add_with_gc_and_uid));
-	} else {
-		message = g_strdup_printf(_("%s added you [%s] to buddy list"), from, to);
-		_qq_sys_msg_log_write(gc, message, from);
-		purple_notify_info(gc, _("QQ Budy"), _("Successed:"), message);
-	}
-
-	g_free(name);
-	g_free(message);
-}
-
-/* you are rejected by the person */
-static void _qq_process_msg_sys_add_contact_rejected(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
-	gchar *message, *reason;
-
-	g_return_if_fail(from != NULL && to != NULL);
-
-	message = g_strdup_printf(_("Requestion rejected by %s"), from);
-	reason = g_strdup_printf(_("Message: %s"), msg_utf8);
-	_qq_sys_msg_log_write(gc, message, from);
-
-	purple_notify_info(gc, _("QQ Buddy"), message, reason);
-	g_free(message);
-	g_free(reason);
-}
-
-/* the buddy approves your request of adding him/her as your friend */
-static void _qq_process_msg_sys_add_contact_approved(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
-	gchar *message;
-	qq_data *qd;
-
-	g_return_if_fail(from != NULL && to != NULL);
-
-	qd = (qq_data *) gc->proto_data;
-	qq_add_buddy_by_recv_packet(gc, strtol(from, NULL, 10), TRUE, TRUE);
-
-	message = g_strdup_printf(_("Requestion approved by %s"), from);
-	_qq_sys_msg_log_write(gc, message, from);
-	purple_notify_info(gc, _("QQ Buddy"), _("Notice:"), message);
-
-	g_free(message);
-}
-
-/* someone wants to add you to his buddy list */
-static void _qq_process_msg_sys_add_contact_request(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
-	gchar *message, *reason;
-	guint32 uid;
-	gc_and_uid *g, *g2;
-	PurpleBuddy *b;
-	gchar *name;
-
-	g_return_if_fail(from != NULL && to != NULL);
-
-	uid = strtol(from, NULL, 10);
-	g = g_new0(gc_and_uid, 1);
-	g->gc = gc;
-	g->uid = uid;
-
-	name = uid_to_purple_name(uid);
-
-	/* TODO: this should go through purple_account_request_authorization() */
-	message = g_strdup_printf(_("%s wants to add you [%s] as a friend"), from, to);
-	reason = g_strdup_printf(_("Message: %s"), msg_utf8);
-	_qq_sys_msg_log_write(gc, message, from);
-
-	purple_request_action
-	    (gc, NULL, message, reason, PURPLE_DEFAULT_ACTION_NONE,
-		purple_connection_get_account(gc), name, NULL,
-		 g, 3,
-	     _("Reject"),
-	     G_CALLBACK(qq_reject_add_request_with_gc_and_uid),
-	     _("Approve"),
-	     G_CALLBACK(qq_approve_add_request_with_gc_and_uid),
-	     _("Search"), G_CALLBACK(_qq_search_before_auth_with_gc_and_uid));
-
-	g_free(message);
-	g_free(reason);
-
-	/* XXX: Is this needed once the above goes through purple_account_request_authorization()? */
-	b = purple_find_buddy(gc->account, name);
-	if (b == NULL) {	/* the person is not in my list  */
-		g2 = g_new0(gc_and_uid, 1);
-		g2->gc = gc;
-		g2->uid = strtol(from, NULL, 10);
-		message = g_strdup_printf(_("%s is not in buddy list"), from);
-		purple_request_action(gc, NULL, message,
-				    _("Would you add?"), PURPLE_DEFAULT_ACTION_NONE,
-					purple_connection_get_account(gc), name, NULL,
-					g2, 3,
-					_("Cancel"), NULL,
-					_("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid),
-				    _("Search"), G_CALLBACK(_qq_search_before_add_with_gc_and_uid));
-		g_free(message);
-	}
-
-	g_free(name);
-}
-
-static void _qq_process_msg_sys_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
-	qq_data *qd = (qq_data *) gc->proto_data;
-	gchar *title, *content;
-
-	g_return_if_fail(from != NULL && to != NULL);
-
-	title = g_strdup_printf(_("From %s:"), from);
-	content = g_strdup_printf(_("%s"), msg_utf8);
-
-	if (qd->is_show_notice) {
-		purple_notify_info(gc, _("QQ Server Notice"), title, content);
-	} else {
-		purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8);
-}
-	g_free(title);
-	g_free(content);
-}
-
-void qq_process_msg_sys(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
-{
-	qq_data *qd;
-	gchar **segments, *code, *from, *to, *msg, *msg_utf8;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-
-	if (NULL == (segments = split_data(data, data_len, "\x1f", 4)))
-		return;
-	code = segments[0];
-	from = segments[1];
-	to = segments[2];
-	msg = segments[3];
-
-	_qq_send_packet_ack_msg_sys(gc, code[0], strtol(from, NULL, 10), seq);
-
-	if (strtol(to, NULL, 10) != qd->uid) {	/* not to me */
-		purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
-		g_strfreev(segments);
-		return;
-	}
-
-	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-	if (from == NULL && msg_utf8) {
-		purple_debug_error("QQ", "Recv NULL sys msg to [%s], discard\n", to);
-		g_strfreev(segments);
-		g_free(msg_utf8);
-		return;
-	}
-
-	switch (strtol(code, NULL, 10)) {
-	case QQ_MSG_SYS_BEING_ADDED:
-		_qq_process_msg_sys_being_added(gc, from, to, msg_utf8);
-		break;
-	case QQ_MSG_SYS_ADD_CONTACT_REQUEST:
-		_qq_process_msg_sys_add_contact_request(gc, from, to, msg_utf8);
-		break;
-	case QQ_MSG_SYS_ADD_CONTACT_APPROVED:
-		_qq_process_msg_sys_add_contact_approved(gc, from, to, msg_utf8);
-		break;
-	case QQ_MSG_SYS_ADD_CONTACT_REJECTED:
-		_qq_process_msg_sys_add_contact_rejected(gc, from, to, msg_utf8);
-		break;
-	case QQ_MSG_SYS_NOTICE:
-		_qq_process_msg_sys_notice(gc, from, to, msg_utf8);
-		break;
-	case QQ_MSG_SYS_NEW_VERSION:
-		purple_debug_warning("QQ",
-			   "QQ server says there is newer version than %s\n", qq_get_ver_desc(QQ_CLIENT));
-		break;
-	default:
-		purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", code);
-		purple_debug_warning("QQ", "the msg is : %s\n", msg_utf8);
-	}
-	g_free(msg_utf8);
-	g_strfreev(segments);
-}
--- a/libpurple/protocols/qq/sys_msg.h	Fri Sep 19 14:43:21 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/**
- * @file sys_msg.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_SYS_MSG_H_
-#define _QQ_SYS_MSG_H_
-
-#include <glib.h>
-#include "connection.h"
-
-void qq_process_msg_sys(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
-
-#endif