changeset 25151:7c21fb8132f7

propagate from branch 'im.pidgin.pidgin' (head 5aca19ea8964e864e3374ac4fd3c1c81cd23a426) to branch 'im.pidgin.pidgin.openq' (head c733d767a0fad23b0f329c177b50cbcf4de0d140)
author SHiNE CsyFeK <csyfek@gmail.com>
date Tue, 24 Feb 2009 14:37:38 +0000
parents ac8defe6eee4 (current diff) d673ccc44a6e (diff)
children 69dae1012aee b16a35e21a3d ba0289e94bdf 7bae3a3ea4c1 3a0c2526364f 754d7d478066 e0e0cecdcd16
files
diffstat 22 files changed, 602 insertions(+), 119 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/AUTHORS	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/AUTHORS	Tue Feb 24 14:37:38 2009 +0000
@@ -23,6 +23,8 @@
 Coly Li		: qun processing
 Emil Alexiev	: captcha verification on login based on LumaQQ for MAC (2007), 
 		  login, add buddy, remove buddy, message exchange and logout
+Chengming Wang 	: buddy memo
+lonicerae 	: chat room window bugfix, server list bugfix, buddy memo
 
 Acknowledgement
 =========
@@ -32,8 +34,16 @@
 yunfan		: http://www.myswear.net
 OpenQ Team	: http://openq.linuxsir.org
 LumaQQ Team	: http://lumaqq.linuxsir.org
-khc@pidgin.im
-qulogic@pidgin.im
-rlaager@pidgin.im
+Pidgin Team	: http://www.pidgin.im
 Huang Guan	: http://home.xxsyzx.com
 OpenQ Google Group	: http://groups.google.com/group/openq
+
+Scrupulous Testers
+=========
+yegle
+cnzhangbx
+casparant
+wd
+x6719620
+netelk
+and more, please let me know... thank you!
--- a/libpurple/protocols/qq/ChangeLog	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Tue Feb 24 14:37:38 2009 +0000
@@ -1,3 +1,22 @@
+2009.02.24 - flos <lonicerae(at)gmail.com>
+	* Changed update_class type from gint to guint32
+	* Fixed a bug of displaying of memo when memo is nothing
+
+2009.02.21 - flos <lonicerae(at)gmail.com>
+	* Added 'qq_strlen' and 'qq_strcmp' funtions in qq.h
+	* Fixed compiling problems with lower version of GTK+
+
+2009.02.21 - flos <lonicerae(at)gmail.com>
+	* Rewrite whole buddy memo part
+	* Remove 'qq_to_utf8_len' and 'utf8_to_qq_len' functions in char_conv.c
+	* Update ChangeLog, AUTHORS
+
+2009.02.09 - Chengming Wang <tiger2007532246(at)gmail.com>
+	* Rewrite buddy_memo using qq_put/qq_get series functions
+
+2009.02.09 - Chengming Wang <tiger2007532246(at)gmail.com>
+	* Added buddy 'get memo', 'remove memo', 'upload memo' functions
+
 2009.02.08 - flos <lonicerae(at)gmail.com>
 	* Fixed showing message of chat room when message comes in
 
--- a/libpurple/protocols/qq/Makefile.am	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Tue Feb 24 14:37:38 2009 +0000
@@ -6,6 +6,8 @@
 QQSOURCES = \
 	buddy_info.c \
 	buddy_info.h \
+	buddy_memo.c \
+	buddy_memo.h \
 	buddy_list.c \
 	buddy_list.h \
 	buddy_opt.c \
--- a/libpurple/protocols/qq/Makefile.mingw	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Tue Feb 24 14:37:38 2009 +0000
@@ -41,6 +41,7 @@
 C_SRC = \
 	buddy_info.c \
 	buddy_list.c \
+	buddy_memo.c \
 	buddy_opt.c \
 	char_conv.c \
 	qq_crypt.c \
--- a/libpurple/protocols/qq/buddy_info.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Tue Feb 24 14:37:38 2009 +0000
@@ -222,7 +222,7 @@
 }
 
 void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
-		gint update_class, int action)
+		guint32 update_class, int action)
 {
 	qq_data *qd;
 	gchar raw_data[16] = {0};
@@ -752,7 +752,7 @@
 	qq_send_cmd(gc, QQ_CMD_GET_LEVEL, buf, bytes);
 }
 
-void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class)
+void qq_request_get_buddies_level(PurpleConnection *gc, guint32 update_class)
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
 	PurpleBuddy *buddy;
--- a/libpurple/protocols/qq/buddy_info.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Tue Feb 24 14:37:38 2009 +0000
@@ -79,14 +79,14 @@
 void qq_change_icon_cb(PurpleConnection *gc, const char *filepath);
 
 void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
-		gint update_class, int action);
+		guint32 update_class, int action);
 void qq_set_custom_icon(PurpleConnection *gc, PurpleStoredImage *img);
 void qq_process_change_info(PurpleConnection *gc, guint8 *data, gint data_len);
 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_level_2007(PurpleConnection *gc, guint32 uid);
-void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class);
+void qq_request_get_buddies_level(PurpleConnection *gc, guint32 update_class);
 void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
 
 void qq_update_buddy_icon(PurpleAccount *account, const gchar *who, gint face);
--- a/libpurple/protocols/qq/buddy_list.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Tue Feb 24 14:37:38 2009 +0000
@@ -31,6 +31,7 @@
 #include "utils.h"
 #include "packet_parse.h"
 #include "buddy_info.h"
+#include "buddy_memo.h"
 #include "buddy_list.h"
 #include "buddy_opt.h"
 #include "char_conv.h"
@@ -55,7 +56,7 @@
 } qq_buddy_online;
 
 /* get a list of online_buddies */
