changeset 24161:7c0a56c5fea0

2008.10.14 - ccpaging <ccpaging(at)gmail.com> * 2007 remove buddy ok * Removed group_search.c/h
author SHiNE CsyFeK <csyfek@gmail.com>
date Tue, 28 Oct 2008 16:44:09 +0000
parents d35672443baa
children f4f29fac96c6
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/buddy_info.c libpurple/protocols/qq/buddy_opt.c libpurple/protocols/qq/buddy_opt.h libpurple/protocols/qq/char_conv.c libpurple/protocols/qq/group_im.c libpurple/protocols/qq/group_join.c libpurple/protocols/qq/group_opt.c libpurple/protocols/qq/group_search.c libpurple/protocols/qq/group_search.h libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq_base.c libpurple/protocols/qq/qq_base.h libpurple/protocols/qq/qq_define.c libpurple/protocols/qq/qq_define.h libpurple/protocols/qq/qq_process.c libpurple/protocols/qq/utils.c
diffstat 17 files changed, 421 insertions(+), 423 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:44:09 2008 +0000
@@ -1,3 +1,7 @@
+2008.10.14 - ccpaging <ccpaging(at)gmail.com>
+	* 2007 remove buddy ok
+	* Removed group_search.c/h
+
 2008.10.10 - ccpaging <ccpaging(at)gmail.com>
 	* Support part of 'buddy' protocol of QQ2007/2008
 
--- a/libpurple/protocols/qq/buddy_info.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Tue Oct 28 16:44:09 2008 +0000
@@ -157,6 +157,7 @@
 #ifdef DEBUG
 static void info_debug(gchar **segments)
 {
+#if 0
 	int index;
 	gchar *utf8_str;
 	for (index = 0; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
@@ -171,6 +172,7 @@
 		}
 		purple_debug_info("QQ_BUDDY_INFO", "%s: %s\n", field_infos[index].text, segments[index]);
 	}
+#endif
 }
 #endif
 
@@ -454,8 +456,8 @@
 
 	data[data_len] = '\0';
 	if (qd->uid != atoi((gchar *) data)) {	/* return should be my uid */
-		purple_debug_info("QQ", "Update info ACK OK\n");
-		qq_got_attention(gc, _("Successed changing buddy information."));
+		purple_debug_info("QQ", "Failed Updating info\n");
+		qq_got_attention(gc, _("Failed changing buddy information."));
 	}
 }
 
@@ -551,7 +553,7 @@
 	if (face < 1 || face > QQ_FACES) {
 		icon = 1;
 	}
-	
+
 	icon_name = g_strdup_printf("%s%d%s", QQ_ICON_PREFIX, icon, QQ_ICON_SUFFIX);
 	return icon_name;
 }
@@ -591,7 +593,7 @@
 	gsize icon_file_size;
 
 	g_return_if_fail(account != NULL && who != NULL);
-	
+
 	purple_debug_info("QQ", "Update %s icon to %d\n", who, face);
 
 	icon_name = qq_get_icon_name(face);
--- a/libpurple/protocols/qq/buddy_opt.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Tue Oct 28 16:44:09 2008 +0000
@@ -42,7 +42,6 @@
 #define PURPLE_GROUP_QQ_FORMAT          "QQ (%s)"
 
 #define QQ_REMOVE_SELF_REPLY_OK       0x00
-#define QQ_ADD_BUDDY_AUTH_REPLY_OK    0x30	/* ASCII value of "0" */
 
 enum {
 	QQ_MY_AUTH_APPROVE = 0x30,	/* ASCII value of "0" */
@@ -202,6 +201,82 @@
 	qq_send_cmd_mess(gc, QQ_CMD_BUDDY_REMOVE, (guint8 *) uid_str, bytes, 0, uid);
 }
 
+static void request_buddy_remove_2007(PurpleConnection *gc,
+		guint32 uid, guint8 *auth, guint8 auth_len)
+{
+	gint bytes;
+	guint8 *raw_data;
+	gchar uid_str[16];
+
+	g_return_if_fail(uid != 0);
+	g_return_if_fail(auth != NULL && auth_len > 0);
+
+	raw_data = g_newa(guint8, auth_len + sizeof(uid_str) );
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, auth_len);
+	bytes += qq_putdata(raw_data + bytes, auth, auth_len);
+
+	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str));
+
+	qq_send_cmd_mess(gc, QQ_CMD_BUDDY_REMOVE, raw_data, bytes, 0, uid);
+}
+
+void qq_request_auth_info(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid)
+{
+	guint8 raw_data[16];
+	gint bytes;
+
+	g_return_if_fail(uid > 0);
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, cmd);
+	bytes += qq_put16(raw_data + bytes, sub_cmd);
+	bytes += qq_put32(raw_data + bytes, uid);
+
+	qq_send_cmd_mess(gc, QQ_CMD_AUTH_INFO, raw_data, bytes, 0, uid);
+}
+
+void qq_process_auth_info(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
+{
+	qq_data *qd;
+	gint bytes;
+	guint8 cmd, reply;
+	guint16 sub_cmd;
+	guint8 *auth = NULL;
+	guint8 auth_len = 0;
+
+	g_return_if_fail(data != NULL && data_len != 0);
+	g_return_if_fail(uid != 0);
+
+	qd = (qq_data *) gc->proto_data;
+
+	qq_show_packet("qq_process_auth_info", data, data_len);
+	bytes = 0;
+	bytes += qq_get8(&cmd, data + bytes);
+	bytes += qq_get16(&sub_cmd, data + bytes);
+	bytes += qq_get8(&reply, data + bytes);
+	if (bytes + 2 <= data_len) {
+		bytes += 1;	/* skip 1 byte, 0x00 */
+		bytes += qq_get8(&auth_len, data + bytes);
+		if (auth_len > 0) {
+			g_return_if_fail(bytes + auth_len <= data_len);
+			auth = g_newa(guint8, auth_len);
+			bytes += qq_getdata(auth, auth_len, data + bytes);
+		}
+	} else {
+		qq_show_packet("No auth info", data, data_len);
+	}
+
+	if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_REMOVE_BUDDY) {
+		g_return_if_fail(auth != NULL && auth_len > 0);
+		request_buddy_remove_2007(gc, uid, auth, auth_len);
+	}
+	if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_ADD_BUDDY) {
+	}
+	purple_debug_info("QQ", "Got auth info cmd 0x%x, sub 0x%x, reply 0x%x\n",
+			cmd, sub_cmd, reply);
+}
+
 /* try to remove myself from someone's buddy list */
 static void request_buddy_remove_me(PurpleConnection *gc, guint32 uid)
 {
@@ -280,11 +355,18 @@
 	g_free(add_req);
 }
 
