changeset 25517:59ed7712be5e

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
author SHiNE CsyFeK <csyfek@gmail.com>
date Fri, 20 Feb 2009 18:00:32 +0000
parents 8bc9f89f305e
children 7da3cf2530b7
files libpurple/protocols/qq/AUTHORS libpurple/protocols/qq/ChangeLog libpurple/protocols/qq/Makefile.mingw libpurple/protocols/qq/buddy_list.c libpurple/protocols/qq/buddy_memo.c libpurple/protocols/qq/buddy_memo.h libpurple/protocols/qq/char_conv.c libpurple/protocols/qq/char_conv.h libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq_define.h libpurple/protocols/qq/qq_process.c
diffstat 11 files changed, 452 insertions(+), 441 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/qq/AUTHORS	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/AUTHORS	Fri Feb 20 18:00:32 2009 +0000
@@ -24,7 +24,7 @@
 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
+lonicerae 	: chat room window bugfix, server list bugfix, buddy memo
 
 Acknowledgement
 =========
@@ -34,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	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Fri Feb 20 18:00:32 2009 +0000
@@ -1,3 +1,8 @@
+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
 
--- a/libpurple/protocols/qq/Makefile.mingw	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Fri Feb 20 18:00:32 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_list.c	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Fri Feb 20 18:00:32 2009 +0000
@@ -347,7 +347,7 @@
 		/* nickname has been copy to buddy_data do not free
 		   g_free(bd.nickname);
 		*/