-void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class)
+void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, guint32 update_class)
 {
 	qq_data *qd;
 	guint8 *raw_data;
@@ -81,7 +82,7 @@
 
 /* position starts with 0x0000,
  * server may return a position tag if list is too long for one packet */
-void qq_request_get_buddies(PurpleConnection *gc, guint16 position, gint update_class)
+void qq_request_get_buddies(PurpleConnection *gc, guint16 position, guint32 update_class)
 {
 	qq_data *qd;
 	guint8 raw_data[16] = {0};
@@ -105,7 +106,7 @@
 }
 
 /* get all list, buddies & Quns with groupsid support */
-void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, gint update_class)
+void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, guint32 update_class)
 {
 	guint8 raw_data[16] = {0};
 	gint bytes = 0;
@@ -346,6 +347,8 @@
 		/* nickname has been copy to buddy_data do not free
 		   g_free(bd.nickname);
 		*/
+		/*qq_request_buddy_memo(gc, ((qq_buddy_data*)buddy->proto_data)->uid, 0, QQ_BUDDY_MEMO_GET);*/
+		qq_request_buddy_memo(gc, bd.uid, bd.uid, QQ_BUDDY_MEMO_GET);
 	}
 
 	if(bytes > data_len) {
@@ -460,7 +463,7 @@
 }
 
 /* send a packet to change my online status */
-void qq_request_change_status(PurpleConnection *gc, gint update_class)
+void qq_request_change_status(PurpleConnection *gc, guint32 update_class)
 {
 	qq_data *qd;
 	guint8 raw_data[16] = {0};
--- a/libpurple/protocols/qq/buddy_list.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/buddy_list.h	Tue Feb 24 14:37:38 2009 +0000
@@ -40,16 +40,16 @@
 	guint8 unknown_key[QQ_KEY_LENGTH];
 } qq_buddy_status;
 
-void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class);
+void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, guint32 update_class);
 guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_request_get_buddies(PurpleConnection *gc, guint16 position, gint update_class);
+void qq_request_get_buddies(PurpleConnection *gc, guint16 position, guint32 update_class);
 guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, gint update_class);
+void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, guint32 update_class);
 guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc);
 
-void qq_request_change_status(PurpleConnection *gc, gint update_class);
+void qq_request_change_status(PurpleConnection *gc, guint32 update_class);
 void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
 void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/buddy_memo.c	Tue Feb 24 14:37:38 2009 +0000