-/* we approve other's request of adding me as friend */
-static void buddy_add_authorize_cb(qq_buddy_req *add_req)
+static void buddy_add_deny_noreason_cb(qq_buddy_req *add_req)
 {
+	buddy_add_deny_reason_cb(add_req, NULL);
+}
+
+/* we approve other's request of adding me as friend */
+static void buddy_add_authorize_cb(gpointer data)
+{
+	qq_buddy_req *add_req = (qq_buddy_req *)data;
+
 	g_return_if_fail(add_req != NULL);
-	if (add_req->gc == NULL || add_req->uid != 0) {
+	if (add_req->gc == NULL || add_req->uid == 0) {
 		g_free(add_req);
 		return;
 	}
@@ -294,61 +376,17 @@
 }
 
 /* we reject other's request of adding me as friend */
-static void buddy_add_deny_cb(qq_buddy_req *add_req)
+static void buddy_add_deny_cb(gpointer data)
 {
-	gint uid;
-	gchar *msg1, *msg2;
-	PurpleConnection *gc;
-	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;
-
-	msg1 = g_strdup_printf(_("You rejected %d's request"), uid);
-	msg2 = g_strdup(_("Message:"));
-
-	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(buddy_add_deny_reason_cb), _("Cancel"), NULL,
-			purple_connection_get_account(gc), purple_name, NULL,
+	qq_buddy_req *add_req = (qq_buddy_req *)data;
+	gchar *who = uid_to_purple_name(add_req->uid);
+	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
+			NULL, _("Sorry, You are not my style."), TRUE, FALSE, NULL,
+			_("OK"), G_CALLBACK(buddy_add_deny_reason_cb),
+			_("Cancel"), G_CALLBACK(buddy_add_deny_noreason_cb),
+			purple_connection_get_account(add_req->gc), who, NULL,
 			add_req);
-	g_free(purple_name);
-}
-
-/* suggested by rakescar@linuxsir, can still approve after search */
-static void buddy_add_check_info_cb(qq_buddy_req *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);
+	g_free(who);
 }
 
 /* add a buddy and send packet to QQ server
@@ -410,17 +448,18 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	if (data[0] != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
-		if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) {
-			purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), NULL);
-			return;
-		}
-		msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
-		purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), msg_utf8);
-		g_free(msg_utf8);
-	} else {
-		qq_got_attention(gc, _("Successed sending authorize"));
+	if (data[0] == '0') {
+		purple_debug_info("QQ", "Reply OK for sending authorize\n");
+		return;
 	}
+
+	if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) {
+		purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), NULL);
+		return;
+	}
+	msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
+	purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), msg_utf8);
+	g_free(msg_utf8);
 }
 
 /* process the server reply for my request to remove a buddy */
@@ -436,19 +475,14 @@
 	if (data[0] != 0) {
 		msg = g_strdup_printf(_("Failed removing buddy %d"), uid);
 		purple_notify_info(gc, _("QQ Buddy"), msg, NULL);
-		if (buddy == NULL) {
-			/* Add buddy with no-auth */
-			qq_buddy_new(gc, uid);
-		}
-	} else {
-		msg = g_strdup_printf(_("Successed removing buddy %d"), uid);
-		qq_got_attention(gc, _("Successed removing budy."));
-		if (buddy != NULL) {
-			/* remove buddy again */
-			qq_buddy_free(buddy);
-		}
+		g_free(msg);
 	}
-	g_free(msg);
+
+	purple_debug_info("QQ", "Reply OK for removing buddy\n");
+	/* remove buddy again */
+	if (buddy != NULL) {
+		qq_buddy_free(buddy);
+	}
 }
 
 /* process the server reply for my request to remove myself from a buddy */
@@ -460,13 +494,12 @@
 	g_return_if_fail(data != NULL && data_len != 0);
 	qd = (qq_data *) gc->proto_data;
 
-	if (data[0] != 0) {
-		msg = g_strdup_printf(_("Failed removing me from %d's buddy list"), uid);
-		purple_notify_info(gc, _("QQ Buddy"), msg, NULL);
-	} else {
-		msg = g_strdup_printf(_("Successed removing me from %d's buddy list"), uid);
-		qq_got_attention(gc, msg);
+	if (data[0] == 0) {
+		purple_debug_info("QQ", "Reply OK for removing me from %d's buddy list\n", uid);
+		return;
 	}
+	msg = g_strdup_printf(_("Failed removing me from %d's buddy list"), uid);
+	purple_notify_info(gc, _("QQ Buddy"), msg, NULL);
 	g_free(msg);
 }
 
@@ -482,7 +515,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	if (uid == 0) {	/* we have no record for this */
+	if (uid == 0) {
 		purple_debug_error("QQ", "Process buddy add, unknow id\n");
 		return;
 	}
@@ -512,9 +545,7 @@
 		}
 		qq_request_get_buddies_online(gc, 0, 0);
 