-		qq_request_buddy_memo_download( gc,((qq_buddy_data*)buddy->proto_data)->uid );
+		qq_request_buddy_memo(gc, ((qq_buddy_data*)buddy->proto_data)->uid, 0, QQ_BUDDY_MEMO_GET);
 	}
 
 	if(bytes > data_len) {
--- a/libpurple/protocols/qq/buddy_memo.c	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/buddy_memo.c	Fri Feb 20 18:00:32 2009 +0000
@@ -1,10 +1,9 @@
-
-#include "buddy_memo.h"
 #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"
@@ -14,314 +13,344 @@
 #include "qq_define.h"
 #include "qq_base.h"
 #include "qq_network.h"
-#include "../../blist.h"
+#include "qq.h"
 
 
-#include<string.h>
-#include<stdlib.h>
-#include<stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
 
 #include <stdlib.h>
 #include <stdio.h>
-static const gchar* buddy_memo_txt[] = {
-	"Name",
-	"Mobile",
-	"Telephone",
-	"Address",
-	"Email",
-	"ZipCode",
-	"Note"
-};/* 备注信息的名称 */
 
-
-/** 
- * 打印出好友备注信息
- * 
- * @param memo 
- */
-static void buddy_memo_debug( gchar* memo[] );
-
-/** 
- * 好友备注对话框中上传按钮的响应函数
- * 
- * @param info_request 
- * @param fields 
- */
-static void buddy_memo_on_upload(void *info_request, PurpleRequestFields *fields);
-
-
-static gchar** buddy_memo_init_data(  );
+/* 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
+};
 
-
-
-/** 
- * 弹出窗口显示好友备注信息
- * 
- * @param node 
- * @param buddy_data 
- */
-static void qq_show_buddy_memo( void* node, void* buddy_data );
-
-
-
-
+/* memo id */
+static const gchar *memo_id[] = {
+	N_("mm_alias"),
+	N_("mm_mobile"),
+	N_("mm_telephone"),
+	N_("mm_address"),
+	N_("mm_email"),
+	N_("mm_zipcode"),
+	N_("mm_note")
+};
 
-/** 
- * 向服务器发送更新好友信息请求
- * 
- * @param gc 
- * @param buddy_data 
- */
-static void qq_request_buddy_memo_upload( PurpleBuddy * buddy );
+/* 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;
 
 
-void buddy_memo_on_upload(void *bd, PurpleRequestFields *fields)
+static void memo_debug(gchar **segments)
 {
-	int index;
-	PurpleBuddy *buddy;
-	qq_buddy_data* buddy_data;
-	int memoChanged;
-	const char *utf8_str;
-	buddy = ( PurpleBuddy* )bd;
-	buddy_data = ( qq_buddy_data* )( buddy->proto_data );
-	
-	
-	purple_debug_info("QQ", "update memo\n");
-	memoChanged = 0;
-	for( index=0; index<QQ_BUDDY_MEMO_SIZE; index++ ){
-		utf8_str = purple_request_fields_get_string(fields, buddy_memo_txt[index]);
-		if( utf8_str == NULL ){
-			if( buddy_data->memo[index] != NULL ){
-				g_free( buddy_data->memo[index] );
-				memoChanged = 1;
-			}
-			buddy_data->memo[index] = g_new0( gchar,1 );
-		}
-		else if( buddy_data->memo[index] == NULL ||
-			strcmp( utf8_str, buddy_data->memo[index] ) != 0 )
-		{
-			if( buddy_data->memo[index] != NULL )
-				g_free( buddy_data->memo[index] );
-			buddy_data->memo[index] = g_new( gchar,strlen(utf8_str)+2 );
-			strcpy( buddy_data->memo[index], utf8_str );
-			memoChanged = 1;
-			purple_debug_info( "QQ","%s=%s\n",buddy_memo_txt[index],utf8_str );
-		}
-
-	}
-	if( memoChanged == 1 ){
-		qq_request_buddy_memo_upload( buddy );
-		purple_blist_alias_buddy( buddy,buddy_data->memo[QQ_BUDDY_MEMO_NAME] );
-	}
-	return;
-}
-
-void qq_request_buddy_memo_upload( PurpleBuddy * buddy )
-{
-	PurpleConnection* gc;
-	qq_buddy_data* buddy_data;
-	guint8* rawData;
-	gint bytes;
-	int rawDataSize;
-	int index;
-	int memoItemSize[QQ_BUDDY_MEMO_SIZE];
-	gchar* qqCharSetTxt[QQ_BUDDY_MEMO_SIZE];
-
-	gc = buddy->account->gc;
-	buddy_data = ( qq_buddy_data* )buddy->proto_data;
-	purple_debug_info( "QQ","call qq_request_buddy_memo_download_upload\n" );
-	rawDataSize = 7;
-	for( index=0; index<QQ_BUDDY_MEMO_SIZE; index++ ){
-		qqCharSetTxt[index] = utf8_to_qq( buddy_data->memo[index], QQ_CHARSET_DEFAULT );
-		memoItemSize[index] = strlen( qqCharSetTxt[index] );
-		rawDataSize += memoItemSize[index]+1;
-	}
-	rawData = g_new0( guint8,rawDataSize );
-	bytes = 0;
-	bytes += qq_put8( rawData+bytes,QQ_BUDDY_MEMO_UPLOAD );
-	bytes += qq_put8( rawData+bytes,0 );
-	bytes += qq_put32( rawData+bytes, buddy_data->uid );
-	bytes += qq_put8( rawData+bytes,0 );
-	for( index=0; index<QQ_BUDDY_MEMO_SIZE; index++ ){
-		bytes += qq_put8( rawData+bytes,0xff&memoItemSize[index] );  //TODO: 0xff?
-		bytes += qq_putdata( rawData+bytes, (const guint8 *)qqCharSetTxt[index], memoItemSize[index] );
-	}
-
-	qq_send_cmd( gc, QQ_CMD_BUDDY_MEMO, rawData, rawDataSize );
-	for( index=0; index<QQ_BUDDY_MEMO_SIZE; index++ ){
-		g_free( qqCharSetTxt[index] );
+	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]);
 	}
 }
 
-
-
-void qq_request_buddy_memo_download(PurpleConnection *gc, guint32 uid)
+static void memo_free(gchar **segments)
 {
-	guint8 raw_data[16] = {0};
-	//unsigned int tmp;
-	gint bytes;
-
-	purple_debug_info("QQ", "Call qq_request_buddy_memo_download! qq number =%u\n", uid);
-	g_return_if_fail(uid != 0);
-	bytes = 0;
-	bytes += qq_put8( raw_data+bytes, QQ_BUDDY_MEMO_GET );
-	bytes += qq_put32( raw_data+bytes, uid );
-	
-	qq_send_cmd(gc, QQ_CMD_BUDDY_MEMO, (guint8*)raw_data, bytes);
+	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");
 }
 
-
-void qq_process_get_buddy_memo( PurpleConnection *gc, guint8* data, gint len )
+static void update_buddy_memo(PurpleConnection *gc, guint32 bd_uid, gchar *alias)
 {
-	qq_data *qd;
+	PurpleAccount *account;
 	PurpleBuddy *buddy;
 	gchar *who;
-	qq_buddy_data* bd;
+	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;
-	guint8 lenth;
-	guint32 qq_number;
-	guint8 receive_cmd;
-	guint8 receive_data;
-	int k;
+	/* Attention, length of each segment must be guint8(0~255),
+	 * so length of memo string is limited.
+	 * convert it to guint8 first before put 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_get8( &receive_cmd, data+bytes );
-	switch( receive_cmd ){
-	    case QQ_BUDDY_MEMO_UPLOAD :
-    	case QQ_BUDDY_MEMO_REMOVE :
-			bytes += qq_get8( &receive_data, data+bytes );
-			if( receive_data == QQ_BUDDY_MEMO_REQUEST_SUCCESS ){//显示服务器接受请求对话框
-				purple_debug_info( "QQ","服务器接受了请求\n" );
-				purple_notify_message( gc,
-									   PURPLE_NOTIFY_MSG_INFO,
-									   _( "Your request was accepted" ),
-									   _( "Your request was accepted" ),
-									   _( "Your request was accepted" ),
-									   NULL,
-									   NULL);
-			}
-			else{
-				purple_debug_info( "QQ","服务器拒绝了请求\n" );
-				purple_notify_message( gc,
-									   PURPLE_NOTIFY_MSG_INFO,
-									   _( "Your request was rejected" ),
-									   _( "Your request was rejected" ),
-									   _( "Your request was rejected" ),
-									   NULL,
-									   NULL);
-			}
-			break;
-	    case QQ_BUDDY_MEMO_GET:
-			qd = (qq_data *) gc->proto_data;
-			bytes += qq_get32( &qq_number, data+bytes );
-			bytes ++;//qq号后面有一个字节不知道什么作用
-			who = uid_to_purple_name( qq_number );
-			buddy = purple_find_buddy( gc->account, who );
-			if (buddy == NULL || buddy->proto_data == NULL) {
-				g_free(who);
-				purple_debug_info("QQ", "Error Can not find %d!\n", qq_number);
-				return;
-			}
-			bd = (qq_buddy_data *)buddy->proto_data;
-			
-			if( bd->memo == NULL ){
-				bd->memo = g_new0( gchar*,QQ_BUDDY_MEMO_SIZE );
-			}
-			for( k=0; k<QQ_BUDDY_MEMO_SIZE; k++ ){
-				bytes += qq_get8( &lenth, data+bytes );
-				if( bd->memo[k] != NULL )
-					g_free( bd->memo[k] );
-				bd->memo[k] = qq_to_utf8_len( (gchar*)(data+bytes), lenth, QQ_CHARSET_DEFAULT );
-				bytes += lenth;
-			}
-			buddy_memo_debug( bd->memo );
-			purple_blist_alias_buddy( buddy,
-									  (const char*)bd->memo[QQ_BUDDY_MEMO_NAME] );//改名
-			break;
-	default:
-			purple_debug_info( "QQ","error: unknown memo cmd \n" );
-	        break;
+	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);
 }
 
-void buddy_memo_debug( gchar* memo[] )
+/* prepare segments to be sent, string all convert to qq charset */
+static void memo_modify_ok_cb(modify_memo_request *memo_request, PurpleRequestFields *fields)
 {
-	gint k=0;
-	for( k=0; k<QQ_BUDDY_MEMO_SIZE; k++ ){
-		purple_debug_info( "QQ","备注: %s=%s\n",buddy_memo_txt[k],memo[k] );
+	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 (!g_strcmp0("(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);
 }
 
-void qq_show_buddy_memo( void* node, void* buddy_data )
+/* memo modify dialogue */
+static void memo_modify_dialogue(PurpleConnection *gc, guint32 bd_uid, gchar **segments, gint iclass)
 {
-	qq_buddy_data* data;
+	modify_memo_request *memo_request;
 	PurpleRequestField *field;
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group;
 	int index;
-	
-	data = ( qq_buddy_data* )buddy_data;
-	
-	fields = purple_request_fields_new();
-	group = purple_request_field_group_new(NULL);
-	purple_request_fields_add_group(fields, group);
+	gchar *utf8_title;
+	gchar *utf8_primary;
+
+	g_return_if_fail(NULL != gc && NULL != segments);
+
+	switch (iclass) {
+		case QQ_BUDDY_MEMO_GET:
+			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]);
+				   */
 
-	for( index=0; index<QQ_BUDDY_MEMO_SIZE; index++ ){
-		if( data->memo == NULL ){
-			data->memo = buddy_memo_init_data(  );
-		}
-		field = purple_request_field_string_new(buddy_memo_txt[index],
-												buddy_memo_txt[index],
-												data->memo[index],
-												FALSE);
-		purple_request_field_group_add_field(group, field);	
+				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", "unknown memo action\n");
+			break;
 	}
-	
-	purple_request_fields(node,
-						  _( "Buddy_memo" ),
-						  "",
-						  NULL,
-						  fields,
-						  _("_Upload"), G_CALLBACK(buddy_memo_on_upload),
-						  _("_Cancel"), NULL,
-						  ((PurpleBuddy *)node)->account, ((PurpleBuddy *)node)->name, NULL,
-						  node);
 }
 
-
+/* process reply to get_memo packet */
+void qq_process_get_buddy_memo(PurpleConnection *gc, guint8* data, gint data_len, guint32 action)
+{
+	gchar **segments;
+	gint bytes;
+	gint index;
+	guint8 rcv_cmd;
+	guint32 rcv_uid;
+	guint8 unk1_8;
+	guint8 is_success;
 
-void qq_on_show_memo(PurpleBlistNode *node, gpointer data)
-{
+	g_return_if_fail(data != NULL && data_len != 0);
+	/*
+	   qq_show_packet("MEMO REACH", data, data_len);
+	   */
+	purple_debug_info("QQ", "action:0x%x\n", action);
+
+	bytes = 0;
 
-	PurpleBuddy *buddy;
+	/* TX looks a bit clever than before... :) */
+	bytes += qq_get8(&rcv_cmd, data+bytes);
+	purple_debug_info("QQ", "rcv_cmd:0x%x\n", rcv_cmd);
+	/* it's possible that no buddy uid and no memo!!! */
+	if (1 == data_len) {
+		purple_debug_info("QQ", "memo packet contains no buddy uid and memo...\n");
+		return;
+	}
 
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
+	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"), _("Your request was accepted."), NULL,
+						NULL, NULL);
+				purple_debug_info("QQ", "memo change succeessfully!");
+			}
+			else {
+				purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO,
+						_("Memo Modify"), _("Your request was rejected."), NULL,
+						NULL, NULL);
+				purple_debug_info("QQ", "memo change failed");
+			}
+			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%x\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]);
+				   */
+			}
 