@@ -0,0 +1,366 @@
+#include "internal.h"
+#include "debug.h"
+#include "notify.h"
+#include "request.h"
+
+#include "buddy_memo.h"
+#include "utils.h"
+#include "packet_parse.h"
+#include "buddy_list.h"
+#include "buddy_info.h"
+#include "char_conv.h"
+#include "im.h"
+#include "qq_define.h"
+#include "qq_base.h"
+#include "qq_network.h"
+#include "qq.h"
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* memo index */
+enum {
+	QQ_MEMO_ALIAS = 0,
+	QQ_MEMO_MOBILD,
+	QQ_MEMO_TELEPHONE,
+	QQ_MEMO_ADDRESS,
+	QQ_MEMO_EMAIL,
+	QQ_MEMO_ZIPCODE,
+	QQ_MEMO_NOTE,
+	QQ_MEMO_SIZE
+};
+
+/* memo id */
+static const gchar *memo_id[] = {
+	"mm_alias",
+	"mm_mobile",
+	"mm_telephone",
+	"mm_address",
+	"mm_email",
+	"mm_zipcode",
+	"mm_note"
+};
+
+/* memo text */
+static const gchar *memo_txt[] = {
+	N_("Alias"),
+	N_("Mobile"),
+	N_("Telephone"),
+	N_("Address"),
+	N_("Email"),
+	N_("ZipCode"),
+	N_("Note")
+};
+
+typedef struct _modify_memo_request {
+	PurpleConnection *gc;
+	guint32 bd_uid;
+	gchar **segments;
+} modify_memo_request;
+
+
+static void memo_debug(gchar **segments)
+{
+	gint index;
+	g_return_if_fail(NULL != segments);
+	for (index = 0;  index < QQ_MEMO_SIZE; index++) {
+		purple_debug_info("QQ","memo[%i]=%s\n", index, segments[index]);
+	}
+}
+
+static void memo_free(gchar **segments)
+{
+	gint index;
+	g_return_if_fail(NULL != segments);
+	for (index = 0; index < QQ_MEMO_SIZE; index++) {
+		g_free(segments[index]);
+	}
+	purple_debug_info("QQ", "memo freed\n");
+}
+
+static void update_buddy_memo(PurpleConnection *gc, guint32 bd_uid, gchar *alias)
+{
+	PurpleAccount *account;
+	PurpleBuddy *buddy;
+	gchar *who;
+	g_return_if_fail(NULL != gc && NULL != alias);
+
+	account = (PurpleAccount *)gc->account;
+	g_return_if_fail(NULL != account);
+
+	who = uid_to_purple_name(bd_uid);
+	buddy = purple_find_buddy(account, who);
+	if (buddy == NULL || buddy->proto_data == NULL) {
+		g_free(who);
+		purple_debug_info("QQ", "Error...Can NOT find %d!\n", bd_uid);
+		return;
+	}
+	purple_blist_alias_buddy(buddy, (const char*)alias);
+}
+
+static void request_change_memo(PurpleConnection *gc, guint32 bd_uid, gchar **segments)
+{
+	gint bytes;
+	/* Attention, length of each segment must be guint8(0~255),
+	 * so length of memo string is limited.
+	 * convert it to guint8 first before putting data */
+	guint seg_len;
+	gint index;
+	guint8 raw_data[MAX_PACKET_SIZE - 16] = {0};
+
+	purple_debug_info( "QQ", "request_change_memo\n" );
+	g_return_if_fail(NULL != gc && NULL != segments);
+
+	bytes = 0;
+	bytes += qq_put8(raw_data+bytes, QQ_BUDDY_MEMO_MODIFY);
+	bytes += qq_put8(raw_data+bytes, 0x00);
+	bytes += qq_put32(raw_data+bytes, (guint32)bd_uid);
+	bytes += qq_put8(raw_data+bytes, 0x00);
+	for (index = 0; index < QQ_MEMO_SIZE; index++) {
+		seg_len = strlen(segments[index]);
+		seg_len = seg_len & 0xff;
+		bytes += qq_put8(raw_data+bytes, (guint8)seg_len);
+		bytes += qq_putdata(raw_data+bytes, (const guint8 *)segments[index], (guint8)seg_len);
+	}
+
+	/* debug */
+	/*
+	   qq_show_packet("MEMO MODIFY", raw_data, bytes);
+	   */
+
+	qq_send_cmd(gc, QQ_CMD_BUDDY_MEMO, raw_data, bytes);
+}
+
+static void memo_modify_cancle_cb(modify_memo_request *memo_request, PurpleRequestFields *fields)
+{
+	memo_free(memo_request->segments);
+	g_free(memo_request);
+}
+
+/* prepare segments to be sent, string all convert to qq charset */
+static void memo_modify_ok_cb(modify_memo_request *memo_request, PurpleRequestFields *fields)
+{
+	PurpleConnection *gc;
+	guint32 bd_uid;
+	gchar **segments;
+	const gchar *utf8_str;
+	gchar *value = NULL;
+	gint index;
+
+	g_return_if_fail(NULL != memo_request);
+	gc = (PurpleConnection *)memo_request->gc;
+	segments = (gchar **)memo_request->segments;
+	g_return_if_fail(NULL != gc && NULL != segments);
+	bd_uid = (guint32)memo_request->bd_uid;
+
+
+	for (index = 0; index < QQ_MEMO_SIZE; index++) {
+		utf8_str = purple_request_fields_get_string(fields, memo_id[index]);
+		/* update alias */
+		if (QQ_MEMO_ALIAS == index) {
+			update_buddy_memo(gc, bd_uid, segments[QQ_MEMO_ALIAS]);
+		}
+		if (NULL == utf8_str) {
+			value = g_strdup("");
+		}
+		else {
+			value = utf8_to_qq(utf8_str, QQ_CHARSET_DEFAULT);
+			/* Warnning: value will be string "(NULL)" instead of NULL */
+			if (!qq_strcmp("(NULL)", value)) {
+				value = g_strdup("");
+			}
+		}
+		g_free(segments[index]);
+		segments[index] = value;
+	}
+
+	memo_debug(segments);
+	/* send segments */
+	request_change_memo(gc, bd_uid, segments);
+
+	/* free segments */
+	memo_free(segments);
+	g_free(memo_request);
+}
+
+/* memo modify dialogue */
+static void memo_modify_dialogue(PurpleConnection *gc, guint32 bd_uid, gchar **segments, guint32 action)
+{
+	modify_memo_request *memo_request;
+	PurpleRequestField *field;
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	int index;
+	gchar *utf8_title;
+	gchar *utf8_primary;
+
+	g_return_if_fail(NULL != gc && NULL != segments);
+
+	switch (action) {
+		case QQ_BUDDY_MEMO_GET:
+			memo_free(segments);
+			break;
+		case QQ_BUDDY_MEMO_MODIFY:
+			/* keep one dialog once a time */
+			purple_request_close_with_handle(gc);
+			/* show dialog */
+			fields = purple_request_fields_new();
+			group = purple_request_field_group_new(NULL);
+			purple_request_fields_add_group(fields, group);
+
+			for(index = 0; index < QQ_MEMO_SIZE; index++) {
+				/*
+				   purple_debug_info("QQ", "id:%s txt:%s segment:%s\n",
+				   memo_id[index], memo_txt[index], segments[index]);
+				   */
+				field = purple_request_field_string_new(memo_id[index], memo_txt[index],
+						segments[index], FALSE);
+				purple_request_field_group_add_field(group, field);
+			}
+
+			/* for upload cb */
+			memo_request = g_new0(modify_memo_request, 1);
+			memo_request->gc = gc;
+			memo_request->bd_uid = bd_uid;
+			memo_request->segments = segments;
+			/* callback */
+			utf8_title = g_strdup(_("Buddy Memo"));
+			utf8_primary = g_strdup(_("Change his/her memo as you like"));
+
+			purple_request_fields(gc, utf8_title, utf8_primary, NULL,
+					fields,
+					_("_Modify"), G_CALLBACK(memo_modify_ok_cb),
+					_("_Cancel"), G_CALLBACK(memo_modify_cancle_cb),
+					purple_connection_get_account(gc), NULL, NULL,
+					memo_request);
+
+			g_free(utf8_title);
+			g_free(utf8_primary);
+			break;
+		default:
+			purple_debug_info("QQ", "Error...unknown memo action, please tell us\n");
+			break;
+	}
+}
+
+static void qq_create_buddy_memo(PurpleConnection *gc, guint32 bd_uid, guint32 action)
+{
+	gchar **segments;
+	gint index;
+	g_return_if_fail(NULL != gc);
+
+	segments = g_new0(gchar*, QQ_MEMO_SIZE);
+	for (index = 0; index < QQ_MEMO_SIZE; index++) {
+		segments[index] = g_strdup("");;
+	}
+	memo_modify_dialogue(gc, bd_uid, segments, action);
+}
+
+/* process reply to get_memo packet */
+void qq_process_get_buddy_memo(PurpleConnection *gc, guint8* data, gint data_len,
+		guint32 update_class, guint32 action)
+{
+	gchar **segments;
+	gint bytes;
+	gint index;
+	guint8 rcv_cmd;
+	guint32 rcv_uid;
+	guint8 unk1_8;
+	guint8 is_success;
+
+	g_return_if_fail(NULL != gc && NULL != data && 0 != data_len);
+	/*
+	   qq_show_packet("MEMO REACH", data, data_len);
+	   */
+	purple_debug_info("QQ", "action=0x%02X\n", action);
+
+	bytes = 0;
+
+	/* TX looks a bit clever than before... :) */
+	bytes += qq_get8(&rcv_cmd, data+bytes);
+	purple_debug_info("QQ", "rcv_cmd=0x%02X\n", rcv_cmd);
+
+	/* it's possible that packet contains no buddy uid and no memo!!!
+	 * go to next step according to previous action sent */
+	if (1 == data_len) { /* only one byte */
+		purple_debug_info("QQ", "memo packet contains no buddy uid and memo...\n");
+		if (QQ_BUDDY_MEMO_MODIFY == action) {
+			qq_create_buddy_memo(gc, (guint32)update_class, QQ_BUDDY_MEMO_MODIFY);
+			return;
+		}
+		return;
+	}
+
+	switch (rcv_cmd) {
+		case QQ_BUDDY_MEMO_MODIFY:
+		case QQ_BUDDY_MEMO_REMOVE:
+			bytes += qq_get8(&is_success, data+bytes);
+			if (QQ_BUDDY_MEMO_REQUEST_SUCCESS == is_success) {
+				purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO,
+						_("Memo Modify"), _("Server says:"),
+						_("Your request was accepted."),
+						NULL, NULL);
+				purple_debug_info("QQ", "memo change succeessfully!\n");
+			}
+			else {
+				purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO,
+						_("Memo Modify"), _("Server says:"),
+						_("Your request was rejected."),
+						NULL, NULL);
+				purple_debug_info("QQ", "memo change failed\n");
+			}
+			break;
+		case QQ_BUDDY_MEMO_GET:
+			bytes += qq_get32(&rcv_uid, data+bytes);
+			purple_debug_info("QQ", "rcv_uid=%u\n", rcv_uid);
+			bytes += qq_get8(&unk1_8, data+bytes);
+			purple_debug_info("QQ", "unk1_8=0x%02X\n", unk1_8);
+			segments = g_new0(gchar*, QQ_MEMO_SIZE);
+			for (index = 0; index < QQ_MEMO_SIZE; index++) {
+				/* get utf8 string */
+				bytes += qq_get_vstr(&segments[index], QQ_CHARSET_DEFAULT, data+bytes);
+				/*
+				   purple_debug_info("QQ", "bytes:%d, seg:%s\n", bytes, segments[index]);
+				   */
+			}
+
+			/* common action, update buddy memo */
+			update_buddy_memo(gc, rcv_uid, segments[QQ_MEMO_ALIAS]);
+
+			/* memo is thing that we regard our buddy as, so we need one more buddy_uid */
+			memo_modify_dialogue(gc, rcv_uid, segments, action);
+			break;
+		default:
+			purple_debug_info("QQ", "received an UNKNOWN memo cmd!!!\n");
+			break;
+	}
+}
+
+/* request buddy memo */
+void qq_request_buddy_memo(PurpleConnection *gc, guint32 bd_uid, guint32 update_class, guint32 action)
+{
+	guint8 raw_data[16] = {0};
+	gint bytes;
+
+	purple_debug_info("QQ", "qq_request_buddy_memo, buddy uid=%u, update_class=%u\n",
+			bd_uid, update_class);
+	g_return_if_fail(NULL != gc);
+	/* '0' is ok
+	   g_return_if_fail(uid != 0);
+	   */
+	bytes = 0;
+	bytes += qq_put8(raw_data+bytes, QQ_BUDDY_MEMO_GET);
+	bytes += qq_put32(raw_data+bytes, bd_uid);
+	/*
+	   qq_show_packet("MEMO REQUEST", raw_data, bytes);
+	   */
+
+	qq_send_cmd_mess(gc, QQ_CMD_BUDDY_MEMO, (guint8*)raw_data, bytes, update_class, action);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/buddy_memo.h	Tue Feb 24 14:37:38 2009 +0000
@@ -0,0 +1,24 @@
+#ifndef _QQ_BUDDY_MEMO_H_
+#define _QQ_BUDDY_MEMO_H_
+
+#include <glib.h>
+#include "connection.h"
+#include "blist.h"
+
+#define QQ_BUDDY_MEMO_REQUEST_SUCCESS 0x00
+
+/* clan command for memo */
+enum
+{ 
+	QQ_BUDDY_MEMO_MODIFY = 0x01,	/* upload memo */
+	QQ_BUDDY_MEMO_REMOVE,		/* remove memo */
+	QQ_BUDDY_MEMO_GET		/* get memo */
+};
+
+
+void qq_process_get_buddy_memo(PurpleConnection *gc, guint8* data, gint data_len, guint32 update_class, guint32 action);
+
+void qq_request_buddy_memo(PurpleConnection *gc, guint32 bd_uid, guint32 update_class, guint32 action);
+
+#endif
+
--- a/libpurple/protocols/qq/group_info.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/group_info.c	Tue Feb 24 14:37:38 2009 +0000
@@ -65,7 +65,7 @@
 }
 
 /* send packet to get info for each group member */
-gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, gint update_class)
+gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, guint32 update_class)
 {
 	guint8 *raw_data;
 	gint bytes, num;
--- a/libpurple/protocols/qq/group_info.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/group_info.h	Tue Feb 24 14:37:38 2009 +0000
@@ -34,7 +34,7 @@
 	QQ_ROOM_INFO_DISPLAY
 };
 
-gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, gint update_class);
+gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, guint32 update_class);
 
 void qq_process_room_cmd_get_info(guint8 *data, gint len, guint32 action, PurpleConnection *gc);
 void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc);
--- a/libpurple/protocols/qq/qq.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq.c	Tue Feb 24 14:37:38 2009 +0000
@@ -36,6 +36,7 @@
 #include "util.h"
 
 #include "buddy_info.h"
+#include "buddy_memo.h"
 #include "buddy_opt.h"
 #include "buddy_list.h"
 #include "char_conv.h"
@@ -56,7 +57,7 @@
 #include "utils.h"
 #include "version.h"
 
-#define OPENQ_VERSION 		"0.3.2-p19" 
+#define OPENQ_VERSION 		"0.3.2-p20" 
 
 static GList *server_list_build(gchar select)
 {
@@ -112,17 +113,17 @@
 	if (qd->use_tcp) {
 		qd->servers =	server_list_build('T');
 		return;
-    }
+	}
 
 	qd->servers =	server_list_build('U');
 }
 
 static void server_list_remove_all(qq_data *qd)
 {
- 	g_return_if_fail(qd != NULL);
+	g_return_if_fail(qd != NULL);
 
 	purple_debug_info("QQ", "free server list\n");
- 	g_list_free(qd->servers);
+	g_list_free(qd->servers);
 	qd->curr_server = NULL;
 }
 