-		msg = g_strdup_printf(_("Successed adding into %d's buddy list"), uid);
-		qq_got_attention(gc, msg);
-		g_free(msg);
+		purple_debug_info("QQ", "Successed adding into %d's buddy list", uid);
 		g_strfreev(segments);
 		return;
 	}
@@ -538,10 +569,10 @@
 	add_req->uid = uid;
 	msg = g_strdup_printf(_("%d needs authentication"), uid);
 	purple_request_input(gc, _("Add buddy authorize"), msg,
-			_("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
+			_("Input request here"),
 			_("Would you be my friend?"),
-			TRUE, FALSE, NULL, _("Send"),
-			G_CALLBACK(request_buddy_add_auth_cb),
+			TRUE, FALSE, NULL,
+			_("Send"), G_CALLBACK(request_buddy_add_auth_cb),
 			_("Cancel"), G_CALLBACK(buddy_cancel_cb),
 			purple_connection_get_account(gc), who, NULL,
 			add_req);
@@ -565,9 +596,13 @@
 		return;
 
 	uid = purple_name_to_uid(buddy->name);
-	if (uid > 0) {
-		request_buddy_remove(gc, uid);
-		request_buddy_remove_me(gc, uid);
+	if (uid > 0 && uid != qd->uid) {
+		if (qd->client_version > 2005) {
+			qq_request_auth_info(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_REMOVE_BUDDY, uid);
+		} else {
+			request_buddy_remove(gc, uid);
+			request_buddy_remove_me(gc, uid);
+		}
 	}
 
 	if (buddy->proto_data) {
@@ -582,99 +617,85 @@
 }
 
 /* someone wants to add you to his buddy list */
-static void server_buddy_add_request(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+static void server_buddy_add_request(PurpleConnection *gc, gchar *from, gchar *to,
+		guint8 *data, gint data_len)
 {
-	gchar *message, *reason;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	qq_buddy_req *add_req;
+	gchar *who;
+	gchar *msg, *reason;
+
+	g_return_if_fail(from != NULL && to != NULL);
+
+	add_req = g_new0(qq_buddy_req, 1);
+	add_req->gc = gc;
+	add_req->uid = strtol(from, NULL, 10);;
+
+	if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) {
+		qq_request_buddy_info(gc, add_req->uid, 0, QQ_BUDDY_INFO_DISPLAY);
+	}
+	who = uid_to_purple_name(add_req->uid);
+
+	if (data_len <= 0) {
+		reason = g_strdup( _("No reason given") );
+	} else {
+		msg = g_strndup((gchar *)data, data_len);
+		reason = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+		if (reason == NULL)	reason = g_strdup( _("Unknown reason") );
+		g_free(msg);
+	}
+	purple_account_request_authorization(account,
+	 		from, NULL,
+			NULL, reason,
+			purple_find_buddy(account, who) != NULL,
+			buddy_add_authorize_cb,
+			buddy_add_deny_cb,
+			add_req);
+
+	g_free(reason);
+	g_free(who);
+}
+
+/* when you are added by a person, QQ server will send sys message */
+static void server_buddy_added(PurpleConnection *gc, gchar *from, gchar *to,
+		guint8 *data, gint data_len)
+{
+	PurpleAccount *account = purple_connection_get_account(gc);
+	PurpleBuddy *buddy;
 	guint32 uid;
-	qq_buddy_req *g, *g2;
-	PurpleBuddy *b;
-	gchar *name;
+	qq_buddy_req *add_req;
+	gchar *who;
+	gchar *primary;
 
 	g_return_if_fail(from != NULL && to != NULL);
 
 	uid = strtol(from, NULL, 10);
-	g = g_new0(qq_buddy_req, 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);
+	who = uid_to_purple_name(uid);
 
-	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_buddy_req, 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);
+	buddy = purple_find_buddy(account, who);
+	if (buddy != NULL) {
+		purple_account_notify_added(account, from, to, NULL, NULL);
 	}
 
-	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_buddy_req *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);
+	add_req = g_new0(qq_buddy_req, 1);
+	add_req->gc = gc;
+	add_req->uid = uid;	/* only need to get value */
+	primary = g_strdup_printf(_("You have been added by %s"), from);
+	purple_request_action(gc, NULL, primary,
+			_("Would you like to add him?"),
+			PURPLE_DEFAULT_ACTION_NONE,
+			purple_connection_get_account(gc), who, NULL,
+			add_req, 2,
+			_("Cancel"), G_CALLBACK(buddy_cancel_cb),
+			_("Add"), G_CALLBACK(buddy_add_no_auth_cb));
 
-	if (b == NULL) {	/* the person is not in my list */
-		add_req = g_new0(qq_buddy_req, 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(_("Successed adding into %s's buddy list"), from);
-		qq_got_attention(gc, message);
-	}
-
-	g_free(name);
-	g_free(message);
+	g_free(who);
+	g_free(primary);
 }
 
 /* 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)
+static void server_buddy_added_me(PurpleConnection *gc, gchar *from, gchar *to,
+		guint8 *data, gint data_len)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
 	qq_data *qd;
@@ -696,28 +717,43 @@
 		qq_request_get_level(gc, uid);
 	}
 
-	purple_account_notify_added(account, from, to, NULL, msg_utf8);
+	purple_account_notify_added(account, to, from, NULL, NULL);
 }
 
 /* you are rejected by the person */
-static void server_buddy_rejected_me(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+static void server_buddy_rejected_me(PurpleConnection *gc, gchar *from, gchar *to,
+		guint8 *data, gint data_len)
 {
-	gchar *message, *reason;
 	guint32 uid;
 	PurpleBuddy *buddy;
+	gchar *msg, *msg_utf8;
+	gchar *primary, *secondary;
 
 	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);
+	if (data_len <= 0) {
+		msg = g_strdup( _("No reason given") );
+	} else {
+		msg = g_strndup((gchar *)data, data_len);
+	}
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+	if (msg_utf8 == NULL) {
+		msg_utf8 = g_strdup( _("Unknown reason") );
+	}
+	g_free(msg);
 
-	purple_notify_info(gc, _("QQ Buddy"), message, reason);
-	g_free(message);
-	g_free(reason);
-	
+	primary = g_strdup_printf(_("Rejected by %s"), from);
+	secondary = g_strdup_printf(_("Message: %s"), msg_utf8);
+
+	purple_notify_info(gc, _("QQ Buddy"), primary, secondary);
+
+	g_free(msg_utf8);
+	g_free(primary);
+	g_free(secondary);
+
 	uid = strtol(from, NULL, 10);
 	g_return_if_fail(uid != 0);
-	
+
 	buddy = qq_buddy_find(gc, uid);
 	if (buddy != NULL && buddy->proto_data != NULL) {
 		/* Not authorized now, free buddy data */
@@ -727,20 +763,23 @@
 }
 
 void qq_process_buddy_from_server(PurpleConnection *gc, int funct,
-		gchar *from, gchar *to, gchar *msg_utf8)
+		gchar *from, gchar *to, guint8 *data, gint data_len)
 {
 	switch (funct) {
 	case QQ_SERVER_BUDDY_ADDED:
-		server_buddy_added(gc, from, to, msg_utf8);
+		server_buddy_added(gc, from, to, data, data_len);
 		break;
 	case QQ_SERVER_BUDDY_ADD_REQUEST:
-		server_buddy_add_request(gc, from, to, msg_utf8);
+		server_buddy_add_request(gc, from, to, data, data_len);
+		break;
+	case QQ_MSG_SYS_ADD_FRIEND_REQUEST_EX:
+		// server_buddy_add_request_ex(gc, from, to, data, data_len);
 		break;
 	case QQ_SERVER_BUDDY_ADDED_ME:
-		server_buddy_added_me(gc, from, to, msg_utf8);
+		server_buddy_added_me(gc, from, to, data, data_len);
 		break;
 	case QQ_SERVER_BUDDY_REJECTED_ME:
-		server_buddy_rejected_me(gc, from, to, msg_utf8);
+		server_buddy_rejected_me(gc, from, to, data, data_len);
 		break;
 	default:
 		purple_debug_warning("QQ", "Unknow buddy operate (%d) from server\n", funct);
--- a/libpurple/protocols/qq/buddy_opt.h	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.h	Tue Oct 28 16:44:09 2008 +0000
@@ -30,6 +30,17 @@
 
 #include "qq.h"
 
+enum {
+	QQ_AUTH_INFO_BUDDY = 0x01,
+	QQ_AUTH_INFO_ROOM = 0x02,
+
+	QQ_AUTH_INFO_ADD_BUDDY = 0x0001,
+	QQ_AUTH_INFO_TEMP_SESSION = 0x0003,
+	QQ_AUTH_INFO_CLUSTER = 0x0002,
+	QQ_AUTH_INFO_REMOVE_BUDDY = 0x0006,
+	QQ_AUTH_INFO_UPDATE_BUDDY_INFO = 0x0007,
+};
+
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 void qq_change_buddys_group(PurpleConnection *gc, const char *who,
 		const char *old_group, const char *new_group);
@@ -41,7 +52,10 @@
 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);
+		gchar *from, gchar *to, guint8 *data, gint data_len);
+
+void qq_request_auth_info(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid);
+void qq_process_auth_info(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
 
 qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, guint32 uid);
 void qq_buddy_data_free(qq_buddy_data *bd);
--- a/libpurple/protocols/qq/char_conv.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Tue Oct 28 16:44:09 2008 +0000
@@ -37,7 +37,7 @@
 #define QQ_CHARSET_ENG        "ISO-8859-1"
 
 #define QQ_NULL_MSG           "(NULL)"	/* return this if conversion fails */
-#define QQ_NULL_SMILEY        "(Broken)"	/* return this if smiley conversion fails */
+#define QQ_NULL_SMILEY        "<IMG ID=\"0\">"	/* return this if smiley conversion fails */
 
 const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
 	0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
--- a/libpurple/protocols/qq/group_im.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Tue Oct 28 16:44:09 2008 +0000
@@ -213,7 +213,7 @@
 	rmd = qq_room_data_find(gc, room_id);
 	g_return_if_fail(rmd != NULL);
 
-	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/show_room_when_newin")) {
+	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/auto_popup_conversation")) {
 		conv = qq_room_conv_open(gc, rmd);
 	}
 