-	qq_show_buddy_memo( node, buddy->proto_data );
+			/* 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);
 
-	
-	purple_debug_info( "QQ","show memo" );
+			break;
+		default:
+			purple_debug_info("QQ", "received an UNKNOWN memo cmd!!!");
+			break;
+	}
 }
 
-
-gchar** buddy_memo_init_data(  )
+/* request buddy memo */
+void qq_request_buddy_memo(PurpleConnection *gc, guint32 bd_uid, gint update_class, int action)
 {
-	gchar** pmemo;
-	int index;
-	pmemo = g_new0( gchar*,QQ_BUDDY_MEMO_SIZE );
-	for( index=0; index<QQ_BUDDY_MEMO_SIZE; index++ ){
-		pmemo[index] = g_new0( gchar,1 );
+	guint8 raw_data[16] = {0};
+	gint bytes;
+
+	purple_debug_info("QQ", "qq_request_buddy_memo, buddy uid=%u\n", bd_uid);
+	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);
+}
+
+void qq_create_buddy_memo(PurpleConnection *gc, guint32 bd_uid, int 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("");;
 	}
-	return pmemo;
+	memo_modify_dialogue(gc, bd_uid, segments, action);
 }
+
--- a/libpurple/protocols/qq/buddy_memo.h	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/buddy_memo.h	Fri Feb 20 18:00:32 2009 +0000
@@ -1,80 +1,26 @@
-
-#ifndef _BUDDY_MEMO_H
-#define _BUDDY_MEMO_H 
+#ifndef _QQ_BUDDY_MEMO_H_
+#define _QQ_BUDDY_MEMO_H_
 
 #include <glib.h>
