changeset 24091:d35672443baa

2008.10.10 - ccpaging <ccpaging(at)gmail.com> * Support part of 'buddy' protocol of QQ2007/2008
author SHiNE CsyFeK <csyfek@gmail.com>
date Tue, 28 Oct 2008 16:42:46 +0000
parents 9be95f0b9472
children 7c0a56c5fea0
files libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/buddy_list.c libpurple/protocols/qq/buddy_opt.c libpurple/protocols/qq/buddy_opt.h libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq_process.c
diffstat 6 files changed, 340 insertions(+), 306 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:40:56 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Tue Oct 28 16:42:46 2008 +0000
@@ -1,3 +1,6 @@
+2008.10.10 - ccpaging <ccpaging(at)gmail.com>
+	* Support part of 'buddy' protocol of QQ2007/2008
+
 2008.10.10 - ccpaging <ccpaging(at)gmail.com>
 	* Keep group_search.c/h for later use
 	* Update 'group' 
--- a/libpurple/protocols/qq/buddy_list.c	Tue Oct 28 16:40:56 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Tue Oct 28 16:42:46 2008 +0000
@@ -609,7 +609,7 @@
 		break;
 	default:
 		status_id = "invisible";
-		purple_debug_error("QQ", "unknown status: %x\n", status);
+		purple_debug_error("QQ", "unknown status: 0x%X\n", status);
 		break;
 	}
 	purple_debug_info("QQ", "Update buddy %s status as %s\n", who, status_id);
--- a/libpurple/protocols/qq/buddy_opt.c	Tue Oct 28 16:40:56 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Tue Oct 28 16:42:46 2008 +0000
@@ -40,9 +40,7 @@
 #include "utils.h"
 
 #define PURPLE_GROUP_QQ_FORMAT          "QQ (%s)"
-#define PURPLE_GROUP_QQ_UNKNOWN      "QQ Unknown"
 
-#define QQ_REMOVE_BUDDY_REPLY_OK      0x00
 #define QQ_REMOVE_SELF_REPLY_OK       0x00
 #define QQ_ADD_BUDDY_AUTH_REPLY_OK    0x30	/* ASCII value of "0" */
 
@@ -57,15 +55,151 @@
 	PurpleConnection *gc;
 } qq_buddy_req;
 