@@ -149,7 +150,7 @@
 	if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
 		qd->login_mode = QQ_LOGIN_MODE_HIDDEN;
 	} else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
-				|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)) {
+			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)) {
 		qd->login_mode = QQ_LOGIN_MODE_AWAY;
 	} else {
 		qd->login_mode = QQ_LOGIN_MODE_NORMAL;
@@ -254,27 +255,27 @@
 	status = g_string_new("");
 
 	switch(bd->status) {
-	case QQ_BUDDY_OFFLINE:
-		g_string_append(status, _("Offline"));
-		break;
-	case QQ_BUDDY_ONLINE_NORMAL:
-		g_string_append(status, _("Online"));
-		break;
-	/* TODO What does this status mean? Labelling it as offline... */
-	case QQ_BUDDY_CHANGE_TO_OFFLINE:
-		g_string_append(status, _("Offline"));
-		break;
-	case QQ_BUDDY_ONLINE_AWAY:
-		g_string_append(status, _("Away"));
-		break;
-	case QQ_BUDDY_ONLINE_INVISIBLE:
-		g_string_append(status, _("Invisible"));
-		break;
-	case QQ_BUDDY_ONLINE_BUSY:
-		g_string_append(status, _("Busy"));
-		break;
-	default:
-		g_string_printf(status, _("Unknown-%d"), bd->status);
+		case QQ_BUDDY_OFFLINE:
+			g_string_append(status, _("Offline"));
+			break;
+		case QQ_BUDDY_ONLINE_NORMAL:
+			g_string_append(status, _("Online"));
+			break;
+			/* TODO What does this status mean? Labelling it as offline... */
+		case QQ_BUDDY_CHANGE_TO_OFFLINE:
+			g_string_append(status, _("Offline"));
+			break;
+		case QQ_BUDDY_ONLINE_AWAY:
+			g_string_append(status, _("Away"));
+			break;
+		case QQ_BUDDY_ONLINE_INVISIBLE:
+			g_string_append(status, _("Invisible"));
+			break;
+		case QQ_BUDDY_ONLINE_BUSY:
+			g_string_append(status, _("Busy"));
+			break;
+		default:
+			g_string_printf(status, _("Unknown-%d"), bd->status);
 	}
 
 	return g_string_free(status, FALSE);
@@ -311,19 +312,19 @@
 	g_free(tmp);
 
 	switch (bd->gender) {
-	case QQ_BUDDY_GENDER_GG:
-		purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male"));
-		break;
-	case QQ_BUDDY_GENDER_MM:
-		purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female"));
-		break;
-	case QQ_BUDDY_GENDER_UNKNOWN:
-		purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown"));
-		break;
-	default:
-		tmp = g_strdup_printf("Error (%d)", bd->gender);
-		purple_notify_user_info_add_pair(user_info, _("Gender"), tmp);
-		g_free(tmp);
+		case QQ_BUDDY_GENDER_GG:
+			purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male"));
+			break;
+		case QQ_BUDDY_GENDER_MM:
+			purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female"));
+			break;
+		case QQ_BUDDY_GENDER_UNKNOWN:
+			purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown"));
+			break;
+		default:
+			tmp = g_strdup_printf("Error (%d)", bd->gender);
+			purple_notify_user_info_add_pair(user_info, _("Gender"), tmp);
+			g_free(tmp);
 	}
 
 	if (bd->level) {
@@ -361,13 +362,13 @@
 
 #ifdef DEBUG
 	tmp = g_strdup_printf( "%s (%04X)",
-										qq_get_ver_desc(bd->client_tag),
-										bd->client_tag );
+			qq_get_ver_desc(bd->client_tag),
+			bd->client_tag );
 	purple_notify_user_info_add_pair(user_info, _("Ver"), tmp);
 	g_free(tmp);
 
 	tmp = g_strdup_printf( "Ext 0x%X, Comm 0x%X",
-												bd->ext_flag, bd->comm_flag );
+			bd->ext_flag, bd->comm_flag );
 	purple_notify_user_info_add_pair(user_info, _("Flag"), tmp);
 	g_free(tmp);
 #endif
@@ -382,7 +383,7 @@
 	qq_buddy_data *buddy;
 
 	if (!b || !(account = b->account) ||
-		!(gc = purple_account_get_connection(account)) || !(qd = gc->proto_data))
+			!(gc = purple_account_get_connection(account)) || !(qd = gc->proto_data))
 		return NULL;
 
 	buddy = (qq_buddy_data *)b->proto_data;
@@ -626,6 +627,7 @@
 	g_string_append(info, _("<p><b>Original Author</b>:<br>\n"));
 	g_string_append(info, "puzzlebird<br>\n");
 	g_string_append(info, "<br>\n");
+
 	g_string_append(info, _("<p><b>Code Contributors</b>:<br>\n"));
 	g_string_append(info, "gfhuang(poppyer) : patches for libpurple 2.0.0beta2, maintainer<br>\n");
 	g_string_append(info, "Yuan Qingyun : patches for libpurple 1.5.0, maintainer<br>\n");
@@ -641,13 +643,17 @@
 	g_string_append(info, "icesky : maintainer since 2007<br>\n");
 	g_string_append(info, "csyfek : faces, maintainer since 2007<br>\n");
 	g_string_append(info, "<br>\n");
+
 	g_string_append(info, _("<p><b>Lovely Patch Writers</b>:<br>\n"));
 	g_string_append(info, "gnap : message displaying, documentation<br>\n");
 	g_string_append(info, "manphiz : qun processing<br>\n");
 	g_string_append(info, "moo : qun processing<br>\n");
 	g_string_append(info, "Coly Li : qun processing<br>\n");
 	g_string_append(info, "Emil Alexiev : captcha verification on login based on LumaQQ for MAC (2007), login, add buddy, remove buddy, message exchange and logout<br>\n");