--- a/libpurple/protocols/qq/group_join.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Tue Oct 28 16:44:09 2008 +0000
@@ -137,13 +137,13 @@
 	qq_room_req *add_req;
 	g_return_if_fail(rmd != NULL);
 
-	purple_debug_info("QQ", "Group (internal id: %d) needs authentication\n", rmd->id);
+	purple_debug_info("QQ", "Room (internal id: %d) needs authentication\n", rmd->id);
 
-	msg = g_strdup_printf("Group \"%s\" needs authentication\n", rmd->title_utf8);
+	msg = g_strdup_printf("QQ Qun %d needs authentication\n", rmd->ext_id);
 	add_req = g_new0(qq_room_req, 1);
 	add_req->gc = gc;
 	add_req->id = rmd->id;
-	purple_request_input(gc, NULL, msg,
+	purple_request_input(gc, _("Join QQ Qun"), msg,
 			   _("Input request here"),
 			   _("Would you be my friend?"), TRUE, FALSE, NULL,
 			   _("Send"),
--- a/libpurple/protocols/qq/group_opt.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Tue Oct 28 16:44:09 2008 +0000
@@ -83,42 +83,12 @@
 		g_free(add_req);
 }
 
-static void member_join_reject_send_cb(qq_room_req *add_req, gchar *msg_utf8)
-{
-	qq_room_data *rmd;
-	g_return_if_fail(add_req != NULL && add_req->gc != NULL && add_req->id > 0 && add_req->member > 0);
-	rmd = qq_room_data_find(add_req->gc, add_req->id);
-	g_return_if_fail(rmd != NULL);
-	qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_REJECT, add_req->member, msg_utf8);
-	g_free(add_req);
-}
-
-static void member_join_reject_cb(qq_room_req *add_req)
+static void member_join_authorize_cb(gpointer data)
 {
-	gchar *msg1, *msg2, *who;
-	g_return_if_fail(add_req != NULL && add_req->gc != NULL && add_req->member > 0);
-
-	msg1 = g_strdup_printf(_("You rejected %d's request"), add_req->member);
-	msg2 = g_strdup(_("Message:"));
-
-	who = uid_to_purple_name(add_req->member);
-	purple_request_input(add_req->gc, /* title */ NULL, msg1, msg2,
-			   _("Sorry, you are not my style..."), /* multiline */ TRUE, /* masked */ FALSE,
-			   /* hint */ NULL,
-			   _("Send"), G_CALLBACK(member_join_reject_send_cb),
-			   _("Cancel"), G_CALLBACK(room_req_cancel_cb),
-			   purple_connection_get_account(add_req->gc), who, NULL,
-			   add_req);
-
-	g_free(msg1);
-	g_free(msg2);
-	g_free(who);
-}
-
-static void member_join_authorize_cb(qq_room_req *add_req)
-{
+	qq_room_req *add_req = (qq_room_req *)data;
 	qq_room_data *rmd;
-	g_return_if_fail(add_req != NULL && add_req->gc != NULL && add_req->id > 0 && add_req->member > 0);
+	g_return_if_fail(add_req != NULL && add_req->gc != NULL);
+	g_return_if_fail(add_req->id > 0 && add_req->member > 0);
 	rmd = qq_room_data_find(add_req->gc, add_req->id);
 	g_return_if_fail(rmd != NULL);
 
@@ -127,17 +97,37 @@
 	g_free(add_req);
 }
 