+PurpleGroup *qq_group_find_or_new(const gchar *group_name)
+{
+	PurpleGroup *g;
+
+	g_return_val_if_fail(group_name != NULL, NULL);
+
+	g = purple_find_group(group_name);
+	if (g == NULL) {
+		g = purple_group_new(group_name);
+		purple_blist_add_group(g, NULL);
+		purple_debug_warning("QQ", "Add new group: %s\n", group_name);
+	}
+
+	return g;
+}
+
+static qq_buddy_data *qq_buddy_data_new(guint32 uid)
+{
+	qq_buddy_data *bd = g_new0(qq_buddy_data, 1);
+	memset(bd, 0, sizeof(qq_buddy_data));
+	bd->uid = uid;
+	bd->status = QQ_BUDDY_OFFLINE;
+	return bd;
+}
+
+qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, guint32 uid)
+{
+	gchar *who;
+	PurpleBuddy *buddy;
+
+	g_return_val_if_fail(gc != NULL, NULL);
+
+	who = uid_to_purple_name(uid);
+	if (who == NULL)	return NULL;
+	buddy = purple_find_buddy(purple_connection_get_account(gc), who);
+	g_free(who);
+
+	if (buddy == NULL) {
+		purple_debug_error("QQ", "Can not find purple buddy of %d\n", uid);
+		return NULL;
+	}
+	if (buddy->proto_data == NULL) {
+		purple_debug_error("QQ", "Can not find buddy data of %d\n", uid);
+		return NULL;
+	}
+	return (qq_buddy_data *)buddy->proto_data;
+}
+
+void qq_buddy_data_free(qq_buddy_data *bd)
+{
+	g_return_if_fail(bd != NULL);
+
+	if (bd->nickname) g_free(bd->nickname);
+	g_free(bd);
+}
+
+/* create purple buddy without data and display with no-auth icon */
+PurpleBuddy *qq_buddy_new(PurpleConnection *gc, guint32 uid)
+{
+	PurpleBuddy *buddy;
+	PurpleGroup *group;
+	gchar *who;
+	gchar *group_name;
+
+	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+
+	group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT,
+			purple_account_get_username(gc->account));
+	group = qq_group_find_or_new(group_name);
+	if (group == NULL) {
+		purple_debug_error("QQ", "Failed creating group %s\n", group_name);
+		return NULL;
+	}
+
+	who = uid_to_purple_name(uid);
+
+	purple_debug_info("QQ", "Add new purple buddy: [%s]\n", who);
+	buddy = purple_buddy_new(gc->account, who, NULL);	/* alias is NULL */
+	buddy->proto_data = NULL;
+
+	g_free(who);
+
+	purple_blist_add_buddy(buddy, NULL, group, NULL);
+
+	g_free(group_name);
+
+	return buddy;
+}
+
+static void qq_buddy_free(PurpleBuddy *buddy)
+{
+	g_return_if_fail(buddy);
+	if (buddy->proto_data)	{
+		qq_buddy_data_free(buddy->proto_data);
+	}
+	buddy->proto_data = NULL;
+	purple_blist_remove_buddy(buddy);
+}
+
+PurpleBuddy *qq_buddy_find(PurpleConnection *gc, guint32 uid)
+{
+	PurpleBuddy *buddy;
+	gchar *who;
+
+	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+
+	who = uid_to_purple_name(uid);
+	buddy = purple_find_buddy(gc->account, who);
+	g_free(who);
+	return buddy;
+}
+
+PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, guint32 uid)
+{
+	PurpleBuddy *buddy;
+
+	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+
+	buddy = qq_buddy_find(gc, uid);
+	if (buddy == NULL) {
+		buddy = qq_buddy_new(gc, uid);
+		if (buddy == NULL) {
+			return NULL;
+		}
+	}
+
+	if (buddy->proto_data != NULL) {
+		return buddy;
+	}
+
+	buddy->proto_data = qq_buddy_data_new(uid);
+	return buddy;
+}
+
 /* send packet to remove a buddy from my buddy list */
 static void request_buddy_remove(PurpleConnection *gc, guint32 uid)
 {
 	gchar uid_str[11];
+	gint bytes;
 
 	g_return_if_fail(uid > 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_BUDDY_REMOVE, (guint8 *) uid_str, strlen(uid_str));
+	bytes = strlen(uid_str);
+	qq_send_cmd_mess(gc, QQ_CMD_BUDDY_REMOVE, (guint8 *) uid_str, bytes, 0, uid);
 }
 
 /* try to remove myself from someone's buddy list */
@@ -78,7 +212,7 @@
 
 	bytes += qq_put32(raw_data + bytes, uid);
 
-	qq_send_cmd(gc, QQ_CMD_REMOVE_ME, raw_data, bytes);
+	qq_send_cmd_mess(gc, QQ_CMD_REMOVE_ME, raw_data, bytes, 0, uid);
 }
 
 /* try to add a buddy without authentication */
@@ -220,12 +354,14 @@
 /* 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) */
+ * define qd->is_login=TRUE AFTER LOGIN */
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	qq_data *qd;
 	guint32 uid;
-	PurpleBuddy *b;
+
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+	g_return_if_fail(buddy != NULL);
 
 	qd = (qq_data *) gc->proto_data;
 	if (!qd->is_login)
@@ -237,39 +373,13 @@
 		return;
 	}
 
-	b = purple_find_buddy(gc->account, buddy->name);
-	if (b != NULL) {
-		purple_blist_remove_buddy(b);
-	}
-	purple_notify_error(gc, _("QQ Buddy"), _("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;
-	g_return_if_fail(who != 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);
-		}
+	purple_notify_error(gc, _("QQ Buddy"), _("Add buddy"), _("Invalid QQ Number"));
+	if (buddy == NULL) {
 		return;
 	}
 
-	if (strcmp(old_group, PURPLE_GROUP_QQ_UNKNOWN) != 0) {
-		return;
-	}
-
-	uid = purple_name_to_uid(who);
-	g_return_if_fail(uid != 0);
-
-	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);
+	purple_debug_info("QQ", "Remove buddy with invalid QQ number %d\n", uid);
+	qq_buddy_free(buddy);
 }
 
 static void buddy_cancel_cb(qq_buddy_req *add_req, const gchar *msg)