+#include "connection.h"
+#include "blist.h"
 
-#include "connection.h"
-#include "buddy_opt.h"
-#include "qq.h"
-
-
+#define QQ_BUDDY_MEMO_REQUEST_SUCCESS 0x00
 
-#include "internal.h"
-#include "debug.h"
-#include "notify.h"
-#include "request.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 "../../blist.h"
-
-
-
-
-
-enum {
-	QQ_BUDDY_MEMO_NAME = 0,
-	QQ_BUDDY_MEMO_MOBILD,
-	QQ_BUDDY_MEMO_TELEPHONE,
-	QQ_BUDDY_MEMO_ADDRESS,
-	QQ_BUDDY_MEMO_EMAIL,
-	QQ_BUDDY_MEMO_ZIPCODE,
-	QQ_BUDDY_MEMO_NOTE,
-	QQ_BUDDY_MEMO_SIZE
+/* 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 action);
 
-/** 
- * 向服务器发送下载好友备注信息的请求
- * 
- * @param gc 
- * @param uid 好友qq号码
- */
-void qq_request_buddy_memo_download(PurpleConnection *gc, guint32 uid);
-
-
-
-
+void qq_request_buddy_memo(PurpleConnection *gc, guint32 bd_uid, gint update_class, int action);
 
-/** 
- * 处理服务器对好友备注信息的响应
- * 
- * @param gc 
- * @param data 解密后的数据
- * @param len data数据长度
- */
-void qq_process_get_buddy_memo( PurpleConnection *gc, guint8* data, gint len );
-
+void qq_create_buddy_memo(PurpleConnection *gc, guint32 bd_uid, int action);
 
-/** 
- * 在好友列表项上右键菜单中显示好友信息的响应函数
- * 
- * @param node 
- * @param data 
- */
-void qq_on_show_memo(PurpleBlistNode *node, gpointer data);
+#endif
 
-
-#endif /* _BUDDY_MEMO_H */
-
--- a/libpurple/protocols/qq/char_conv.c	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Fri Feb 20 18:00:32 2009 +0000
@@ -111,14 +111,3 @@
 	return do_convert(str, -1, UTF8, from_charset);
 }
 
