changeset 25803:f03a067fcfba

propagate from branch 'im.pidgin.pidgin' (head 196060989ecbfa760153e43530294b78f8999df6) to branch 'im.pidgin.cpw.malu.xmpp.idle' (head bb697289b68b0046f498cf9cb8fa8363622d4f2f)
author Marcus Lundblad <ml@update.uu.se>
date Sun, 07 Dec 2008 01:42:47 +0000
parents 4895879ebafb (diff) e3bb0bbfa388 (current diff)
children 9100b9176a16
files libpurple/protocols/jabber/jabber.c pidgin/pixmaps/protocols/16/facebook.png pidgin/pixmaps/protocols/22/facebook.png pidgin/pixmaps/protocols/48/facebook.png
diffstat 46 files changed, 1931 insertions(+), 1200 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Dec 02 22:55:49 2008 +0000
+++ b/COPYRIGHT	Sun Dec 07 01:42:47 2008 +0000
@@ -418,6 +418,7 @@
 Brian Tarricone
 Peter Teichman
 Philip Tellis
+Michael Terry
 Arun A. Tharuvai
 Cestonaro Thilo
 Will Thompson
--- a/ChangeLog	Tue Dec 02 22:55:49 2008 +0000
+++ b/ChangeLog	Sun Dec 07 01:42:47 2008 +0000
@@ -8,7 +8,6 @@
 	  --with-system-ssl-certs and GnuTLS need to include these in the
 	  system certs directory.
 	* Corrected maximum message lengths for Yahoo!
-	* Enable auto-reply on Zephyr, to emulate 'zaway' (Toby Schaffer)
 	* The Buddy State Notification plugin no longer prints duplicate
 	  notifications when the same buddy is in multiple groups (Florian Quèze)
 	* The Buddy State Notification plugin no longer turns JID's, MSN Passport
@@ -16,10 +15,15 @@
 	* Fix a crash in SIMPLE when a malformed message is received.
 	* purple-remote now has a "getstatusmessage" command to retrieve the text
 	  of the current status message.
+	* Various fixes to the nullprpl (Paul Aurich)
+	* Fix a crash when accessing the roomlist for an account that's not
+	  connected (Paul Aurich)
+	* Fix a crash in purple_accounts_delete that happens when this function is
+	  called before the buddy list is initialized (Florian Quèze)
 
 	Gadu-Gadu:
 	* Fix some problems with Gadu-Gadu buddy icons (Adam Strzelecki)
-	* Gadu-Gadu now validates that UID's are valid (Adam Strzelecki)
+	* Gadu-Gadu now checks that UID's are valid (Adam Strzelecki)
 	* Gadu-Gadu now does proper charset translations where needed (Adam
 	  Strzelecki)
 
@@ -35,6 +39,13 @@
 	* Send "client-accepts-full-bind-result" attribute during SASL login.
 	  This will fix Google Talk login failures if the user configures the
 	  wrong domain for his/her account.
+	
+	Zephyr:
+	* Enable auto-reply, to emulate 'zaway' (Toby Schaffer)
+	* Fix a crash when an account is configured to use tzc but tzc is not
+	  installed or the configured tzc command is invalid (Michael Terry)
+	* Fix a 10 second delay waiting on tzc if it is not installed or the
+	  configured command is invalid (Michael Terry)
 
 	Pidgin:
 	* On GTK+ 2.14 and higher, we're using the gtk-tooltip-delay setting
@@ -47,6 +58,9 @@
 	      gtk-enable-tooltips = 0
 	* Moved the release notification dialog to a mini-dialog in the
 	  buddylist.  (Thanks to Casey Ho)
+	* Fix a crash when closing an authorization minidialog with the X then
+	  immediately going offline (Paul Aurich)
+	* Fix compatibility with old GTK+ yet again
 
 	Finch:
 	* Allow binding meta+arrow keys for actions.
--- a/finch/gntaccount.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/finch/gntaccount.c	Sun Dec 07 01:42:47 2008 +0000
@@ -1069,6 +1069,8 @@
 			_("Authorize"), auth_cb,
 			_("Deny"), deny_cb);
 	}
+	g_signal_connect(G_OBJECT(uihandle), "destroy",
+		G_CALLBACK(purple_account_request_close), NULL);
 	g_free(buffer);
 	return uihandle;
 }