@@ -290,86 +400,6 @@
 	g_free(add_req);
 }
 
-static void buddy_remove_both_cb(qq_buddy_req *add_req)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-	gchar *purple_name;
-	PurpleBuddy *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;
-	}
-
-	if (buddy->proto_data != NULL) {
-		qq_buddy_data_free(buddy->proto_data);
-		buddy->proto_data = NULL;
-	} else {
-		purple_debug_warning("QQ", "We have no qq_buddy_data record for %s\n", buddy->name);
-	}
-
-	purple_blist_remove_buddy(buddy);
-	g_free(add_req);
-}
-
-/* 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_buddy_req *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);
-
-	if (uid == qd->uid) {
-		return;
-	}
-
-	add_req = g_new0(qq_buddy_req, 1);
-	add_req->gc = gc;
-	add_req->uid = 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_buddy_add_auth(guint8 *data, gint data_len, PurpleConnection *gc)
 {
@@ -381,61 +411,72 @@
 	qd = (qq_data *) gc->proto_data;
 
 	if (data[0] != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
-		purple_debug_warning("QQ", "Failed authorizing of adding requestion\n");
 		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 authorizing of adding requestion"), msg_utf8);
+		purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), msg_utf8);
 		g_free(msg_utf8);
 	} else {
-		qq_got_attention(gc, _("Successed authorizing of adding requestion"));
+		qq_got_attention(gc, _("Successed sending authorize"));
 	}
 }
 
 /* process the server reply for my request to remove a buddy */
