propagate from branch 'im.pidgin.pidgin' (head 053ddd0ba31755b4f4606baeb3fb1d8bac23d138)

Sat, 06 Dec 2008 13:30:06 +0000

author
SHiNE CsyFeK <csyfek@gmail.com>
date
Sat, 06 Dec 2008 13:30:06 +0000
changeset 25075
4dd1f66c7b84
parent 24865
053ddd0ba317 (current diff)
parent 25074
93e82f3ab074 (diff)
child 25076
bf4b297d2336
child 25077
f33d8d0be478
child 25079
c4f9aabcc77d
child 25937
0e25d1df776d
child 25959
b0e1613627c0
child 26011
909a704b8319

propagate from branch 'im.pidgin.pidgin' (head 053ddd0ba31755b4f4606baeb3fb1d8bac23d138)
to branch 'im.pidgin.pidgin.openq' (head 93e82f3ab074355c78ea0b83969de20548f415c7)

--- a/libpurple/protocols/qq/ChangeLog	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Sat Dec 06 13:30:06 2008 +0000
@@ -1,3 +1,35 @@
+2008.12.06 - flos <lonicerae(at)gmail.com>
+	* Removed version checking script in Makefiles since our developers all migrated to monotone
+	* Use our development revision as OPENQ_VERSION in qq.c
+
+2008.12.05 - flos <lonicerae(at)gmail.com>
+	* Fixed a bug after propagating
+
+2008.11.18 - ccpaging <ccpaging(at)gmail.com>
+	* Fixed: IM format suuport in IM and QUN IM
+	* Divide long IM message into segment and sending
+	* Divide long QUN IM message in to segment and sending
+	* Add some new function in im.c to put format when sending
+	* Add some new function in im.c to get format when receiving
+	* Need improvement:
+	    Merge long IM message when receiving. Need a buffer to store segments of long IM message.
+	    Send segment of long IM message one by one. Need a buffer to store segments of long IM message.
+
+2008.11.11 - ccpaging <ccpaging(at)gmail.com>
+	* Change QQ number to unsigned long
+	* Change Qun ID and Qun extend ID to unsigned long
+	* Rewrite smiley convert function, use qsort and bsearch
+	* Update smiley map according EVA and pidgin theme file
+	* Support long IM message in private and Qun
+
+2008.10.27 - ccpaging <ccpaging(at)gmail.com>
+	* Fixed a bug in group_join.c
+
+2008.10.30 - flos <lonicerae(at)gmail.com>
+	* Fixed a bug which made xgettext failed in buddy_info.c
+	* Fixed a bug in Makefile.am and Makefile.mingw
+	* Updated acknowledgement in qq.c
+
 2008.10.28 - flos <lonicerae(at)gmail.com>
 	* Updated AUTHORS
 
@@ -22,15 +54,15 @@
 
 2008.10.10 - ccpaging <ccpaging(at)gmail.com>
 	* Keep group_search.c/h for later use
-	* Update 'group' 
+	* Update 'group'
 
 2008.10.09 - ccpaging <ccpaging(at)gmail.com>
 	* 20081009-1
 
 2008.10.09 - ccpaging <ccpaging(at)gmail.com>
 	* Update 'group' protocol
-	* Functions of group_find, group_free, group_search merged into group_join and group_internal 
-	* Removed group_find.c/h, group_free.c/h, group_search.c/h 
+	* Functions of group_find, group_free, group_search merged into group_join and group_internal
+	* Removed group_find.c/h, group_free.c/h, group_search.c/h
 
 2008.10.08 - ccpaging <ccpaging(at)gmail.com>
 	* Update 'group' protocol
@@ -138,7 +170,7 @@
 		1. send next package till the previous package received
 		2. fix duplicated get_room_info and get_room_buddies commands
 
-2008.08.16 - ccpaging <ecc_hy(at)hotmail.com>
+2008.08.16 - ccpaging <ccpaging(at)gmail.com>
 	* Rename group to room. If you used pidginqq before, this may create a new room with same title, you may delete old one
 	* Replace purple_debug with purple_debug_info, purple_debug_warning, purple_debug_error
 	* Add server notice and server new, and two options to turn on/off
@@ -151,17 +183,17 @@
 2008.08.10 - csyfek <csyfek(at)gmail.com>
 	* Commit to Pidgin
 
-2008.08.07 - ccpaging <ecc_hy(at)hotmail.com>
+2008.08.07 - ccpaging <ccpaging(at)gmail.com>
 	* Support managing multi-connections according to simple.c
 
-2008.08.06 - ccpaging <ecc_hy(at)hotmail.com>
+2008.08.06 - ccpaging <ccpaging(at)gmail.com>
 	* Rename names of variables, Group, to Room
 	* Functions of group_network merged into qq_network and qq_process
 	* Canceled managing glist of group packet, add sub_cmdd and room_id  to transaction
 	* Fixed error of demo group:
 		If 'room list' and 'room infor' are not setup, response received from server will emits 'room_id = 0' packet.
 
-2008.08.04 - ccpaging <ecc_hy(at)hotmail.com>
+2008.08.04 - ccpaging <ccpaging(at)gmail.com>
 	* Use new crypt/decrypt functions
 	* Rename crypt.c/h to qq_crypt.c/h
 	* Clean code of decrypt functions
@@ -180,17 +212,17 @@
 		Fixes #1902
 		References #5112
 
-2008.08.02 - ccpaging <ecc_hy(at)hotmail.com>
+2008.08.02 - ccpaging <ccpaging(at)gmail.com>
 	* Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH]
 	* Use random value in inikey
 	* TEA header padding in crypt.c
 	* Rewrite login part of qq_process
 
-2008.07.31 - ccpaging <ecc_hy(at)hotmail.com>
+2008.07.31 - ccpaging <ccpaging(at)gmail.com>
 	* Fixed: send reply when get duplicate server command. The server may not get our reply before.
 	* Tag custom picture as text "(Broken)"
 
-2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+2008.07.30 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com>
 	* Change some debug message
 	* Modify buddy status flag according to eva for QQ2006
 	* Modify buddy status parse and correspond to eva2
@@ -224,10 +256,10 @@
 	* Rewrite qq_proc_cmd_reply and qq_proc_cmd_server:
 		In QQ protocol, one packet reply may need a new packet send later.
 		We may call it packet trigger. The triggers always is hided in every qq_process_reply.
-		Now we try to extract those triggers and put into a single function, 
+		Now we try to extract those triggers and put into a single function,
 		and then every trigger should be obviously and easy to manage.
-	
-2008.07.12 - ccpaging <ecc_hy(at)hotmail.com>
+
+2008.07.12 - ccpaging <ccpaging(at)gmail.com>
 	* Fixed: Always lost connection. Now send keep alive packet in every 30 seconds
 	* Minor fix for debug information
 	* Filter \r\n and replace with SPCAE in group notive
@@ -240,37 +272,37 @@
 	* Add some doxygen syntax for preparing development documentation
 	* References #6199
 
-2008.06.28 - ccpaging <ecc_hy(at)hotmail.com>, moo <phpxcache(at)gmail.com>
+2008.06.28 - ccpaging <ccpaging(at)gmail.com>, moo <phpxcache(at)gmail.com>
 	* Patches from moo<phpxcache@gmail.com> and ccpaging<ccpaging@foxmail.com>.
 	* Tickets:
 	* Fixes #4956.
 	* Fixes #2998.
 
-2008.06.07 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+2008.06.07 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com>
 	* Clean code and apply patches from QuLogic
 
-2008.05.19 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+2008.05.19 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com>
 	* Reconnect server 5 time in 5000 ms, when connect failed
 	* Rename sendqueue.c/sendqueue.h to qq_trans.c/qq_trans.h
 	* Rewrite packet_process
 	* Rewrite qq_send_cmd
 	* Create server list, try to connect every server when failed
 
-2008.05.14 - ccpaging <ecc_hy(at)hotmail.com>
+2008.05.14 - ccpaging <ccpaging(at)gmail.com>
 	* Move function for before login packets storing to sendqueue
 	* Use transaction data structure to store before login packets
 	* Rewrite tcp_pending and packet_process in qq_network.c
 
-2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+2008.05.09 - ccpaging <ccpaging(at)gmail.com>
 	* Remove function _create_packet_head_seq in qq_network.c
 	* Create new function encap in qq_netowork.c
 	* Clean code of qq_send_packet_request_login_token and qq_send_packet_login in login_out.c
 
-2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+2008.05.09 - ccpaging <ccpaging(at)gmail.com>
 	* Clean code of packet_parse.c, enable PARSER_DEBUG
 	* Rewrite send_queue
 
-2008.05.08 - ccpaging <ecc_hy(at)hotmail.com>
+2008.05.08 - ccpaging <ccpaging(at)gmail.com>
 	* Rewrite qq_network
 	* Add srv resolve function when qq_login
 	* Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
@@ -278,20 +310,20 @@
 	* qq_data alloc in qq_open and release in qq_close
 	* Network connect of QQ is created in qq_connect, and release in qq_disconnect
 
-2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+2008.05.05 - ccpaging <ccpaging(at)gmail.com>
 	* Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
 	* Move orignal qq_disconnect to qq_close
 	* qq_data alloc in qq_open and release in qq_close
 	* Network connect of QQ is created in qq_connect, and release in qq_disconnect
 
-2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+2008.05.05 - ccpaging <ccpaging(at)gmail.com>
 	* Add qq_hex_dump function
 
-2008.04.25 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+2008.04.25 - ccpaging <ccpaging(at)gmail.com>, csyfek <csyfek(at)gmail.com>
 	* Rewrite read_packet and create_packet functions, use qq_put and qq_get functions instead
 	* New logic in accord with protocol models to handle packets, some related functions rewritten
 
-2008.03.24 - ccpaging <ecc_hy(at)hotmail.com>
+2008.03.24 - ccpaging <ccpaging(at)gmail.com>
 	* Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly
 
 ** since pidgin-2.4.0 ***
--- a/libpurple/protocols/qq/Makefile.mingw	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Sat Dec 06 13:30:06 2008 +0000
@@ -6,6 +6,7 @@
 
 PIDGIN_TREE_TOP := ../../..
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
+
 TARGET = libqq
 TYPE = PLUGIN
 
--- a/libpurple/protocols/qq/buddy_info.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Sat Dec 06 13:30:06 2008 +0000
@@ -228,7 +228,7 @@
 	g_return_if_fail(uid != 0);
 
 	qd = (qq_data *) gc->proto_data;
-	g_snprintf(raw_data, sizeof(raw_data), "%d", uid);
+	g_snprintf(raw_data, sizeof(raw_data), "%u", uid);
 	qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) raw_data, strlen(raw_data),
 			update_class, action);
 }
@@ -457,7 +457,7 @@
 	data[data_len] = '\0';
 	if (qd->uid != atoi((gchar *) data)) {	/* return should be my uid */
 		purple_debug_info("QQ", "Failed Updating info\n");
-		qq_got_attention(gc, _("Could not change buddy information."));
+		qq_got_message(gc, _("Could not change buddy information."));
 	}
 }
 
@@ -504,6 +504,8 @@
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
 	const gchar *icon_path = purple_account_get_buddy_icon_path(account);
+	gchar **segments;
+	gint index;
 
 	g_return_if_fail(icon_path != NULL);
 
@@ -512,6 +514,12 @@
 	 *  purple_imgstore_get_filename is always new file
 	 *  QQ buddy may set custom icon if level is over 16 */
 	purple_debug_info("QQ", "Change my icon to %s\n", icon_path);
+	segments = g_strsplit_set(icon_path, G_DIR_SEPARATOR_S, 0);
+	for (index = 0; segments[index] != NULL; index++) {
+		purple_debug_info("QQ", "Split to %s\n", segments[index]);
+	}
+
+	g_strfreev(segments);
 }
 
 gchar *qq_get_icon_name(gint face)
@@ -553,7 +561,7 @@
 	return icon_path;
 }
 
-static void update_buddy_icon(PurpleAccount *account, const gchar *who, gint face)
+void qq_update_buddy_icon(PurpleAccount *account, const gchar *who, gint face)
 {
 	PurpleBuddy *buddy;
 	const gchar *icon_name_prev = NULL;
@@ -564,19 +572,21 @@
 
 	g_return_if_fail(account != NULL && who != NULL);
 
-	purple_debug_info("QQ", "Update %s icon to %d\n", who, face);
+	/* purple_debug_info("QQ", "Update %s icon to %d\n", who, face); */
 
 	icon_name = qq_get_icon_name(face);
-	purple_debug_info("QQ", "icon file name is %s\n", icon_name);
+	g_return_if_fail(icon_name != NULL);
+	/* purple_debug_info("QQ", "icon file name is %s\n", icon_name); */
 
 	if ((buddy = purple_find_buddy(account, who))) {
 		icon_name_prev = purple_buddy_icons_get_checksum_for_user(buddy);
-		if (icon_name_prev != NULL) {
-			purple_debug_info("QQ", "Previous icon is %s\n", icon_name_prev);
-		}
+		/*
+		purple_debug_info("QQ", "Previous icon is %s\n",
+				icon_name_prev != NULL ? icon_name_prev : "(NULL)");
+		*/
 	}
 	if (icon_name_prev != NULL && !strcmp(icon_name, icon_name_prev)) {
-		purple_debug_info("QQ", "Icon is not changed\n");
+		/* purple_debug_info("QQ", "Icon is not changed\n"); */
 		g_free(icon_name);
 		return;
 	}
@@ -590,6 +600,8 @@
 	if (!g_file_get_contents(icon_path, &icon_file_content, &icon_file_size, NULL)) {
 		purple_debug_error("QQ", "Failed reading icon file %s\n", icon_path);
 	} else {
+		purple_debug_info("QQ", "Update %s icon to %d (%s)\n",
+				who, face, icon_path);
 		purple_buddy_icons_set_for_user(account, who,
 				icon_file_content, icon_file_size, icon_name);
 	}
@@ -610,7 +622,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	uid = strtol(segments[QQ_INFO_UID], NULL, 10);
+	uid = strtoul(segments[QQ_INFO_UID], NULL, 10);
 	who = uid_to_purple_name(uid);
 
 	qq_filter_str(segments[QQ_INFO_NICK]);
@@ -648,7 +660,7 @@
 	purple_blist_server_alias_buddy(buddy, bd->nickname);
 
 	/* convert face num from packet (0-299) to local face (1-100) */
-	update_buddy_icon(gc->account, who, bd->face);
+	qq_update_buddy_icon(gc->account, who, bd->face);
 
 	g_free(who);
 	g_free(alias_utf8);
@@ -786,12 +798,12 @@
 		bytes += qq_get32(&onlineTime, data + bytes);
 		bytes += qq_get16(&level, data + bytes);
 		bytes += qq_get16(&timeRemainder, data + bytes);
-		purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
-				uid, onlineTime, level, timeRemainder);
+		purple_debug_info("QQ", "level: %d, uid %d, tmOnline: %d, tmRemainder: %d\n",
+				level, uid, onlineTime, timeRemainder);
 
 		bd = qq_buddy_data_find(gc, uid);
 		if (bd == NULL) {
-			purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid);
+			purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid);
 			continue;
 		}
 
@@ -821,8 +833,8 @@
 	bytes += qq_get32(&onlineTime, data + bytes);
 	bytes += qq_get16(&level, data + bytes);
 	bytes += qq_get16(&timeRemainder, data + bytes);
-	purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
-			uid, onlineTime, level, timeRemainder);
+	purple_debug_info("QQ", "level: %d, uid %d, tmOnline: %d, tmRemainder: %d\n",
+			level, uid, onlineTime, timeRemainder);
 
 	bd = qq_buddy_data_find(gc, uid);
 	if (bd == NULL) {
--- a/libpurple/protocols/qq/buddy_info.h	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Sat Dec 06 13:30:06 2008 +0000
@@ -88,4 +88,6 @@
 void qq_request_get_level_2007(PurpleConnection *gc, guint32 uid);
 void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class);
 void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
+
+void qq_update_buddy_icon(PurpleAccount *account, const gchar *who, gint face);
 #endif
--- a/libpurple/protocols/qq/buddy_list.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Sat Dec 06 13:30:06 2008 +0000
@@ -148,10 +148,9 @@
 	/* 015-030: unknown key */
 	bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes);
 
-	purple_debug_info("QQ_STATUS",
-			"uid: %d, U1: %d, ip: %s:%d, U2:%d, status:%d, U3:%04X\n",
-			bs->uid, bs->unknown1, inet_ntoa(bs->ip), bs->port,
-			bs->unknown2, bs->status, bs->unknown3);
+	purple_debug_info("QQ", "Status:%d, uid: %u, ip: %s:%d, U: %d - %d - %04X\n",
+			bs->status, bs->uid, inet_ntoa(bs->ip), bs->port,
+			bs->unknown1, bs->unknown2, bs->unknown3);
 
 	return bytes;
 }
@@ -163,6 +162,8 @@
 	gint bytes, bytes_start;
 	gint count;
 	guint8  position;
+	gchar *who;
+	PurpleBuddy *buddy;
 	qq_buddy_data *bd;
 	int entry_len = 38;
 
@@ -220,17 +221,23 @@
 		}	/* check if it is a valid entry */
 
 		if (bs.uid == qd->uid) {
-			purple_debug_warning("QQ", "I am in online list %d\n", bs.uid);
+			purple_debug_warning("QQ", "I am in online list %u\n", bs.uid);
 		}
 
 		/* update buddy information */
-		bd = qq_buddy_data_find(gc, bs.uid);
+		who = uid_to_purple_name(bs.uid);
+		buddy = purple_find_buddy(gc->account, who);
+		g_free(who);
+		if (buddy == NULL) {
+			/* create no-auth buddy */
+			buddy = qq_buddy_new(gc, bs.uid);
+		}
+		bd = (buddy == NULL) ? NULL : (qq_buddy_data *)buddy->proto_data;
 		if (bd == NULL) {
 			purple_debug_error("QQ",
-					"Got an online buddy %d, but not in my buddy list\n", bs.uid);
+					"Got an online buddy %u, but not in my buddy list\n", bs.uid);
 			continue;
 		}
-		/* we find one and update qq_buddy_data */
 		/*
 		if(0 != fe->s->client_tag)
 			q_bud->client_tag = fe->s->client_tag;
@@ -313,7 +320,8 @@
 
 		if (bd.uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
 			purple_debug_info("QQ",
-					"Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes);
+					"Buddy entry, expect %d bytes, read %d bytes\n",
+					bytes_expected, bytes - buddy_bytes);
 			g_free(bd.nickname);
 			continue;
 		} else {
@@ -387,7 +395,7 @@
 		/* 05: skip unknow 0x00 */
 		bytes += 1;
 		if (uid == 0 || (type != 0x1 && type != 0x4)) {
-			purple_debug_info("QQ", "Buddy entry, uid=%d, type=%d", uid, type);
+			purple_debug_info("QQ", "Buddy entry, uid=%u, type=%d", uid, type);
 			continue;
 		}
 		if(0x1 == type) { /* a buddy */
@@ -397,7 +405,7 @@
 		} else { /* a group */
 			rmd = qq_room_data_find(gc, uid);
 			if(rmd == NULL) {
-				purple_debug_info("QQ", "Unknow room id %d", uid);
+				purple_debug_info("QQ", "Unknow room id %u", uid);
 				qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, uid);
 			} else {
 				rmd->my_role = QQ_ROOM_ROLE_YES;
@@ -528,6 +536,8 @@
 	qq_data *qd;
 	gint bytes;
 	guint32 my_uid;
+	gchar *who;
+	PurpleBuddy *buddy;
 	qq_buddy_data *bd;
 	qq_buddy_status bs;
 
@@ -549,9 +559,17 @@
 	 * QQ_BUDDY_ONLINE_INVISIBLE */
 	bytes += qq_get32(&my_uid, data + bytes);
 
-	bd = qq_buddy_data_find(gc, bs.uid);
+	/* update buddy information */
+	who = uid_to_purple_name(bs.uid);
+	buddy = purple_find_buddy(gc->account, who);
+	g_free(who);
+	if (buddy == NULL) {
+		/* create no-auth buddy */
+		buddy = qq_buddy_new(gc, bs.uid);
+	}
+	bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data;
 	if (bd == NULL) {
-		purple_debug_warning("QQ", "Get status of unknown buddy %d\n", bs.uid);
+		purple_debug_warning("QQ", "Got status of no-auth buddy %u\n", bs.uid);
 		return;
 	}
 
@@ -582,8 +600,6 @@
 
 	g_return_if_fail(uid != 0);
 
-	who = uid_to_purple_name(uid);
-
 	/* purple supports signon and idle time
 	 * but it is not much use for QQ, I do not use them */
 	/* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
@@ -612,7 +628,9 @@
 		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);
+
+	purple_debug_info("QQ", "buddy %u status = %s\n", uid, status_id);
+	who = uid_to_purple_name(uid);
 	purple_prpl_got_user_status(gc->account, who, status_id, NULL);
 
 	if (flag & QQ_COMM_FLAG_MOBILE && status != QQ_BUDDY_OFFLINE)
--- a/libpurple/protocols/qq/buddy_opt.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Sat Dec 06 13:30:06 2008 +0000
@@ -110,11 +110,11 @@
 	g_free(who);
 
 	if (buddy == NULL) {
-		purple_debug_error("QQ", "Can not find purple buddy of %d\n", uid);
+		purple_debug_error("QQ", "Can not find purple buddy of %u\n", uid);
 		return NULL;
 	}
 	if (buddy->proto_data == NULL) {
-		purple_debug_error("QQ", "Can not find buddy data of %d\n", uid);
+		purple_debug_error("QQ", "Can not find buddy data of %u\n", uid);
 		return NULL;
 	}
 	return (qq_buddy_data *)buddy->proto_data;
@@ -146,9 +146,8 @@
 		return NULL;
 	}
 
+	purple_debug_info("QQ", "Add new purple buddy: [%u]\n", uid);
 	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;
 
@@ -214,7 +213,7 @@
 
 	g_return_if_fail(uid > 0);
 
-	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
+	g_snprintf(uid_str, sizeof(uid_str), "%u", uid);
 	bytes = strlen(uid_str);
 	qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, (guint8 *) uid_str, bytes, 0, uid);
 }
@@ -234,7 +233,7 @@
 	bytes += qq_put8(raw_data + bytes, auth_len);
 	bytes += qq_putdata(raw_data + bytes, auth, auth_len);
 
-	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
+	g_snprintf(uid_str, sizeof(uid_str), "%u", uid);
 	bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str));
 
 	qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, raw_data, bytes, 0, uid);
@@ -318,7 +317,7 @@
 	add_req->auth_len = 0;
 
 	who = uid_to_purple_name(uid);
-	msg = g_strdup_printf(_("%d needs Q&A"), uid);
+	msg = g_strdup_printf(_("%u needs Q&A"), uid);
 	purple_request_input(gc, _("Add buddy Q&A"), msg,
 			_("Input answer here"),
 			NULL,
@@ -481,7 +480,7 @@
 	g_return_if_fail(uid > 0);
 
 	/* we need to send the ascii code of this uid to qq server */
-	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
+	g_snprintf(uid_str, sizeof(uid_str), "%u", uid);
 	qq_send_cmd_mess(gc, QQ_CMD_ADD_BUDDY_NO_AUTH,
 			(guint8 *) uid_str, strlen(uid_str), 0, uid);
 }