-static void member_join_search_cb(qq_room_req *add_req)
+static void member_join_deny_reason_cb(qq_room_req *add_req, gchar *msg_utf8)
 {
-	g_return_if_fail(add_req != NULL && add_req->gc != NULL && add_req->member > 0);
+	qq_room_data *rmd;
+	g_return_if_fail(add_req != NULL && add_req->gc != NULL);
+	g_return_if_fail(add_req->id > 0 && add_req->member > 0);
+	rmd = qq_room_data_find(add_req->gc, add_req->id);
+	g_return_if_fail(rmd != NULL);
+	qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_REJECT, add_req->member, msg_utf8);
+	g_free(add_req);
+}
+
+static void member_join_deny_noreason_cb(qq_room_req *add_req, gchar *msg_utf8)
+{
+	member_join_deny_reason_cb(add_req, NULL);
+}
 
-	qq_request_buddy_info(add_req->gc, add_req->member, 0, QQ_BUDDY_INFO_DISPLAY);
-	purple_request_action(add_req->gc, NULL, _("Do you want to approve the request?"), "",
-				PURPLE_DEFAULT_ACTION_NONE,
-				purple_connection_get_account(add_req->gc), NULL, NULL,
-				add_req, 2,
-				_("Reject"), G_CALLBACK(member_join_reject_cb),
-				_("Approve"), G_CALLBACK(member_join_authorize_cb));
+static void member_join_deny_cb(gpointer data)
+{
+	qq_room_req *add_req = (qq_room_req *)data;
+	gchar *who;
+	g_return_if_fail(add_req != NULL && add_req->gc != NULL);
+	g_return_if_fail(add_req->id > 0 && add_req->member > 0);
+
+	who = uid_to_purple_name(add_req->member);
+	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
+			NULL, _("Sorry, you are not our style ..."), TRUE, FALSE, NULL,
+			_("OK"), G_CALLBACK(member_join_deny_reason_cb),
+			_("Cancel"), G_CALLBACK(member_join_deny_noreason_cb),
+			purple_connection_get_account(add_req->gc), who, NULL,
+			add_req);
+	g_free(who);
 }
 
 void qq_group_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *new_members)
@@ -425,7 +415,7 @@
 {
 	guint32 ext_id, member_id;
 	guint8 type8;
-	gchar *reason_utf8, *msg, *reason;
+	gchar *msg, *reason;
 	qq_room_req *add_req;
 	gchar *who;
 	gint bytes = 0;
@@ -442,7 +432,7 @@
 
 	g_return_if_fail(ext_id > 0 && member_id > 0);
 
-	bytes += qq_get_vstr(&reason_utf8, QQ_CHARSET_DEFAULT, data + bytes);
+	bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
 
 	add_req = g_new0(qq_room_req, 1);
 	add_req->gc = gc;
@@ -456,35 +446,32 @@
 	if (qq_room_buddy_find(rmd, member_id)) {
 		purple_debug_info("QQ", "Approve join, buddy joined before\n");
 		msg = g_strdup_printf(_("%d requested to join Qun %d for %s"),
-				member_id, ext_id, reason_utf8);
+				member_id, ext_id, reason);
 		qq_room_got_chat_in(gc, id, 0, msg, now);
 		qq_send_cmd_group_auth(gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, member_id, "");
 		g_free(msg);
-		g_free(reason_utf8);
+		g_free(reason);
 		return;
 	}
 
+	if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) {
+		qq_request_buddy_info(gc, member_id, 0, QQ_BUDDY_INFO_DISPLAY);
+	}
 	who = uid_to_purple_name(member_id);
 	msg = g_strdup_printf(_("%d request to join Qun %d"), member_id, ext_id);