-void qq_process_buddy_remove(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_buddy_remove(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
 {
-	qq_data *qd;
+	PurpleBuddy *buddy = NULL;
+	gchar *msg;
 
 	g_return_if_fail(data != NULL && data_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
+	g_return_if_fail(uid != 0);
 
-	if (data[0] != QQ_REMOVE_BUDDY_REPLY_OK) {
-		/* there is no reason return from server */
-		purple_debug_warning("QQ", "Remove buddy fails\n");
-		purple_notify_info(gc, _("QQ Buddy"), _("Failed:"),  _("Remove buddy"));
-	} else {		/* if reply */
+	buddy = qq_buddy_find(gc, uid);
+	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);
 }
 
 /* process the server reply for my request to remove myself from a buddy */
-void qq_process_buddy_remove_me(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_buddy_remove_me(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
 {
 	qq_data *qd;
+	gchar *msg;
 
 	g_return_if_fail(data != NULL && data_len != 0);
-
 	qd = (qq_data *) gc->proto_data;
 
-	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 me from other's buddy list"));
-	} else {		/* if reply */
-		qq_got_attention(gc, _("Successed removing me from other's budy list."));
+	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);
 	}
+	g_free(msg);
 }
 
 void qq_process_buddy_add_no_auth(guint8 *data, gint data_len, guint32 uid, PurpleConnection *gc)
 {
 	qq_data *qd;
 	gchar *msg, **segments, *dest_uid, *reply;
-	PurpleBuddy *b;
+	PurpleBuddy *buddy;
 	qq_buddy_req *add_req;
-	gchar *nombre;
+	gchar *who;
 
 	g_return_if_fail(data != NULL && data_len != 0);
 
@@ -444,9 +485,9 @@
 	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", "Process buddy add for id [%d]\n", uid);
 	}
+	purple_debug_info("QQ", "Process buddy add for id [%d]\n", uid);
+	qq_show_packet("buddy_add_no_auth", data, data_len);
 
 	if (NULL == (segments = split_data(data, data_len, "\x1f", 2)))
 		return;
@@ -459,163 +500,55 @@
 		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(uid);
-		b = purple_find_buddy(gc->account, nombre);
-		if (b != NULL)
-			purple_blist_remove_buddy(b);
-		add_req = g_new0(qq_buddy_req, 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(request_buddy_add_auth_cb),
-				_("Cancel"), G_CALLBACK(buddy_cancel_cb),
-				purple_connection_get_account(gc), nombre, NULL,
-				add_req);
-		g_free(msg);
-		g_free(nombre);
-	} else {	/* add OK */
+	if (strtol(reply, NULL, 10) == 0) {
+		/* add OK */
 		qq_buddy_find_or_new(gc, uid);
+
 		qq_request_buddy_info(gc, uid, 0, 0);
-		qq_request_get_buddies_online(gc, 0, 0);
 		if (qd->client_version >= 2007) {
 			qq_request_get_level_2007(gc, uid);
 		} else {
 			qq_request_get_level(gc, uid);
 		}
+		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);
-	}
-	g_strfreev(segments);
-}
-
-PurpleGroup *qq_group_find_or_new(const gchar *group_name)
-{
-	PurpleGroup *g;
-
-	g_return_val_if_fail(group_name != NULL, NULL);
-
-	g = purple_find_group(group_name);
-	if (g == NULL) {
-		g = purple_group_new(group_name);
-		purple_blist_add_group(g, NULL);
-		purple_debug_warning("QQ", "Add new group: %s\n", group_name);
+		g_strfreev(segments);
+		return;
 	}
 
-	return g;
-}
-
-static qq_buddy_data *qq_buddy_data_new(guint32 uid)
-{
-	qq_buddy_data *bd = g_new0(qq_buddy_data, 1);
-	memset(bd, 0, sizeof(qq_buddy_data));
-	bd->uid = uid;
-	bd->status = QQ_BUDDY_ONLINE_NORMAL;
-	return bd;
-}
-
-qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, guint32 uid)
-{
-	gchar *who;
-	PurpleBuddy *buddy;
-
-	g_return_val_if_fail(gc != NULL, NULL);
-
-	who = uid_to_purple_name(uid);
-	if (who == NULL)	return NULL;
-	buddy = purple_find_buddy(purple_connection_get_account(gc), who);
-	g_free(who);
-
-	if (buddy == NULL) {
-		purple_debug_error("QQ", "Can not find purple buddy of %d\n", uid);
-		return NULL;
-	}
-	if (buddy->proto_data == NULL) {
-		purple_debug_error("QQ", "Can not find buddy data of %d\n", uid);
-		return NULL;
-	}
-	return (qq_buddy_data *)buddy->proto_data;
-}
-
-void qq_buddy_data_free(qq_buddy_data *bd)
-{
-	g_return_if_fail(bd != NULL);
-
-	if (bd->nickname) g_free(bd->nickname);
-	g_free(bd);
-}
-
-/* create purple buddy without data and display with no-auth icon */
-PurpleBuddy *qq_buddy_new(PurpleConnection *gc, guint32 uid)
-{
-	PurpleBuddy *buddy;
-	PurpleGroup *group;
-	gchar *who;
-	gchar *group_name;
-
-	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
-
-	group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT,
-			purple_account_get_username(gc->account));
-	group = qq_group_find_or_new(group_name);
-	if (group == NULL) {
-		purple_debug_error("QQ", "Failed creating group %s\n", group_name);
-		return NULL;
-	}
-
-	who = uid_to_purple_name(uid);
-	buddy = purple_buddy_new(gc->account, who, NULL);	/* alias is NULL */
-	g_free(who);
-	buddy->proto_data = NULL;
-
-	purple_blist_add_buddy(buddy, NULL, group, NULL);
-	purple_debug_info("QQ", "Add new purple buddy: [%s]\n", who);
-
-	g_free(group_name);
-
-	return buddy;
-}
-
-PurpleBuddy *qq_buddy_find(PurpleConnection *gc, guint32 uid)
-{
-	PurpleBuddy *buddy;
-	gchar *who;
-
-	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
-
-	who = uid_to_purple_name(uid);
-	buddy = purple_find_buddy(gc->account, who);
-	g_free(who);
-	return buddy;
-}
-
-PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, guint32 uid)
-{
-	PurpleBuddy *buddy;
-
-	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+	/* need auth */
+	purple_debug_warning("QQ", "Failed adding buddy, need authorize\n");
 
 	buddy = qq_buddy_find(gc, uid);
 	if (buddy == NULL) {
 		buddy = qq_buddy_new(gc, uid);
-		if (buddy == NULL) {
-			return NULL;
-		}
+	}
+	if (buddy != NULL && buddy->proto_data != NULL) {
+		/* Not authorized now, free buddy data */
+		qq_buddy_data_free(buddy->proto_data);
+		buddy->proto_data = NULL;
 	}
 
-	if (buddy->proto_data != NULL) {
-		return buddy;
-	}
+	who = uid_to_purple_name(uid);
+	add_req = g_new0(qq_buddy_req, 1);
+	add_req->gc = gc;
+	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 */
+			_("Would you be my friend?"),
+			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);
 