-
-
-gchar *utf8_to_qq_len(const gchar *str, const gint len, const gchar *to_charset)
-{
-	return do_convert(str, len, to_charset, UTF8);
-}
-gchar *qq_to_utf8_len(const gchar *str, const gint len, const gchar *from_charset)
-{
-	return do_convert(str, len, UTF8, from_charset);
-}
-
--- a/libpurple/protocols/qq/char_conv.h	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/char_conv.h	Fri Feb 20 18:00:32 2009 +0000
@@ -35,7 +35,4 @@
 gchar *utf8_to_qq(const gchar *str, const gchar *to_charset);
 gchar *qq_to_utf8(const gchar *str, const gchar *from_charset);
 
-gchar *utf8_to_qq_len(const gchar *str, const gint len, const gchar *to_charset);
-gchar *qq_to_utf8_len(const gchar *str, const gint len, const gchar *from_charset);
-
 #endif
--- a/libpurple/protocols/qq/qq.c	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/qq.c	Fri Feb 20 18:00:32 2009 +0000
@@ -113,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;
 }
 
@@ -150,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;
@@ -255,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);
@@ -312,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) {
@@ -362,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
@@ -383,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;
@@ -627,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");
@@ -642,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");
@@ -656,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>");
@@ -674,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)
@@ -742,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
 
@@ -784,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;
@@ -807,32 +820,64 @@
 	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;
+	const gchar *alias;
+	const gchar *server_alias;
+
+	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;
+
+	/* gc, uid, update_class, action */
+	qq_request_buddy_memo(gc, bd_uid, 0, QQ_BUDDY_MEMO_MODIFY);
+
+	/* if buddy does NOT have a memo, open the memo dialogue directly */
+	alias = purple_buddy_get_alias_only(buddy);
+	server_alias = purple_buddy_get_server_alias(buddy);
+
+	purple_debug_info("QQ", "alias=%s\n", alias);
+	purple_debug_info("QQ", "server_alias=%s\n", server_alias);
+
+	if (!g_strcmp0(alias, server_alias)) {
+		qq_create_buddy_memo(gc, 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;
-
 	}
 
 
-	act = purple_menu_action_new(_("Buddy memo"), PURPLE_CALLBACK(qq_on_show_memo), NULL, NULL); /* add NULL 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 */
+
+	/* 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 */
@@ -1014,9 +1059,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	*/
@@ -1045,9 +1090,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_define.h	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Fri Feb 20 18:00:32 2009 +0000
@@ -134,15 +134,6 @@
 };
 
 
-enum //好友备注相关         
-{   
-	QQ_BUDDY_MEMO_UPLOAD = 0x01,   // 上传好友备注
-	QQ_BUDDY_MEMO_REMOVE,          // 清除好友备注
-	QQ_BUDDY_MEMO_GET              // 获取好友备注
-};
-
-#define QQ_BUDDY_MEMO_REQUEST_SUCCESS 0x00
-
 gboolean is_online(guint8 status);
 
 #endif
--- a/libpurple/protocols/qq/qq_process.c	Mon Feb 09 15:52:13 2009 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Fri Feb 20 18:00:32 2009 +0000
@@ -1143,9 +1143,9 @@
 		case QQ_CMD_BUDDY_CHECK_CODE:
 			qq_process_buddy_check_code(gc, data, data_len);
 			break;
-    	case QQ_CMD_BUDDY_MEMO:
+		case QQ_CMD_BUDDY_MEMO:
 			purple_debug_info("QQ", "Receive memo from server!\n");
-			qq_process_get_buddy_memo( gc, data, data_len );
+			qq_process_get_buddy_memo(gc, data, data_len, ship32);
 			break;
 		default:
 			process_unknow_cmd(gc, _("Unknown CLIENT CMD"), data, data_len, cmd, seq);