-	reason = g_strdup_printf(_("Message: %s"), reason_utf8);
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			msg, reason,
 			PURPLE_DEFAULT_ACTION_NONE,
 			purple_connection_get_account(gc), who, NULL,
-			add_req, 3,
-			_("Approve"),
-			G_CALLBACK
-			(member_join_authorize_cb),
-			_("Reject"),
-			G_CALLBACK
-			(member_join_reject_cb),
-			_("Search"), G_CALLBACK(member_join_search_cb));
+			add_req, 2,
+			_("Deny"), G_CALLBACK(member_join_deny_cb),
+			_("Authorize"), G_CALLBACK(member_join_authorize_cb));
 
 	g_free(who);
 	g_free(msg);
 	g_free(reason);
-	g_free(reason_utf8);
+	g_free(reason);
 }
 
 /* the request to join a group is rejected */
--- a/libpurple/protocols/qq/group_search.c	Tue Oct 28 16:42:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/**
- * @file group_search.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "internal.h"
-
-#include "debug.h"
-
-#include "char_conv.h"
-#include "group_internal.h"
-#include "group_join.h"
-#include "group_search.h"
-#include "utils.h"
-#include "qq_define.h"
-#include "packet_parse.h"
-#include "qq_network.h"
-
--- a/libpurple/protocols/qq/group_search.h	Tue Oct 28 16:42:46 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/**
- * @file group_search.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#ifndef _QQ_GROUP_SEARCH_H_
-#define _QQ_GROUP_SEARCH_H_
-
-#include <glib.h>
-#include "connection.h"
-
-#endif
--- a/libpurple/protocols/qq/qq.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Tue Oct 28 16:44:09 2008 +0000
@@ -1212,7 +1212,8 @@
 	purple_prefs_add_none("/plugins/prpl/qq");
 	purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE);
 	purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE);
-	purple_prefs_add_bool("/plugins/prpl/qq/show_room_when_newin", TRUE);
+	purple_prefs_add_bool("/plugins/prpl/qq/auto_popup_conversation", FALSE);
+	purple_prefs_add_bool("/plugins/prpl/qq/auto_get_authorize_info", TRUE);
 	purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3);
 	purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10);
 }
--- a/libpurple/protocols/qq/qq_base.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Tue Oct 28 16:44:09 2008 +0000
@@ -293,50 +293,44 @@
 guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len)
 {
 	qq_data *qd;
+	gint bytes;
 	guint8 ret;
-	int token_len;
-	gchar *error_msg;
+	guint8 token_len;
+	gchar *msg;
 
 	g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR);
 
 	g_return_val_if_fail(gc != NULL  && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
 	qd = (qq_data *) gc->proto_data;
 
-	ret = buf[0];
+	bytes = 0;
+	bytes += qq_get8(&ret, buf + bytes);
+	bytes += qq_get8(&token_len, buf + bytes);
 
 	if (ret != QQ_LOGIN_REPLY_OK) {
-		purple_debug_error("QQ", "Failed to request token: %d\n", buf[0]);
-		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
-				buf, buf_len,
-				">>> [default] decrypt and dump");
-		error_msg = try_dump_as_gbk(buf, buf_len);
-		if (error_msg == NULL) {
-				error_msg = g_strdup_printf( _("Invalid token reply code, 0x%02X"), ret);
-		}
+		qq_show_packet("Failed requesting token", buf, buf_len);
+
+		msg = g_strdup_printf( _("Failed requesting token, 0x%02X"), ret );
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
-				error_msg);
-		g_free(error_msg);
+				msg);
+		g_free(msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
-	token_len = buf_len-2;
-	if (token_len <= 0) {
-		error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
+	if (bytes + token_len < buf_len) {
+		msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
-				error_msg);
-		g_free(error_msg);
+				msg);
+		g_free(msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
-	if (buf[1] != token_len) {
-		purple_debug_info("QQ",
-				"Invalid token len. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2);
+	if (bytes + token_len > buf_len) {
+		purple_debug_info("QQ", "Extra token data, %d %d\n", token_len, buf_len - bytes);
 	}
-	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
-			buf+2, token_len,
-			"<<< got a token -> [default] decrypt and dump");
+	qq_show_packet("Got token", buf + bytes, buf_len - bytes);
 
 	if (qd->ld.token != NULL) {
 		g_free(qd->ld.token);
@@ -645,7 +639,7 @@
 	qd = (qq_data *) gc->proto_data;
 
 	g_return_val_if_fail (data != NULL, QQ_LOGIN_REPLY_ERR);
-	
+
 	/* qq_show_packet("Get Server", data, data_len); */
 	bytes = 0;
 	bytes += qq_get16(&ret, data + bytes);
@@ -913,7 +907,7 @@
 	qd->ld.token_ex = g_realloc(qd->ld.token_ex, qd->ld.token_ex_len);
 	bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len, data + bytes);
 	/* qq_show_packet("Get token ex", qd->ld.token_ex, qd->ld.token_ex_len); */
-	
+
 	if(reply != 1)
 	{
 		purple_debug_info("QQ", "Captcha verified, result %d\n", reply);
@@ -934,7 +928,7 @@
 	bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, data + bytes);
 	/* qq_show_packet("Get captcha token", qd->captcha.token, qd->captcha.token_len); */
 