-	buddy->proto_data = qq_buddy_data_new(uid);
-	return buddy;
+	g_free(msg);
+	g_free(who);
+	g_strfreev(segments);
 }
 
 /* remove a buddy and send packet to QQ server accordingly */
@@ -626,21 +559,22 @@
 
 	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;
 
+	uid = purple_name_to_uid(buddy->name);
 	if (uid > 0) {
 		request_buddy_remove(gc, uid);
+		request_buddy_remove_me(gc, uid);
 	}
 
 	if (buddy->proto_data) {
 		qq_buddy_data_free(buddy->proto_data);
 		buddy->proto_data = NULL;
 	} else {
-		purple_debug_warning("QQ", "We have no qq_buddy_data record for %s\n", buddy->name);
+		purple_debug_warning("QQ", "Empty buddy data of %s\n", buddy->name);
 	}
 
 	/* Do not call purple_blist_remove_buddy,
@@ -769,6 +703,8 @@
 static void server_buddy_rejected_me(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
 {
 	gchar *message, *reason;
+	guint32 uid;
+	PurpleBuddy *buddy;
 
 	g_return_if_fail(from != NULL && to != NULL);
 
@@ -778,6 +714,16 @@
 	purple_notify_info(gc, _("QQ Buddy"), message, reason);
 	g_free(message);
 	g_free(reason);
+	
+	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 */
+		qq_buddy_data_free(buddy->proto_data);
+		buddy->proto_data = NULL;
+	}
 }
 
 void qq_process_buddy_from_server(PurpleConnection *gc, int funct,
--- a/libpurple/protocols/qq/buddy_opt.h	Tue Oct 28 16:40:56 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.h	Tue Oct 28 16:42:46 2008 +0000
@@ -36,8 +36,8 @@
 void qq_remove_buddy_and_me(PurpleBlistNode * node);
 void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 
-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_remove(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
+void qq_process_buddy_remove_me(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
 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,
--- a/libpurple/protocols/qq/qq.c	Tue Oct 28 16:40:56 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Tue Oct 28 16:42:46 2008 +0000
@@ -29,6 +29,7 @@
 #include "notify.h"
 #include "prefs.h"
 #include "prpl.h"
+#include "privacy.h"
 #include "request.h"
 #include "roomlist.h"
 #include "server.h"
@@ -441,6 +442,60 @@
 	qq_request_change_status(gc, 0);
 }
 
+static void qq_add_deny(PurpleConnection *gc, const char *who)
+{
+	qq_data *qd;
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+
+	qd = (qq_data *) gc->proto_data;
+	if (!qd->is_login)
+		return;
+
+	if (!who || who[0] == '\0')
+		return;
+
+	purple_debug_info("QQ", "Add deny for %s\n", who);
+}
+
+static void qq_rem_deny(PurpleConnection *gc, const char *who)
+{
+	qq_data *qd;
+	g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+
+	qd = (qq_data *) gc->proto_data;
+	if (!qd->is_login)
+		return;
+
+	if (!who || who[0] == '\0')
+		return;
+
+	purple_debug_info("QQ", "Rem deny for %s\n", who);
+}
+
+static void qq_set_permit_deny(PurpleConnection *gc)
+{
+	PurpleAccount *account;
+	GSList *deny;
+
+	purple_debug_info("QQ", "Set permit deny\n");
+	account = purple_connection_get_account(gc);
+	switch (account->perm_deny)
+	{
+		case PURPLE_PRIVACY_ALLOW_ALL:
+			for (deny = account->deny; deny; deny = deny->next)
+				qq_rem_deny(gc, deny->data);
+			break;
+
+		case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
+		case PURPLE_PRIVACY_ALLOW_USERS:
+		case PURPLE_PRIVACY_DENY_USERS:
+		case PURPLE_PRIVACY_DENY_ALL:
+			for (deny = account->deny; deny; deny = deny->next)
+				qq_add_deny(gc, deny->data);
+			break;
+	}
+}
+
 /* IMPORTANT: PurpleConvImFlags -> PurpleMessageFlags */
 /* send an instant msg to a buddy */
 static gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
@@ -842,6 +897,49 @@
 	return m;
 }
 
+static void qq_add_buddy_from_menu_cb(PurpleBlistNode *node, gpointer data)
+{
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *) node;
+	gc = purple_account_get_connection(buddy->account);
+
+	qq_add_buddy(gc, buddy, NULL);
+}
+
+static GList *qq_buddy_menu(PurpleBuddy *buddy)
+{
+	GList *m = NULL;
+	PurpleMenuAction *act;
+	/*
+	PurpleConnection *gc = purple_account_get_connection(buddy->account);
+	qq_data *qd = gc->proto_data;
+	*/
+	qq_buddy_data *bd = (qq_buddy_data *)buddy->proto_data;
+
+	if (bd == NULL) {
+		act = purple_menu_action_new(_("Add Buddy"),
+		                           PURPLE_CALLBACK(qq_add_buddy_from_menu_cb),
+		                           NULL, NULL);
+		m = g_list_append(m, act);
+
+		return m;
+
+	}
+
+/* TODO : not working, temp commented out by gfhuang */
+#if 0
+	if (bd && is_online(bd->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);
+	}
+#endif
+	return m;
+}
+
 /* chat-related (QQ Qun) menu shown up with right-click */
 static GList *qq_chat_menu(PurpleBlistNode *node)
 {
@@ -858,28 +956,15 @@
 }
 
 /* buddy-related menu shown up with right-click */