@@ -501,25 +500,26 @@
 /* this buddy needs authentication, text conversion is done at lowest level */
 static void request_add_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
 {
-	gchar *text_qq, uid_str[11];
-	guint8 bar, *raw_data;
-	gint bytes = 0;
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
+	gint bytes;
+	gchar *msg, uid_str[11];
+	guint8 bar;
 
 	g_return_if_fail(uid != 0);
 
-	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
+	g_snprintf(uid_str, sizeof(uid_str), "%u", uid);
 	bar = 0x1f;
-	raw_data = g_newa(guint8, QQ_MSG_IM_MAX);
 
+	bytes = 0;
 	bytes += qq_putdata(raw_data + bytes, (guint8 *) uid_str, strlen(uid_str));
 	bytes += qq_put8(raw_data + bytes, bar);
 	bytes += qq_put8(raw_data + bytes, response);
 
 	if (text != NULL) {
-		text_qq = utf8_to_qq(text, QQ_CHARSET_DEFAULT);
+		msg = utf8_to_qq(text, QQ_CHARSET_DEFAULT);
 		bytes += qq_put8(raw_data + bytes, bar);
-		bytes += qq_putdata(raw_data + bytes, (guint8 *) text_qq, strlen(text_qq));
-		g_free(text_qq);
+		bytes += qq_putdata(raw_data + bytes, (guint8 *) msg, strlen(msg));
+		g_free(msg);
 	}
 
 	qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH, raw_data, bytes);
@@ -661,7 +661,7 @@
 	}
 
 	who = uid_to_purple_name(uid);
-	msg = g_strdup_printf(_("%d needs authentication"), uid);
+	msg = g_strdup_printf(_("%u needs authentication"), uid);
 	purple_request_input(gc, _("Add buddy authorize"), msg,
 			_("Input request here"),
 			_("Would you be my friend?"),
@@ -706,7 +706,7 @@
 		return;
 	}
 
-	purple_debug_info("QQ", "Remove buddy with invalid QQ number %d\n", uid);
+	purple_debug_info("QQ", "Remove buddy with invalid QQ number %u\n", uid);
 	qq_buddy_free(buddy);
 }
 
@@ -745,7 +745,7 @@
 
 	buddy = qq_buddy_find(gc, uid);
 	if (data[0] != 0) {
-		msg = g_strdup_printf(_("Failed removing buddy %d"), uid);
+		msg = g_strdup_printf(_("Failed removing buddy %u"), uid);
 		purple_notify_info(gc, _("QQ Buddy"), msg, NULL);
 		g_free(msg);
 	}
@@ -767,7 +767,7 @@
 	qd = (qq_data *) gc->proto_data;
 
 	if (data[0] == 0) {
-		purple_debug_info("QQ", "Reply OK for removing me from %d's buddy list\n", uid);
+		purple_debug_info("QQ", "Reply OK for removing me from %u's buddy list\n", uid);
 		return;
 	}
 	msg = g_strdup_printf(_("Failed removing me from %d's buddy list"), uid);
@@ -788,7 +788,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	purple_debug_info("QQ", "Process buddy add for id [%d]\n", uid);
+	purple_debug_info("QQ", "Process buddy add for id [%u]\n", uid);
 	qq_show_packet("buddy_add_no_auth", data, data_len);
 
 	if (NULL == (segments = split_data(data, data_len, "\x1f", 2)))
@@ -796,7 +796,7 @@
 
 	dest_uid = segments[0];
 	reply = segments[1];
-	if (strtol(dest_uid, NULL, 10) != qd->uid) {	/* should not happen */
+	if (strtoul(dest_uid, NULL, 10) != qd->uid) {	/* should not happen */
 		purple_debug_error("QQ", "Add buddy reply is to [%s], not me!", dest_uid);
 		g_strfreev(segments);
 		return;
@@ -814,7 +814,7 @@
 		}
 		qq_request_get_buddies_online(gc, 0, 0);
 
-		purple_debug_info("QQ", "Successed adding into %d's buddy list", uid);
+		purple_debug_info("QQ", "Successed adding into %u's buddy list", uid);
 		g_strfreev(segments);
 		return;
 	}
@@ -850,7 +850,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	purple_debug_info("QQ", "Process buddy add no auth for id [%d]\n", uid);
+	purple_debug_info("QQ", "Process buddy add no auth for id [%u]\n", uid);
 	qq_show_packet("buddy_add_no_auth_ex", data, data_len);
 
 	bytes = 0;
@@ -860,7 +860,7 @@
 	g_return_if_fail(dest_uid == uid);
 
 	if (reply == 0x99) {
-		purple_debug_info("QQ", "Successed adding buddy %d\n", uid);
+		purple_debug_info("QQ", "Successed adding buddy %u\n", uid);
 		qq_buddy_find_or_new(gc, uid);
 
 		qq_request_buddy_info(gc, uid, 0, 0);
@@ -874,7 +874,7 @@
 	}
 
 	if (reply != 0) {
-		purple_debug_info("QQ", "Failed adding buddy %d, Unknow reply 0x%02X\n",
+		purple_debug_info("QQ", "Failed adding buddy %u, Unknow reply 0x%02X\n",
 			uid, reply);
 	}
 
@@ -943,7 +943,7 @@
 
 	g_return_if_fail(uid != 0 && reason != NULL);
 
-	purple_debug_info("QQ", "Buddy %d request adding, msg: %s\n", uid, reason);
+	purple_debug_info("QQ", "Buddy %u request adding, msg: %s\n", uid, reason);
 
 	add_req = g_new0(qq_buddy_req, 1);
 	add_req->gc = gc;
@@ -973,7 +973,7 @@
 	gchar *msg, *reason;
 
 	g_return_if_fail(from != NULL && to != NULL);
-	uid = strtol(from, NULL, 10);
+	uid = strtoul(from, NULL, 10);
 	g_return_if_fail(uid != 0);
 
 	if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) {
@@ -1022,7 +1022,7 @@
 	g_return_if_fail(uid != 0);
 	bytes += qq_get16(&flag1, data + bytes);
 	bytes += qq_get16(&flag2, data + bytes);
-	purple_debug_info("QQ", "Check code reply Ok, uid %d, flag 0x%04X-0x%04X\n",
+	purple_debug_info("QQ", "Check code reply Ok, uid %u, flag 0x%04X-0x%04X\n",
 			uid, flag1, flag2);
 	return;
 }
@@ -1036,7 +1036,7 @@
 
 	g_return_if_fail(code != NULL && code_len > 0 && from != NULL);
 
-	uid = strtol(from, NULL, 10);
+	uid = strtoul(from, NULL, 10);
 	raw_data = g_newa(guint8, code_len + 16);
 	bytes = 0;
 	bytes += qq_put8(raw_data + bytes, 0x03);
@@ -1085,7 +1085,7 @@
 
 	g_return_if_fail(from != NULL && to != NULL);
 	g_return_if_fail(data != NULL && data_len >= 3);
-	uid = strtol(from, NULL, 10);
+	uid = strtoul(from, NULL, 10);
 	g_return_if_fail(uid != 0);
 
 	/* qq_show_packet("server_buddy_add_request_ex", data, data_len); */
@@ -1116,7 +1116,7 @@
 
 	g_return_if_fail(from != NULL && to != NULL);
 
-	uid = strtol(from, NULL, 10);
+	uid = strtoul(from, NULL, 10);
 	who = uid_to_purple_name(uid);
 
 	buddy = purple_find_buddy(account, who);
@@ -1189,7 +1189,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	uid = strtol(from, NULL, 10);
+	uid = strtoul(from, NULL, 10);
 	g_return_if_fail(uid > 0);
 
 	server_buddy_check_code(gc, from, data, data_len);
@@ -1251,7 +1251,7 @@
 	g_free(primary);
 	g_free(secondary);
 
-	uid = strtol(from, NULL, 10);
+	uid = strtoul(from, NULL, 10);
 	g_return_if_fail(uid != 0);
 
 	buddy = qq_buddy_find(gc, uid);
--- a/libpurple/protocols/qq/char_conv.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Sat Dec 06 13:30:06 2008 +0000
@@ -27,77 +27,16 @@
 
 #include "char_conv.h"
 #include "packet_parse.h"
-#include "qq.h"
 #include "utils.h"
 
-#define QQ_SMILEY_AMOUNT      96
-
 #define UTF8                  "UTF-8"
 #define QQ_CHARSET_ZH_CN      "GB18030"
 #define QQ_CHARSET_ENG        "ISO-8859-1"
 
 #define QQ_NULL_MSG           "(NULL)"	/* return this if conversion fails */
-#define QQ_NULL_SMILEY        "<IMG ID=\"0\">"	/* return this if smiley conversion fails */
-
-const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
-	0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
-	0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73,
-	0x74, 0x75, 0x76, 0x77, 0x8a, 0x8b, 0x8c, 0x8d,
-	0x8e, 0x8f, 0x78, 0x79, 0x7a, 0x7b, 0x90, 0x91,
-	0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
-	0x59, 0x5a, 0x5c, 0x58, 0x57, 0x55, 0x7c, 0x7d,
-	0x7e, 0x7f, 0x9a, 0x9b, 0x60, 0x67, 0x9c, 0x9d,
-	0x9e, 0x5e, 0x9f, 0x89, 0x80, 0x81, 0x82, 0x62,
-	0x63, 0x64, 0x65, 0x66, 0x83, 0x68, 0x84, 0x85,
-	0x86, 0x87, 0x6b, 0x6e, 0x6f, 0x70, 0x88, 0xa0,
-	0x50, 0x51, 0x52, 0x53, 0x54, 0x56, 0x5b, 0x5d,
-	0x5f, 0x61, 0x69, 0x6a, 0x6c, 0x6d, 0x71, 0x72
-};
-
-
-const gchar *purple_smiley_map[QQ_SMILEY_AMOUNT] = {
-	"/jy", "/pz", "/se", "/fd", "/dy", "/ll", "/hx", "/bz",
-	"/shui", "/dk	", "/gg", "/fn", "/tp", "/cy", "/wx", "/ng",
-	"/kuk", "/feid", "/zk", "/tu", "/tx", "/ka", "/by", "/am",
-	"/jie", "/kun", "/jk", "/lh", "/hanx", "/db", "/fendou",
-	"/zhm",
-	"/yiw", "/xu", "/yun", "/zhem", "/shuai", "/kl", "/qiao",
-	"/zj",
-	"/shan", "/fad", "/aiq", "/tiao", "/zhao", "/mm", "/zt",
-	"/maom",
-	"/xg", "/yb", "/qianc", "/dp", "/bei", "/dg", "/shd",
-	"/zhd",
-	"/dao", "/zq", "/yy", "/bb", "/gf", "/fan", "/yw", "/mg",
-	"/dx", "/wen", "/xin", "/xs", "/hy", "/lw", "/dh", "/sj",
-	"/yj", "/ds", "/ty", "/yl", "/qiang", "/ruo", "/ws",
-	"/shl",
-	"/dd", "/mn", "/hl", "/mamao", "/qz", "/fw", "/oh", "/bj",
-	"/qsh", "/xig", "/xy", "/duoy", "/xr", "/xixing", "/nv",
-	"/nan"
-};
-
-/* these functions parse font-attr */
-static gchar _get_size(gchar font_attr)
-{
-	return font_attr & 0x1f;
-}
-
-static gboolean _check_bold(gchar font_attr)
-{
-	return (font_attr & 0x20) ? TRUE : FALSE;
-}
-
-static gboolean _check_italic(gchar font_attr)
-{
-	return (font_attr & 0x40) ? TRUE : FALSE;
-}
-
-static gboolean _check_underline(gchar font_attr)
-{
-	return (font_attr & 0x80) ? TRUE : FALSE;
-}
 
 /* convert a string from from_charset to to_charset, using g_convert */
+/* Warning: do not return NULL */
 static gchar *do_convert(const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset)
 {
 	GError *error = NULL;
@@ -109,15 +48,12 @@
 	ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error);
 
 	if (error == NULL) {
-		return ret;	/* conversion is OK */
+		return ret;	/* convert is OK */
 	}
 
-	/* conversion error */
+	/* convert error */
 	purple_debug_error("QQ_CONVERT", "%s\n", error->message);
-
-	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ_CONVERT",
-		(guint8 *) str, (len == -1) ? strlen(str) : len,
-		"Dump failed text");
+	qq_show_packet("Dump failed text", (guint8 *) str, (len == -1) ? strlen(str) : len);
 
 	g_error_free(error);
 	return g_strdup(QQ_NULL_MSG);
@@ -127,6 +63,7 @@
  * take the input as a pascal string and return a converted c-string in UTF-8
  * returns the number of bytes read, return -1 if fatal error
  * the converted UTF-8 will be saved in ret
+ * Return: *ret != NULL
  */
 gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data)
 {
@@ -162,156 +99,15 @@
 	return 1 + len;
 }
 
-/* convert QQ formatted msg to Purple formatted msg (and UTF-8) */
-gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg, const gint client_version)
-{
-	GString *encoded;
-	guint8 font_attr, font_size, color[3], bar;
-	gboolean is_bold, is_italic, is_underline;
-	guint16 charset_code;
-	gchar *font_name, *color_code, *msg_utf8, *tmp, *ret;
-	gint bytes = 0;
-
-	/* checked qq_show_packet OK */
-	/* qq_show_packet("QQ_MESG recv for font style", data, len); */
-
-	bytes += qq_get8(&font_attr, data + bytes);
-	bytes += qq_getdata(color, 3, data + bytes);	/* red,green,blue */
-	color_code = g_strdup_printf("#%02x%02x%02x", color[0], color[1], color[2]);
-
-	bytes += qq_get8(&bar, data + bytes);	/* skip, not sure of its use */
-	bytes += qq_get16(&charset_code, data + bytes);
-
-	tmp = g_strndup((gchar *)(data + bytes), len - bytes);
-	font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT);
-	g_free(tmp);
-
-	font_size = _get_size(font_attr);
-	is_bold = _check_bold(font_attr);
-	is_italic = _check_italic(font_attr);
-	is_underline = _check_underline(font_attr);
-
-	/* Although there is charset returned from QQ msg, it can't be used.
-	 * For example, if a user send a Chinese message from English Windows
-	 * the charset_code in QQ msg is 0x0000, not 0x8602.
-	 * Therefore, it is better to use uniform conversion.
-	 * By default, we use GBK, which includes all character of SC, TC, and EN. */
-	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-	encoded = g_string_new("");
-
-	/* Henry: The range QQ sends rounds from 8 to 22, where a font size
-	 * of 10 is equal to 3 in html font tag */
-	g_string_append_printf(encoded,
-			"<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
-			color_code, font_name, font_size / 3);
-	purple_debug_info("QQ_MESG",
-			"recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
-			color_code, font_name, font_size / 3);
-	g_string_append(encoded, msg_utf8);
-
-	if (is_bold) {
-		g_string_prepend(encoded, "<b>");
-		g_string_append(encoded, "</b>");
-	}
-	if (is_italic) {
-		g_string_prepend(encoded, "<i>");
-		g_string_append(encoded, "</i>");
-	}
-	if (is_underline) {
-		g_string_prepend(encoded, "<u>");
-		g_string_append(encoded, "</u>");
-	}
-
-	g_string_append(encoded, "</font></font></font>");
-	ret = encoded->str;
-
-	g_free(msg_utf8);
-	g_free(font_name);
-	g_free(color_code);
-	g_string_free(encoded, FALSE);
-
-	return ret;
-}
-
-/* two convenience methods, using do_convert */
+/* Warning: do not return NULL */
 gchar *utf8_to_qq(const gchar *str, const gchar *to_charset)
 {
 	return do_convert(str, -1, to_charset, UTF8);
 }
 
+/* Warning: do not return NULL */
 gchar *qq_to_utf8(const gchar *str, const gchar *from_charset)
 {
 	return do_convert(str, -1, UTF8, from_charset);
 }
 
-/* QQ uses binary code for smiley, while purple uses strings.
- * There is a mapping relation between these two. */
-gchar *qq_smiley_to_purple(gchar *text)
-{
-	gint index;
-	gchar qq_smiley, *cur_seg, **segments, *ret;
-	GString *converted;
-
-	converted = g_string_new("");
-	segments = split_data((guint8 *) text, strlen(text), "\x14\x15", 0);
-	if(segments == NULL)
-		return NULL;
-
-	g_string_append(converted, segments[0]);
-	while ((*(++segments)) != NULL) {
-		cur_seg = *segments;
-		qq_smiley = cur_seg[0];
-		for (index = 0; index < QQ_SMILEY_AMOUNT; index++) {
-			if (qq_smiley_map[index] == qq_smiley)
-				break;
-		}
-		if (index >= QQ_SMILEY_AMOUNT) {
-			g_string_append(converted, QQ_NULL_SMILEY);
-		} else {
-			g_string_append(converted, purple_smiley_map[index]);
-			g_string_append(converted, (cur_seg + 1));
-		}
-	}
-
-	ret = converted->str;
-	g_string_free(converted, FALSE);
-	return ret;
-}
-
-/* convert smiley from purple style to qq binary code */
-gchar *purple_smiley_to_qq(gchar *text)
-{
-	gchar *begin, *cursor, *ret;
-	gint index;
-	GString *converted;
-
-	converted = g_string_new(text);
-
-	for (index = 0; index < QQ_SMILEY_AMOUNT; index++) {
-		begin = cursor = converted->str;
-		while ((cursor = g_strstr_len(cursor, -1, purple_smiley_map[index]))) {
-			g_string_erase(converted, (cursor - begin), strlen(purple_smiley_map[index]));
-			g_string_insert_c(converted, (cursor - begin), 0x14);
-			g_string_insert_c(converted, (cursor - begin + 1), qq_smiley_map[index]);
-			cursor++;
-		}
-	}
-	g_string_append_c(converted, 0x20);	/* important for last smiiley */
-
-	ret = converted->str;
-	g_string_free(converted, FALSE);
-	return ret;
-}
-
-void qq_filter_str(gchar *str) {
-	gchar *temp;
-	if (str == NULL) {
-		return;
-	}
-
-	for (temp = str; *temp != 0; temp++) {
-		/*if (*temp == '\r' || *temp == '\n')  *temp = ' ';*/
-		if (*temp > 0 && *temp < 0x20)  *temp = ' ';
-	}
-	g_strstrip(str);
-}
--- a/libpurple/protocols/qq/char_conv.h	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.h	Sat Dec 06 13:30:06 2008 +0000
@@ -32,13 +32,7 @@
 gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data);
 gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset);
 
-gchar *qq_smiley_to_purple(gchar *text);
-gchar *purple_smiley_to_qq(gchar *text);
-
 gchar *utf8_to_qq(const gchar *str, const gchar *to_charset);
 gchar *qq_to_utf8(const gchar *str, const gchar *from_charset);
-gchar *qq_encode_to_purple(guint8 *font_attr_data, gint len, const gchar *msg, const gint client_version);
 
-gchar *qq_im_filter_html(const gchar *text);
-void qq_filter_str(gchar *str);
 #endif
--- a/libpurple/protocols/qq/group.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Sat Dec 06 13:30:06 2008 +0000
@@ -40,7 +40,7 @@
 	guint32 ext_id;
 
 	g_return_if_fail(input != NULL);
-	ext_id = strtol(input, NULL, 10);
+	ext_id = strtoul(input, NULL, 10);
 	/* 0x00000000 means search for demo group */
 	qq_request_room_search(gc, ext_id, QQ_ROOM_SEARCH_ONLY);
 }
--- a/libpurple/protocols/qq/group_im.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Sat Dec 06 13:30:06 2008 +0000
@@ -64,8 +64,12 @@
 	serv_got_joined_chat(gc, rmd->id, rmd->title_utf8);
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, rmd->title_utf8, purple_connection_get_account(gc));
 	if (conv != NULL) {
-		topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8);
-		purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
+		if (rmd->notice_utf8 != NULL) {
+			topic_utf8 = g_strdup_printf("%u %s", rmd->ext_id, rmd->notice_utf8);
+		} else {
+			topic_utf8 = g_strdup_printf("%u", rmd->ext_id);
+		}
+		purple_debug_info("QQ", "Chat topic = %s\n", topic_utf8);
 		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
 		g_free(topic_utf8);
 
@@ -157,48 +161,6 @@
 	g_list_free(flags);
 }
 
-/* send IM to a group */
-void qq_request_room_send_im(PurpleConnection *gc, guint32 room_id, const gchar *msg)
-{
-	gint data_len, bytes;
-	guint8 *raw_data, *send_im_tail;
-	guint16 msg_len;
-	gchar *msg_filtered;
-
-	g_return_if_fail(room_id != 0 && msg != NULL);
-
-	msg_filtered = purple_markup_strip_html(msg);
-	/* purple_debug_info("QQ", "Send qun mesg filterd: %s\n", msg_filtered); */
-	msg_len = strlen(msg_filtered);
-
-	data_len = 2 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
-	raw_data = g_newa(guint8, data_len);
-
-	bytes = 0;
-	bytes += qq_put16(raw_data + bytes, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
-	send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL,
-			FALSE, FALSE, FALSE,
-			QQ_SEND_IM_AFTER_MSG_LEN);
-	bytes += qq_putdata(raw_data + bytes, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN);
-	g_free(send_im_tail);
-	g_free(msg_filtered);
-
-	if (bytes == data_len)	/* create OK */
-		qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_MSG, room_id, raw_data, data_len);
-	else
-		purple_debug_error("QQ",
-				"Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
-}
-
-/* this is the ACK */
-void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len)
-{
-	/* return should be the internal group id
-	 * but we have nothing to do with it */
-	return;
-}
-
 void qq_room_got_chat_in(PurpleConnection *gc,
 		guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time)
 {
@@ -208,6 +170,7 @@
 	gchar *from;
 
 	g_return_if_fail(gc != NULL && room_id != 0);
+	g_return_if_fail(msg != NULL);
 
 	conv = purple_find_chat(gc, room_id);
 	rmd = qq_room_data_find(gc, room_id);
@@ -218,6 +181,8 @@
 	}
 
 	if (conv == NULL) {
+		purple_debug_info("QQ", "Conversion of %u is not open, missing from %d:/n%s/v",
+				room_id, uid_from, msg);
 		return;
 	}
 
@@ -225,7 +190,7 @@
 
 		bd = qq_room_buddy_find(rmd, uid_from);
 		if (bd == NULL || bd->nickname == NULL)
-			from = g_strdup_printf("%d", uid_from);
+			from = g_strdup_printf("%u", uid_from);
 		else
 			from = g_strdup(bd->nickname);
 	} else {
@@ -238,10 +203,9 @@
 /* recv an IM from a group chat */
 void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type)
 {
-	gchar *msg_with_purple_smiley, *msg_utf8_encoded;
 	qq_data *qd;
-	gint skip_len;
-	gint bytes ;
+	gchar *msg_smiley, *msg_fmt, *msg_utf8;
+	gint bytes, tail_len;
 	struct {
 		guint32 ext_id;
 		guint8 type8;
@@ -249,87 +213,224 @@
 		guint16 unknown;
 		guint16 msg_seq;
 		time_t send_time;
-		guint32 unknown4;
+		guint32 version;
 		guint16 msg_len;
 		gchar *msg;
-		guint8 *font_attr;
-		gint font_attr_len;
-	} packet;
+	} im_text;
+	guint32 temp_id;
+	guint16 content_type;
+	guint8 frag_count, frag_index;
+	guint16 msg_id;
+	qq_im_format *fmt = NULL;
 
-	g_return_if_fail(data != NULL && data_len > 0);
-
-	/* FIXME: check length here */
-
+	/* at least include im_text.msg_len */
+	g_return_if_fail(data != NULL && data_len > 23);
 	qd = (qq_data *) gc->proto_data;
 
 	/* qq_show_packet("ROOM_IM", data, data_len); */
-
-	memset(&packet, 0, sizeof(packet));
+	memset(&im_text, 0, sizeof(im_text));
 	bytes = 0;
-	bytes += qq_get32(&(packet.ext_id), data + bytes);
-	bytes += qq_get8(&(packet.type8), data + bytes);
+	bytes += qq_get32(&(im_text.ext_id), data + bytes);
+	bytes += qq_get8(&(im_text.type8), data + bytes);
 
 	if(QQ_MSG_TEMP_QUN_IM == msg_type) {
-		bytes += qq_get32(&(id), data + bytes);
+		bytes += qq_get32(&temp_id, data + bytes);
 	}
 
-	bytes += qq_get32(&(packet.member_uid), bytes + data);
-	bytes += qq_get16(&packet.unknown, data + bytes);	/* 0x0001? */
-	bytes += qq_get16(&(packet.msg_seq), data + bytes);
-	bytes += qq_getime(&packet.send_time, data + bytes);
-	bytes += qq_get32(&packet.unknown4, data + bytes);	/* versionID */
-	/*
-	 * length includes font_attr
-	 * this msg_len includes msg and font_attr
-	 **** the format is ****
-	 * length of all
-	 * 1. unknown 10 bytes
-	 * 2. 0-ended string
-	 * 3. font_attr
-	 */
+	bytes += qq_get32(&(im_text.member_uid), bytes + data);
+	bytes += qq_get16(&im_text.unknown, data + bytes);	/* 0x0001? */
+	bytes += qq_get16(&(im_text.msg_seq), data + bytes);
+	bytes += qq_getime(&im_text.send_time, data + bytes);
+	bytes += qq_get32(&im_text.version, data + bytes);
+	bytes += qq_get16(&(im_text.msg_len), data + bytes);
+	purple_debug_info("QQ", "Room IM, ext id %u, seq %u, version 0x%04X, len %u\n",
+		im_text.ext_id, im_text.msg_seq, im_text.version, im_text.msg_len);
+
+	if (im_text.msg_len != data_len - bytes) {
+		purple_debug_warning("QQ", "Room IM length %d should be %d\n",
+			im_text.msg_len, data_len - bytes);
+		im_text.msg_len = data_len - bytes;
+	}
 
-	bytes += qq_get16(&(packet.msg_len), data + bytes);
-	g_return_if_fail(packet.msg_len > 0);
-	/*
-	 * 10 bytes from lumaqq
-	 *    contentType = buf.getChar();
-	 *    totalFragments = buf.get() & 255;
-	 *    fragmentSequence = buf.get() & 255;
-	 *    messageId = buf.getChar();
-	 *    buf.getInt();
-	 */
+	g_return_if_fail(im_text.msg_len > 0 && bytes + im_text.msg_len <= data_len);
+	if(msg_type != QQ_MSG_QUN_IM_UNKNOWN) {
+		g_return_if_fail(im_text.msg_len >= 10);
 
-	if(msg_type != QQ_MSG_UNKNOWN_QUN_IM)
-		skip_len = 10;
-	else
-		skip_len = 0;
-	bytes += skip_len;
+		bytes += qq_get16(&content_type, data + bytes);
+		bytes += qq_get8(&frag_count, data + bytes);
+		bytes += qq_get8(&frag_index, data + bytes);
+		bytes += qq_get16(&msg_id, data + bytes);
+		bytes += 4;	/* skip 0x(00 00 00 00) */
+		purple_debug_info("QQ", "Room IM, content %d, frag %d-%d, msg id %u\n",
+			content_type, frag_count, frag_index, msg_id);
+		im_text.msg_len -= 10;
+	}
+	g_return_if_fail(im_text.msg_len > 0);
 
 	/* qq_show_packet("Message", data + bytes, data_len - bytes); */
-
-	packet.msg = g_strdup((gchar *) data + bytes);
-	bytes += strlen(packet.msg) + 1;
-	/* there might not be any font_attr, check it */
-	packet.font_attr_len = data_len - bytes;
-	if (packet.font_attr_len > 0) {
-		packet.font_attr = g_memdup(data + bytes, packet.font_attr_len);
-		/* qq_show_packet("font_attr", packet.font_attr, packet.font_attr_len); */
+	if (frag_count <= 1 || frag_count == frag_index + 1) {
+		fmt = qq_im_fmt_new();
+		tail_len = qq_get_im_tail(fmt, data + bytes, data_len - bytes);
+		im_text.msg = g_strndup((gchar *)(data + bytes), data_len - tail_len);
 	} else {
-		packet.font_attr = NULL;
+		im_text.msg = g_strndup((gchar *)(data + bytes), data_len - bytes);
 	}
 
 	/* group im_group has no flag to indicate whether it has font_attr or not */
-	msg_with_purple_smiley = qq_smiley_to_purple(packet.msg);
-	if (packet.font_attr_len > 0) {
-		msg_utf8_encoded = qq_encode_to_purple(packet.font_attr,
-				packet.font_attr_len, msg_with_purple_smiley, qd->client_version);
+	msg_smiley = qq_emoticon_to_purple(im_text.msg);
+	if (fmt != NULL) {
+		msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley);
+		msg_utf8 =  qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT);
+		g_free(msg_fmt);
+		qq_im_fmt_free(fmt);
 	} else {
-		msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+		msg_utf8 =  qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT);
+	}
+	g_free(msg_smiley);
+
+	purple_debug_info("QQ", "Room (%u) IM from %u: %s\n",
+			im_text.ext_id, im_text.member_uid, msg_utf8);
+ 	qq_room_got_chat_in(gc, id, im_text.member_uid, msg_utf8, im_text.send_time);
+
+	g_free(msg_utf8);
+	g_free(im_text.msg);
+}
+
+/* send IM to a group */
+static void request_room_send_im(PurpleConnection *gc, guint32 room_id, qq_im_format *fmt, const gchar *msg)
+{
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
+	gint bytes;
+
+	g_return_if_fail(room_id != 0 && msg != NULL);
+
+	bytes = 0;
+	bytes += qq_put16(raw_data + bytes, 0);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg));
+	bytes += qq_put_im_tail(raw_data + bytes, fmt);
+	/* reset first two bytes */
+	qq_put16(raw_data, bytes - 2);
+
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM, room_id, raw_data, bytes);
+}
+
+/* this is the ACK */
+void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len)
+{
+	/* return should be the internal group id
+	 * but we have nothing to do with it */
+	return;
+}
+
+void qq_process_room_send_im_ex(PurpleConnection *gc, guint8 *data, gint len)
+{
+	/* return should be the internal group id
+	 * but we have nothing to do with it */
+	return;
+}
+
+static void request_room_send_im_ex(PurpleConnection *gc, guint32 room_id,
+	qq_im_format *fmt, gchar *msg, guint16 msg_id, guint8 frag_count, guint8 frag_index)
+{
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
+	gint bytes;
+
+
+	g_return_if_fail(room_id != 0 && msg != NULL);
+
+	bytes = 0;
+	bytes += qq_put16(raw_data + bytes, 0);			/* packet len */
+	/* type 0x0001, text only; 0x0002, with custom emoticon */
+	bytes += qq_put16(raw_data + bytes, 0x0001);
+	bytes += qq_put8(raw_data + bytes, frag_count);
+	bytes += qq_put8(raw_data + bytes, frag_index);
+	bytes += qq_put16(raw_data + bytes, msg_id);
+	bytes += qq_put32(raw_data + bytes, 0);			/* unknow 4 bytes */
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg));
+	if (frag_count == frag_index + 1) {
+		bytes += qq_put8(raw_data + bytes, 0x20);	/* add extra SPACE */
+		bytes += qq_put_im_tail(raw_data + bytes, fmt);
 	}
- 	qq_room_got_chat_in(gc, id, packet.member_uid, msg_utf8_encoded, packet.send_time);
+
+	/* reset first two bytes as length */
+	qq_put16(raw_data, bytes - 2);
+
+	/*qq_show_packet("QQ_ROOM_CMD_SEND_IM_EX", raw_data, bytes); */
+	qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM_EX, room_id, raw_data, bytes);
+}
+
+/* send a chat msg to a QQ Qun
+ * called by purple */
+int qq_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags)
+{
+	qq_data *qd;
+	qq_im_format *fmt;
+	gchar *msg_stripped, *tmp;
+	GSList *segments, *it;
+	gint msg_len;
+	const gchar *start_invalid;
+	gboolean is_smiley_none;
+	guint8 frag_count, frag_index;
+
+	g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1);
+	g_return_val_if_fail(id != 0 && what != NULL, -1);
+
+	qd = (qq_data *) gc->proto_data;
+	purple_debug_info("QQ", "Send chat IM to %u, len %d:\n%s\n", id, strlen(what), what);
+
+	/* qq_show_packet("chat IM UTF8", (guint8 *)what, strlen(what)); */
+
+	fmt = qq_im_fmt_new_by_purple(what);
+	is_smiley_none = qq_im_smiley_none(what);
+
+	msg_stripped = purple_markup_strip_html(what);
+	g_return_val_if_fail(msg_stripped != NULL, -1);
+	/* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */
 
-	g_free(msg_with_purple_smiley);
-	g_free(msg_utf8_encoded);
-	g_free(packet.msg);
-	g_free(packet.font_attr);
+	/* Check and valid utf8 string */
+	msg_len = strlen(msg_stripped);
+	if (!g_utf8_validate(msg_stripped, msg_len, &start_invalid)) {
+		if (start_invalid > msg_stripped) {
+			tmp = g_strndup(msg_stripped, start_invalid - msg_stripped);
+			g_free(msg_stripped);
+			msg_stripped = g_strconcat(tmp, _("(Invalid UTF-8 string)"), NULL);
+			g_free(tmp);
+		} else {
+			g_free(msg_stripped);
+			msg_stripped = g_strdup(_("(Invalid UTF-8 string)"));
+		}
+	}
+
+	is_smiley_none = qq_im_smiley_none(what);
+	segments = qq_im_get_segments(msg_stripped, is_smiley_none);
+	g_free(msg_stripped);
+
+	if (segments == NULL) {
+		return -1;
+	}
+
+	qd->send_im_id++;
+	fmt = qq_im_fmt_new_by_purple(what);
+	frag_count = g_slist_length(segments);
+	frag_index = 0;
+/*
+	if (frag_count <= 1) {
+*/
+		for (it = segments; it; it = it->next) {
+			request_room_send_im(gc, id, fmt, (gchar *)it->data);
+			g_free(it->data);
+		}
+/*
+	} else {
+		for (it = segments; it; it = it->next) {
+			request_room_send_im_ex(gc, id, fmt, (gchar *)it->data,
+					qd->send_im_id, frag_count, frag_index);
+			g_free(it->data);
+			frag_index++;
+		}
+	}
+*/
+	qq_im_fmt_free(fmt);
+	g_slist_free(segments);
+	return 1;
 }
--- a/libpurple/protocols/qq/group_im.h	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Sat Dec 06 13:30:06 2008 +0000
@@ -36,8 +36,9 @@
 void qq_room_got_chat_in(PurpleConnection *gc,
 		guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time);
 
-void qq_request_room_send_im(PurpleConnection *gc, guint32 room_id, const gchar *msg);
+int qq_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags);
 void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len);
+void qq_process_room_send_im_ex(PurpleConnection *gc, guint8 *data, gint len);
 
 void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type);
 
--- a/libpurple/protocols/qq/group_info.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Sat Dec 06 13:30:06 2008 +0000
@@ -144,7 +144,7 @@
 
 	purple_notify_user_info_add_section_break(room_info);
 
-	utf8_value = g_strdup_printf(("%d"), rmd->creator_uid);
+	utf8_value = g_strdup_printf(("%u"), rmd->creator_uid);
 	purple_notify_user_info_add_pair(room_info, _("Creator"), utf8_value);
 	g_free(utf8_value);
 
@@ -160,7 +160,7 @@
 	purple_notify_user_info_add_pair(room_info, _("Authorize"), utf8_value);
 	g_free(utf8_value);
 
-	utf8_value = g_strdup_printf(("%d"), rmd->ext_id);
+	utf8_value = g_strdup_printf(("%u"), rmd->ext_id);
 	purple_notify_userinfo(gc, utf8_value, room_info, NULL, NULL);
 	g_free(utf8_value);
 
@@ -214,7 +214,7 @@
 	 * 2(qunNoticeLen), qunNoticeLen(qunNoticeContent, 1(qunDescLen),
 	 * qunDestLen(qunDestcontent)) */
 	bytes += qq_get8(&unknown1, data + bytes);
-	purple_debug_info("QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
+	purple_debug_info("QQ", "type: %u creator: %u category: %u maxmembers: %u\n",
 			rmd->type8, rmd->creator_uid, rmd->category, max_members);
 
 	if (qd->client_version >= 2007) {
@@ -241,7 +241,7 @@
 
 #if 0
 		if(organization != 0 || role != 0) {
-			purple_debug_info("QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role);
+			purple_debug_info("QQ", "%u, organization=%d, role=%d\n", member_uid, organization, role);
 		}
 #endif
 
@@ -277,7 +277,7 @@
 		return;
 	}
 
-	topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8);
+	topic_utf8 = g_strdup_printf("%u %s", rmd->ext_id, rmd->notice_utf8);
 	purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
 	purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
 	g_free(topic_utf8);
@@ -285,7 +285,7 @@
 
 void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc)
 {
-	guint32 id, member_uid;
+	guint32 room_id, member_uid;
 	guint8 unknown;
 	gint bytes, num;
 	qq_room_data *rmd;
@@ -299,13 +299,13 @@
 	}
 
 	bytes = 0;
-	bytes += qq_get32(&id, data + bytes);
+	bytes += qq_get32(&room_id, data + bytes);
 	bytes += qq_get8(&unknown, data + bytes);	/* 0x3c ?? */
-	g_return_if_fail(id > 0);
+	g_return_if_fail(room_id > 0);
 
-	rmd = qq_room_data_find(gc, id);
+	rmd = qq_room_data_find(gc, room_id);
 	if (rmd == NULL) {
-		purple_debug_error("QQ", "We have no group info for internal id [%d]\n", id);
+		purple_debug_error("QQ", "Can not info of room id [%u]\n", room_id);
 		return;
 	}
 
@@ -384,7 +384,7 @@
 		purple_debug_error("QQ",
 				"group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
 	}
-	purple_debug_info("QQ", "Group \"%s\" obtained %d member info\n", rmd->title_utf8, num);
+	purple_debug_info("QQ", "Group \"%s\" got %d member info\n", rmd->title_utf8, num);
 
 	rmd->is_got_buddies = TRUE;
 	qq_room_conv_set_onlines(gc, rmd);
--- a/libpurple/protocols/qq/group_internal.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Sat Dec 06 13:30:06 2008 +0000
@@ -34,8 +34,9 @@
 {
 	qq_room_data *rmd;
 
-	purple_debug_info("QQ", "Created room data: %s, ext id %d, id %d\n",
-			title, ext_id, id);
+	purple_debug_info("QQ", "Created room data: %s, ext id %u, id %u\n",
+			title == NULL ? "(NULL)" : title,
+			ext_id, id);
 	rmd = g_new0(qq_room_data, 1);
 	rmd->my_role = QQ_ROOM_ROLE_NO;
 	rmd->id = id;
@@ -60,9 +61,9 @@
 	gchar *value;
 
 	value = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
-	id = value ? strtol(value, NULL, 10) : 0;
+	id = value ? strtoul(value, NULL, 10) : 0;
 	value= g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
-	ext_id = value ? strtol(value, NULL, 10) : 0;
+	ext_id = value ? strtoul(value, NULL, 10) : 0;
 	value = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8));
 
 	rmd = room_data_new(id, ext_id, value);
@@ -107,10 +108,10 @@
 	}
 	g_hash_table_replace(chat->components,
 		     g_strdup(QQ_ROOM_KEY_INTERNAL_ID),
-		     g_strdup_printf("%d", rmd->id));
+		     g_strdup_printf("%u", rmd->id));
 	g_hash_table_replace(chat->components,
 		     g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
-		     g_strdup_printf("%d", rmd->ext_id));
+		     g_strdup_printf("%u", rmd->ext_id));
 	g_hash_table_replace(chat->components,
 		     g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8));
 }
@@ -121,14 +122,15 @@
 	PurpleGroup *g;
 	PurpleChat *chat;
 
-	purple_debug_info("QQ", "Add new chat: id %d, ext id %d, title %s\n",
-		rmd->id, rmd->ext_id, rmd->title_utf8);
+	purple_debug_info("QQ", "Add new chat: id %u, ext id %u, title %s\n",
+		rmd->id, rmd->ext_id,
+		rmd->title_utf8 == NULL ? "(NULL)" : rmd->title_utf8);
 
 	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 	g_hash_table_insert(components,
-			    g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%d", rmd->id));
+			    g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%u", rmd->id));
 	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
-			    g_strdup_printf("%d", rmd->ext_id));
+			    g_strdup_printf("%u", rmd->ext_id));
 	g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8));
 
 	chat = purple_chat_new(purple_connection_get_account(gc), rmd->title_utf8, components);
@@ -150,7 +152,7 @@
 
 	g_return_val_if_fail(id != 0 && ext_id != 0, NULL);
 
-	purple_debug_info("QQ", "Find or add new room: id %d, ext id %d\n", id, ext_id);
+	purple_debug_info("QQ", "Find or add new room: id %u, ext id %u\n", id, ext_id);
 
 	rmd = qq_room_data_find(gc, id);
 	if (rmd == NULL) {
@@ -160,7 +162,7 @@
 		qd->groups = g_list_append(qd->groups, rmd);
 	}
 
-	num_str = g_strdup_printf("%d", ext_id);
+	num_str = g_strdup_printf("%u", ext_id);
 	chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
 	g_free(num_str);
 	if (chat) {
@@ -181,7 +183,7 @@
 	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	purple_debug_info("QQ", "Find and remove room data, id %d", id);
+	purple_debug_info("QQ", "Find and remove room data, id %u", id);
 	rmd = qq_room_data_find(gc, id);
 	g_return_if_fail (rmd != NULL);
 
@@ -189,8 +191,8 @@
 	qd->groups = g_list_remove(qd->groups, rmd);
 	room_data_free(rmd);
 
-	purple_debug_info("QQ", "Find and remove chat, ext_id %d", ext_id);
-	num_str = g_strdup_printf("%d", ext_id);
+	purple_debug_info("QQ", "Find and remove chat, ext_id %u", ext_id);
+	num_str = g_strdup_printf("%u", ext_id);
 	chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
 	g_free(num_str);
 
--- a/libpurple/protocols/qq/group_join.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Sat Dec 06 13:30:06 2008 +0000
@@ -116,7 +116,7 @@
 
 	rmd = qq_room_data_find(add_req->gc, add_req->id);
 	if (rmd == NULL) {
-		purple_debug_error("QQ", "Can not find room data of %d\n", add_req->id);
+		purple_debug_error("QQ", "Can not find room data of %u\n", add_req->id);
 		g_free(add_req);
 		return;
 	}
@@ -137,9 +137,9 @@
 	qq_room_req *add_req;
 	g_return_if_fail(rmd != NULL);
 
-	purple_debug_info("QQ", "Room (internal id: %d) needs authentication\n", rmd->id);
+	purple_debug_info("QQ", "Room id %u needs authentication\n", rmd->id);
 
-	msg = g_strdup_printf("QQ Qun %d needs authentication\n", rmd->ext_id);
+	msg = g_strdup_printf("QQ Qun %u needs authentication\n", rmd->ext_id);
 	add_req = g_new0(qq_room_req, 1);
 	add_req->gc = gc;
 	add_req->id = rmd->id;
@@ -154,7 +154,7 @@
 	g_free(msg);
 }
 
-void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, 
+void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd,
 		guint8 opt, guint32 uid, const gchar *reason_utf8)
 {
 	guint8 raw_data[MAX_PACKET_SIZE - 16];
@@ -219,11 +219,11 @@
 
 	rmd = qq_room_data_find(gc, id);
 	if (rmd != NULL) {
-		msg = g_strdup_printf(_("Successfully joined Qun %s (%d)"), rmd->title_utf8, rmd->ext_id);
-		qq_got_attention(gc, msg);
+		msg = g_strdup_printf(_("Successed join to Qun %s (%u)"), rmd->title_utf8, rmd->ext_id);
+		qq_got_message(gc, msg);
 		g_free(msg);
 	} else {
-		qq_got_attention(gc, _("Successfully joined Qun"));
+		qq_got_message(gc, _("Successed join to Qun"));
 	}
 }
 
@@ -261,19 +261,19 @@
 		break;
 	case QQ_ROOM_JOIN_NEED_AUTH:
 		purple_debug_info("QQ",
-			   "Fail joining group [%d] %s, needs authentication\n",
+			   "Failed to join room ext id %u %s, needs authentication\n",
 			   rmd->ext_id, rmd->title_utf8);
 		rmd->my_role = QQ_ROOM_ROLE_NO;
 		do_room_join_request(gc, rmd);
 		break;
 	case QQ_ROOM_JOIN_DENIED:
-		msg = g_strdup_printf(_("Qun %d denied to join"), rmd->ext_id);
+		msg = g_strdup_printf(_("Qun %u denied to join"), rmd->ext_id);
 		purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), msg);
 		g_free(msg);
 		break;
 	default:
 		purple_debug_info("QQ",
-			   "Failed joining group [%d] %s, unknown reply: 0x%02x\n",
+			   "Failed to join room ext id %u %s, unknown reply: 0x%02x\n",
 			   rmd->ext_id, rmd->title_utf8, reply);
 
 		purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknow Reply"));
@@ -298,7 +298,7 @@
 	purple_debug_info("QQ", "Join room %s, extend id %s\n", id_str, ext_id_str);
 
 	if (id_str != NULL) {
-		id = strtol(id_str, NULL, 10);
+		id = strtoul(id_str, NULL, 10);
 		if (id != 0) {
 			rmd = qq_room_data_find(gc, id);
 			if (rmd) {
@@ -312,7 +312,7 @@
 	if (ext_id_str == NULL) {
 		return;
 	}
-	ext_id = strtol(ext_id_str, NULL, 10);
+	ext_id = strtoul(ext_id_str, NULL, 10);
 	if (ext_id == 0) {
 		return;
 	}
@@ -345,7 +345,7 @@
 	gint bytes = 0;
 	guint8 type;
 
-	purple_debug_info("QQ", "Search QQ Qun %d\n", ext_id);
+	purple_debug_info("QQ", "Search QQ Qun %u\n", ext_id);
 	type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID;
 
 	bytes = 0;
@@ -361,12 +361,12 @@
 	gchar field[11];
 
 	room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, rmd->title_utf8, NULL);
-	g_snprintf(field, sizeof(field), "%d", rmd->ext_id);
+	g_snprintf(field, sizeof(field), "%u", rmd->ext_id);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
-	g_snprintf(field, sizeof(field), "%d", rmd->creator_uid);
+	g_snprintf(field, sizeof(field), "%u", rmd->creator_uid);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
 	purple_roomlist_room_add_field(qd->roomlist, room, rmd->desc_utf8);
-	g_snprintf(field, sizeof(field), "%d", rmd->id);
+	g_snprintf(field, sizeof(field), "%u", rmd->id);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
 	g_snprintf(field, sizeof(field), "%d", rmd->type8);
 	purple_roomlist_room_add_field(qd->roomlist, room, field);
--- a/libpurple/protocols/qq/group_opt.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Sat Dec 06 13:30:06 2008 +0000
@@ -202,7 +202,7 @@
 	rmd = qq_room_data_find(gc, id);
 	g_return_if_fail(rmd != NULL);
 
-	purple_debug_info("QQ", "Succeed in modify members for room %d\n", rmd->ext_id);
+	purple_debug_info("QQ", "Succeed in modify members for room %u\n", rmd->ext_id);
 
 	qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun member"), now);
 }
@@ -246,7 +246,7 @@
 	bytes += qq_get32(&id, data + bytes);
 	g_return_if_fail(id > 0);
 
-	purple_debug_info("QQ", "Succeed modify room info of %d\n", id);
+	purple_debug_info("QQ", "Successfully modified room info of %u\n", id);
 
 	qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun information"), now);
 }
@@ -339,7 +339,7 @@
 	qq_send_room_cmd_only(gc, QQ_ROOM_CMD_ACTIVATE, id);
 	qq_update_room(gc, 0, rmd->id);
 
-	purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", rmd->ext_id);
+	purple_debug_info("QQ", "Succeed in create Qun, ext id %u\n", rmd->ext_id);
 
 	add_req = g_new0(qq_room_req, 1);
 	add_req->gc = gc;
@@ -347,7 +347,7 @@
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			    _("You have successfully created a Qun"),
-			    _("Would you like to set detailed information now?"),
+			    _("Would you like to set up the detail information now?"),
 			    1,
 				purple_connection_get_account(gc), NULL, NULL,
 				add_req, 2,
@@ -370,7 +370,7 @@
 	rmd = qq_room_data_find(gc, id);
 	g_return_if_fail(rmd != NULL);
 
-	purple_debug_info("QQ", "Succeed in activate Qun %d\n", rmd->ext_id);
+	purple_debug_info("QQ", "Succeed in activate Qun %u\n", rmd->ext_id);
 }
 
 void qq_group_manage_group(PurpleConnection *gc, GHashTable *data)
@@ -382,7 +382,7 @@
 	g_return_if_fail(data != NULL);
 
 	id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
-	id = strtol(id_ptr, NULL, 10);
+	id = strtoul(id_ptr, NULL, 10);
 	g_return_if_fail(id > 0);
 
 	rmd = qq_room_data_find(gc, id);
@@ -421,13 +421,13 @@
 	add_req->id = id;
 	add_req->member = member_id;
 
-	purple_debug_info("QQ", "%d requested to join room, ext id %d\n", member_id, ext_id);
+	purple_debug_info("QQ", "%u requested to join room, ext id %u\n", member_id, ext_id);
 
 	rmd = qq_room_data_find(gc, id);
 	g_return_if_fail(rmd != NULL);
 	if (qq_room_buddy_find(rmd, member_id)) {
 		purple_debug_info("QQ", "Approve join, buddy joined before\n");
-		msg = g_strdup_printf(_("%d requested to join Qun %d for %s"),
+		msg = g_strdup_printf(_("%u requested to join Qun %u for %s"),
 				member_id, ext_id, reason);
 		qq_room_got_chat_in(gc, id, 0, msg, now);
 		qq_send_cmd_group_auth(gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, member_id, "");
@@ -440,7 +440,7 @@
 		qq_request_buddy_info(gc, member_id, 0, QQ_BUDDY_INFO_DISPLAY);
 	}
 	who = uid_to_purple_name(member_id);
-	msg = g_strdup_printf(_("%d request to join Qun %d"), member_id, ext_id);
+	msg = g_strdup_printf(_("%u request to join Qun %u"), member_id, ext_id);
 
 	purple_request_action(gc, _("QQ Qun Operation"),
 			msg, reason,
@@ -453,7 +453,6 @@
 	g_free(who);
 	g_free(msg);
 	g_free(reason);
-	g_free(reason);
 }
 
 /* the request to join a group is rejected */
@@ -478,7 +477,7 @@
 	bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
 
 	msg = g_strdup_printf
-		(_("Failed to join Qun %d, operated by admin %d"), ext_id, admin_uid);
+		(_("Failed to join Qun %u, operated by admin %u"), ext_id, admin_uid);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
 
@@ -520,7 +519,7 @@
 		rmd->my_role = QQ_ROOM_ROLE_YES;
 	}
 
-	msg = g_strdup_printf(_("<b>Joining Qun %d is approved by admin %d for %s</b>"),
+	msg = g_strdup_printf(_("<b>Joinning Qun %u is approved by admin %u for %s</b>"),
 			ext_id, admin_uid, reason);
 	now = time(NULL);
 	qq_room_got_chat_in(gc, id, 0, msg, now);
@@ -555,7 +554,7 @@
 		rmd->my_role = QQ_ROOM_ROLE_NO;
 	}
 
-	msg = g_strdup_printf(_("<b>Removed buddy %d.</b>"), uid);
+	msg = g_strdup_printf(_("<b>Removed buddy %u.</b>"), uid);
 	qq_room_got_chat_in(gc, id, 0, msg, now);
 	g_free(msg);
 }
@@ -588,7 +587,7 @@
 
 	qq_update_room(gc, 0, rmd->id);
 
-	msg = g_strdup_printf(_("<b>New buddy %d joined.</b>"), uid);
+	msg = g_strdup_printf(_("<b>New buddy %u joined.</b>"), uid);
 	qq_room_got_chat_in(gc, id, 0, msg, now);
 	g_free(msg);
 }
--- a/libpurple/protocols/qq/im.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Sat Dec 06 13:30:06 2008 +0000
@@ -42,7 +42,12 @@
 #include "send_file.h"
 #include "utils.h"
 
-#define DEFAULT_FONT_NAME_LEN 	  4
+#define QQ_MSG_IM_MAX               700	/* max length of IM */
+
+enum {
+	QQ_IM_TEXT = 0x01,
+	QQ_IM_AUTO_REPLY = 0x02
+};
 
 enum
 {
@@ -63,8 +68,6 @@
 };
 
 typedef struct _qq_im_header qq_im_header;
-typedef struct _qq_recv_extended_im_text qq_recv_extended_im_text;
-
 struct _qq_im_header {
 	/* this is the common part of normal_text */
 	guint16 version_from;
@@ -74,75 +77,6 @@
 	guint16 im_type;
 };
 
-#define QQ_SEND_IM_AFTER_MSG_HEADER_LEN 8
-#define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5"
-
-guint8 *qq_get_send_im_tail(const gchar *font_color,
-		const gchar *font_size,
-		const gchar *font_name,
-		gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len)
-{
-	gchar *s1;
-	unsigned char *rgb;
-	gint font_name_len;
-	guint8 *send_im_tail;
-	const guint8 simsun[] = { 0xcb, 0xce, 0xcc, 0xe5 };
-
-	if (font_name) {
-		font_name_len = strlen(font_name);
-	} else {
-		font_name_len = DEFAULT_FONT_NAME_LEN;
-		font_name = (const gchar *) simsun;
-	}
-
-	send_im_tail = g_new0(guint8, tail_len);
-
-	g_strlcpy((gchar *) (send_im_tail + QQ_SEND_IM_AFTER_MSG_HEADER_LEN),
-			font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN);
-	send_im_tail[tail_len - 1] = (guint8) tail_len;
-
-	send_im_tail[0] = 0x00;
-	if (font_size) {
-		send_im_tail[1] = (guint8) (atoi(font_size) * 3 + 1);
-	} else {
-		send_im_tail[1] = 10;
-	}
-	if (is_bold)
-		send_im_tail[1] |= 0x20;
-	if (is_italic)
-		send_im_tail[1] |= 0x40;
-	if (is_underline)
-		send_im_tail[1] |= 0x80;
-
-	if (font_color) {
-		s1 = g_strndup(font_color + 1, 6);
-		/* Henry: maybe this is a bug of purple, the string should have
-		 * the length of odd number @_@
-		 * George Ang: This BUG maybe fixed by Purple. adding new byte
-		 * would cause a crash.
-		 */
-		/* s2 = g_strdup_printf("%sH", s1); */
-		rgb = purple_base16_decode(s1, NULL);
-		g_free(s1);
-		/* g_free(s2); */
-		if (rgb)
-		{
-			memcpy(send_im_tail + 2, rgb, 3);
-			g_free(rgb);
-		} else {
-			send_im_tail[2] = send_im_tail[3] = send_im_tail[4] = 0;
-		}
-	} else {
-		send_im_tail[2] = send_im_tail[3] = send_im_tail[4] = 0;
-	}
-
-	send_im_tail[5] = 0x00;
-	send_im_tail[6] = 0x86;
-	send_im_tail[7] = 0x22;	/* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */
-	/* qq_show_packet("QQ_MESG", send_im_tail, tail_len); */
-	return (guint8 *) send_im_tail;
-}
-
 /* read the common parts of the normal_im,
  * returns the bytes read if succeed, or -1 if there is any error */
 static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len)
@@ -159,7 +93,615 @@
 	return bytes;
 }
 
-void qq_got_attention(PurpleConnection *gc, const gchar *msg)
+typedef struct _qq_emoticon qq_emoticon;
+struct _qq_emoticon {
+	guint8 symbol;
+	gchar *name;
+};
+
+static gboolean emoticons_is_sorted = FALSE;
+/* Map for purple smiley convert to qq, need qsort */
+static qq_emoticon emoticons_std[] = {
+	{0x4f, "/:)"},      {0x4f, "/wx"},      {0x4f, "/small_smile"},
+	{0x42, "/:~"},      {0x42, "/pz"},      {0x42, "/curl_lip"},
+	{0x43, "/:*"},      {0x43, "/se"},      {0x43, "/desire"},
+	{0x44, "/:|"},      {0x44, "/fd"},      {0x44, "/dazed"},
+	{0x45, "/8-)"},     {0x45, "/dy"},      {0x45, "/revel"},
+	{0x46, "/:<"},      {0x46, "/ll"},      {0x46, "/cry"},
+	{0x47, "/:$"},      {0x47, "/hx"},      {0x47, "/bashful"},
+	{0x48, "/:x"},      {0x48, "/bz"},      {0x48, "/shut_mouth"},
+	{0x8f, "/|-)"},     {0x8f, "/kun"},     {0x8f, "/sleepy"},
+	{0x49, "/:z"},      {0x49, "/shui"},    {0x49, "/sleep"},	/* after sleepy */
+	{0x4a, "/:'"},      {0x4a, "/dk"},      {0x4a, "/weep"},
+	{0x4b, "/:-|"},     {0x4b, "/gg"},      {0x4b, "/embarassed"},
+	{0x4c, "/:@"},      {0x4c, "/fn"},      {0x4c, "/pissed_off"},
+	{0x4d, "/:P"},      {0x4d, "/tp"},      {0x4d, "/act_up"},
+	{0x4e, "/:D"},      {0x4e, "/cy"},      {0x4e, "/toothy_smile"},
+	{0x41, "/:O"},      {0x41, "/jy"},      {0x41, "/surprised"},
+	{0x73, "/:("},      {0x73, "/ng"},      {0x73, "/sad"},
+	{0x74, "/:+"},      {0x74, "/kuk"},     {0x74, "/cool"},
+	{0xa1, "/--b"},     {0xa1, "/lengh"},
+	{0x76, "/:Q"},      {0x76, "/zk"},      {0x76, "/crazy"},
+	{0x8a, "/;P"},      {0x8a, "/tx"},      {0x8a, "/titter"},
+	{0x8b, "/;-D"},     {0x8b, "/ka"},      {0x8b, "/cute"},
+	{0x8c, "/;d"},      {0x8c, "/by"},      {0x8c, "/disdain"},
+	{0x8d, "/;o"},      {0x8d, "/am"},      {0x8d, "/arrogant"},
+	{0x8e, "/:g"},      {0x8e, "/jie"},     {0x8e, "/starving"},
+	{0x78, "/:!"},      {0x78, "/jk"},      {0x78, "/terror"},
+	{0x79, "/:L"},      {0x79, "/lh"},      {0x79, "/sweat"},
+	{0x7a, "/:>"},      {0x7a, "/hanx"},    {0x7a, "/smirk"},
+	{0x7b, "/:;"},      {0x7b, "/db"},      {0x7b, "/soldier"},
+	{0x90, "/;f"},      {0x90, "/fendou"},  {0x90, "/struggle"},
+	{0x91, "/:-S"},     {0x91, "/zhm"},     {0x91, "/curse"},
+	{0x92, "/?"},       {0x92, "/yiw"},     {0x92, "/question"},
+	{0x93, "/;x"},      {0x93, "/xu"},      {0x93, "/shh"},
+	{0x94, "/;@"},      {0x94, "/yun"},     {0x94, "/dizzy"},
+	{0x95, "/:8"},      {0x95, "/zhem"},    {0x95, "/excrutiating"},
+	{0x96, "/;!"},      {0x96, "/shuai"},   {0x96, "/freaked_out"},
+	{0x97, "/!!!"},     {0x97, "/kl"},      {0x97, "/skeleton"},
+	{0x98, "/xx"},      {0x98, "/qiao"},    {0x98, "/hammer"},
+	{0x99, "/bye"},     {0x99, "/zj"},      {0x99, "/bye"},
+	{0xa2, "/wipe"},    {0xa2, "/ch"},
+	{0xa3, "/dig"},     {0xa3, "/kb"},
+	{0xa4, "/handclap"},{0xa4, "/gz"},
+	{0xa5, "/&-("},     {0xa5, "/qd"},
+	{0xa6, "/B-)"},     {0xa6, "/huaix"},
+	{0xa7, "/<@"},      {0xa7, "/zhh"},
+	{0xa8, "/@>"},      {0xa8, "/yhh"},
+	{0xa9, "/:-O"},     {0xa9, "/hq"},
+	{0xaa, "/>-|"},     {0xaa, "/bs"},
+	{0xab, "/P-("},     {0xab, "/wq"},
+	{0xac, "/:'|"},     {0xac, "/kk"},
+	{0xad, "/X-)"},     {0xad, "/yx"},
+	{0xae, "/:*"},      {0xae, "/qq"},
+	{0xaf, "/@x"},      {0xaf, "/xia"},
+	{0xb0, "/8*"},      {0xb0, "/kel"},
+	{0xb1, "/pd"},      {0xb1, "/cd"},
+	{0x61, "/<W>"},     {0x61, "/xig"},     {0x61, "/watermelon"},
+	{0xb2, "/beer"},    {0xb2, "/pj"},
+	{0xb3, "/basketb"}, {0xb3, "/lq"},
+	{0xb4, "/oo"},      {0xb4, "/pp"},
+	{0x80, "/coffee"},  {0x80, "/kf"},
+	{0x81, "/eat"},     {0x81, "/fan"},
+	{0x62, "/rose"},    {0x62, "/mg"},
+	{0x63, "/fade"},    {0x63, "/dx"},      {0x63, "/wilt"},
+	{0xb5, "/showlove"},{0xb5, "/sa"},		/* after sad */
+	{0x65, "/heart"},   {0x65, "/xin"},
+	{0x66, "/break"},   {0x66, "/xs"},      {0x66, "/broken_heart"},
+	{0x67, "/cake"},    {0x67, "/dg"},
+	{0x9c, "/li"},      {0x9c, "/shd"},     {0x9c, "/lightning"},
+	{0x9d, "/bome"},    {0x9d, "/zhd"},     {0x9d, "/bomb"},
+	{0x9e, "/kn"},      {0x9e, "/dao"},     {0x9e, "/knife"},
+	{0x5e, "/footb"},   {0x5e, "/zq"},      {0x5e, "/soccer"},
+	{0xb6, "/ladybug"}, {0xb6, "/pc"},
+	{0x89, "/shit"},    {0x89, "/bb"},
+	{0x6e, "/moon"},    {0x6e, "/yl"},
+	{0x6b, "/sun"},     {0x6b, "/ty"},
+	{0x68, "/gift"},    {0x68, "/lw"},
+	{0x7f, "/hug"},     {0x7f, "/yb"},
+	{0x6f, "/strong"},  {0x6f, "/qiang"},   {0x6f, "/thumbs_up"},
+	{0x70, "/weak"},    {0x70, "/ruo"},     {0x70, "/thumbs_down"},
+	{0x88, "/share"},   {0x88, "/ws"},      {0x88, "/handshake"},
+	{0xb7, "/@)"},      {0xb7, "/bq"},
+	{0xb8, "/jj"},      {0xb8, "/gy"},
+	{0xb9, "/@@"},      {0xb9, "/qt"},
+	{0xba, "/bad"},     {0xba, "/cj"},
+	{0xbb, "/loveu"},   {0xbb, "/aini"},
+	{0xbc, "/no"},      {0xbc, "/bu"},
+	{0xbd, "/ok"},      {0xbd, "/hd"},
+	{0x5c, "/love"},    {0x5c, "/aiq"},		/* after loveu */
+	{0x56, "/<L>"},     {0x56, "/fw"},      {0x56, "/blow_kiss"},
+	{0x58, "/jump"},    {0x58, "/tiao"},
+	{0x5a, "/shake"},   {0x5a, "/fad"},		/* after fade */
+	{0x5b, "/<O>"},     {0x5b, "/oh"},      {0x5b, "/angry"},
+	{0xbe, "/circle"},  {0xbe, "/zhq"},
+	{0xbf, "/kotow"},   {0xbf, "/kt"},
+	{0xc0, "/turn"},    {0xc0, "/ht"},
+	{0x77, "/:t"},      {0x77, "/tu"},      {0x77, "/vomit"},		/* after turn */
+	{0xa0, "/victory"}, {0xa0, "/shl"},     {0xa0, "/v"},			/* end of v */
+	{0xc1, "/skip"},    {0xc1, "/tsh"},
+	{0xc2, "/oY"},      {0xc2, "/hsh"},
+	{0xc3, "/#-O"},     {0xc3, "/jd"},
+	{0xc4, "/hiphop"},  {0xc4, "/jw"},
+	{0xc5, "/kiss"},    {0xc5, "/xw"},
+	{0xc6, "/<&"},      {0xc6, "/ztj"},
+	{0x7c, "/pig"},     {0x7c, "/zt"},		/* after ztj */
+	{0xc7, "/&>"},      {0xc7, "/ytj"},		/* must be end of "&" */
+	{0x75, "/:#"},      {0x75, "/feid"},    {0x75, "/SARS"},
+	{0x59, "/go"},      {0x59, "/shan"},
+	{0x57, "/find"},    {0x57, "/zhao"},    {0x57, "/search"},
+	{0x55, "/&"},       {0x55, "/mm"},      {0x55, "/beautiful_eyebrows"},
+	{0x7d, "/cat"},     {0x7d, "/maom"},
+	{0x7e, "/dog"},     {0x7e, "/xg"},
+	{0x9a, "/$"},       {0x9a, "/qianc"},   {0x9a, "/money"},
+	{0x9b, "/(!)"},     {0x9b, "/dp"},      {0x9b, "/lightbulb"},
+	{0x60, "/cup"},     {0x60, "/bei"},
+	{0x9f, "/music"},   {0x9f, "/yy"},
+	{0x82, "/pill"},    {0x82, "/yw"},
+	{0x64, "/kiss"},    {0x64, "/wen"},
+	{0x83, "/meeting"}, {0x83, "/hy"},
+	{0x84, "/phone"},   {0x84, "/dh"},
+	{0x85, "/time"},    {0x85, "/sj"},
+	{0x86, "/email"},   {0x86, "/yj"},
+	{0x87, "/tv"},      {0x87, "/ds"},
+	{0x50, "/<D>"},     {0x50, "/dd"},
+	{0x51, "/<J>"},     {0x51,  "/mn"},     {0x51,  "/beauty"},
+	{0x52, "/<H>"},     {0x52,  "/hl"},
+	{0x53, "/<M>"},     {0x53,  "/mamao"},
+	{0x54, "/<QQ>"},    {0x54,  "/qz"},     {0x54,  "/qq"},
+	{0x5d, "/<B>"},     {0x5d,  "/bj"},     {0x5d,  "/baijiu"},
+	{0x5f, "/<U>"},     {0x5f,  "/qsh"},    {0x5f,  "/soda"},
+	{0x69, "/<!!>"},    {0x69,  "/xy"},     {0x69,  "/rain"},
+	{0x6a, "/<~>"},     {0x6a,  "/duoy"},   {0x6a,  "/cloudy"},
+	{0x6c, "/<Z>"},     {0x6c,  "/xr"},     {0x6c,  "/snowman"},
+	{0x6d, "/<*>"},     {0x6d,  "/xixing"}, {0x6d,  "/star"},		/* after starving */
+	{0x71, "/<00>"},    {0x71,  "/nv"},     {0x71,  "/woman"},
+	{0x72, "/<11>"},    {0x72,  "/nan"},    {0x72,  "/man"},
+	{0, NULL}
+};
+gint emoticons_std_num = sizeof(emoticons_std) / sizeof(qq_emoticon) - 1;
+
+/* Map for purple smiley convert to qq, need qsort */
+static qq_emoticon emoticons_ext[] = {
+	{0xc7, "/&>"},		{0xa5, "/&-("},
+	{0xbb, "/loveu"},
+	{0x63, "/fade"},
+	{0x8f, "/sleepy"},	{0x73, "/sad"},		{0x8e, "/starving"},
+	{0xc0, "/turn"},
+	{0xa0, "/victory"}, {0x77, "/vomit"},
+	{0xc6, "/ztj"},
+	{0, NULL}
+};
+gint emoticons_ext_num = sizeof(emoticons_ext) / sizeof(qq_emoticon) - 1;
+
+/* Map for qq smiley convert to purple */
+static qq_emoticon emoticons_sym[] = {
+	{0x41, "/jy"},
+	{0x42, "/pz"},
+	{0x43, "/se"},
+	{0x44, "/fd"},
+	{0x45, "/dy"},
+	{0x46, "/ll"},
+	{0x47, "/hx"},
+	{0x48, "/bz"},
+	{0x49, "/shui"},
+	{0x4a, "/dk"},
+	{0x4b, "/gg"},
+	{0x4c, "/fn"},
+	{0x4d, "/tp"},
+	{0x4e, "/cy"},
+	{0x4f, "/wx"},
+	{0x50, "/dd"},
+	{0x51, "/mn"},
+	{0x52, "/hl"},
+	{0x53, "/mamao"},
+	{0x54, "/qz"},
+	{0x55, "/mm"},
+	{0x56, "/fw"},
+	{0x57, "/zhao"},
+	{0x58, "/tiao"},
+	{0x59, "/shan"},
+	{0x5a, "/fad"},
+	{0x5b, "/oh"},
+	{0x5c, "/aiq"},
+	{0x5d, "/bj"},
+	{0x5e, "/zq"},
+	{0x5f, "/qsh"},
+	{0x60, "/bei"},
+	{0x61, "/xig"},
+	{0x62, "/mg"},
+	{0x63, "/dx"},
+	{0x64, "/wen"},
+	{0x65, "/xin"},
+	{0x66, "/xs"},
+	{0x67, "/dg"},
+	{0x68, "/lw"},
+	{0x69, "/xy"},
+	{0x6a, "/duoy"},
+	{0x6b, "/ty"},
+	{0x6c, "/xr"},
+	{0x6d, "/xixing"},
+	{0x6e, "/yl"},
+	{0x6f, "/qiang"},
+	{0x70, "/ruo"},
+	{0x71, "/nv"},
+	{0x72, "/nan"},
+	{0x73, "/ng"},
+	{0x74, "/kuk"},
+	{0x75, "/feid"},
+	{0x76, "/zk"},
+	{0x77, "/tu"},
+	{0x78, "/jk"},
+	{0x79, "/sweat"},
+	{0x7a, "/hanx"},
+	{0x7b, "/db"},
+	{0x7c, "/zt"},
+	{0x7d, "/maom"},
+	{0x7e, "/xg"},
+	{0x7f, "/yb"},
+	{0x80, "/coffee"},
+	{0x81, "/fan"},
+	{0x82, "/yw"},
+	{0x83, "/hy"},
+	{0x84, "/dh"},
+	{0x85, "/sj"},
+	{0x86, "/yj"},
+	{0x87, "/ds"},
+	{0x88, "/ws"},
+	{0x89, "/bb"},
+	{0x8a, "/tx"},
+	{0x8b, "/ka"},
+	{0x8c, "/by"},
+	{0x8d, "/am"},
+	{0x8e, "/jie"},
+	{0x8f, "/kun"},
+	{0x90, "/fendou"},
+	{0x91, "/zhm"},
+	{0x92, "/yiw"},
+	{0x93, "/xu"},
+	{0x94, "/yun"},
+	{0x95, "/zhem"},
+	{0x96, "/shuai"},
+	{0x97, "/kl"},
+	{0x98, "/qiao"},
+	{0x99, "/zj"},
+	{0x9a, "/qianc"},
+	{0x9b, "/dp"},
+	{0x9c, "/shd"},
+	{0x9d, "/zhd"},
+	{0x9e, "/dao"},
+	{0x9f, "/yy"},
+	{0xa0, "/shl"},
+	{0xa1, "/lengh"},
+	{0xa2, "/wipe"},
+	{0xa3, "/kb"},
+	{0xa4, "/gz"},
+	{0xa5, "/qd"},
+	{0xa6, "/huaix"},
+	{0xa7, "/zhh"},
+	{0xa8, "/yhh"},
+	{0xa9, "/hq"},
+	{0xaa, "/bs"},
+	{0xab, "/wq"},
+	{0xac, "/kk"},
+	{0xad, "/yx"},
+	{0xae, "/qq"},
+	{0xaf, "/xia"},
+	{0xb0, "/kel"},
+	{0xb1, "/cd"},
+	{0xb2, "/pj"},
+	{0xb3, "/lq"},
+	{0xb4, "/pp"},
+	{0xb5, "/sa"},
+	{0xb6, "/pc"},
+	{0xb7, "/bq"},
+	{0xb8, "/gy"},
+	{0xb9, "/qt"},
+	{0xba, "/cj"},
+	{0xbb, "/aini"},
+	{0xbc, "/bu"},
+	{0xbd, "/hd"},
+	{0xbe, "/zhq"},
+	{0xbf, "/kt"},
+	{0xc0, "/ht"},
+	{0xc1, "/tsh"},
+	{0xc2, "/hsh"},
+	{0xc3, "/jd"},
+	{0xc4, "/jw"},
+	{0xc5, "/xw"},
+	{0xc6, "/ztj"},
+	{0xc7, "/ytj"},
+	{0, NULL}
+};
+gint emoticons_sym_num = sizeof(emoticons_sym) / sizeof(qq_emoticon) - 1;;
+
+static int emoticon_cmp(const void *k1, const void *k2)
+{
+	const qq_emoticon *e1 = (const qq_emoticon *) k1;
+	const qq_emoticon *e2 = (const qq_emoticon *) k2;
+	if (e1->symbol == 0) {
+		/* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e2->name)); */
+		return strncmp(e1->name, e2->name, strlen(e2->name));
+	}
+	if (e2->symbol == 0) {
+		/* purple_debug_info("QQ", "emoticon_cmp len %d\n", strlen(e1->name)); */
+		return strncmp(e1->name, e2->name, strlen(e1->name));
+	}
+	return strcmp(e1->name, e2->name);
+}
+
+static void emoticon_try_sort()
+{
+	if (emoticons_is_sorted)
+		return;
+
+	purple_debug_info("QQ", "qsort stand emoticons\n");
+	qsort(emoticons_std, emoticons_std_num, sizeof(qq_emoticon), emoticon_cmp);
+	purple_debug_info("QQ", "qsort extend emoticons\n");
+	qsort(emoticons_ext, emoticons_ext_num, sizeof(qq_emoticon), emoticon_cmp);
+	emoticons_is_sorted = TRUE;
+}
+
+static qq_emoticon *emoticon_find(gchar *name)
+{
+	qq_emoticon *ret = NULL;
+	qq_emoticon key;
+
+	g_return_val_if_fail(name != NULL, NULL);
+	emoticon_try_sort();
+
+	key.name = name;
+	key.symbol = 0;
+
+	/* purple_debug_info("QQ", "bsearch emoticon %.20s\n", name); */
+	ret = (qq_emoticon *)bsearch(&key, emoticons_ext, emoticons_ext_num,
+			sizeof(qq_emoticon), emoticon_cmp);
+	if (ret != NULL) {
+		return ret;
+	}
+	ret = (qq_emoticon *)bsearch(&key, emoticons_std, emoticons_std_num,
+			sizeof(qq_emoticon), emoticon_cmp);
+	return ret;
+}
+
+static gchar *emoticon_get(guint8 symbol)
+{
+	g_return_val_if_fail(symbol >= emoticons_sym[0].symbol, NULL);
+	g_return_val_if_fail(symbol <= emoticons_sym[emoticons_sym_num - 2].symbol, NULL);
+
+	return emoticons_sym[symbol - emoticons_sym[0].symbol].name;
+}
+
+/* convert qq emote icon to purple sytle
+   Notice: text is in qq charset, GB18030 or utf8 */
+gchar *qq_emoticon_to_purple(gchar *text)
+{
+	gchar *ret;
+	GString *converted;
+	gchar **segments;
+	gboolean have_smiley;
+	gchar *purple_smiley;
+	gchar *cur;
+	guint8 symbol;
+
+	/* qq_show_packet("text", (guint8 *)text, strlen(text)); */
+	g_return_val_if_fail(text != NULL && strlen(text) != 0, g_strdup(""));
+
+	segments = g_strsplit_set(text, "\x14\x15", 0);
+	if(segments == NULL) {
+		return g_strdup("");
+	}
+
+	converted = g_string_new("");
+	have_smiley = FALSE;
+	if (segments[0] != NULL) {
+		g_string_append(converted, segments[0]);
+	} else {
+		purple_debug_info("QQ", "segments[0] is NULL\n");
+	}
+	while ((*(++segments)) != NULL) {
+		have_smiley = TRUE;
+
+		cur = *segments;
+		if (cur == NULL) {
+			purple_debug_info("QQ", "current segment is NULL\n");
+			break;
+		}
+		if (strlen(cur) == 0) {
+			purple_debug_info("QQ", "current segment length is 0\n");
+			break;
+		}
+		symbol = (guint8)cur[0];
+
+		purple_smiley = emoticon_get(symbol);
+		if (purple_smiley == NULL) {
+			purple_debug_info("QQ", "Not found smiley of 0x%02X\n", symbol);
+			g_string_append(converted, "<IMG ID=\"0\">");
+		} else {
+			purple_debug_info("QQ", "Found 0x%02X smiley is %s\n", symbol, purple_smiley);
+			g_string_append(converted, purple_smiley);
+			g_string_append(converted, cur + 1);
+		}
+		/* purple_debug_info("QQ", "next segment\n"); */
+	}
+
+	/* purple_debug_info("QQ", "end of convert\n"); */
+	if (!have_smiley) {
+		g_string_prepend(converted, "<font sml=\"none\">");
+		g_string_append(converted, "</font>");
+	}
+	ret = converted->str;
+	g_string_free(converted, FALSE);
+	return ret;
+}
+
+void qq_im_fmt_free(qq_im_format *fmt)
+{
+	g_return_if_fail(fmt != NULL);
+	if (fmt->font)	g_free(fmt->font);
+	g_free(fmt);
+}
+
+qq_im_format *qq_im_fmt_new(void)
+{
+	qq_im_format *fmt;
+	const gchar simsun[] = { 0xcb, 0xce, 0xcc, 0xe5, 0};	/* simsun in Chinese */
+
+	fmt = g_new0(qq_im_format, 1);
+	memset(fmt, 0, sizeof(qq_im_format));
+	fmt->font_len = strlen(simsun);
+	fmt->font = g_strdup(simsun);
+	fmt->attr = 10;
+	/* encoding, 0x8602=GB, 0x0000=EN, define BIG5 support here */
+	fmt->charset = 0x8602;
+
+	return fmt;
+}
+
+qq_im_format *qq_im_fmt_new_by_purple(const gchar *msg)
+{
+	qq_im_format *fmt;
+	const gchar *start, *end, *last;
+	GData *attribs;
+	gchar *tmp;
+	unsigned char *rgb;
+
+	g_return_val_if_fail(msg != NULL, NULL);
+
+	fmt = qq_im_fmt_new();
+
+	last = msg;
+	while (purple_markup_find_tag("font", last, &start, &end, &attribs)) {
+		tmp = g_datalist_get_data(&attribs, "face");
+		if (tmp && strlen(tmp) > 0) {
+			if (fmt->font)	g_free(fmt->font);
+			fmt->font_len = strlen(tmp);
+			fmt->font = g_strdup(tmp);
+		}
+
+		tmp = g_datalist_get_data(&attribs, "size");
+		if (tmp) {
+			fmt->attr = atoi(tmp) * 3 + 1;
+			fmt->attr &= 0x0f;
+		}
+
+		tmp = g_datalist_get_data(&attribs, "color");
+		if (tmp && strlen(tmp) > 1) {
+			rgb = purple_base16_decode(tmp + 1, NULL);
+			g_memmove(fmt->rgb, rgb, 3);
+			g_free(rgb);
+		}
+
+		g_datalist_clear(&attribs);
+		last = end + 1;
+	}
+
+	if (purple_markup_find_tag("b", msg, &start, &end, &attribs)) {
+		fmt->attr |= 0x20;
+		g_datalist_clear(&attribs);
+	}
+
+	if (purple_markup_find_tag("i", msg, &start, &end, &attribs)) {
+		fmt->attr |= 0x40;
+		g_datalist_clear(&attribs);
+	}
+
+	if (purple_markup_find_tag("u", msg, &start, &end, &attribs)) {
+		fmt->attr |= 0x80;
+		g_datalist_clear(&attribs);
+	}
+
+	return fmt;
+}
+
+/* convert qq format to purple
+   Notice: text is in qq charset, GB18030 or utf8 */
+gchar *qq_im_fmt_to_purple(qq_im_format *fmt, gchar *text)
+{
+	GString *converted, *tmp;
+	gchar *ret;
+	gint size;
+
+	converted = g_string_new(text);
+	tmp = g_string_new("");
+	g_string_append_printf(tmp, "<font color=\"#%02x%02x%02x\">",
+		fmt->rgb[0], fmt->rgb[1], fmt->rgb[2]);
+	g_string_prepend(converted, tmp->str);
+	g_string_set_size(tmp, 0);
+	g_string_append(converted, "</font>");
+
+	/* Fixme:
+	 * check font face can be convert to utf8 or not?
+	 * If failed, prepending font face cause msg display as "(NULL)" */
+	if (fmt->font != NULL) {
+		g_string_append_printf(tmp, "<font face=\"%s\">", fmt->font);
+		g_string_prepend(converted, tmp->str);
+		g_string_set_size(tmp, 0);
+		g_string_append(converted, "</font>");
+	}
+	size = (fmt->attr & 0x1f) / 3;
+	if (size >= 0) {
+		g_string_append_printf(tmp, "<font size=\"%d\">", size);
+		g_string_prepend(converted, tmp->str);
+		g_string_set_size(tmp, 0);
+		g_string_append(converted, "</font>");
+	}
+	if (fmt->attr & 0x20) {
+		/* bold */
+		g_string_prepend(converted, "<b>");
+		g_string_append(converted, "</b>");
+	}
+	if (fmt->attr & 0x40) {
+		/* italic */
+		g_string_prepend(converted, "<i>");
+		g_string_append(converted, "</i>");
+	}
+	if (fmt->attr & 0x80) {
+		/* underline */
+		g_string_prepend(converted, "<u>");
+		g_string_append(converted, "</u>");
+	}
+
+	g_string_free(tmp, TRUE);
+	ret = converted->str;
+	g_string_free(converted, FALSE);
+	return ret;
+}
+
+gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt)
+{
+	gint bytes;
+
+	g_return_val_if_fail(buf != NULL && fmt != NULL, 0);
+
+	bytes = 0;
+	bytes += qq_put8(buf + bytes, 0);
+	bytes += qq_put8(buf + bytes, fmt->attr);
+	bytes += qq_putdata(buf + bytes, fmt->rgb, sizeof(fmt->rgb));
+	bytes += qq_put8(buf + bytes, 0);
+	bytes += qq_put16(buf + bytes, fmt->charset);
+	if (fmt->font != NULL && fmt->font_len > 0) {
+		bytes += qq_putdata(buf + bytes, (guint8 *)fmt->font, fmt->font_len);
+	} else {
+		purple_debug_warning("QQ", "Font name is empty\n");
+	}
+	bytes += qq_put8(buf + bytes, bytes + 1);
+	/* qq_show_packet("IM tail", buf, bytes); */
+	return bytes;
+}
+
+/* data includes text msg and font attr*/
+gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len)
+{
+	gint bytes, text_len;
+	guint8 tail_len;
+	guint8 font_len;
+
+	g_return_val_if_fail(fmt != NULL && data != NULL, 0);
+	g_return_val_if_fail(data_len > 1, 0);
+	tail_len = data[data_len - 1];
+	g_return_val_if_fail(tail_len > 2, 0);
+	text_len = data_len - tail_len;
+	g_return_val_if_fail(text_len >= 0, 0);
+
+	bytes = text_len;
+	/* qq_show_packet("IM tail", data + bytes, tail_len); */
+	bytes += 1;		/* skip 0x00 */
+	bytes += qq_get8(&fmt->attr, data + bytes);
+	bytes += qq_getdata(fmt->rgb, sizeof(fmt->rgb), data + bytes);	/* red,green,blue */
+ 	bytes += 1;	/* skip 0x00 */
+	bytes += qq_get16(&fmt->charset, data + bytes);
+
+	font_len = data_len - bytes - 1;
+	g_return_val_if_fail(font_len > 0, bytes + 1);
+
+	fmt->font_len = font_len;
+	if (fmt->font != NULL)	g_free(fmt->font);
+	fmt->font = g_strndup((gchar *)data + bytes, fmt->font_len);
+	return tail_len;
+}
+
+void qq_got_message(PurpleConnection *gc, const gchar *msg)
 {
 	qq_data *qd;
 	gchar *from;
@@ -180,27 +722,28 @@
 /* process received normal text IM */
 static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
 {
+	qq_data *qd;
 	guint16 purple_msg_type;
 	gchar *who;
-	gchar *msg_with_purple_smiley;
-	gchar *msg_utf8_encoded;
-	qq_data *qd;
-	gint bytes = 0;
-	PurpleBuddy *b;
+	gchar *msg_smiley, *msg_fmt, *msg_utf8;
+	PurpleBuddy *buddy;
 	qq_buddy_data *bd;
+	gint bytes, tail_len;
+	qq_im_format *fmt = NULL;
 
 	struct {
 		/* now comes the part for text only */
 		guint16 msg_seq;
 		guint32 send_time;
 		guint16 sender_icon;
-		guint8 unknown2[3];
-		guint8 is_there_font_attr;
-		guint8 unknown3[4];
+		guint8 unknown1[3];
+		guint8 has_font_attr;
+		guint8 fragment_count;
+		guint8 fragment_index;
+		guint8 msg_id;
+		guint8 unknown2;
 		guint8 msg_type;
 		gchar *msg;		/* no fixed length, ends with 0x00 */
-		guint8 *font_attr;
-		gint font_attr_len;
 	} im_text;
 
 	g_return_if_fail (data != NULL && len > 0);
@@ -209,99 +752,97 @@
 	qd = (qq_data *) gc->proto_data;
 	memset(&im_text, 0, sizeof(im_text));
 
-	/* push data into im_text */
+	/* qq_show_packet("IM text", data, len); */
+	bytes = 0;
 	bytes += qq_get16(&(im_text.msg_seq), data + bytes);
 	bytes += qq_get32(&(im_text.send_time), data + bytes);
 	bytes += qq_get16(&(im_text.sender_icon), data + bytes);
-	bytes += qq_getdata((guint8 *) & (im_text.unknown2), 3, data + bytes);
-	bytes += qq_get8(&(im_text.is_there_font_attr), data + bytes);
-	/**
-	 * from lumaqq	for unknown3
-	 *	totalFragments = buf.get() & 255;
-	 *	fragmentSequence = buf.get() & 255;
-	 *	messageId = buf.getChar();
-	 */
-	bytes += qq_getdata((guint8 *) & (im_text.unknown3), 4, data + bytes);
+	bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes); /* 0x(00 00 00)*/
+	bytes += qq_get8(&(im_text.has_font_attr), data + bytes);
+	bytes += qq_get8(&(im_text.fragment_count), data + bytes);
+	bytes += qq_get8(&(im_text.fragment_index), data + bytes);
+	bytes += qq_get8(&(im_text.msg_id), data + bytes);
+	bytes += 1; 	/* skip 0x00 */
 	bytes += qq_get8(&(im_text.msg_type), data + bytes);
+	purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n",
+			im_text.msg_seq, im_text.msg_id,
+			im_text.fragment_count, im_text.fragment_index,
+			im_text.msg_type,
+			im_text.has_font_attr ? "exist font atrr" : "");
 
-	/* we need to check if this is auto-reply
-	 * QQ2003iii build 0304, returns the msg without font_attr
-	 * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */
-	if (im_text.msg_type == QQ_IM_AUTO_REPLY) {
-		im_text.is_there_font_attr = 0x00;	/* indeed there is no this flag */
+	if (im_text.has_font_attr) {
+		fmt = qq_im_fmt_new();
+		tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes);
+		im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len);
+	} else	{
 		im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes);
-	} else {		/* it is normal mesasge */
-		if (im_text.is_there_font_attr) {
-			im_text.msg = g_strdup((gchar *)(data + bytes));
-			bytes += strlen(im_text.msg) + 1; /* length decided by strlen! will it cause a crash? */
-			im_text.font_attr_len = len - bytes;
-			im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len);
-		} else		/* not im_text.is_there_font_attr */
-			im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes);
-	}			/* if im_text.msg_type */
+	}
+	/* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg) ); */
 
 	who = uid_to_purple_name(im_header->uid_from);
-	b = purple_find_buddy(gc->account, who);
-	if (b == NULL) {
+	buddy = purple_find_buddy(gc->account, who);
+	if (buddy == NULL) {
 		/* create no-auth buddy */
-		b = qq_buddy_new(gc, im_header->uid_from);
+		buddy = qq_buddy_new(gc, im_header->uid_from);
 	}
-	bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data;
+	bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data;
 	if (bd != NULL) {
 		bd->client_tag = im_header->version_from;
+		bd->face = im_text.sender_icon;
+		qq_update_buddy_icon(gc->account, who, bd->face);
 	}
 
-	purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
+	purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY)
+		? PURPLE_MESSAGE_AUTO_RESP : 0;
 
-	msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg);
-	msg_utf8_encoded = im_text.is_there_font_attr ?
-		qq_encode_to_purple(im_text.font_attr,
-				im_text.font_attr_len,
-				msg_with_purple_smiley, qd->client_version)
-		: qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+	msg_smiley = qq_emoticon_to_purple(im_text.msg);
+	if (fmt != NULL) {
+		msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley);
+		msg_utf8 =  qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT);
+		g_free(msg_fmt);
+		qq_im_fmt_free(fmt);
+	} else {
+		msg_utf8 =  qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT);
+	}
+	g_free(msg_smiley);
 
 	/* send encoded to purple, note that we use im_text.send_time,
 	 * not the time we receive the message
 	 * as it may have been delayed when I am not online. */
-	serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time);
+	purple_debug_info("QQ", "IM from %u: %s\n", im_header->uid_from,msg_utf8);
+	serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time);
 
-	g_free(msg_utf8_encoded);
-	g_free(msg_with_purple_smiley);
+	g_free(msg_utf8);
 	g_free(who);
 	g_free(im_text.msg);
-	if (im_text.font_attr)	g_free(im_text.font_attr);
 }
 
 /* process received extended (2007) text IM */
-static void process_extend_im_text(
-		PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
+static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
 {
+	qq_data *qd;
 	guint16 purple_msg_type;
 	gchar *who;
-	gchar *msg_with_purple_smiley;
-	gchar *msg_utf8_encoded;
-	qq_data *qd;
-	PurpleBuddy *b;
+	gchar *msg_smiley, *msg_fmt, *msg_utf8;
+	PurpleBuddy *buddy;
 	qq_buddy_data *bd;
-	gint bytes, text_len;
+	gint bytes, tail_len;
+	qq_im_format *fmt = NULL;
 
 	struct {
 		/* now comes the part for text only */
-		guint16 sessionId;
+		guint16 msg_seq;
 		guint32 send_time;
-		guint16 senderHead;
-		guint32 flag;
-		guint8 unknown2[8];
-		guint8 fragmentCount;
-		guint8 fragmentIndex;
-		guint16 messageId;
-		guint8 replyType;
+		guint16 sender_icon;
+		guint32 has_font_attr;
+		guint8 unknown1[8];
+		guint8 fragment_count;
+		guint8 fragment_index;
+		guint8 msg_id;
+		guint8 unknown2;
+		guint8 msg_type;
 		gchar *msg;		/* no fixed length, ends with 0x00 */
 		guint8 fromMobileQQ;
-
-		guint8 is_there_font_attr;
-		guint8 *font_attr;
-		gint8 font_attr_len;
 	} im_text;
 
 	g_return_if_fail (data != NULL && len > 0);
@@ -310,79 +851,69 @@
 	qd = (qq_data *) gc->proto_data;
 	memset(&im_text, 0, sizeof(im_text));
 
-	/* push data into im_text */
+	/* qq_show_packet("Extend IM text", data, len); */
 	bytes = 0;
-	bytes += qq_get16(&(im_text.sessionId), data + bytes);
+	bytes += qq_get16(&(im_text.msg_seq), data + bytes);
 	bytes += qq_get32(&(im_text.send_time), data + bytes);
-	bytes += qq_get16(&(im_text.senderHead), data + bytes);
-	bytes += qq_get32(&(im_text.flag), data + bytes);
-
-	bytes += qq_getdata(im_text.unknown2, 8, data + bytes);
-	bytes += qq_get8(&(im_text.fragmentCount), data + bytes);
-	bytes += qq_get8(&(im_text.fragmentIndex), data + bytes);
-
-	bytes += qq_get16(&(im_text.messageId), data + bytes);
-	bytes += qq_get8(&(im_text.replyType), data + bytes);
-
-	im_text.font_attr_len = data[len-1] & 0xff;
+	bytes += qq_get16(&(im_text.sender_icon), data + bytes);
+	bytes += qq_get32(&(im_text.has_font_attr), data + bytes);
+	bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes);
+	bytes += qq_get8(&(im_text.fragment_count), data + bytes);
+	bytes += qq_get8(&(im_text.fragment_index), data + bytes);
+	bytes += qq_get8(&(im_text.msg_id), data + bytes);
+	bytes += 1; 	/* skip 0x00 */
+	bytes += qq_get8(&(im_text.msg_type), data + bytes);
+	purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n",
+			im_text.msg_seq, im_text.msg_id,
+			im_text.fragment_count, im_text.fragment_index,
+			im_text.msg_type,
+			im_text.has_font_attr ? "exist font atrr" : "");
 
-	text_len = len - bytes - im_text.font_attr_len;
-	im_text.msg = g_strndup((gchar *)(data + bytes), text_len);
-	bytes += text_len;
-	if(im_text.font_attr_len >= 0)
-		im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len);
-	else
-	{
-		purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n",
-			im_text.font_attr_len);
-		return;
+	if (im_text.has_font_attr) {
+		fmt = qq_im_fmt_new();
+		tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes);
+		im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len);
+	} else	{
+		im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes);
 	}
+	/* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg)); */
 
-	if(im_text.fragmentCount == 0)
-		im_text.fragmentCount = 1;
-
-	/* Filter tail space */
-	if(im_text.fragmentIndex == im_text.fragmentCount -1)
-	{
-		gint real_len = text_len;
-		while(real_len > 0 && im_text.msg[real_len - 1] == 0x20)
-			real_len --;
-
-		text_len = real_len;
-		/* Null string instead of space */
-		im_text.msg[text_len] = 0;
-	}
+	if(im_text.fragment_count == 0) 	im_text.fragment_count = 1;
 
 	who = uid_to_purple_name(im_header->uid_from);
-	b = purple_find_buddy(gc->account, who);
-	if (b == NULL) {
+	buddy = purple_find_buddy(gc->account, who);
+	if (buddy == NULL) {
 		/* create no-auth buddy */
-		b = qq_buddy_new(gc, im_header->uid_from);
+		buddy = qq_buddy_new(gc, im_header->uid_from);
 	}
-	bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data;
+	bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data;
 	if (bd != NULL) {
 		bd->client_tag = im_header->version_from;
+		bd->face = im_text.sender_icon;
+		qq_update_buddy_icon(gc->account, who, bd->face);
 	}
 
 	purple_msg_type = 0;
 
-	msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg);
-	msg_utf8_encoded = im_text.font_attr ?
-	    qq_encode_to_purple(im_text.font_attr,
-			      im_text.font_attr_len,
-			      msg_with_purple_smiley, qd->client_version)
-		: qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+	msg_smiley = qq_emoticon_to_purple(im_text.msg);
+	if (fmt != NULL) {
+		msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley);
+		msg_utf8 =  qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT);
+		g_free(msg_fmt);
+		qq_im_fmt_free(fmt);
+	} else {
+		msg_utf8 =  qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT);
+	}
+	g_free(msg_smiley);
 
 	/* send encoded to purple, note that we use im_text.send_time,
 	 * not the time we receive the message
 	 * as it may have been delayed when I am not online. */
-	serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time);
+	serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time);
 
-	g_free(msg_utf8_encoded);
-	g_free(msg_with_purple_smiley);
+	g_free(msg_utf8);
 	g_free(who);
 	g_free(im_text.msg);
-	if (im_text.font_attr) g_free(im_text.font_attr);
 }
 
 /* it is a normal IM, maybe text or video request */
@@ -400,7 +931,7 @@
 		return;
 	}
 	purple_debug_info("QQ",
-			"Got IM to %d, type: %02X from: %d ver: %s (%04X)\n",
+			"Got IM to %u, type: %02X from: %u ver: %s (%04X)\n",
 			im_header.uid_to, im_header.im_type, im_header.uid_from,
 			qq_get_ver_desc(im_header.version_from), im_header.version_from);
 
@@ -461,105 +992,63 @@
 		return;
 	}
 	purple_debug_info("QQ",
-			"Got Extend IM to %d, type: %02X from: %d ver: %s (%04X)\n",
+			"Got Extend IM to %u, type: %02X from: %u ver: %s (%04X)\n",
 			im_header.uid_to, im_header.im_type, im_header.uid_from,
 			qq_get_ver_desc(im_header.version_from), im_header.version_from);
 
 	switch (im_header.im_type) {
-	case QQ_NORMAL_IM_TEXT:
-		process_extend_im_text(gc, data + bytes, len - bytes, &im_header);
-		break;
-	case QQ_NORMAL_IM_FILE_REJECT_UDP:
-		qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_APPROVE_UDP:
-		qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_REQUEST_UDP:
-		qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_CANCEL:
-		qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_NOTIFY:
-		qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc);
-		break;
-	default:
-		/* a simple process here, maybe more later */
-		qq_show_packet ("Unknow", data + bytes, len - bytes);
-		break;
+		case QQ_NORMAL_IM_TEXT:
+			process_extend_im_text(gc, data + bytes, len - bytes, &im_header);
+			break;
+		case QQ_NORMAL_IM_FILE_REJECT_UDP:
+			qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_APPROVE_UDP:
+			qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_REQUEST_UDP:
+			qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_CANCEL:
+			qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_NOTIFY:
+			qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_REQUEST_TCP:
+			/* Check ReceivedFileIM::parseContents in eva*/
+			/* some client use this function for detect invisable buddy*/
+		case QQ_NORMAL_IM_FILE_APPROVE_TCP:
+		case QQ_NORMAL_IM_FILE_REJECT_TCP:
+		case QQ_NORMAL_IM_FILE_PASV:
+		case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP:
+		case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT:
+		case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL:
+		case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP:
+			qq_show_packet ("Not support", data, len);
+			break;
+		default:
+			/* a simple process here, maybe more later */
+			qq_show_packet ("Unknow", data + bytes, len - bytes);
+			break;
 	}
 }
 
 /* send an IM to uid_to */
-void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type)
+static void request_send_im(PurpleConnection *gc, guint32 uid_to, gint type,
+	qq_im_format *fmt, gchar *msg, guint8 id, guint8 frag_count, guint8 frag_index)
 {
 	qq_data *qd;
-	guint8 *raw_data, *send_im_tail;
+	guint8 raw_data[MAX_PACKET_SIZE - 16];
 	guint16 im_type;
-	gint msg_len, raw_len, font_name_len, tail_len, bytes;
+	gint bytes;
 	time_t now;
-	gchar *msg_filtered;
-	GData *attribs;
-	gchar *font_size = NULL, *font_color = NULL, *font_name = NULL, *tmp;
-	gboolean is_bold = FALSE, is_italic = FALSE, is_underline = FALSE;
-	const gchar *start, *end, *last;
 
 	qd = (qq_data *) gc->proto_data;
 	im_type = QQ_NORMAL_IM_TEXT;
 
-	last = msg;
-	while (purple_markup_find_tag("font", last, &start, &end, &attribs)) {
-		tmp = g_datalist_get_data(&attribs, "size");
-		if (tmp) {
-			if (font_size)
-				g_free(font_size);
-			font_size = g_strdup(tmp);
-		}
-		tmp = g_datalist_get_data(&attribs, "color");
-		if (tmp) {
-			if (font_color)
-				g_free(font_color);
-			font_color = g_strdup(tmp);
-		}
-		tmp = g_datalist_get_data(&attribs, "face");
-		if (tmp) {
-			if (font_name)
-				g_free(font_name);
-			font_name = g_strdup(tmp);
-		}
-
-		g_datalist_clear(&attribs);
-		last = end + 1;
-	}
-
-	if (purple_markup_find_tag("b", msg, &start, &end, &attribs)) {
-		is_bold = TRUE;
-		g_datalist_clear(&attribs);
-	}
-
-	if (purple_markup_find_tag("i", msg, &start, &end, &attribs)) {
-		is_italic = TRUE;
-		g_datalist_clear(&attribs);
-	}
-
-	if (purple_markup_find_tag("u", msg, &start, &end, &attribs)) {
-		is_underline = TRUE;
-		g_datalist_clear(&attribs);
-	}
-
-	purple_debug_info("QQ_MESG", "send mesg: %s\n", msg);
-	msg_filtered = purple_markup_strip_html(msg);
-	msg_len = strlen(msg_filtered);
-	now = time(NULL);
-
-	font_name_len = (font_name) ? strlen(font_name) : DEFAULT_FONT_NAME_LEN;
-	tail_len = font_name_len + QQ_SEND_IM_AFTER_MSG_HEADER_LEN + 1;
-
-	raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len;
-	raw_data = g_newa(guint8, raw_len);
+	/* purple_debug_info("QQ", "Send IM %d-%d\n", frag_count, frag_index); */
 	bytes = 0;
-
 	/* 000-003: receiver uid */
 	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 004-007: sender uid */
@@ -573,44 +1062,256 @@
 	/* 018-033: md5 of (uid+session_key) */
 	bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
 	/* 034-035: message type */
-	bytes += qq_put16(raw_data + bytes, im_type);
+	bytes += qq_put16(raw_data + bytes, QQ_NORMAL_IM_TEXT);
 	/* 036-037: sequence number */
 	bytes += qq_put16(raw_data + bytes, qd->send_seq);
 	/* 038-041: send time */
+	now = time(NULL);
 	bytes += qq_put32(raw_data + bytes, (guint32) now);
 	/* 042-043: sender icon */
 	bytes += qq_put16(raw_data + bytes, qd->my_icon);
 	/* 044-046: always 0x00 */
 	bytes += qq_put16(raw_data + bytes, 0x0000);
 	bytes += qq_put8(raw_data + bytes, 0x00);
-	/* 047-047: we use font attr */
+	/* 047-047: always use font attr */
 	bytes += qq_put8(raw_data + bytes, 0x01);
 	/* 048-051: always 0x00 */
-	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	/* Fixme: frag_count, frag_index not working now */
+	bytes += qq_put8(raw_data + bytes, frag_count);
+	bytes += qq_put8(raw_data + bytes, frag_index);
+	bytes += qq_put8(raw_data + bytes, id);
+	bytes += qq_put8(raw_data + bytes, 0);
 	/* 052-052: text message type (normal/auto-reply) */
 	bytes += qq_put8(raw_data + bytes, type);
 	/* 053-   : msg ends with 0x00 */
-	bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
-	send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold,
-			is_italic, is_underline, tail_len);
-	/* qq_show_packet("qq_get_send_im_tail", send_im_tail, tail_len); */
-	bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg));
+	if (frag_count == frag_index + 1) {
+		bytes += qq_put8(raw_data + bytes, 0x20);	/* add extra SPACE */
+	}
+	bytes += qq_put_im_tail(raw_data + bytes, fmt);
+
+	/* qq_show_packet("QQ_CMD_SEND_IM", raw_data, bytes); */
+	qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
+}
+
+static void im_convert_and_merge(GString *dest, GString *append)
+{
+	gchar *converted;
+	g_return_if_fail(dest != NULL && append != NULL);
 
-	/* qq_show_packet("QQ_CMD_SEND_IM, raw_data, bytes); */
+	if (append->str == NULL || append->len <= 0) {
+		return;
+	}
+	/* purple_debug_info("QQ", "Append:\n%s\n", append->str); */
+	converted = utf8_to_qq(append->str, QQ_CHARSET_DEFAULT);
+	g_string_append(dest, converted);
+	g_string_set_size(append, 0);
+	g_free(converted);
+}
+
+GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none)
+{
+	GSList *string_list = NULL;
+	GString *new_string;
+	GString *append_utf8;
+	gchar *start, *p;
+	gint count, len;
+	qq_emoticon *emoticon;
+
+	g_return_val_if_fail(msg_stripped != NULL, NULL);
+
+	start = msg_stripped;
+	count = 0;
+	new_string = g_string_new("");
+	append_utf8 = g_string_new("");
+	while (*start) {
+		p = start;
 
-	if (bytes == raw_len)	/* create packet OK */
-		qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
-	else
-		purple_debug_error("QQ",
-				"Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
+		/* Convert emoticon */
+		if (!is_smiley_none && *p == '/') {
+			if (new_string->len + append_utf8->len + 2 > QQ_MSG_IM_MAX) {
+				/* enough chars to send */
+				im_convert_and_merge(new_string, append_utf8);
+				string_list = g_slist_append(string_list, strdup(new_string->str));
+				g_string_set_size(new_string, 0);
+				continue;
+			}
+			emoticon = emoticon_find(p);
+			if (emoticon != NULL) {
+				purple_debug_info("QQ", "found emoticon %s as 0x%02X\n",
+						emoticon->name, emoticon->symbol);
+				/* QQ emoticon code prevent converting from utf8 to QQ charset
+				 * convert append_utf8 to QQ charset
+				 * merge the result to dest
+				 * append qq QQ emoticon code to dest */
+				im_convert_and_merge(new_string, append_utf8);
+				g_string_append_c(new_string, 0x14);
+				g_string_append_c(new_string, emoticon->symbol);
+				start += strlen(emoticon->name);
+				continue;
+			} else {
+				purple_debug_info("QQ", "Not found emoticon %.20s\n", p);
+			}
+		}
 
-	if (font_color)
-		g_free(font_color);
-	if (font_size)
-		g_free(font_size);
-	g_free(send_im_tail);
-	g_free(msg_filtered);
+		/* Get next char */
+		start = g_utf8_next_char(p);
+		len = start - p;
+		if (new_string->len + append_utf8->len + len > QQ_MSG_IM_MAX) {
+			/* enough chars to send */
+			im_convert_and_merge(new_string, append_utf8);
+			string_list = g_slist_append(string_list, strdup(new_string->str));
+			g_string_set_size(new_string, 0);
+		}
+		g_string_append_len(append_utf8, p, len);
+	}
+
+	if (new_string->len + append_utf8->len > 0) {
+		im_convert_and_merge(new_string, append_utf8);
+		string_list = g_slist_append(string_list, strdup(new_string->str));
+	}
+	g_string_free(new_string, TRUE);
+	g_string_free(append_utf8, TRUE);
+	return string_list;
+}
+
+gboolean qq_im_smiley_none(const gchar *msg)
+{
+	const gchar *start, *end, *last;
+	GData *attribs;
+	gchar *tmp;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail(msg != NULL, TRUE);
+
+	last = msg;
+	while (purple_markup_find_tag("font", last, &start, &end, &attribs)) {
+		tmp = g_datalist_get_data(&attribs, "sml");
+		if (tmp && strlen(tmp) > 0) {
+			if (strcmp(tmp, "none") == 0) {
+				ret = TRUE;
+				break;
+			}
+		}
+		g_datalist_clear(&attribs);
+		last = end + 1;
+	}
+	return ret;
 }
 
+/* Grab custom emote icons
+static GSList*  qq_grab_emoticons(const char *msg, const char*username)
+{
+	GSList *list;
+	GList *smileys;
+	PurpleSmiley *smiley;
+	const char *smiley_shortcut;
+	char *ptr;
+	int length;
+	PurpleStoredImage *img;
 
+	smileys = purple_smileys_get_all();
+	length = strlen(msg);
 
+	for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
+		smiley = smileys->data;
+		smiley_shortcut = purple_smiley_get_shortcut(smiley);
+		purple_debug_info("QQ", "Smiley shortcut [%s]\n", smiley_shortcut);
+
+		ptr = g_strstr_len(msg, length, smiley_shortcut);
+
+		if (!ptr)
+			continue;
+
+		purple_debug_info("QQ", "Found Smiley shortcut [%s]\n", smiley_shortcut);
+
+		img = purple_smiley_get_stored_image(smiley);
+
+		emoticon = g_new0(MsnEmoticon, 1);
+		emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley));
+		emoticon->obj = msn_object_new_from_image(img,
+				purple_imgstore_get_filename(img),
+				username, MSN_OBJECT_EMOTICON);
+
+ 		purple_imgstore_unref(img);
+		list = g_slist_prepend(list, emoticon);
+	}
+	return list;
+}
+*/
+
+gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *what, PurpleMessageFlags flags)
+{
+	qq_data *qd;
+	guint32 uid_to;
+	gint type;
+	qq_im_format *fmt;
+	gchar *msg_stripped, *tmp;
+	GSList *segments, *it;
+	gint msg_len;
+	const gchar *start_invalid;
+	gboolean is_smiley_none;
+	guint8 frag_count, frag_index;
+	guint8 msg_id;
+
+	g_return_val_if_fail(NULL != gc && NULL != gc->proto_data, -1);
+	g_return_val_if_fail(who != NULL && what != NULL, -1);
+
+	qd = (qq_data *) gc->proto_data;
+	purple_debug_info("QQ", "Send IM to %s, len %d:\n%s\n", who, strlen(what), what);
+
+	uid_to = purple_name_to_uid(who);
+	if (uid_to == qd->uid) {
+		/* if msg is to myself, bypass the network */
+		serv_got_im(gc, who, what, flags, time(NULL));
+		return 1;
+	}
+
+	type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT);
+	/* qq_show_packet("IM UTF8", (guint8 *)what, strlen(what)); */
+
+	msg_stripped = purple_markup_strip_html(what);
+	g_return_val_if_fail(msg_stripped != NULL, -1);
+	/* qq_show_packet("IM Stripped", (guint8 *)what, strlen(what)); */
+
+	/* Check and valid utf8 string */
+	msg_len = strlen(msg_stripped);
+	g_return_val_if_fail(msg_len > 0, -1);
+	if (!g_utf8_validate(msg_stripped, msg_len, &start_invalid)) {
+		if (start_invalid > msg_stripped) {
+			tmp = g_strndup(msg_stripped, start_invalid - msg_stripped);
+			g_free(msg_stripped);
+			msg_stripped = g_strconcat(tmp, _("(Invalid UTF-8 string)"), NULL);
+			g_free(tmp);
+		} else {
+			g_free(msg_stripped);
+			msg_stripped = g_strdup(_("(Invalid UTF-8 string)"));
+		}
+	}
+
+	is_smiley_none = qq_im_smiley_none(what);
+	segments = qq_im_get_segments(msg_stripped, is_smiley_none);
+	g_free(msg_stripped);
+
+	if (segments == NULL) {
+		return -1;
+	}
+
+	qd->send_im_id++;
+	msg_id = (guint8)(qd->send_im_id && 0xFF);
+	fmt = qq_im_fmt_new_by_purple(what);
+	frag_count = g_slist_length(segments);
+	frag_index = 0;
+	for (it = segments; it; it = it->next) {
+		/*
+		request_send_im(gc, uid_to, type, fmt, (gchar *)it->data,
+			msg_id, frag_count, frag_index);
+		*/
+		request_send_im(gc, uid_to, type, fmt, (gchar *)it->data, 0, 0, 0);
+		g_free(it->data);
+		frag_index++;
+	}
+	g_slist_free(segments);
+	qq_im_fmt_free(fmt);
+	return 1;
+}
--- a/libpurple/protocols/qq/im.h	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/im.h	Sat Dec 06 13:30:06 2008 +0000
@@ -27,22 +27,13 @@
 
 #include <glib.h>
 #include "connection.h"
-#include "group.h"
-
-#define QQ_MSG_IM_MAX               500	/* max length of IM */
-#define QQ_SEND_IM_BEFORE_MSG_LEN   53
-#define QQ_SEND_IM_AFTER_MSG_LEN    13	/* there is one 0x00 at the end */
-
-enum {
-	QQ_IM_TEXT = 0x01,
-	QQ_IM_AUTO_REPLY = 0x02
-};
 
 enum {
 	QQ_MSG_TO_BUDDY = 0x0009,
 	QQ_MSG_TO_UNKNOWN = 0x000a,
+	QQ_MSG_SMS = 0x0014,	/* not sure */
 	QQ_MSG_NEWS = 0x0018,
-	QQ_MSG_UNKNOWN_QUN_IM = 0x0020,
+	QQ_MSG_QUN_IM_UNKNOWN = 0x0020,
 	QQ_MSG_ADD_TO_QUN = 0x0021,
 	QQ_MSG_DEL_FROM_QUN = 0x0022,
 	QQ_MSG_APPLY_ADD_TO_QUN = 0x0023,
@@ -57,15 +48,29 @@
 	QQ_MSG_EXTEND_85 = 0x0085,
 };
 
-void qq_got_attention(PurpleConnection *gc, const gchar *msg);
+typedef struct {
+	guint8 attr;
+	guint8 rgb[3];
+	guint16 charset;
+	gchar *font;		/* Attension: font may NULL. font name is in QQ charset */
+	guint8 font_len;
+} qq_im_format;
 
-guint8 *qq_get_send_im_tail(const gchar *font_color,
-		const gchar *font_size,
-		const gchar *font_name,
-		gboolean is_bold, gboolean is_italic, gboolean is_underline, gint len);
+gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt);
+gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len);
 
-void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type);
+qq_im_format *qq_im_fmt_new(void);
+void qq_im_fmt_free(qq_im_format *fmt);
+qq_im_format *qq_im_fmt_new_by_purple(const gchar *msg);
+gchar *qq_im_fmt_to_purple(qq_im_format *fmt, gchar *text);
+gboolean qq_im_smiley_none(const gchar *msg);
+GSList *qq_im_get_segments(gchar *msg_stripped, gboolean is_smiley_none);
+
+void qq_got_message(PurpleConnection *gc, const gchar *msg);
+gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags);
 
 void qq_process_im(PurpleConnection *gc, guint8 *data, gint len);
 void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len);
+
+gchar *qq_emoticon_to_purple(gchar *text);
 #endif
--- a/libpurple/protocols/qq/qq.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Sat Dec 06 13:30:06 2008 +0000
@@ -56,9 +56,7 @@
 #include "utils.h"
 #include "version.h"
 
-#ifndef OPENQ_VERSION
-#define OPENQ_VERSION           DISPLAY_VERSION
-#endif
+#define OPENQ_VERSION 		"0.3.2-p19" 
 
 static GList *server_list_build(gchar select)
 {
@@ -91,7 +89,7 @@
 	PurpleConnection *gc;
 	qq_data *qd;
 	PurpleProxyInfo *gpi;
-	const gchar *user_server;
+	const gchar *custom_server;
 
 	gc = purple_account_get_connection(account);
 	g_return_if_fail(gc != NULL  && gc->proto_data != NULL);
@@ -101,10 +99,10 @@
 
 	qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
 
-	user_server = purple_account_get_string(account, "server", NULL);
-	purple_debug_info("QQ", "Select server '%s'\n", user_server);
-	if ( (user_server != NULL && strlen(user_server) > 0) && strcasecmp(user_server, "auto") != 0) {
-		qd->servers = g_list_append(qd->servers, g_strdup(user_server));
+	custom_server = purple_account_get_string(account, "server", NULL);
+	purple_debug_info("QQ", "Select server '%s'\n", custom_server);
+	if ( (custom_server != NULL && strlen(custom_server) > 0) && strcasecmp(custom_server, "auto") != 0) {
+		qd->servers = g_list_append(qd->servers, g_strdup(custom_server));
 		return;
 	}
 
@@ -268,9 +266,9 @@
 	case QQ_BUDDY_ONLINE_INVISIBLE:
 		g_string_append(status, _("Invisible"));
 		break;
-	case QQ_BUDDY_ONLINE_BUSY:
-		g_string_append(status, _("Busy"));
-		break;
+	case QQ_BUDDY_ONLINE_BUSY:
+		g_string_append(status, _("Busy"));
+		break;
 	default:
 		g_string_printf(status, _("Unknown-%d"), bd->status);
 	}
@@ -416,9 +414,9 @@
 			"invisible", _("Invisible"), FALSE, TRUE, FALSE);
 	types = g_list_append(types, status);
 
-	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
-			"busy", _("Busy"), TRUE, TRUE, FALSE);
-	types = g_list_append(types, status);
+	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
+			"busy", _("Busy"), TRUE, TRUE, FALSE);
+	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
 			"offline", _("Offline"), FALSE, TRUE, FALSE);
@@ -439,110 +437,6 @@
 	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)
-{
-	gint type, uid_to;
-	gchar *msg, *msg_with_qq_smiley;
-	qq_data *qd;
-
-	g_return_val_if_fail(who != NULL, -1);
-
-	qd = (qq_data *) gc->proto_data;
-
-	g_return_val_if_fail(strlen(message) <= QQ_MSG_IM_MAX, -E2BIG);
-
-	type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT);
-	uid_to = purple_name_to_uid(who);
-
-	/* if msg is to myself, bypass the network */
-	if (uid_to == qd->uid) {
-		serv_got_im(gc, who, message, flags, time(NULL));
-	} else {
-		msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT);
-		msg_with_qq_smiley = purple_smiley_to_qq(msg);
-		qq_request_send_im(gc, uid_to, msg_with_qq_smiley, type);
-		g_free(msg);
-		g_free(msg_with_qq_smiley);
-	}
-
-	return 1;
-}
-
-/* send a chat msg to a QQ Qun */
-static int qq_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
-{
-	gchar *msg, *msg_with_qq_smiley;
-	guint32 room_id = id;
-
-	g_return_val_if_fail(message != NULL, -1);
-	g_return_val_if_fail(strlen(message) <= QQ_MSG_IM_MAX, -E2BIG);
-
-	purple_debug_info("QQ_MESG", "Send qun mesg in utf8: %s\n", message);
-	msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT);
-	msg_with_qq_smiley = purple_smiley_to_qq(msg);
-	qq_request_room_send_im(gc, room_id, msg_with_qq_smiley);
-	g_free(msg);
-	g_free(msg_with_qq_smiley);
-
-	return 1;
-}
-
 /* send packet to get who's detailed information */
 static void qq_show_buddy_info(PurpleConnection *gc, const gchar *who)
 {
@@ -760,13 +654,14 @@
 	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, "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><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>");
 
-	title = g_strdup_printf(_("About OpenQ r%s"), OPENQ_VERSION);
+	title = g_strdup_printf(_("About OpenQ %s"), OPENQ_VERSION);
 	purple_notify_formatted(gc, title, title, NULL, info->str, NULL, NULL);
 
 	g_free(title);
@@ -805,7 +700,7 @@
 	g_return_if_fail(components != NULL);
 
 	num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
-	room_id = strtol(num_str, NULL, 10);
+	room_id = strtoul(num_str, NULL, 10);
 	g_return_if_fail(room_id != 0);
 
 	qq_room_quit(gc, room_id);
@@ -824,7 +719,7 @@
 	g_return_if_fail(components != NULL);
 
 	num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
-	room_id = strtol(num_str, NULL, 10);
+	room_id = strtoul(num_str, NULL, 10);
 	g_return_if_fail(room_id != 0);
 
 	qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0,
@@ -1037,26 +932,26 @@
 	qq_status_types,					/* away_states	*/
 	qq_blist_node_menu,			/* blist_node_menu */
 	qq_chat_info,						/* chat_info */
-	qq_chat_info_defaults,					/* chat_info_defaults */
-	qq_login,							/* open */
-	qq_close,						/* close */
-	qq_send_im,						/* send_im */
+	qq_chat_info_defaults,		/* chat_info_defaults */
+	qq_login,					/* open */
+	qq_close,					/* close */
+	qq_send_im,				/* send_im */
 	NULL,							/* set_info */
 	NULL,							/* send_typing	*/
-	qq_show_buddy_info,						/* get_info */
-	qq_change_status,						/* change status */
+	qq_show_buddy_info,		/* get_info */
+	qq_change_status,			/* change status */
 	NULL,							/* set_idle */
 	NULL,							/* change_passwd */
-	qq_add_buddy,						/* add_buddy */
+	qq_add_buddy,			/* add_buddy */
 	NULL,							/* add_buddies	*/
-	qq_remove_buddy,					/* remove_buddy */
+	qq_remove_buddy,		/* remove_buddy */
 	NULL,							/* remove_buddies */
 	NULL,							/* add_permit */
-	qq_add_deny,							/* add_deny */
+	NULL,							/* add_deny */
 	NULL,							/* rem_permit */
 	NULL,							/* rem_deny */
-	qq_set_permit_deny,			/* set_permit_deny */
-	qq_group_join,						/* join_chat */
+	NULL,							/* set_permit_deny */
+	qq_group_join,			/* join_chat */
 	NULL,							/* reject chat	invite */
 	NULL,							/* get_chat_name */
 	NULL,							/* chat_invite	*/
@@ -1075,11 +970,11 @@
 	NULL,							/* normalize */
 	qq_set_custom_icon,
 	NULL,							/* remove_group */
-	qq_get_chat_buddy_real_name,				/* get_cb_real_name */
+	qq_get_chat_buddy_real_name,		/* get_cb_real_name */
 	NULL,							/* set_chat_topic */
 	NULL,							/* find_blist_chat */
-	qq_roomlist_get_list,					/* roomlist_get_list */
-	qq_roomlist_cancel,					/* roomlist_cancel */
+	qq_roomlist_get_list,	/* roomlist_get_list */
+	qq_roomlist_cancel,		/* roomlist_cancel */
 	NULL,							/* roomlist_expand_category */
 	NULL,							/* can_receive_file */
 	NULL,							/* qq_send_file send_file */
@@ -1170,7 +1065,6 @@
 	option = purple_account_option_list_new(_("Select Server"), "server", server_kv_list);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
-//#ifdef DEBUG
 	kvp = g_new0(PurpleKeyValuePair, 1);
 	kvp->key = g_strdup(_("QQ2005"));
 	kvp->value = g_strdup("qq2005");
@@ -1188,7 +1082,6 @@
 
 	option = purple_account_option_list_new(_("Client Version"), "client_version", version_kv_list);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-//#endif
 
 	option = purple_account_option_bool_new(_("Connect by TCP"), "use_tcp", TRUE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
@@ -1208,7 +1101,7 @@
 	purple_prefs_add_none("/plugins/prpl/qq");
 	purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE);
 	purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE);
-	purple_prefs_add_bool("/plugins/prpl/qq/auto_popup_conversation", FALSE);
+	purple_prefs_add_bool("/plugins/prpl/qq/auto_popup_conversation", TRUE);
 	purple_prefs_add_bool("/plugins/prpl/qq/auto_get_authorize_info", TRUE);
 	purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3);
 	purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10);
--- a/libpurple/protocols/qq/qq.h	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Sat Dec 06 13:30:06 2008 +0000
@@ -182,6 +182,8 @@
 
 	gboolean is_show_notice;
 	gboolean is_show_news;
+
+	guint16 send_im_id;		/* send IM sequence number */
 };
 
 #endif
--- a/libpurple/protocols/qq/qq_base.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Sat Dec 06 13:30:06 2008 +0000
@@ -68,7 +68,8 @@
 	qd = (qq_data *) gc->proto_data;
 	/* qq_show_packet("Login reply", data, len); */
 
-	if (len < 139) {
+	if (len < 148) {
+		qq_show_packet("Login reply OK, but length < 139", data, len);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
 				_("Can not decrypt server reply"));
@@ -82,7 +83,7 @@
 	purple_debug_info("QQ", "Got session_key\n");
 	bytes += qq_get32(&uid, data + bytes);
 	if (uid != qd->uid) {
-		purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+		purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid);
 	}
 	bytes += qq_getIP(&qd->my_ip, data + bytes);
 	bytes += qq_get16(&qd->my_port, data + bytes);
@@ -137,8 +138,8 @@
 			tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
 	/* unknow 9 bytes, 0x(00 0a 00 0a 01 00 00 0e 10) */
 
-	if (len > 139) {
-		purple_debug_warning("QQ", "Login reply more than expected %d bytes, read %d bytes\n", 139, bytes);
+	if (len > 148) {
+		qq_show_packet("Login reply OK, but length > 139", data, len);
 	}
 	return QQ_LOGIN_REPLY_OK;
 }
@@ -330,7 +331,7 @@
 	if (bytes + token_len > buf_len) {
 		purple_debug_info("QQ", "Extra token data, %d %d\n", token_len, buf_len - bytes);
 	}
-	qq_show_packet("Got token", buf + bytes, buf_len - bytes);
+	/* qq_show_packet("Got token", buf + bytes, buf_len - bytes); */
 
 	if (qd->ld.token != NULL) {
 		g_free(qd->ld.token);
@@ -502,7 +503,7 @@
 	/* In fact, we can send whatever we like to server
 	 * with this command, server return the same result including
 	 * the amount of online QQ users, my ip and port */
-	uid_str = g_strdup_printf("%u", qd->uid);
+	uid_str = g_strdup_printf("%u", qd->uid);
 	bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str));
 	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
 
@@ -521,17 +522,17 @@
 
 	/* qq_show_packet("Keep alive reply packet", data, len); */
 
-	bytes = 0;
-	bytes += qq_get8(&ret, data + bytes);
-	bytes += qq_get32(&qd->online_total, data + bytes);
+	bytes = 0;
+	bytes += qq_get8(&ret, data + bytes);
+	bytes += qq_get32(&qd->online_total, data + bytes);
 	if(0 == qd->online_total) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Keep alive error"));
 	}
-
-	bytes += qq_getIP(&qd->my_ip, data + bytes);
-	bytes += qq_get16(&qd->my_port, data + bytes);
+
+	bytes += qq_getIP(&qd->my_ip, data + bytes);
+	bytes += qq_get16(&qd->my_port, data + bytes);
 	return TRUE;
 }
 
@@ -565,16 +566,16 @@
 
 	/* qq_show_packet("Keep alive reply packet", data, len); */
 
-	bytes = 0;
-	bytes += qq_get8(&ret, data + bytes);
-	bytes += qq_get32(&qd->online_total, data + bytes);
+	bytes = 0;
+	bytes += qq_get8(&ret, data + bytes);
+	bytes += qq_get32(&qd->online_total, data + bytes);
 	if(0 == qd->online_total) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Keep alive error"));
 	}
-
-	bytes += qq_getIP(&qd->my_ip, data + bytes);
+
+	bytes += qq_getIP(&qd->my_ip, data + bytes);
 	bytes += qq_get16(&qd->my_port, data + bytes);
 	/* skip 2 byytes, 0x(00 3c) */
 	bytes += 2;
@@ -1279,7 +1280,7 @@
 
 	bytes += qq_get32(&uid, data + bytes);
 	if (uid != qd->uid) {
-		purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+		purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid);
 	}
 	bytes += qq_getIP(&qd->my_ip, data + bytes);
 	bytes += qq_get16(&qd->my_port, data + bytes);
@@ -1468,7 +1469,7 @@
 
 	bytes += qq_get32(&uid, data + bytes);
 	if (uid != qd->uid) {
-		purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+		purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid);
 	}
 	bytes += qq_getIP(&qd->my_ip, data + bytes);
 	bytes += qq_get16(&qd->my_port, data + bytes);
--- a/libpurple/protocols/qq/qq_define.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Sat Dec 06 13:30:06 2008 +0000
@@ -132,65 +132,65 @@
 	case QQ_CMD_LOGOUT:
 		return "QQ_CMD_LOGOUT";
 	case QQ_CMD_KEEP_ALIVE:
-		return "QQ_CMD_KEEP_ALIVE";
+		return "CMD_KEEP_ALIVE";
 	case QQ_CMD_UPDATE_INFO:
-		return "QQ_CMD_UPDATE_INFO";
+		return "CMD_UPDATE_INFO";
 	case QQ_CMD_SEARCH_USER:
-		return "QQ_CMD_SEARCH_USER";
+		return "CMD_SEARCH_USER";
 	case QQ_CMD_GET_BUDDY_INFO:
-		return "QQ_CMD_GET_BUDDY_INFO";
+		return "CMD_GET_BUDDY_INFO";
 	case QQ_CMD_ADD_BUDDY_NO_AUTH:
-		return "QQ_CMD_ADD_BUDDY_NO_AUTH";
+		return "CMD_ADD_BUDDY_NO_AUTH";
 	case QQ_CMD_REMOVE_BUDDY:
-		return "QQ_CMD_REMOVE_BUDDY";
+		return "CMD_REMOVE_BUDDY";
 	case QQ_CMD_ADD_BUDDY_AUTH:
-		return "QQ_CMD_ADD_BUDDY_AUTH";
+		return "CMD_ADD_BUDDY_AUTH";
 	case QQ_CMD_CHANGE_STATUS:
-		return "QQ_CMD_CHANGE_STATUS";
+		return "CMD_CHANGE_STATUS";
 	case QQ_CMD_ACK_SYS_MSG:
-		return "QQ_CMD_ACK_SYS_MSG";
+		return "CMD_ACK_SYS_MSG";
 	case QQ_CMD_SEND_IM:
-		return "QQ_CMD_SEND_IM";
+		return "CMD_SEND_IM";
 	case QQ_CMD_RECV_IM:
-		return "QQ_CMD_RECV_IM";
+		return "CMD_RECV_IM";
 	case QQ_CMD_REMOVE_ME:
-		return "QQ_CMD_REMOVE_ME";
+		return "CMD_REMOVE_ME";
 	case QQ_CMD_LOGIN:
-		return "QQ_CMD_LOGIN";
+		return "CMD_LOGIN";
 	case QQ_CMD_GET_BUDDIES_LIST:
-		return "QQ_CMD_GET_BUDDIES_LIST";
+		return "CMD_GET_BUDDIES_LIST";
 	case QQ_CMD_GET_BUDDIES_ONLINE:
-		return "QQ_CMD_GET_BUDDIES_ONLINE";
+		return "CMD_GET_BUDDIES_ONLINE";
 	case QQ_CMD_ROOM:
-		return "QQ_CMD_ROOM";
+		return "CMD_ROOM";
 	case QQ_CMD_GET_BUDDIES_AND_ROOMS:
-		return "QQ_CMD_GET_BUDDIES_AND_ROOMS";
+		return "CMD_GET_BUDDIES_AND_ROOMS";
 	case QQ_CMD_GET_LEVEL:
-		return "QQ_CMD_GET_LEVEL";
+		return "CMD_GET_LEVEL";
 	case QQ_CMD_TOKEN:
-		return "QQ_CMD_TOKEN";
+		return "CMD_TOKEN";
 	case QQ_CMD_RECV_MSG_SYS:
-		return "QQ_CMD_RECV_MSG_SYS";
+		return "CMD_RECV_MSG_SYS";
 	case QQ_CMD_BUDDY_CHANGE_STATUS:
-		return "QQ_CMD_BUDDY_CHANGE_STATUS";
+		return "CMD_BUDDY_CHANGE_STATUS";
 	case QQ_CMD_GET_SERVER:
-		return "QQ_CMD_GET_SERVER";
+		return "CMD_GET_SERVER";
 	case QQ_CMD_TOKEN_EX:
-		return "QQ_CMD_TOKEN_EX";
+		return "CMD_TOKEN_EX";
 	case QQ_CMD_CHECK_PWD:
-		return "QQ_CMD_CHECK_PWD";
+		return "CMD_CHECK_PWD";
 	case QQ_CMD_AUTH_CODE:
-		return "QQ_CMD_AUTH_CODE";
+		return "CMD_AUTH_CODE";
 	case QQ_CMD_ADD_BUDDY_NO_AUTH_EX:
-		return "QQ_CMD_ADD_BUDDY_NO_AUTH_EX";
+		return "CMD_ADD_BUDDY_NO_AUTH_EX";
 	case QQ_CMD_ADD_BUDDY_AUTH_EX:
-		return "QQ_CMD_BUDDY_ADD_AUTH_EX";
+		return "CMD_BUDDY_ADD_AUTH_EX";
 	case QQ_CMD_BUDDY_CHECK_CODE:
-		return "QQ_CMD_BUDDY_CHECK_CODE";
+		return "CMD_BUDDY_CHECK_CODE";
 	case QQ_CMD_BUDDY_QUESTION:
-		return "QQ_CMD_BUDDY_QUESTION";
+		return "CMD_BUDDY_QUESTION";
 	default:
-		return "Unknown CMD";
+		return "CMD_UNKNOW";
 	}
 }
 
@@ -198,55 +198,55 @@
 {
 	switch (room_cmd) {
 	case QQ_ROOM_CMD_CREATE:
-		return "QQ_ROOM_CMD_CREATE";
+		return "ROOM_CMD_CREATE";
 	case QQ_ROOM_CMD_MEMBER_OPT:
-		return "QQ_ROOM_CMD_MEMBER_OPT";
+		return "ROOM_CMD_MEMBER_OPT";
 	case QQ_ROOM_CMD_CHANGE_INFO:
-		return "QQ_ROOM_CMD_CHANGE_INFO";
+		return "ROOM_CMD_CHANGE_INFO";
 	case QQ_ROOM_CMD_GET_INFO:
-		return "QQ_ROOM_CMD_GET_INFO";
+		return "ROOM_CMD_GET_INFO";
 	case QQ_ROOM_CMD_ACTIVATE:
-		return "QQ_ROOM_CMD_ACTIVATE";
+		return "ROOM_CMD_ACTIVATE";
 	case QQ_ROOM_CMD_SEARCH:
-		return "QQ_ROOM_CMD_SEARCH";
+		return "ROOM_CMD_SEARCH";
 	case QQ_ROOM_CMD_JOIN:
-		return "QQ_ROOM_CMD_JOIN";
+		return "ROOM_CMD_JOIN";
 	case QQ_ROOM_CMD_AUTH:
-		return "QQ_ROOM_CMD_AUTH";
+		return "ROOM_CMD_AUTH";
 	case QQ_ROOM_CMD_QUIT:
-		return "QQ_ROOM_CMD_QUIT";
-	case QQ_ROOM_CMD_SEND_MSG:
-		return "QQ_ROOM_CMD_SEND_MSG";
+		return "ROOM_CMD_QUIT";
+	case QQ_ROOM_CMD_SEND_IM:
+		return "ROOM_CMD_SEND_IM";
 	case QQ_ROOM_CMD_GET_ONLINES:
-		return "QQ_ROOM_CMD_GET_ONLINES";
+		return "ROOM_CMD_GET_ONLINES";
 	case QQ_ROOM_CMD_GET_BUDDIES:
-		return "QQ_ROOM_CMD_GET_BUDDIES";
+		return "ROOM_CMD_GET_BUDDIES";
 	case QQ_ROOM_CMD_CHANGE_CARD:
-		return "QQ_ROOM_CMD_CHANGE_CARD";
+		return "ROOM_CMD_CHANGE_CARD";
 	case QQ_ROOM_CMD_GET_REALNAMES:
-		return "QQ_ROOM_CMD_GET_REALNAMES";
+		return "ROOM_CMD_GET_REALNAMES";
 	case QQ_ROOM_CMD_GET_CARD:
-		return "QQ_ROOM_CMD_GET_CARD";
+		return "ROOM_CMD_GET_CARD";
 	case QQ_ROOM_CMD_SEND_IM_EX:
-		return "QQ_ROOM_CMD_SEND_IM_EX";
+		return "ROOM_CMD_SEND_IM_EX";
 	case QQ_ROOM_CMD_ADMIN:
-		return "QQ_ROOM_CMD_ADMIN";
+		return "ROOM_CMD_ADMIN";
 	case QQ_ROOM_CMD_TRANSFER:
-		return "QQ_ROOM_CMD_TRANSFER";
+		return "ROOM_CMD_TRANSFER";
 	case QQ_ROOM_CMD_TEMP_CREATE:
-		return "QQ_ROOM_CMD_TEMP_CREATE";
+		return "ROOM_CMD_TEMP_CREATE";
 	case QQ_ROOM_CMD_TEMP_CHANGE_MEMBER:
-		return "QQ_ROOM_CMD_TEMP_CHANGE_MEMBER";
+		return "ROOM_CMD_TEMP_CHANGE_MEMBER";
 	case QQ_ROOM_CMD_TEMP_QUIT:
-		return "QQ_ROOM_CMD_TEMP_QUIT";
+		return "ROOM_CMD_TEMP_QUIT";
 	case QQ_ROOM_CMD_TEMP_GET_INFO:
-		return "QQ_ROOM_CMD_TEMP_GET_INFO";
+		return "ROOM_CMD_TEMP_GET_INFO";
 	case QQ_ROOM_CMD_TEMP_SEND_IM:
-		return "QQ_ROOM_CMD_TEMP_SEND_IM";
+		return "ROOM_CMD_TEMP_SEND_IM";
 	case QQ_ROOM_CMD_TEMP_GET_MEMBERS:
-		return "QQ_ROOM_CMD_TEMP_GET_MEMBERS";
+		return "ROOM_CMD_TEMP_GET_MEMBERS";
 	default:
-		return "Unknown Room Command";
+		return "ROOM_CMD_UNKNOW";
 	}
 }
 
--- a/libpurple/protocols/qq/qq_define.h	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Sat Dec 06 13:30:06 2008 +0000
@@ -89,7 +89,7 @@
 	QQ_ROOM_CMD_JOIN = 0x07,
 	QQ_ROOM_CMD_AUTH = 0x08,
 	QQ_ROOM_CMD_QUIT = 0x09,
-	QQ_ROOM_CMD_SEND_MSG = 0x0a,
+	QQ_ROOM_CMD_SEND_IM = 0x0a,
 	QQ_ROOM_CMD_GET_ONLINES = 0x0b,
 	QQ_ROOM_CMD_GET_BUDDIES = 0x0c,
 
--- a/libpurple/protocols/qq/qq_network.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Sat Dec 06 13:30:06 2008 +0000
@@ -302,8 +302,7 @@
 	update_class = qq_trans_get_class(trans);
 	ship32 = qq_trans_get_ship(trans);
 	if (update_class != 0 || ship32 != 0) {
-		purple_debug_info("QQ", "Process in Update class %d, ship32 %d\n",
-				update_class, ship32);
+		purple_debug_info("QQ", "Update class %d, ship32 %d\n", update_class, ship32);
 	}
 
 	switch (cmd) {
@@ -323,10 +322,6 @@
 		case QQ_CMD_ROOM:
 			room_cmd = qq_trans_get_room_cmd(trans);
 			room_id = qq_trans_get_room_id(trans);
-#if 1
-			purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n",
-					qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
-#endif
 			qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
 			break;
 		default:
@@ -630,11 +625,13 @@
 	}
 
 	if (ret < data_len) {
-		purple_debug_info("TCP_SEND_OUT",
-			"Add %d bytes to buffer\n", data_len - ret);
+		purple_debug_info("TCP_SEND_OUT", "Add %d bytes to buffer\n", data_len - ret);
 		if (conn->can_write_handler == 0) {
 			conn->can_write_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, gc);
 		}
+		if (conn->tcp_txbuf == NULL) {
+			conn->tcp_txbuf = purple_circ_buffer_new(4096);
+		}
 		purple_circ_buffer_append(conn->tcp_txbuf, data + ret, data_len - ret);
 	}
 	return ret;
@@ -707,7 +704,7 @@
 	qd->send_seq = rand() & 0xffff;
 
 	qd->is_login = FALSE;
-	qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
+	qd->uid = strtoul(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
 
 #ifdef DEBUG
 	memset(qd->ld.random_key, 0x01, sizeof(qd->ld.random_key));
@@ -1107,7 +1104,7 @@
 
 #if 1
 		/* qq_show_packet("qq_send_cmd_encrypted", data, data_len); */
-		purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n",
+		purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n",
 				seq, qq_get_cmd_desc(cmd), cmd, encrypted_len);
 #endif
 
@@ -1161,7 +1158,7 @@
 
 	seq = ++qd->send_seq;
 #if 1
-		purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n",
+		purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n",
 				seq, qq_get_cmd_desc(cmd), cmd, data_len);
 #endif
 	return send_cmd_detail(gc, cmd, seq, data, data_len, TRUE, update_class, ship32);
@@ -1186,7 +1183,7 @@
 		is_save2trans = FALSE;
 	}
 #if 1
-		purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n",
+		purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n",
 				seq, qq_get_cmd_desc(cmd), cmd, data_len);
 #endif
 	return send_cmd_detail(gc, cmd, seq, data, data_len, is_save2trans, 0, 0);
@@ -1205,7 +1202,7 @@
 	g_return_val_if_fail(data != NULL && data_len > 0, -1);
 
 #if 1
-		purple_debug_info("QQ", "<== [SRV-%05d], %s(0x%04X), datalen %d\n",
+		purple_debug_info("QQ", "<== [SRV-%05d] %s(0x%04X), datalen %d\n",
 				seq, qq_get_cmd_desc(cmd), cmd, data_len);
 #endif
 	/* at most 16 bytes more */
@@ -1244,7 +1241,7 @@
 	buf_len = 0;
 	buf_len += qq_put8(buf + buf_len, room_cmd);
 	if (room_id != 0) {
-		/* id 0 is for QQ Demo Group, now there are not existed*/
+		/* id 0 is for QQ Demo Group, now they are closed*/
 		buf_len += qq_put32(buf + buf_len, room_id);
 	}
 	if (data != NULL && data_len > 0) {
@@ -1268,7 +1265,7 @@
 #if 1
 		/* qq_show_packet("send_room_cmd", buf, buf_len); */
 		purple_debug_info("QQ",
-				"<== [%05d], %s (0x%02X) to room %d, datalen %d\n",
+				"<== [%05d] %s (0x%02X) to room %d, datalen %d\n",
 				seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
 #endif
 
--- a/libpurple/protocols/qq/qq_process.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Sat Dec 06 13:30:06 2008 +0000
@@ -94,7 +94,7 @@
 	purple_debug_info("QQ", "OK sent IM\n");
 }
 
-static void do_server_news(guint8 *data, gint data_len, PurpleConnection *gc)
+static void do_server_news(PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	qq_data *qd = (qq_data *) gc->proto_data;
 	gint bytes;
@@ -114,7 +114,7 @@
 	content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url);
 
 	if (qd->is_show_news) {
-		qq_got_attention(gc, content);
+		qq_got_message(gc, content);
 	} else {
 		purple_debug_info("QQ", "QQ Server news:\n%s\n", content);
 	}
@@ -124,6 +124,40 @@
 	g_free(content);
 }
 
+static void do_got_sms(PurpleConnection *gc, guint8 *data, gint data_len)
+{
+	gint bytes;
+	gchar *mobile = NULL;
+	gchar *msg = NULL;
+	gchar *msg_utf8 = NULL;
+	gchar *msg_formated;
+
+	g_return_if_fail(data != NULL && data_len > 26);
+
+	qq_show_packet("Rcv sms", data, data_len);
+
+	bytes = 0;
+	bytes += 1;	/* skip 0x00 */
+	mobile = g_strndup((gchar *)data + bytes, 20);
+	bytes += 20;
+	bytes += 5; /* skip 0x(49 11 98 d5 03)*/
+	if (bytes < data_len) {
+		msg = g_strndup((gchar *)data + bytes, data_len - bytes);
+		msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+		g_free(msg);
+	} else {
+		msg_utf8 = g_strdup("");
+	}
+
+	msg_formated = g_strdup_printf(_("%s:%s"), mobile, msg_utf8);
+
+	qq_got_message(gc, msg_formated);
+
+	g_free(msg_formated);
+	g_free(msg_utf8);
+	g_free(mobile);
+}
+
 static void do_msg_sys_30(PurpleConnection *gc, guint8 *data, gint data_len)
 {
 	gint len;
@@ -142,7 +176,7 @@
 		purple_debug_warning("QQ", "We are kicked out by QQ server\n");
 
 	msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
-	qq_got_attention(gc, msg_utf8);
+	qq_got_message(gc, msg_utf8);
 }
 
 static void do_msg_sys_4c(PurpleConnection *gc, guint8 *data, gint data_len)
@@ -172,7 +206,7 @@
 		purple_debug_warning("QQ", "Failed to read QQ_MSG_SYS_4C\n");
 		qq_show_packet("do_msg_sys_4c", data, data_len);
 	}
-	qq_got_attention(gc, content->str);
+	qq_got_message(gc, content->str);
 	g_string_free(content, FALSE);
 }
 
@@ -183,8 +217,8 @@
 			return "QQ_MSG_TO_BUDDY";
 		case QQ_MSG_TO_UNKNOWN:
 			return "QQ_MSG_TO_UNKNOWN";
-		case QQ_MSG_UNKNOWN_QUN_IM:
-			return "QQ_MSG_UNKNOWN_QUN_IM";
+		case QQ_MSG_QUN_IM_UNKNOWN:
+			return "QQ_MSG_QUN_IM_UNKNOWN";
 		case QQ_MSG_ADD_TO_QUN:
 			return "QQ_MSG_ADD_TO_QUN";
 		case QQ_MSG_DEL_FROM_QUN:
@@ -207,6 +241,8 @@
 			return "QQ_MSG_QUN_IM";
 		case QQ_MSG_NEWS:
 			return "QQ_MSG_NEWS";
+		case QQ_MSG_SMS:
+			return "QQ_MSG_SMS";
 		case QQ_MSG_EXTEND:
 			return "QQ_MSG_EXTEND";
 		case QQ_MSG_EXTEND_85:
@@ -262,7 +298,7 @@
 	/* im_header prepared */
 
 	if (header.uid_to != qd->uid) {	/* should not happen */
-		purple_debug_error("QQ", "MSG to [%d], NOT me\n", header.uid_to);
+		purple_debug_error("QQ", "MSG to %u, NOT me\n", header.uid_to);
 		return;
 	}
 
@@ -274,7 +310,10 @@
 
 	switch (header.msg_type) {
 		case QQ_MSG_NEWS:
-			do_server_news(data + bytes, data_len - bytes, gc);
+			do_server_news(gc, data + bytes, data_len - bytes);
+			break;
+		case QQ_MSG_SMS:
+			do_got_sms(gc, data + bytes, data_len - bytes);
 			break;
 		case QQ_MSG_EXTEND:
 		case QQ_MSG_EXTEND_85:
@@ -286,7 +325,7 @@
 			purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
 			qq_process_im(gc, data + bytes, data_len - bytes);
 			break;
-		case QQ_MSG_UNKNOWN_QUN_IM:
+		case QQ_MSG_QUN_IM_UNKNOWN:
 		case QQ_MSG_TEMP_QUN_IM:
 		case QQ_MSG_QUN_IM:
 			purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from);
@@ -327,9 +366,12 @@
 			do_msg_sys_4c(gc, data + bytes, data_len - bytes);
 			break;
 		default:
-			purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%04X]\n",
+			purple_debug_warning("QQ", "MSG from %u, unknown type %s [0x%04X]\n",
 					header.uid_from, get_im_type_desc(header.msg_type), header.msg_type);
-			qq_show_packet("Unknown MSG type", data, data_len);
+			qq_show_packet("MSG header", data, bytes);
+			if (data_len - bytes > 0) {
+				qq_show_packet("MSG data", data + bytes, data_len - bytes);
+			}
 			break;
 	}
 }
@@ -381,7 +423,7 @@
 	content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8);
 
 	if (qd->is_show_notice) {
-		qq_got_attention(gc, content);
+		qq_got_message(gc, content);
 	} else {
 		purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8);
 	}
@@ -425,7 +467,7 @@
 	request_server_ack(gc, funct_str, from, seq);
 
 	/* qq_show_packet("Server MSG", data, data_len); */
-	if (strtol(to, NULL, 10) != qd->uid) {	/* not to me */
+	if (strtoul(to, NULL, 10) != qd->uid) {	/* not to me */
 		purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
 		g_strfreev(segments);
 		return;
@@ -512,7 +554,7 @@
 	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
 	g_free(msg);
 
-	prim = g_strdup_printf(_("Error reply of %s(0x%02X)\nRoom %d, reply 0x%02X"),
+	prim = g_strdup_printf(_("Error reply of %s(0x%02X)\nRoom %u, reply 0x%02X"),
 		qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, reply);
 
 	purple_notify_error(gc, _("QQ Qun Command"), prim, msg_utf8);
@@ -562,13 +604,13 @@
 	qd = (qq_data *) gc->proto_data;
 
 	next_id = qq_room_get_next(gc, room_id);
-	purple_debug_info("QQ", "Update rooms, next id %d, prev id %d\n", next_id, room_id);
+	purple_debug_info("QQ", "Update rooms, next id %u, prev id %u\n", next_id, room_id);
 
 	if (next_id <= 0) {
 		if (room_id > 0) {
 			is_new_turn = TRUE;
 			next_id = qq_room_get_next(gc, 0);
-			purple_debug_info("QQ", "new turn, id %d\n", next_id);
+			purple_debug_info("QQ", "New turn, id %u\n", next_id);
 		} else {
 			purple_debug_info("QQ", "No room. Finished update\n");
 			return;
@@ -798,9 +840,12 @@
 	case QQ_ROOM_CMD_QUIT:
 		qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc);
 		break;
-	case QQ_ROOM_CMD_SEND_MSG:
+	case QQ_ROOM_CMD_SEND_IM:
 		qq_process_room_send_im(gc, data + bytes, data_len - bytes);
 		break;
+	case QQ_ROOM_CMD_SEND_IM_EX:
+		qq_process_room_send_im_ex(gc, data + bytes, data_len - bytes);
+		break;
 	case QQ_ROOM_CMD_GET_ONLINES:
 		qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc);
 		break;
@@ -934,20 +979,20 @@
 			if (ret_8 != QQ_LOGIN_REPLY_OK) {
 				return ret_8;
 			}
-			if (qd->client_version == 2008) {
+			if (qd->client_version >= 2008) {
 				qq_request_login_2008(gc);
 			} else {
 				qq_request_login_2007(gc);
 			}
 			break;
 		case QQ_CMD_LOGIN:
-			if (qd->client_version == 2008) {
+			if (qd->client_version >= 2008) {
 				ret_8 = qq_process_login_2008(gc, data, data_len);
 				if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
                 		qq_request_get_server(gc);
                 		return QQ_LOGIN_REPLY_OK;
             	}
-			} else if (qd->client_version == 2007) {
+			} else if (qd->client_version >= 2007) {
 				ret_8 = qq_process_login_2007(gc, data, data_len);
 				if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
                 		qq_request_get_server(gc);
@@ -961,7 +1006,7 @@
 			}
 
 			purple_connection_update_progress(gc, _("Logging in"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
-			purple_debug_info("QQ", "Login repliess OK; everything is fine\n");
+			purple_debug_info("QQ", "Login replies OK; everything is fine\n");
 			purple_connection_set_state(gc, PURPLE_CONNECTED);
 			qd->is_login = TRUE;	/* must be defined after sev_finish_login */
 
--- a/libpurple/protocols/qq/utils.c	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Sat Dec 06 13:30:06 2008 +0000
@@ -39,8 +39,6 @@
 #include "util.h"
 #include "utils.h"
 
-#define QQ_NAME_FORMAT    "%d"
-
 /* These functions are used only in development phase */
 /*
    static void _qq_show_socket(gchar *desc, gint fd) {
@@ -135,7 +133,7 @@
 	guint32 ret;
 	g_return_val_if_fail(name != NULL, 0);
 
-	ret = strtol(name, NULL, 10);
+	ret = strtoul(name, NULL, 10);
 	if (errno == ERANGE)
 		return 0;
 	else
@@ -169,7 +167,7 @@
  * the return needs to be freed */
 gchar *uid_to_purple_name(guint32 uid)
 {
-	return g_strdup_printf(QQ_NAME_FORMAT, uid);
+	return g_strdup_printf("%u", uid);
 }
 
 /* try to dump the data as GBK */
@@ -339,3 +337,15 @@
 	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", buf, len, desc);
 }
 
+void qq_filter_str(gchar *str) {
+	gchar *temp;
+	if (str == NULL) {
+		return;
+	}
+
+	for (temp = str; *temp != 0; temp++) {
+		/*if (*temp == '\r' || *temp == '\n')  *temp = ' ';*/
+		if (*temp > 0 && *temp < 0x20)  *temp = ' ';
+	}
+	g_strstrip(str);
+}
--- a/libpurple/protocols/qq/utils.h	Sat Dec 06 06:02:42 2008 +0000
+++ b/libpurple/protocols/qq/utils.h	Sat Dec 06 13:30:06 2008 +0000
@@ -51,4 +51,5 @@
 		const char *format, ...);
 guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len);
 
+void qq_filter_str(gchar *str);
 #endif

mercurial