+	g_string_append(info, "Chengming Wang : buddy memo<br>\n");
+	g_string_append(info, "lonicerae : chat room window bugfix, server list bugfix, buddy memo<br>\n");
 	g_string_append(info, "<br>\n");
+
 	g_string_append(info, _("<p><b>Acknowledgement</b>:<br>\n"));
 	g_string_append(info, "Shufeng Tan : http://sf.net/projects/perl-oicq<br>\n");
 	g_string_append(info, "Jeff Ye : http://www.sinomac.com<br>\n");
@@ -655,12 +661,20 @@
 	g_string_append(info, "yunfan : http://www.myswear.net<br>\n");
 	g_string_append(info, "OpenQ Team : http://openq.linuxsir.org<br>\n");
 	g_string_append(info, "LumaQQ Team : http://lumaqq.linuxsir.org<br>\n");
-	g_string_append(info, "khc(at)pidgin.im<br>\n");
-	g_string_append(info, "qulogic(at)pidgin.im<br>\n");
-	g_string_append(info, "rlaager(at)pidgin.im<br>\n");
+	g_string_append(info, "Pidgin Team : http://www.pidgin.im<br>\n");
 	g_string_append(info, "Huang Guan : http://home.xxsyzx.com<br>\n");
 	g_string_append(info, "OpenQ Google Group : http://groups.google.com/group/openq<br>\n");
 	g_string_append(info, "<br>\n");
+
+	g_string_append(info, _("<p><b>Scrupulous Testers</b>:<br>\n"));
+	g_string_append(info, "yegle<br>\n");
+	g_string_append(info, "cnzhangbx<br>\n");
+	g_string_append(info, "casparant<br>\n");
+	g_string_append(info, "wd<br>\n");
+	g_string_append(info, "x6719620<br>\n");
+	g_string_append(info, "netelk<br>\n");
+	g_string_append(info, "and more, please let me know... thank you!<br>\n");
+	g_string_append(info, "<br>\n");
 	g_string_append(info, _("<p><i>And, all the boys in the backroom...</i><br>\n"));
 	g_string_append(info, _("<i>Feel free to join us!</i> :)"));
 	g_string_append(info, "</body></html>");
@@ -673,22 +687,22 @@
 }
 
 /*
-static void _qq_menu_search_or_add_permanent_group(PurplePluginAction *action)
-{
-	purple_roomlist_show_with_account(NULL);
-}
+   static void _qq_menu_search_or_add_permanent_group(PurplePluginAction *action)
+   {
+   purple_roomlist_show_with_account(NULL);
+   }
 */
 
 /*
-static void _qq_menu_create_permanent_group(PurplePluginAction * action)
-{
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, _("Create QQ Qun"),
-			   _("Input Qun name here"),
-			   _("Only QQ members can create permanent Qun"),
-			   "OpenQ", FALSE, FALSE, NULL,
-			   _("Create"), G_CALLBACK(qq_create_room), _("Cancel"), NULL, gc);
-}
+   static void _qq_menu_create_permanent_group(PurplePluginAction * action)
+   {
+   PurpleConnection *gc = (PurpleConnection *) action->context;
+   purple_request_input(gc, _("Create QQ Qun"),
+   _("Input Qun name here"),
+   _("Only QQ members can create permanent Qun"),
+   "OpenQ", FALSE, FALSE, NULL,
+   _("Create"), G_CALLBACK(qq_create_room), _("Cancel"), NULL, gc);
+   }
 */
 
 static void action_chat_quit(PurpleBlistNode * node)
@@ -741,11 +755,11 @@
 	g_return_if_fail (PURPLE_BLIST_NODE_IS_BUDDY (node));
 	buddy = (PurpleBuddy *) node;
 	bd = (qq_buddy_data *) buddy->proto_data;
-/*	if (is_online (bd->status)) { */
+	/*	if (is_online (bd->status)) { */
 	gc = purple_account_get_connection (buddy->account);
 	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
 	qq_send_file(gc, buddy->name, NULL);
-/*	} */
+	/*	} */
 }
 #endif
 
@@ -783,11 +797,11 @@
 	act = purple_plugin_action_new(_("About OpenQ"), action_about_openq);
 	m = g_list_append(m, act);
 	/*
-	act = purple_plugin_action_new(_("Qun: Search a permanent Qun"), _qq_menu_search_or_add_permanent_group);
-	m = g_list_append(m, act);
+	   act = purple_plugin_action_new(_("Qun: Search a permanent Qun"), _qq_menu_search_or_add_permanent_group);
+	   m = g_list_append(m, act);
 
-	act = purple_plugin_action_new(_("Qun: Create a permanent Qun"), _qq_menu_create_permanent_group);
-	m = g_list_append(m, act);
+	   act = purple_plugin_action_new(_("Qun: Create a permanent Qun"), _qq_menu_create_permanent_group);
+	   m = g_list_append(m, act);
 	*/
 
 	return m;
@@ -806,27 +820,53 @@
 	qq_add_buddy(gc, buddy, NULL);
 }
 
+static void qq_modify_buddy_memo_from_menu_cb(PurpleBlistNode *node, gpointer data)
+{
+	PurpleBuddy *buddy;
+	qq_buddy_data *bd;
+	PurpleConnection *gc;
+	guint32 bd_uid;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *)node;
+	g_return_if_fail(NULL != buddy && NULL != buddy->proto_data);
+
+	gc = purple_account_get_connection(buddy->account);
+	g_return_if_fail(NULL != gc);
+
+	bd = (qq_buddy_data *)buddy->proto_data;
+	g_return_if_fail(NULL != bd);
+	bd_uid = bd->uid;
+
+	/* param: gc, uid, update_class, action
+	 * here, update_class is set to bd_uid. because some memo packages returned
+	 * without uid, which will make us confused */
+	qq_request_buddy_memo(gc, bd_uid, bd_uid, QQ_BUDDY_MEMO_MODIFY);
+}
+
 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);