-	purple_debug_info("QQ", "Request next captcha %d, new %d, total %d\n", 
+	purple_debug_info("QQ", "Request next captcha %d, new %d, total %d\n",
 			qd->captcha.next_index, captcha_len, qd->captcha.data_len);
 	if(qd->captcha.next_index > 0)
 	{
@@ -1031,7 +1025,7 @@
 
 	/* put length into first 2 bytes */
 	qq_put8(raw_data + 1, bytes - 2);
-	
+
 	/* tail */
 	bytes += qq_put16(raw_data + bytes, 0x0003);
 	bytes += qq_put8(raw_data + bytes, 0);
@@ -1251,13 +1245,12 @@
 	if (ret != 0) {
 		msg = g_strndup((gchar *)data + bytes, data_len - bytes);
 		msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+		g_free(msg);
 
 		switch (ret) {
 			case 0x05:
-				error = g_strdup_printf(
-						_("Server is busy now (0x%02X), Please try later\n%s"),
-						ret, msg_utf8);
-				break;
+				purple_debug_error("QQ", "Server busy for %s\n", msg_utf8);
+				return QQ_LOGIN_REPLY_REDIRECT;
 			case 0x0A:
 				/* 0a 2d 9a 4b 9a 01 01 00 00 00 05 00 00 00 00 79 0e 5f fd */
 				/* Missing get server before login*/
@@ -1277,7 +1270,6 @@
 
 		g_free(error);
 		g_free(msg_utf8);
-		g_free(msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
@@ -1444,12 +1436,12 @@
 	if (ret != 0) {
 		msg = g_strndup((gchar *)data + bytes, data_len - bytes);
 		msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+		g_free(msg);
 
 		switch (ret) {
 			case 0x05:
-				error = g_strdup_printf(
-						_("Server is busy now (0x%02X), Please try later\n%s"),
-						ret, msg_utf8);
+				purple_debug_error("QQ", "Server busy for %s\n", msg_utf8);
+				return QQ_LOGIN_REPLY_REDIRECT;
 				break;
 			default:
 				error = g_strdup_printf(
@@ -1467,7 +1459,6 @@
 
 		g_free(error);
 		g_free(msg_utf8);
-		g_free(msg);
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
--- a/libpurple/protocols/qq/qq_base.h	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.h	Tue Oct 28 16:44:09 2008 +0000
@@ -31,8 +31,8 @@
 #define QQ_LOGIN_REPLY_OK							0x00
 #define QQ_LOGIN_REPLY_REDIRECT				0x01
 /* defined by myself */
-#define QQ_LOGIN_REPLY_CAPTCHA_DLG			0xfc
-#define QQ_LOGIN_REPLY_NEXT_TOKEN_EX		0xfd
+#define QQ_LOGIN_REPLY_CAPTCHA_DLG			0xfd
+#define QQ_LOGIN_REPLY_NEXT_TOKEN_EX		0xfe
 #define QQ_LOGIN_REPLY_ERR							0xff
 
 #define QQ_LOGIN_MODE_NORMAL		0x0a
--- a/libpurple/protocols/qq/qq_define.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Tue Oct 28 16:44:09 2008 +0000
@@ -179,8 +179,8 @@
 		return "QQ_CMD_TOKEN_EX";
 	case QQ_CMD_CHECK_PWD:
 		return "QQ_CMD_CHECK_PWD";
-	case QQ_CMD_BUDDY_AUTH:
-		return "QQ_CMD_BUDDY_AUTH";
+	case QQ_CMD_AUTH_INFO:
+		return "QQ_CMD_AUTH_INFO";
 	case QQ_CMD_BUDDY_ADD_NO_AUTH_EX:
 		return "QQ_CMD_BUDDY_ADD_NO_AUTH_EX";
 	case QQ_CMD_BUDDY_ADD_AUTH_EX:
--- a/libpurple/protocols/qq/qq_define.h	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Tue Oct 28 16:44:09 2008 +0000
@@ -70,7 +70,7 @@
 	QQ_CMD_GET_SERVER = 0x0091,					/* select login server */
 	QQ_CMD_TOKEN_EX = 0x00BA,						/* get LOGIN token */
 	QQ_CMD_CHECK_PWD = 0x00DD,				/* Password verify */
-	QQ_CMD_BUDDY_AUTH = 0x00AE,				/* the request verification of information */
+	QQ_CMD_AUTH_INFO = 0x00AE,				/* the request verification of information */
 	QQ_CMD_BUDDY_ADD_NO_AUTH_EX = 0x00A7,			/* add friend without auth */
 	QQ_CMD_BUDDY_ADD_AUTH_EX = 0x00A8, 				/* add buddy with auth */
 };
@@ -108,12 +108,16 @@
 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
+	QQ_SERVER_BUDDY_ADDED = 1,
+	QQ_SERVER_BUDDY_ADD_REQUEST = 2,
+	QQ_SERVER_BUDDY_ADDED_ME = 3,
+	QQ_SERVER_BUDDY_REJECTED_ME = 4,
+	QQ_SERVER_NOTICE= 6,
+	QQ_SERVER_NEW_CLIENT = 9,
+	QQ_MSG_SYS_BEING_ADDED_EX = 40,
+	QQ_MSG_SYS_ADD_FRIEND_REQUEST_EX = 41,
+	QQ_MSG_SYS_ADDED_BY_CORRECT_ANSWER = 42,
+	QQ_MSG_SYS_ADD_FRIEND_APPROVED_AND_ADD = 43,
 };
 
 enum {
--- a/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:44:09 2008 +0000
@@ -58,7 +58,7 @@
 static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
 {
 	qq_data *qd;
-	gchar *msg_utf8 = NULL;
+	gchar *msg;
 
 	g_return_if_fail(data != NULL && data_len != 0);
 
@@ -71,11 +71,9 @@
 			">>> [%d] %s -> [default] decrypt and dump",
 			seq, qq_get_cmd_desc(cmd));
 
-	msg_utf8 = try_dump_as_gbk(data, data_len);
-	if (msg_utf8 != NULL) {
-		purple_notify_info(gc, _("QQ Error"), title, msg_utf8);
-		g_free(msg_utf8);
-	}
+	msg = g_strdup_printf("Unknow command 0x%02X, %s", cmd, qq_get_cmd_desc(cmd));
+	purple_notify_info(gc, _("QQ Error"), title, msg);
+	g_free(msg);
 }
 
 /* parse the reply to send_im */
@@ -356,42 +354,47 @@
 }
 
 /* Send ACK if the sys message needs an ACK */
-static void request_server_ack(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
+static void request_server_ack(PurpleConnection *gc, gchar *funct_str, gchar *from, guint16 seq)
 {
 	qq_data *qd;
-	guint8 bar, *ack;
-	gchar *str;
-	gint ack_len, bytes;
+	guint8 *raw_data;
+	gint bytes;
+	guint8 bar;
 
+	g_return_if_fail(funct_str != NULL && from != NULL);
 	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);
+	raw_data = g_newa(guint8, strlen(funct_str) + strlen(from) + 16);
 
 	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);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)funct_str, strlen(funct_str));
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)from, strlen(from));
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_put16(raw_data + 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);
+	qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, raw_data, bytes);
 }
 