--- a/finch/gntroomlist.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/finch/gntroomlist.c	Sun Dec 07 01:42:47 2008 +0000
@@ -239,7 +239,8 @@
 		PurpleConnection *gc = list->data;
 
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
-		if (prpl_info->roomlist_get_list != NULL) {
+		if (PURPLE_CONNECTION_IS_CONNECTED(gc) &&
+		        prpl_info->roomlist_get_list != NULL) {
 			PurpleAccount *account = purple_connection_get_account(gc);
 			char *text = g_strdup_printf("%s (%s)",
 					purple_account_get_username(account),
--- a/finch/libgnt/gntentry.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/finch/libgnt/gntentry.c	Sun Dec 07 01:42:47 2008 +0000
@@ -856,7 +856,7 @@
 gnt_entry_class_init(GntEntryClass *klass)
 {
 	GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
-	char s[2] = {erasechar(), 0};
+	char s[3] = {'\033', erasechar(), 0};
 
 	parent_class = GNT_WIDGET_CLASS(klass);
 	parent_class->clicked = gnt_entry_clicked;
@@ -892,7 +892,7 @@
 	gnt_bindable_register_binding(bindable, "cursor-end", GNT_KEY_END, NULL);
 	gnt_bindable_class_register_action(bindable, "delete-prev", backspace,
 				GNT_KEY_BACKSPACE, NULL);
-	gnt_bindable_register_binding(bindable, "delete-prev", s, NULL);
+	gnt_bindable_register_binding(bindable, "delete-prev", s + 1, NULL);
 	gnt_bindable_register_binding(bindable, "delete-prev", GNT_KEY_CTRL_H, NULL);
 	gnt_bindable_class_register_action(bindable, "delete-next", delkey,
 				GNT_KEY_DEL, NULL);
@@ -903,7 +903,7 @@
 				GNT_KEY_CTRL_K, NULL);
 	gnt_bindable_class_register_action(bindable, "delete-prev-word", del_prev_word,
 				GNT_KEY_CTRL_W, NULL);
-	gnt_bindable_register_binding(bindable, "delete-prev-word", "\033", s, NULL);
+	gnt_bindable_register_binding(bindable, "delete-prev-word", s, NULL);
 	gnt_bindable_class_register_action(bindable, "cursor-prev-word", move_back_word,
 				"\033" "b", NULL);
 	gnt_bindable_class_register_action(bindable, "cursor-prev", move_back,
--- a/libpurple/account.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/account.c	Sun Dec 07 01:42:47 2008 +0000
@@ -2499,7 +2499,7 @@
 	purple_accounts_remove(account);
 
 	/* Remove this account's buddies */
-	for (gnode = purple_get_blist()->root; gnode != NULL; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root(); gnode != NULL; gnode = gnode->next) {
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
 
--- a/libpurple/protocols/jabber/jabber.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun Dec 07 01:42:47 2008 +0000
@@ -154,7 +154,7 @@
 		return NULL;
 
 	if (strstr(input, "__HOSTNAME__") == NULL)
-		return input;
+		return g_strdup(input);
 
 	/* Replace __HOSTNAME__ with hostname */
 	if (gethostname(hostname, sizeof(hostname) - 1)) {
--- a/libpurple/protocols/null/nullprpl.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Sun Dec 07 01:42:47 2008 +0000
@@ -160,8 +160,8 @@
 
 static void discover_status(PurpleConnection *from, PurpleConnection *to,
                             gpointer userdata) {
-  char *from_username = from->account->username;
-  char *to_username = to->account->username;
+  const char *from_username = from->account->username;
+  const char *to_username = to->account->username;
 
   if (purple_find_buddy(from->account, to_username)) {
     PurpleStatus *status = purple_account_get_active_status(to->account);
@@ -262,7 +262,7 @@
 
   } else {
     purple_debug_info("nullprpl", "...but %s is not logged in\n", buddy->name);
-    return "Not logged in";
+    return g_strdup("Not logged in");
   }
 }
 
@@ -275,9 +275,10 @@
     /* they're logged in */
     PurplePresence *presence = purple_buddy_get_presence(buddy);
     PurpleStatus *status = purple_presence_get_active_status(presence);
-    const char *msg = nullprpl_status_text(buddy);
+    char *msg = nullprpl_status_text(buddy);
     purple_notify_user_info_add_pair(info, purple_status_get_name(status),
                                      msg);
+    g_free(msg);
 
     if (full) {
       const char *user_info = purple_account_get_user_info(gc->account);
@@ -289,7 +290,7 @@
     /* they're not logged in */
     purple_notify_user_info_add_pair(info, _("User info"), _("not logged in"));
   }
-    
+
   purple_debug_info("nullprpl", "showing %s tooltip for %s\n",
                     (full) ? "full" : "short", buddy->name);
 }
@@ -307,21 +308,21 @@
                                 NULL_STATUS_ONLINE, TRUE);
   purple_status_type_add_attr(type, "message", _("Online"),
                               purple_value_new(PURPLE_TYPE_STRING));
-  types = g_list_append(types, type);
+  types = g_list_prepend(types, type);
 
   type = purple_status_type_new(PURPLE_STATUS_AWAY, NULL_STATUS_AWAY,
                                 NULL_STATUS_AWAY, TRUE);
   purple_status_type_add_attr(type, "message", _("Away"),
                               purple_value_new(PURPLE_TYPE_STRING));
-  types = g_list_append(types, type);
+  types = g_list_prepend(types, type);
   
   type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL_STATUS_OFFLINE,
                                 NULL_STATUS_OFFLINE, TRUE);
   purple_status_type_add_attr(type, "message", _("Offline"),
                               purple_value_new(PURPLE_TYPE_STRING));
-  types = g_list_append(types, type);
+  types = g_list_prepend(types, type);
 
-  return types;
+  return g_list_reverse(types);
 }
 
 static void blist_example_menu_item(PurpleBlistNode *node, gpointer userdata) {
@@ -355,7 +356,7 @@
   purple_debug_info("nullprpl", "returning chat setting 'room'\n");
 
   pce = g_new0(struct proto_chat_entry, 1);
-  pce->label = _(_("Chat _room"));
+  pce->label = _("Chat _room");
   pce->identifier = "room";
   pce->required = TRUE;
 
@@ -477,7 +478,7 @@
                     gc->account->username, info);
 }
 
-static char *typing_state_to_string(PurpleTypingState typing) {
+static const char *typing_state_to_string(PurpleTypingState typing) {
   switch (typing) {
   case PURPLE_NOT_TYPING:  return "is not typing";
   case PURPLE_TYPING:      return "is typing";
@@ -488,8 +489,8 @@
 
 static void notify_typing(PurpleConnection *from, PurpleConnection *to,
                           gpointer typing) {
-  char *from_username = from->account->username;
-  char *action = typing_state_to_string((PurpleTypingState)typing);
+  const char *from_username = from->account->username;
+  const char *action = typing_state_to_string((PurpleTypingState)typing);
   purple_debug_info("nullprpl", "notifying %s that %s %s\n",
                     to->account->username, from_username, action);
 
@@ -561,7 +562,7 @@
 static void nullprpl_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
                                PurpleGroup *group)
 {
-  char *username = gc->account->username;
+  const char *username = gc->account->username;
   PurpleConnection *buddy_gc = get_nullprpl_gc(buddy->name);
 
   purple_debug_info("nullprpl", "adding %s to %s's buddy list\n", buddy->name,
@@ -679,8 +680,8 @@
 }
 
 static void nullprpl_join_chat(PurpleConnection *gc, GHashTable *components) {
-  char *username = gc->account->username;
-  char *room = g_hash_table_lookup(components, "room");
+  const char *username = gc->account->username;
+  const char *room = g_hash_table_lookup(components, "room");
   int chat_id = g_str_hash(room);
   purple_debug_info("nullprpl", "%s is joining chat room %s\n", username, room);
 
@@ -690,20 +691,20 @@
     /* tell everyone that we joined, and add them if they're already there */
     foreach_gc_in_chat(joined_chat, gc, chat_id, NULL);
   } else {
+    char *tmp = g_strdup_printf(_("%s is already in chat room %s."),
+                                username,
+                                room);
     purple_debug_info("nullprpl", "%s is already in chat room %s\n", username,
                       room);
-    purple_notify_info(gc,
-                       _("Join chat"),
-                       _("Join chat"),
-                       g_strdup_printf("%s is already in chat room %s.",
-                                       username, room));
+    purple_notify_info(gc, _("Join chat"), _("Join chat"), tmp);
+    g_free(tmp);
   }
 }
 
 static void nullprpl_reject_chat(PurpleConnection *gc, GHashTable *components) {
-  char *invited_by = g_hash_table_lookup(components, "invited_by");
-  char *room = g_hash_table_lookup(components, "room");
-  char *username = gc->account->username;
+  const char *invited_by = g_hash_table_lookup(components, "invited_by");
+  const char *room = g_hash_table_lookup(components, "room");
+  const char *username = gc->account->username;
   PurpleConnection *invited_by_gc = get_nullprpl_gc(invited_by);
   char *message = g_strdup_printf(
     "%s %s %s.",
@@ -719,19 +720,20 @@
                      _("Chat invitation rejected"),
                      _("Chat invitation rejected"),
                      message);
+  g_free(message);
 }
 
 static char *nullprpl_get_chat_name(GHashTable *components) {
-  char *room = g_hash_table_lookup(components, "room");
+  const char *room = g_hash_table_lookup(components, "room");
   purple_debug_info("nullprpl", "reporting chat room name '%s'\n", room);
-  return room;
+  return g_strdup(room);
 }
 
 static void nullprpl_chat_invite(PurpleConnection *gc, int id,
                                  const char *message, const char *who) {
-  char *username = gc->account->username;
+  const char *username = gc->account->username;
   PurpleConversation *conv = purple_find_chat(gc, id);
-  char *room = conv->name;
+  const char *room = conv->name;
   PurpleAccount *to_acct = purple_accounts_find(who, NULLPRPL_ID);
 
   purple_debug_info("nullprpl", "%s is inviting %s to join chat room %s\n",
@@ -740,18 +742,16 @@
   if (to_acct) {
     PurpleConversation *to_conv = purple_find_chat(to_acct->gc, id);
     if (to_conv) {
+      char *tmp = g_strdup_printf("%s is already in chat room %s.", who, room);
       purple_debug_info("nullprpl",
                         "%s is already in chat room %s; "
                         "ignoring invitation from %s\n",
                         who, room, username);
-      purple_notify_info(gc,
-                         _("Chat invitation"),
-                         _("Chat invitation"),
-                         g_strdup_printf("%s is already in chat room %s.",
-                                         who, room));
+      purple_notify_info(gc, _("Chat invitation"), _("Chat invitation"), tmp);
+      g_free(tmp);
     } else {
       GHashTable *components;
-      components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free);
+      components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
       g_hash_table_replace(components, "room", g_strdup(room));
       g_hash_table_replace(components, "invited_by", g_strdup(username));
       serv_got_chat_invite(to_acct->gc, room, username, message, components);
@@ -833,7 +833,7 @@
 
 static void nullprpl_chat_whisper(PurpleConnection *gc, int id, const char *who,
                                   const char *message) {
-  char *username = gc->account->username;
+  const char *username = gc->account->username;
   PurpleConversation *conv = purple_find_chat(gc, id);
   purple_debug_info("nullprpl",
                     "%s receives whisper from %s in chat room %s: %s\n",
@@ -858,7 +858,7 @@
 
 static int nullprpl_chat_send(PurpleConnection *gc, int id, const char *message,
                               PurpleMessageFlags flags) {
-  char *username = gc->account->username;
+  const char *username = gc->account->username;
   PurpleConversation *conv = purple_find_chat(gc, id);
 
   if (conv) {
@@ -981,7 +981,7 @@
 }
 
 static PurpleRoomlist *nullprpl_roomlist_get_list(PurpleConnection *gc) {
-  char *username = gc->account->username;
+  const char *username = gc->account->username;
   PurpleRoomlist *roomlist = purple_roomlist_new(gc->account);
   GList *fields = NULL;
   PurpleRoomlistField *field;
@@ -1005,14 +1005,17 @@
   for (chats  = purple_get_chats(); chats; chats = g_list_next(chats)) {
     PurpleConversation *conv = (PurpleConversation *)chats->data;
     PurpleRoomlistRoom *room;
-    char *name = conv->name;
+    const char *name = conv->name;
     int id = purple_conversation_get_chat_data(conv)->id;
 
     /* have we already added this room? */
     if (g_list_find_custom(seen_ids, name, (GCompareFunc)strcmp))
       continue;                                /* yes! try the next one. */
 
-    seen_ids = g_list_append(seen_ids, name);  /* no, it's new. */
+    /* This cast is OK because this list is only staying around for the life
+     * of this function and none of the conversations are being deleted
+	 * in that timespan. */
+    seen_ids = g_list_prepend(seen_ids, (char *)name); /* no, it's new. */
     purple_debug_info("nullprpl", "%s (%d), ", name, id);
 
     room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
@@ -1021,6 +1024,7 @@
     purple_roomlist_room_add(roomlist, room);
   }
 
+  g_list_free(seen_ids);
   purple_timeout_add(1 /* ms */, nullprpl_finish_get_roomlist, roomlist);
   return roomlist;
 }
--- a/libpurple/protocols/qq/ChangeLog	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.h	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/im.h	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Sun Dec 07 01:42:47 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	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/qq/utils.h	Sun Dec 07 01:42:47 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
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sun Dec 07 01:42:47 2008 +0000
@@ -2122,7 +2122,7 @@
 	char *url = NULL;
 	char *fullmsg;
 	PurpleAccount *account = gc->account;
-	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -2136,6 +2136,10 @@
 	}
 
 	switch (err) {
+	case 0:
+		msg = g_strdup(_("Unknown error."));
+		reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+		break;
 	case 3:
 		msg = g_strdup(_("Invalid username."));
 		reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
@@ -2160,9 +2164,11 @@
 			purple_account_set_password(account, NULL);
 
 		msg = g_strdup(_("Incorrect password."));
+		reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
 		break;
 	case 14:
 		msg = g_strdup(_("Your account is locked, please log in to the Yahoo! website."));
+		reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
 		break;
 	default:
 		msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err);
@@ -2700,6 +2706,7 @@
 			  strncmp(buf, "HTTP/1.1 302", strlen("HTTP/1.1 302")))) {
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Received unexpected HTTP response from server."));
+		purple_debug_misc("yahoo", "Unexpected HTTP response: %s\n", buf);
 		return;
 	}
 
@@ -2796,7 +2803,7 @@
 
 static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url)
 {
-	if (!strcmp(key, "passwd"))
+	if (!strcmp(key, "passwd") || !strcmp(key, "login"))
 		return;
 	g_string_append_c(url, '&');
 	g_string_append(url, key);
--- a/libpurple/protocols/zephyr/zephyr.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Sun Dec 07 01:42:47 2008 +0000
@@ -1607,27 +1607,21 @@
 			gboolean found_ps = FALSE;
 			gchar ** tzc_cmd_array = g_strsplit(purple_account_get_string(gc->account,"tzc_command","/usr/bin/tzc -e %s")," ",0);
 			if (close(1) == -1) {
-				purple_debug_error("zephyr", "stdout couldn't be closed. dying\n");
 				exit(-1);
 			}
 			if (dup2(zephyr->fromtzc[1], 1) == -1) {
-				purple_debug_error("zephyr", "dup2 of stdout failed \n");
 				exit(-1);
 			}
 			if (close(zephyr->fromtzc[1]) == -1) {
-				purple_debug_error("zephyr", "closing of piped stdout failed\n");
 				exit(-1);
 			}
 			if (close(0) == -1) {
-				purple_debug_error("zephyr", "stdin couldn't be closed. dying\n");
 				exit(-1);
 			}
 			if (dup2(zephyr->totzc[0], 0) == -1) {
-				purple_debug_error("zephyr", "dup2 of stdin failed \n");
 				exit(-1);
 			}
 			if (close(zephyr->totzc[0]) == -1) {
-				purple_debug_error("zephyr", "closing of piped stdin failed\n");
 				exit(-1);
 			}
 			/* tzc_command should really be of the form 
@@ -1651,11 +1645,11 @@
 			}
 
 			if (!found_ps) {
-				purple_connection_error(gc,"Tzc command needs %s to set the exposure\n");
-				return;
+				exit(-1);
 			}
 
 			execvp(tzc_cmd_array[0], tzc_cmd_array);
+			exit(-1);
 		}
 		else {
 			fd_set rfds;
@@ -1667,6 +1661,7 @@
 			int parenlevel=0;
 			char* tempstr;
 			int tempstridx;
+			int select_status;
 
 			zephyr->tzc_pid = pid;
 			/* wait till we have data to read from ssh */
@@ -1678,11 +1673,19 @@
 
 			purple_debug_info("zephyr", "about to read from tzc\n");
 
-			select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, NULL);
+			if (waitpid(pid, NULL, WNOHANG) == 0) { // Only select if tzc is still running
+				purple_debug_info("zephyr", "about to read from tzc\n");
+				select_status = select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, NULL);
+			}
+			else {
+				purple_debug_info("zephyr", "tzc exited early\n");
+				select_status = -1;
+			}
 
 			FD_ZERO(&rfds);
 			FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds);
-			while (select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv)) {
+			while (select_status > 0 &&
+			       select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv) > 0) {
 				read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1);
 				bufcur++;
 				if ((bufcur - buf) > (bufsize - 1)) {
--- a/libpurple/prpl.h	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/prpl.h	Sun Dec 07 01:42:47 2008 +0000
@@ -274,7 +274,16 @@
 					PurpleMessageFlags flags);
 
 	void (*set_info)(PurpleConnection *, const char *info);
+
+	/**
+	 * @return If this protocol requires the PURPLE_TYPING message to
+	 *         be sent repeatedly to signify that the user is still
+	 *         typing, then the PRPL should return the number of
+	 *         seconds to wait before sending a subsequent notification.
+	 *         Otherwise the PRPL should return 0.
+	 */
 	unsigned int (*send_typing)(PurpleConnection *, const char *name, PurpleTypingState state);
+
 	/**
 	 * Should arrange for purple_notify_userinfo() to be called with
 	 * @a who's user info.
--- a/libpurple/roomlist.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/roomlist.c	Sun Dec 07 01:42:47 2008 +0000
@@ -173,6 +173,7 @@
 	PurplePluginProtocolInfo *prpl_info = NULL;
 
 	g_return_val_if_fail(gc != NULL, NULL);
+	g_return_val_if_fail(PURPLE_CONNECTION_IS_CONNECTED(gc), NULL);
 
 	prpl = purple_connection_get_prpl(gc);
 
--- a/libpurple/util.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/libpurple/util.c	Sun Dec 07 01:42:47 2008 +0000
@@ -56,6 +56,8 @@
 	gsize request_written;
 	gboolean include_headers;
 
+	gboolean is_ssl;
+	PurpleSslConnection *ssl_connection;
 	PurpleProxyConnectData *connect_data;
 	int fd;
 	guint inpa;
@@ -3443,9 +3445,6 @@
 	char *cmd;
 	GHashTable *params = NULL;
 	int len;
-
-	g_return_if_fail(uri != NULL);
-
 	if (!(tmp = strchr(uri, ':')) || tmp == uri) {
 		purple_debug_error("util", "Malformed protocol handler message - missing protocol.\n");
 		return;
@@ -3518,6 +3517,7 @@
 purple_url_parse(const char *url, char **ret_host, int *ret_port,
 			   char **ret_path, char **ret_user, char **ret_passwd)
 {
+	gboolean is_https = FALSE;
 	char scan_info[255];
 	char port_str[6];
 	int f;
@@ -3541,6 +3541,7 @@
 	}
 	else if ((turl = purple_strcasestr(url, "https://")) != NULL)
 	{
+		is_https = TRUE;
 		turl += 8;
 		url = turl;
 	}
@@ -3581,7 +3582,11 @@
 				   "%%255[%s]/%%255[%s]",
 				   addr_ctrl, page_ctrl);
 		f = sscanf(url, scan_info, host, path);
-		g_snprintf(port_str, sizeof(port_str), "80");
+		/* Use the default port */
+		if (is_https)
+			g_snprintf(port_str, sizeof(port_str), "443");
+		else
+			g_snprintf(port_str, sizeof(port_str), "80");
 	}
 
 	if (f == 0)
@@ -3620,6 +3625,8 @@
 }
 
 static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message);