-static GList *qq_buddy_menu(PurpleBlistNode * node)
+static GList *qq_blist_node_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);
+	if(PURPLE_BLIST_NODE_IS_BUDDY(node))
+		return qq_buddy_menu((PurpleBuddy *) node);
 
-/* TODO : not working, temp commented out by gfhuang */
-#if 0
-/*	if (bd && is_online(bd->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);
-/*	} */
-#endif
-
-	return m;
+	return NULL;
 }
 
 /* convert name displayed in a chat channel to original QQ UID */
@@ -953,7 +1038,7 @@
 	qq_status_text,					/* status_text	*/
 	qq_tooltip_text,					/* tooltip_text */
 	qq_status_types,					/* away_states	*/
-	qq_buddy_menu,						/* blist_node_menu */
+	qq_blist_node_menu,			/* blist_node_menu */
 	qq_chat_info,						/* chat_info */
 	qq_chat_info_defaults,					/* chat_info_defaults */
 	qq_login,							/* open */
@@ -970,10 +1055,10 @@
 	qq_remove_buddy,					/* remove_buddy */
 	NULL,							/* remove_buddies */
 	NULL,							/* add_permit */
-	NULL,							/* add_deny */
+	qq_add_deny,							/* add_deny */
 	NULL,							/* rem_permit */
 	NULL,							/* rem_deny */
-	NULL,							/* set_permit_deny */
+	qq_set_permit_deny,			/* set_permit_deny */
 	qq_group_join,						/* join_chat */
 	NULL,							/* reject chat	invite */
 	NULL,							/* get_chat_name */
@@ -986,7 +1071,7 @@
 	qq_get_chat_buddy_info,				/* get_cb_info	*/
 	NULL,							/* get_cb_away	*/
 	NULL,							/* alias_buddy	*/
-	qq_change_buddys_group,							/* group_buddy	*/
+	NULL,							/* change buddy's group	*/
 	NULL,							/* rename_group */
 	NULL,							/* buddy_free */
 	NULL,							/* convo_closed */
--- a/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:40:56 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Tue Oct 28 16:42:46 2008 +0000
@@ -1018,10 +1018,10 @@
 			qq_process_buddy_add_no_auth(data, data_len, ship32, gc);
 			break;
 		case QQ_CMD_BUDDY_REMOVE:
-			qq_process_buddy_remove(data, data_len, gc);
+			qq_process_buddy_remove(gc, data, data_len, ship32);
 			break;
 		case QQ_CMD_REMOVE_ME:
-			qq_process_buddy_remove_me(data, data_len, gc);
+			qq_process_buddy_remove_me(gc, data, data_len, ship32);
 			break;
 		case QQ_CMD_BUDDY_ADD_AUTH:
 			qq_process_buddy_add_auth(data, data_len, gc);