-static void do_server_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
+static void do_server_notice(PurpleConnection *gc, gchar *from, gchar *to,
+		guint8 *data, gint data_len)
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
+	gchar *msg, *msg_utf8;
 	gchar *title, *content;
 
-	g_return_if_fail(from != NULL && to != NULL);
+	g_return_if_fail(from != NULL && to != NULL && data_len > 0);
+
+	msg = g_strndup((gchar *)data, data_len);
+	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+	g_free(msg);
+	if (msg_utf8 == NULL) {
+		purple_debug_error("QQ", "Recv NULL sys msg from %s to %s, discard\n",
+				from, to);
+		return;
+	}
 
 	title = g_strdup_printf(_("From %s:"), from);
 	content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8);
@@ -401,62 +404,72 @@
 	} else {
 		purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8);
 	}
+	g_free(msg_utf8);
 	g_free(title);
 	g_free(content);
 }
 
-static void process_server_msg(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
+static void process_server_msg(PurpleConnection *gc, guint8 *data, gint data_len, guint16 seq)
 {
 	qq_data *qd;
-	gchar **segments, *code, *from, *to, *msg, *msg_utf8;
-	int funct;
+	guint8 *data_str;
+	gchar **segments;
+	gchar *funct_str, *from, *to;
+	gint bytes, 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)))
+	data_str = g_newa(guint8, data_len + 1);
+	g_memmove(data_str, data, data_len);
+	data_str[data_len] = 0x00;
+
+	segments = g_strsplit_set((gchar *) data_str, "\x1f", 0);
+	g_return_if_fail(segments != NULL);
+	if (g_strv_length(segments) < 3) {
+		purple_debug_warning("QQ", "Server message segments is less than 3\n");
+		g_strfreev(segments);
 		return;
-	code = segments[0];
+	}
+
+	bytes = 0;
+	funct_str = segments[0];
+	bytes += strlen(funct_str) + 1;
 	from = segments[1];
+	bytes += strlen(from) + 1;
 	to = segments[2];
-	msg = segments[3];
+	bytes += strlen(to) + 1;
 
-	request_server_ack(gc, code[0], strtol(from, NULL, 10), seq);
+	request_server_ack(gc, funct_str, from, seq);
 
+	qq_show_packet("Server MSG", data, data_len);
 	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);
+	funct = strtol(funct_str, 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);
+		case QQ_MSG_SYS_ADD_FRIEND_REQUEST_EX:
+			qq_process_buddy_from_server(gc,  funct, from, to, data + bytes, data_len - bytes);
 			break;
 		case QQ_SERVER_NOTICE:
-			do_server_notice(gc, from, to, msg_utf8);
+			do_server_notice(gc, from, to, data + bytes, data_len - bytes);
 			break;
 		case QQ_SERVER_NEW_CLIENT:
 			purple_debug_warning("QQ", "QQ Server has newer client version\n");
 			break;
 		default:
-			purple_debug_warning("QQ", "Recv unknown sys msg code: %s\nMsg: %s\n", code, msg_utf8);
+			qq_show_packet("Recv unknown sys msg", data, data_len);
+			purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", funct_str);
 			break;
 	}
-	g_free(msg_utf8);
 	g_strfreev(segments);
 }
 
@@ -493,7 +506,7 @@
 			process_private_msg(data, data_len, seq, gc);
 			break;
 		case QQ_CMD_RECV_MSG_SYS:
-			process_server_msg(data, data_len, seq, gc);
+			process_server_msg(gc, data, data_len, seq);
 			break;
 		case QQ_CMD_BUDDY_CHANGE_STATUS:
 			qq_process_buddy_change_status(data, data_len, gc);
@@ -946,8 +959,16 @@
 		case QQ_CMD_LOGIN:
 			if (qd->client_version == 2008) {
 				ret_8 = qq_process_login_2008(gc, data, data_len);
+				if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
+                		qq_request_get_server(gc);
+                		return QQ_LOGIN_REPLY_OK;
+            	}
 			} else if (qd->client_version == 2007) {
 				ret_8 = qq_process_login_2007(gc, data, data_len);
+				if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
+                		qq_request_get_server(gc);
+                		return QQ_LOGIN_REPLY_OK;
+            	}
 			} else {
 				ret_8 = qq_process_login(gc, data, data_len);
 			}
@@ -1075,6 +1096,9 @@
 			}
 			purple_debug_info("QQ", "All buddies and groups received\n");
 			break;
+		case QQ_CMD_AUTH_INFO:
+			qq_process_auth_info(gc, data, data_len, ship32);
+			break;
 		default:
 			process_unknow_cmd(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq);
 			is_unknow = TRUE;
--- a/libpurple/protocols/qq/utils.c	Tue Oct 28 16:42:46 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Tue Oct 28 16:44:09 2008 +0000
@@ -336,6 +336,6 @@
 
 void qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
 {
-	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", buf, len, desc);
+	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", buf, len, desc);
 }