+static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond);
+static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data);
 
 static gboolean
 parse_redirect(const char *data, size_t data_len,
@@ -3686,10 +3693,16 @@
 	g_free(gfud->request);
 	gfud->request = NULL;
 
-	purple_input_remove(gfud->inpa);
-	gfud->inpa = 0;
-	close(gfud->fd);
-	gfud->fd = -1;
+	if (gfud->is_ssl) {
+		gfud->is_ssl = FALSE;
+		purple_ssl_close(gfud->ssl_connection);
+		gfud->ssl_connection = NULL;
+	} else {
+		purple_input_remove(gfud->inpa);
+		gfud->inpa = 0;
+		close(gfud->fd);
+		gfud->fd = -1;
+	}
 	gfud->request_written = 0;
 	gfud->len = 0;
 	gfud->data_len = 0;
@@ -3701,11 +3714,18 @@
 	purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
 				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
 
-	gfud->connect_data = purple_proxy_connect(NULL, NULL,
-			gfud->website.address, gfud->website.port,
-			url_fetch_connect_cb, gfud);
-
-	if (gfud->connect_data == NULL)
+	if (purple_strcasestr(new_url, "https://") != NULL) {
+		gfud->is_ssl = TRUE;
+		gfud->ssl_connection = purple_ssl_connect(NULL,
+				gfud->website.address, gfud->website.port,
+				ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
+	} else {
+		gfud->connect_data = purple_proxy_connect(NULL, NULL,
+				gfud->website.address, gfud->website.port,
+				url_fetch_connect_cb, gfud);
+	}
+
+	if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
 	{
 		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
 				gfud->website.address);
@@ -3766,8 +3786,14 @@
 	char *data_cursor;
 	gboolean got_eof = FALSE;
 
-	while((len = read(source, buf, sizeof(buf))) > 0) {
-
+	/*
+	 * Read data in a loop until we can't read any more!  This is a
+	 * little confusing because we read using a different function
+	 * depending on whether the socket is ssl or cleartext.
+	 */
+	while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) ||
+			(!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0))
+	{
 		if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) {
 			purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"),
 						    gfud->website.address, gfud->max_len);
@@ -3887,6 +3913,21 @@
 	}
 }
 