+				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 */
+
+	act = purple_menu_action_new(_("Modify Buddy Memo"),
+			PURPLE_CALLBACK(qq_modify_buddy_memo_from_menu_cb),
+			NULL, NULL);
+	m = g_list_append(m, act);
+
+
+	/* 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 */
@@ -1008,9 +1048,9 @@
 	"prpl-qq",			/**< id			*/
 	"QQ",				/**< name		*/
 	DISPLAY_VERSION,		/**< version		*/
-					/**  summary		*/
+	/**  summary		*/
 	N_("QQ Protocol Plugin"),
-					/**  description	*/
+	/**  description	*/
 	N_("QQ Protocol Plugin"),
 	NULL,				/**< author		*/
 	PURPLE_WEBSITE,		/**< homepage	*/
@@ -1039,9 +1079,9 @@
 	GList *server_list = NULL;
 	GList *server_kv_list = NULL;
 	GList *it;
-//#ifdef DEBUG
+	/* #ifdef DEBUG */
 	GList *version_kv_list = NULL;
-//#endif
+	/* #endif */
 
 	server_list = server_list_build('A');
 
--- a/libpurple/protocols/qq/qq.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq.h	Tue Feb 24 14:37:38 2009 +0000
@@ -36,6 +36,10 @@
 
 #define QQ_KEY_LENGTH       16
 
+/* steal from kazehakase :) */
+#define qq_strlen(s) ((s)!=NULL?strlen(s):0)
+#define qq_strcmp(s1,s2) ((s1)!=NULL && (s2)!=NULL?strcmp(s1,s2):0)
+
 typedef struct _qq_data qq_data;
 typedef struct _qq_buddy_data qq_buddy_data;
 typedef struct _qq_interval qq_interval;
@@ -98,6 +102,7 @@
 	time_t signon;
 	time_t idle;
 	time_t last_update;
+	gchar** memo;
 
 	gint8  role;		/* role in group, used only in group->members list */
 };
--- a/libpurple/protocols/qq/qq_define.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Tue Feb 24 14:37:38 2009 +0000
@@ -189,6 +189,8 @@
 		return "CMD_BUDDY_CHECK_CODE";
 	case QQ_CMD_BUDDY_QUESTION:
 		return "CMD_BUDDY_QUESTION";
+	case QQ_CMD_BUDDY_MEMO:
+		return "CMD_BUDDY_MEMO";
 	default:
 		return "CMD_UNKNOW";
 	}
--- a/libpurple/protocols/qq/qq_define.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Tue Feb 24 14:37:38 2009 +0000
@@ -66,7 +66,9 @@
 	QQ_CMD_TOKEN  = 0x0062, 		/* get login token */
 	QQ_CMD_RECV_MSG_SYS = 0x0080,			/* receive a system message */
 	QQ_CMD_BUDDY_CHANGE_STATUS = 0x0081,	/* buddy change status */
-	/* for QQ2007*/
+	QQ_CMD_BUDDY_MEMO = 0x003E,    /* the message about buddy memo */
+
+    /* for QQ2007*/
 	QQ_CMD_GET_SERVER = 0x0091,					/* select login server */
 	QQ_CMD_TOKEN_EX = 0x00BA,						/* get LOGIN token */
 	QQ_CMD_CHECK_PWD = 0x00DD,				/* Password verify */
@@ -131,6 +133,7 @@
 	QQ_BUDDY_ONLINE_BUSY = 50
 };
 
+
 gboolean is_online(guint8 status);
 
 #endif
--- a/libpurple/protocols/qq/qq_network.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Tue Feb 24 14:37:38 2009 +0000
@@ -268,7 +268,7 @@
 	guint16 seq;		/* May be ack_seq or send_seq, depends on cmd */
 	guint8 room_cmd;
 	guint32 room_id;
-	gint update_class;
+	guint32 update_class;
 	guint32 ship32;
 	int ret;
 
@@ -1130,7 +1130,8 @@
 
 /* Encrypt data with session_key, and send packet out */
 static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq,
-	guint8 *data, gint data_len, gboolean is_save2trans, gint update_class, guint32 ship32)
+	guint8 *data, gint data_len, gboolean is_save2trans,
+        guint32 update_class, guint32 ship32)
 {
 	qq_data *qd;
 	guint8 *encrypted;
@@ -1160,7 +1161,7 @@
 }
 
 gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len,
-		gint update_class, guint32 ship32)
+		guint32 update_class, guint32 ship32)
 {
 	qq_data *qd;
 	guint16 seq;
@@ -1234,7 +1235,7 @@
 }
 
 static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
-		guint8 *data, gint data_len, gint update_class, guint32 ship32)
+		guint8 *data, gint data_len, guint32 update_class, guint32 ship32)
 {
 	qq_data *qd;
 	guint8 *buf;
@@ -1288,7 +1289,7 @@
 }
 
 gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
-		guint8 *data, gint data_len, gint update_class, guint32 ship32)
+		guint8 *data, gint data_len, guint32 update_class, guint32 ship32)
 {
 	g_return_val_if_fail(room_cmd > 0, -1);
 	return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32);
--- a/libpurple/protocols/qq/qq_network.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_network.h	Tue Feb 24 14:37:38 2009 +0000
@@ -39,7 +39,7 @@
 		guint8 *encrypted_data, gint encrypted_len, gboolean is_save2trans);
 gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint datalen);
 gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len,
-		gint update_class, guint32 ship32);
+		guint32 update_class, guint32 ship32);
 
 gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq,
 		guint8 *data, gint data_len);
@@ -47,7 +47,7 @@
 gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
 		guint8 *data, gint data_len);
 gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
-		guint8 *data, gint data_len, gint update_class, guint32 ship32);
+		guint8 *data, gint data_len, guint32 update_class, guint32 ship32);
 gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
 gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd,
 		guint8 *data, gint data_len);