+static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
+{
+	url_fetch_recv_cb(data, -1, cond);
+}
+
+/*
+ * This function is called when the socket is available to be written
+ * to.
+ *
+ * @param source The file descriptor that can be written to.  This can
+ *        be an http connection or it can be the SSL connection of an
+ *        https request.  So be careful what you use it for!  If it's
+ *        an https request then use purple_ssl_write() instead of
+ *        writing to it directly.
+ */
 static void
 url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
@@ -3895,53 +3936,14 @@
 
 	gfud = data;
 
-	total_len = strlen(gfud->request);
-
-	len = write(gfud->fd, gfud->request + gfud->request_written,
-			total_len - gfud->request_written);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len < 0) {
-		purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"),
-				gfud->website.address, g_strerror(errno));
-		return;
-	}
-	gfud->request_written += len;
-
-	if (gfud->request_written < total_len)
-		return;
-
-	/* We're done writing our request, now start reading the response */
-	purple_input_remove(gfud->inpa);
-	gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb,
-		gfud);
-}
-
-static void
-url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message)
-{
-	PurpleUtilFetchUrlData *gfud;
-
-	gfud = url_data;
-	gfud->connect_data = NULL;
-
-	if (source == -1)
+	if (gfud->request == NULL)
 	{
-		purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
-				(gfud->website.address ? gfud->website.address : ""), error_message);
-		return;
-	}
-
-	gfud->fd = source;
-
-	if (!gfud->request) {
+		/* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1
+		 * clients must know how to handle the "chunked" transfer encoding.
+		 * Purple doesn't know how to handle "chunked", so should always send
+		 * the Host header regardless, to get around some observed problems
+		 */
 		if (gfud->user_agent) {
-			/* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1
-			 * clients must know how to handle the "chunked" transfer encoding.
-			 * Purple doesn't know how to handle "chunked", so should always send
-			 * the Host header regardless, to get around some observed problems
-			 */
 			gfud->request = g_strdup_printf(
 				"GET %s%s HTTP/%s\r\n"
 				"Connection: close\r\n"
@@ -3968,11 +3970,84 @@
 
 	purple_debug_misc("util", "Request: '%s'\n", gfud->request);
 
+	total_len = strlen(gfud->request);
+
+	if (gfud->is_ssl)
+		len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written,
+				total_len - gfud->request_written);
+	else
+		len = write(gfud->fd, gfud->request + gfud->request_written,
+				total_len - gfud->request_written);
+
+	if (len < 0 && errno == EAGAIN)
+		return;
+	else if (len < 0) {
+		purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"),
+				gfud->website.address, g_strerror(errno));
+		return;
+	}
+	gfud->request_written += len;
+
+	if (gfud->request_written < total_len)
+		return;
+
+	/* We're done writing our request, now start reading the response */
+	if (gfud->is_ssl) {
+		purple_input_remove(gfud->inpa);
+		gfud->inpa = 0;
+		purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud);
+	} else {
+		purple_input_remove(gfud->inpa);
+		gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb,
+			gfud);
+	}
+}
+
+static void
+url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message)
+{
+	PurpleUtilFetchUrlData *gfud;
+
+	gfud = url_data;
+	gfud->connect_data = NULL;
+
+	if (source == -1)
+	{
+		purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
+				(gfud->website.address ? gfud->website.address : ""), error_message);
+		return;
+	}
+
+	gfud->fd = source;
+
 	gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
 								url_fetch_send_cb, gfud);
 	url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE);
 }
 
+static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
+{
+	PurpleUtilFetchUrlData *gfud;
+
+	gfud = data;
+
+	gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE,
+			url_fetch_send_cb, gfud);
+	url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE);
+}
+
+static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data)
+{
+	PurpleUtilFetchUrlData *gfud;
+
+	gfud = data;
+	gfud->ssl_connection = NULL;
+
+	purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
+			(gfud->website.address ? gfud->website.address : ""),
+	purple_ssl_strerror(error));
+}
+
 PurpleUtilFetchUrlData *
 purple_util_fetch_url_request(const char *url, gboolean full,
 		const char *user_agent, gboolean http11,
@@ -3985,13 +4060,6 @@
 					     callback, user_data);
 }
 
-static gboolean
-url_fetch_connect_failed(gpointer data)
-{
-	url_fetch_connect_cb(data, -1, "");
-	return FALSE;
-}
-
 PurpleUtilFetchUrlData *
 purple_util_fetch_url_request_len(const char *url, gboolean full,
 		const char *user_agent, gboolean http11,
@@ -4023,14 +4091,22 @@
 	purple_url_parse(url, &gfud->website.address, &gfud->website.port,
 				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
 
-	gfud->connect_data = purple_proxy_connect(NULL, NULL,
-			gfud->website.address, gfud->website.port,
-			url_fetch_connect_cb, gfud);
-
-	if (gfud->connect_data == NULL)
+	if (purple_strcasestr(url, "https://") != NULL) {
+		gfud->is_ssl = TRUE;
+		gfud->ssl_connection = purple_ssl_connect(NULL,
+				gfud->website.address, gfud->website.port,
+				ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
+	} else {
+		gfud->connect_data = purple_proxy_connect(NULL, NULL,
+				gfud->website.address, gfud->website.port,
+				url_fetch_connect_cb, gfud);
+	}
+
+	if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
 	{
-		/* Trigger the connect_cb asynchronously. */
-		purple_timeout_add(10, url_fetch_connect_failed, gfud);
+		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
+				gfud->website.address);
+		return NULL;
 	}
 
 	return gfud;
@@ -4039,6 +4115,9 @@
 void
 purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud)
 {
+	if (gfud->ssl_connection != NULL)
+		purple_ssl_close(gfud->ssl_connection);
+
 	if (gfud->connect_data != NULL)
 		purple_proxy_connect_cancel(gfud->connect_data);
 
--- a/pidgin/gtkaccount.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/pidgin/gtkaccount.c	Sun Dec 07 01:42:47 2008 +0000
@@ -2425,25 +2425,25 @@
 };
 
 static void
-authorize_and_add_cb(struct auth_and_add *aa)
+free_auth_and_add(struct auth_and_add *aa)
 {
-	aa->auth_cb(aa->data);
-	purple_blist_request_add_buddy(aa->account, aa->username,
-	 	                    NULL, aa->alias);
-
 	g_free(aa->username);
 	g_free(aa->alias);
 	g_free(aa);
 }
 
 static void
+authorize_and_add_cb(struct auth_and_add *aa)
+{
+	aa->auth_cb(aa->data);
+	purple_blist_request_add_buddy(aa->account, aa->username,
+	 	                    NULL, aa->alias);
+}
+
+static void
 deny_no_add_cb(struct auth_and_add *aa)
 {
 	aa->deny_cb(aa->data);
-
-	g_free(aa->username);
-	g_free(aa->alias);
-	g_free(aa);
 }
 
 static void *
@@ -2492,7 +2492,7 @@
 						  _("Authorize"), authorize_and_add_cb,
 						  _("Deny"), deny_no_add_cb,
 						  NULL);
-		g_object_set_data(G_OBJECT(alert), "auth_and_add", aa);
+		g_signal_connect_swapped(G_OBJECT(alert), "destroy", G_CALLBACK(free_auth_and_add), aa);
 	} else {
 		alert = pidgin_make_mini_dialog(gc, PIDGIN_STOCK_DIALOG_QUESTION,
 						  _("Authorize buddy?"), buffer, user_data,
@@ -2501,6 +2501,8 @@
 						  NULL);
 	}
 	pidgin_blist_add_alert(alert);
+	g_signal_connect(G_OBJECT(alert), "destroy",
+		G_CALLBACK(purple_account_request_close), NULL);
 
 	g_free(buffer);
 
@@ -2510,13 +2512,6 @@
 static void
 pidgin_accounts_request_close(void *ui_handle)
 {
-	/* This is super ugly, but without API changes, this is how it works */
-	struct auth_and_add *aa = g_object_get_data(G_OBJECT(ui_handle), "auth_and_add");
-	if (aa != NULL) {
-		g_free(aa->username);
-		g_free(aa->alias);
-		g_free(aa);
-	}
 	gtk_widget_destroy(GTK_WIDGET(ui_handle));
 }
 
--- a/pidgin/gtkconv.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/pidgin/gtkconv.c	Sun Dec 07 01:42:47 2008 +0000
@@ -7282,18 +7282,14 @@
 	if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus))
 		return;
 
-	while ((l = hidden_convwin->gtkconvs) != NULL)
-	{
+	for (l = hidden_convwin->gtkconvs; l; ) {
 		gtkconv = l->data;
+		l = l->next;
 
 		conv = gtkconv->active_conv;
-
-		while(l && !purple_status_is_available(
-					purple_account_get_active_status(
-					purple_conversation_get_account(conv))))
-			l = l->next;
-		if (!l)
-			break;
+		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT ||
+				account != purple_conversation_get_account(conv))
+			continue;
 
 		pidgin_conv_attach_to_conversation(conv);
 
--- a/pidgin/gtkroomlist.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/pidgin/gtkroomlist.c	Sun Dec 07 01:42:47 2008 +0000
@@ -488,7 +488,7 @@
 	PurpleConnection *conn = purple_account_get_connection(account);
 	PurplePluginProtocolInfo *prpl_info = NULL;
 
-	if (conn)
+	if (conn && PURPLE_CONNECTION_IS_CONNECTED(conn))
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl);
 
 	return (prpl_info && prpl_info->roomlist_get_list != NULL);
--- a/pidgin/gtkutils.c	Tue Dec 02 22:55:49 2008 +0000
+++ b/pidgin/gtkutils.c	Sun Dec 07 01:42:47 2008 +0000
@@ -3276,7 +3276,13 @@
 static void
 combo_box_changed_cb(GtkComboBox *combo_box, GtkEntry *entry)
 {
+#if GTK_CHECK_VERSION(2, 6, 0)
 	char *text = gtk_combo_box_get_active_text(combo_box);
+#else
+	GtkWidget *widget = gtk_bin_get_child(GTK_BIN(combo_box));
+	char *text = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
+#endif
+
 	gtk_entry_set_text(entry, text ? text : "");
 	g_free(text);
 }
Binary file pidgin/pixmaps/protocols/16/facebook.png has changed
Binary file pidgin/pixmaps/protocols/22/facebook.png has changed
Binary file pidgin/pixmaps/protocols/48/facebook.png has changed