--- a/libpurple/protocols/qq/qq_process.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Tue Feb 24 14:37:38 2009 +0000
@@ -47,6 +47,7 @@
 #include "qq_network.h"
 #include "qq_trans.h"
 #include "utils.h"
+#include "buddy_memo.h"
 
 enum {
 	QQ_ROOM_CMD_REPLY_OK = 0x00,
@@ -740,7 +741,7 @@
 
 void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
 		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
-		gint update_class, guint32 ship32)
+		guint32 update_class, guint32 ship32)
 {
 	qq_data *qd;
 	guint8 *data;
@@ -876,7 +877,7 @@
 }
 
 guint8 qq_proc_login_cmds(PurpleConnection *gc,  guint16 cmd, guint16 seq,
-		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32)
+		guint8 *rcved, gint rcved_len, guint32 update_class, guint32 ship32)
 {
 	qq_data *qd;
 	guint8 *data = NULL;
@@ -1028,7 +1029,7 @@
 }
 
 void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
-		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32)
+		guint8 *rcved, gint rcved_len, guint32 update_class, guint32 ship32)
 {
 	qq_data *qd;
 
@@ -1142,6 +1143,12 @@
 		case QQ_CMD_BUDDY_CHECK_CODE:
 			qq_process_buddy_check_code(gc, data, data_len);
 			break;
+		case QQ_CMD_BUDDY_MEMO:
+			purple_debug_info("QQ", "Receive memo from server!\n");
+			qq_process_get_buddy_memo(gc, data, data_len, update_class, ship32);
+			return;
+			purple_debug_info("QQ", "Should NOT be here...\n");
+			break;
 		default:
 			process_unknow_cmd(gc, _("Unknown CLIENT CMD"), data, data_len, cmd, seq);
 			is_unknow = TRUE;
--- a/libpurple/protocols/qq/qq_process.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_process.h	Tue Feb 24 14:37:38 2009 +0000
@@ -39,12 +39,12 @@
 };
 
 guint8 qq_proc_login_cmds(PurpleConnection *gc,  guint16 cmd, guint16 seq,
-		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32);
+		guint8 *rcved, gint rcved_len, guint32 update_class, guint32 ship32);
 void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
-		guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32);
+		guint8 *rcved, gint rcved_len, guint32 update_class, guint32 ship32);
 void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
 		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
-		gint update_class, guint32 ship32);
+		guint32 update_class, guint32 ship32);
 
 void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len);
 
--- a/libpurple/protocols/qq/qq_trans.c	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Tue Feb 24 14:37:38 2009 +0000
@@ -58,7 +58,7 @@
 	gint rcved_times;
 	gint scan_times;
 
-	gint update_class;
+	guint32 update_class;
 	guint32 ship32;
 };
 
@@ -94,20 +94,20 @@
 	return trans->room_id;
 }
 
-gint qq_trans_get_class(qq_transaction *trans)
+guint32 qq_trans_get_class(qq_transaction *trans)
 {
 	g_return_val_if_fail(trans != NULL, QQ_CMD_CLASS_NONE);
 	return trans->update_class;
 }
 
-gint qq_trans_get_ship(qq_transaction *trans)
+guint32 qq_trans_get_ship(qq_transaction *trans)
 {
 	g_return_val_if_fail(trans != NULL, 0);
 	return trans->ship32;
 }
 
 static qq_transaction *trans_create(PurpleConnection *gc, gint fd,
-	guint16 cmd, guint16 seq, guint8 *data, gint data_len, gint update_class, guint32 ship32)
+	guint16 cmd, guint16 seq, guint8 *data, gint data_len, guint32 update_class, guint32 ship32)
 {
 	qq_data *qd;
 	qq_transaction *trans;
@@ -179,7 +179,7 @@
 }
 
 void qq_trans_add_client_cmd(PurpleConnection *gc,
-	guint16 cmd, guint16 seq, guint8 *data, gint data_len, gint update_class, guint32 ship32)
+	guint16 cmd, guint16 seq, guint8 *data, gint data_len, guint32 update_class, guint32 ship32)
 {
 	qq_data *qd = (qq_data *)gc->proto_data;
 	qq_transaction *trans = trans_create(gc, qd->fd, cmd, seq, data, data_len, update_class, ship32);
@@ -219,7 +219,7 @@
 
 void qq_trans_add_room_cmd(PurpleConnection *gc,
 		guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *data, gint data_len,
-		gint update_class, guint32 ship32)
+		guint32 update_class, guint32 ship32)
 {
 	qq_data *qd = (qq_data *)gc->proto_data;
 	qq_transaction *trans = trans_create(gc, qd->fd, QQ_CMD_ROOM, seq, data, data_len,
--- a/libpurple/protocols/qq/qq_trans.h	Tue Feb 24 08:09:17 2009 +0000
+++ b/libpurple/protocols/qq/qq_trans.h	Tue Feb 24 14:37:38 2009 +0000
@@ -35,14 +35,14 @@
 gboolean qq_trans_is_dup(qq_transaction *trans);
 guint8 qq_trans_get_room_cmd(qq_transaction *trans);
 guint32 qq_trans_get_room_id(qq_transaction *trans);
-gint qq_trans_get_class(qq_transaction *trans);
-gint qq_trans_get_ship(qq_transaction *trans);
+guint32 qq_trans_get_class(qq_transaction *trans);
+guint32 qq_trans_get_ship(qq_transaction *trans);
 
 void qq_trans_add_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
-		guint8 *data, gint data_len, gint update_class, guint32 ship32);
+		guint8 *data, gint data_len, guint32 update_class, guint32 ship32);
 void qq_trans_add_room_cmd(PurpleConnection *gc,
 		guint16 seq, guint8 room_cmd, guint32 room_id,
-		guint8 *data, gint data_len, gint update_class, guint32 ship32);
+		guint8 *data, gint data_len, guint32 update_class, guint32 ship32);
 void qq_trans_add_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
 	guint8 *rcved, gint rcved_len);
 void qq_trans_add_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq,