changeset 26054:68f4edb42f39

propagate from branch 'im.pidgin.pidgin' (head e36b0b63962b9c710ecd798a529e1d2b78d7879a) to branch 'im.pidgin.pidgin.vv' (head bb0c02f78281c46db41ef3efba2896a2edad4441)
author Mike Ruprecht <maiku@soc.pidgin.im>
date Mon, 15 Dec 2008 08:39:08 +0000
parents 99c3489e06b0 (current diff) 6d57674c5fe3 (diff)
children 4a814967104e
files configure.ac finch/gntaccount.c libpurple/protocols/bonjour/bonjour.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/presence.c libpurple/protocols/msn/msn.c libpurple/protocols/myspace/CHANGES libpurple/protocols/myspace/ChangeLog libpurple/protocols/myspace/LICENSE libpurple/protocols/myspace/myspace.c libpurple/protocols/myspace/release.sh libpurple/protocols/null/nullprpl.c libpurple/protocols/qq/qq.c libpurple/protocols/sametime/sametime.c libpurple/protocols/simple/simple.c libpurple/protocols/zephyr/zephyr.c libpurple/prpl.h pidgin/gtkblist.c
diffstat 128 files changed, 4718 insertions(+), 4719 deletions(-) [+]
line wrap: on
line diff
--- a/COPYING	Sun Dec 14 23:43:52 2008 +0000
+++ b/COPYING	Mon Dec 15 08:39:08 2008 +0000
@@ -1,8 +1,8 @@
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-     51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -15,7 +15,7 @@
 General Public License applies to most of the Free Software
 Foundation's software and to any other program whose authors commit to
 using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
+the GNU Lesser General Public License instead.)  You can apply it to
 your programs, too.
 
   When we speak of free software, we are referring to freedom, not
@@ -55,7 +55,7 @@
 
   The precise terms and conditions for copying, distribution and
 modification follow.
-
+
 		    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -110,7 +110,7 @@
     License.  (Exception: if the Program itself is interactive but
     does not normally print such an announcement, your work based on
     the Program is not required to print an announcement.)
-
+
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-
+
   4. You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@
 
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-
+
   8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
@@ -278,7 +278,7 @@
 POSSIBILITY OF SUCH DAMAGES.
 
 		     END OF TERMS AND CONDITIONS
-
+
 	    How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
@@ -291,7 +291,7 @@
 the "copyright" line and a pointer to where the full notice is found.
 
     <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) 19yy  <name of author>
+    Copyright (C) <year>  <name of author>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -303,17 +303,16 @@
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
-
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 Also add information on how to contact you by electronic and paper mail.
 
 If the program is interactive, make it output a short notice like this
 when it starts in an interactive mode:
 
-    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision version 69, Copyright (C) year name of author
     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
@@ -336,5 +335,5 @@
 This General Public License does not permit incorporating your program into
 proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
+library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.
--- a/COPYRIGHT	Sun Dec 14 23:43:52 2008 +0000
+++ b/COPYRIGHT	Mon Dec 15 08:39:08 2008 +0000
@@ -207,6 +207,7 @@
 Benjamin Kahn
 Anders Kaseorg
 Praveen Karadakal
+Jaromír Karmazín
 John Kelm
 Jochen Kemnade
 Akuke Kok
@@ -271,6 +272,7 @@
 David Mohr
 Andrew Molloy
 Michael Monreal
+Laurent Montaron
 Marco Monteiro
 Benjamin Moody
 John Moody
@@ -418,6 +420,7 @@
 Brian Tarricone
 Peter Teichman
 Philip Tellis
+Michael Terry
 Arun A. Tharuvai
 Cestonaro Thilo
 Will Thompson
--- a/ChangeLog	Sun Dec 14 23:43:52 2008 +0000
+++ b/ChangeLog	Mon Dec 15 08:39:08 2008 +0000
@@ -1,28 +1,56 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.5.3 (??/??/????):
+version 2.5.3 (12/??/2008):
 	libpurple:
-	* Fix an error with MSN offline messages by shipping the *new*
-	  "Microsoft Secure Server Authority" and the
-	  "Microsoft Internet Authority" certificates.  People that use
-	  --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
 	  ID's, etc. into links (Florian Quèze)
-	* 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)
+	* Fix use of av_len in perl bindings to fix some off-by-one bugs (Paul
+	  Aurich)
+	* On ICQ, advertise the ICQ 6 typing capability.  This should fix the
+	  reports of typing notifications not working with third-party clients
+	  (Jaromír Karmazín)
+	* Many QQ fixes and improvements, including the ability to connect
+	  using QQ2008 protocol and sending/receiving of long messages.
+	* Fix a crash with DNS SRV lookups (Florian Quèze)
+	* Fix insanely long idle times for Sametime 7.5 buddies by assuming 0 idle
+	  time if the idle timestamp is in the future (Laurent Montaron)
 
 	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)
 
+	MSN:
+	* Fix an error with offline messages by shipping the *new* "Microsoft
+	  Secure Server Authority" and the "Microsoft Internet Authority"
+	  certificates. These are now always installed even when using
+	  --with-system-ssl-certs because most systems don't ship those
+	  intermediate certificates.
+	* The Games and Office media can now be set and displayed (in addition
+	  to the previous Music media). The Media status text now shows the
+	  album, if possible.
+	* Messages sent from a mobile device while you were offline are now
+	  correctly received.
+	* Server transfers after you've been connected for a long time should 
+	  now be handled correctly.
+	* Many other fixes and code cleanup.
+
+	SIMPLE:
+	* Fix a crash when a malformed message is received.
+	* Don't allow connecting accounts if no server name has been specified
+	  (Florian Quèze)
+
 	XMPP:
 	* Fix the namespace URL we look for in PEP reply stanzas to match the URL
 	  used in the 'get' requests (Paul Aurich)
@@ -35,6 +63,17 @@
 	* 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.
+	* Support new <metadata/> element to indicate no XEP-0084 User Avatar
+	  (Paul Aurich)
+	* Fix SHA1 avatar checksum errors that occur when one of the bytes in a
+	  checksum begins with 0 (Paul Aurich)
+	
+	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 +86,8 @@
 	      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)
 
 	Finch:
 	* Allow binding meta+arrow keys for actions.
--- a/ChangeLog.win32	Sun Dec 14 23:43:52 2008 +0000
+++ b/ChangeLog.win32	Mon Dec 15 08:39:08 2008 +0000
@@ -1,3 +1,7 @@
+version 2.5.3 (12/??/2008):
+	* Upgrade SILC to use the 1.1.8 toolkit
+	* Updated included Meanwhile library to include patch referenced in #7563
+
 version 2.5.2 (10/19/2008):
 	* Updated GTK+ to 2.12.12
 	  This will resolve an issue with stuff in QQ appearing as "(NULL)"
@@ -28,7 +32,7 @@
 
 version 2.4.0 (02/29/2008):
 	* Updated GTK+ to 2.12.8
-	* Updated include Meanwhile library to include patches referenced at:
+	* Updated included Meanwhile library to include patches referenced at:
 	  https://sourceforge.net/tracker/?func=detail&atid=656718&aid=1626349&group_id=110565
 	* Build the xmpp protocol with SASL support (and include Cyrus SASL
 	  2.1.22).
--- a/configure.ac	Sun Dec 14 23:43:52 2008 +0000
+++ b/configure.ac	Mon Dec 15 08:39:08 2008 +0000
@@ -1226,6 +1226,8 @@
 			"-Wendif-labels" \
 			"-Werror-implicit-function-declaration" \
 			"-Wextra -Wno-sign-compare -Wno-unused-parameter" \
+			"-Wformat-security" \
+				"-Werror=format-security" \
 			"-Winit-self" \
 			"-Wmissing-declarations" \
 			"-Wmissing-noreturn" \
--- a/finch/gntaccount.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/finch/gntaccount.c	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/finch/gntroomlist.c	Mon Dec 15 08:39:08 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/libpurple/account.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/account.c	Mon Dec 15 08:39:08 2008 +0000
@@ -75,6 +75,7 @@
 	gpointer userdata;
 	PurpleAccountRequestAuthorizationCb auth_cb;
 	PurpleAccountRequestAuthorizationCb deny_cb;
+	guint ref;
 } PurpleAccountRequestInfo;
 
 static PurpleAccountUiOps *account_ui_ops = NULL;
@@ -1211,6 +1212,18 @@
 		ui_ops->request_add(account, remote_user, id, alias, message);
 }
 
+static PurpleAccountRequestInfo *
+purple_account_request_info_unref(PurpleAccountRequestInfo *info)
+{
+	if (--info->ref)
+		return info;
+
+	/* TODO: This will leak info->user_data, but there is no callback to just clean that up */
+	g_free(info->user);
+	g_free(info);
+	return NULL;
+}
+
 static void
 purple_account_request_close_info(PurpleAccountRequestInfo *info)
 {
@@ -1221,11 +1234,7 @@
 	if (ops != NULL && ops->close_account_request != NULL)
 		ops->close_account_request(info->ui_handle);
 
-	/* TODO: This will leak info->user_data, but there is no callback to just clean that up */
-
-	g_free(info->user);
-	g_free(info);
-
+	purple_account_request_info_unref(info);
 }
 
 void
@@ -1278,8 +1287,7 @@
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-granted", info->account, info->user);
 
-	g_free(info->user);
-	g_free(info);
+	purple_account_request_info_unref(info);
 }
 
 static void
@@ -1294,8 +1302,7 @@
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-denied", info->account, info->user);
 
-	g_free(info->user);
-	g_free(info);
+	purple_account_request_info_unref(info);
 }
 
 void *
@@ -1332,11 +1339,18 @@
 		info->deny_cb   = deny_cb;
 		info->userdata  = user_data;
 		info->user      = g_strdup(remote_user);
+		info->ref       = 2;  /* We hold an extra ref to make sure info remains valid
+		                         if any of the callbacks are called synchronously. We
+		                         unref it after the function call */
+
 		info->ui_handle = ui_ops->request_authorize(account, remote_user, id, alias, message,
 							    on_list, request_auth_cb, request_deny_cb, info);
 
-		handles = g_list_append(handles, info);
-		return info->ui_handle;
+		info = purple_account_request_info_unref(info);
+		if (info) {
+			handles = g_list_append(handles, info);
+			return info->ui_handle;
+		}
 	}
 
 	return NULL;
@@ -2499,7 +2513,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/blist.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/blist.h	Mon Dec 15 08:39:08 2008 +0000
@@ -71,7 +71,7 @@
 
 typedef enum
 {
-	PURPLE_BLIST_NODE_FLAG_NO_SAVE      = 1 << 0, /**< node should not be saved with the buddy list */
+	PURPLE_BLIST_NODE_FLAG_NO_SAVE      = 1 << 0 /**< node should not be saved with the buddy list */
 
 } PurpleBlistNodeFlags;
 
--- a/libpurple/certificate.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/certificate.c	Mon Dec 15 08:39:08 2008 +0000
@@ -748,9 +748,9 @@
 # ifdef SSL_CERTIFICATES_DIR
 		x509_ca_paths = g_list_append(NULL, g_strdup(SSL_CERTIFICATES_DIR));
 # else
-		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
-						   "purple", "ca-certs", NULL));
 # endif
+		x509_ca_paths = g_list_append(x509_ca_paths,
+			g_build_filename(DATADIR, "purple", "ca-certs", NULL));
 #endif
 	}
 
--- a/libpurple/cmds.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/cmds.h	Mon Dec 15 08:39:08 2008 +0000
@@ -38,7 +38,7 @@
 	PURPLE_CMD_STATUS_NOT_FOUND,
 	PURPLE_CMD_STATUS_WRONG_ARGS,
 	PURPLE_CMD_STATUS_WRONG_PRPL,
-	PURPLE_CMD_STATUS_WRONG_TYPE,
+	PURPLE_CMD_STATUS_WRONG_TYPE
 } PurpleCmdStatus;
 
 /** Commands registered with the core return one of these values when run.
@@ -51,7 +51,7 @@
 typedef enum _PurpleCmdRet {
 	PURPLE_CMD_RET_OK,       /**< Everything's okay; Don't look for another command to call. */
 	PURPLE_CMD_RET_FAILED,   /**< The command failed, but stop looking.*/
-	PURPLE_CMD_RET_CONTINUE, /**< Continue, looking for other commands with the same name to call. */
+	PURPLE_CMD_RET_CONTINUE /**< Continue, looking for other commands with the same name to call. */
 } PurpleCmdRet;
 
 #define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func)
@@ -76,7 +76,7 @@
 	PURPLE_CMD_P_PLUGIN    =  3000,
 	PURPLE_CMD_P_ALIAS     =  4000,
 	PURPLE_CMD_P_HIGH      =  5000,
-	PURPLE_CMD_P_VERY_HIGH =  6000,
+	PURPLE_CMD_P_VERY_HIGH =  6000
 } PurpleCmdPriority;
 
 /** Flags used to set various properties of commands.  Every command should
@@ -93,7 +93,7 @@
 	/** Command is usable only for a particular prpl. */
 	PURPLE_CMD_FLAG_PRPL_ONLY        = 0x04,
 	/** Incorrect arguments to this command should be accepted anyway. */
-	PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08,
+	PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08
 } PurpleCmdFlag;
 
 
--- a/libpurple/connection.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/connection.h	Mon Dec 15 08:39:08 2008 +0000
@@ -44,7 +44,7 @@
 	PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */
 	PURPLE_CONNECTION_NO_URLDESC = 0x0040,  /**< Connection does not support descriptions with links */ 
 	PURPLE_CONNECTION_NO_IMAGES = 0x0080,  /**< Connection does not support sending of images */
-	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */
+	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100 /**< Connection supports sending and receiving custom smileys */
 
 } PurpleConnectionFlags;
 
--- a/libpurple/conversation.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/conversation.h	Mon Dec 15 08:39:08 2008 +0000
@@ -84,7 +84,7 @@
 	PURPLE_CONV_UPDATE_TITLE,
 	PURPLE_CONV_UPDATE_CHATLEFT,
 
-	PURPLE_CONV_UPDATE_FEATURES, /**< The features for a chat have changed */
+	PURPLE_CONV_UPDATE_FEATURES  /**< The features for a chat have changed */
 
 } PurpleConvUpdateType;
 
@@ -126,7 +126,7 @@
 	PURPLE_MESSAGE_NOTIFY      = 0x2000, /**< Message is a notification */
 	PURPLE_MESSAGE_NO_LINKIFY  = 0x4000, /**< Message should not be auto-
 										   linkified @since 2.1.0 */
-	PURPLE_MESSAGE_INVISIBLE   = 0x8000, /**< Message should not be displayed */
+	PURPLE_MESSAGE_INVISIBLE   = 0x8000  /**< Message should not be displayed */
 } PurpleMessageFlags;
 
 /**
@@ -139,7 +139,7 @@
 	PURPLE_CBFLAGS_HALFOP        = 0x0002, /**< Half-op                      */
 	PURPLE_CBFLAGS_OP            = 0x0004, /**< Channel Op or Moderator      */
 	PURPLE_CBFLAGS_FOUNDER       = 0x0008, /**< Channel Founder              */
-	PURPLE_CBFLAGS_TYPING        = 0x0010, /**< Currently typing             */
+	PURPLE_CBFLAGS_TYPING        = 0x0010  /**< Currently typing             */
 
 } PurpleConvChatBuddyFlags;
 
--- a/libpurple/dnssrv.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/dnssrv.c	Mon Dec 15 08:39:08 2008 +0000
@@ -336,6 +336,12 @@
 	static gboolean initialized = FALSE;
 #endif
 
+	if (!protocol || !*protocol || !transport || !*transport || !domain || !*domain) {
+		purple_debug_error("dnssrv", "Wrong arguments\n");
+		cb(NULL, 0, extradata);
+		g_return_val_if_reached(NULL);
+	}
+
 	query = g_strdup_printf("_%s._%s.%s", protocol, transport, domain);
 	purple_debug_info("dnssrv","querying SRV record for %s\n", query);
 
--- a/libpurple/plugins/perl/common/Account.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/plugins/perl/common/Account.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -107,7 +107,7 @@
     t_GL = NULL;
     t_len = av_len((AV *)SvRV(status_types));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(status_types), i, 0)));
 
     purple_account_set_status_types(account, t_GL);
@@ -209,7 +209,7 @@
     t_GL = NULL;
     t_len = av_len((AV *)SvRV(list));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(list), i, 0)));
 
     purple_account_add_buddies(account, t_GL);
@@ -238,13 +238,13 @@
     t_GL1 = NULL;
     t_len = av_len((AV *)SvRV(A));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL1 = g_list_append(t_GL1, SvPVutf8_nolen(*av_fetch((AV *)SvRV(A), i, 0)));
 
     t_GL2 = NULL;
     t_len = av_len((AV *)SvRV(B));
 
-    for (i = 0; i < t_len; i++)
+    for (i = 0; i <= t_len; i++)
         t_GL2 = g_list_append(t_GL2, SvPVutf8_nolen(*av_fetch((AV *)SvRV(B), i, 0)));
 
     purple_account_remove_buddies(account, t_GL1, t_GL2);
--- a/libpurple/plugins/perl/common/AccountOpts.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/plugins/perl/common/AccountOpts.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -44,7 +44,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(values));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(values), i, 0)));
 
 	RETVAL  = purple_account_option_list_new(text, pref_name, t_GL);
@@ -132,7 +132,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(values));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(values), i, 0)));
 
 	purple_account_option_set_list(option, t_GL);
--- a/libpurple/plugins/perl/common/Certificate.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/plugins/perl/common/Certificate.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -231,8 +231,8 @@
 		int len = 0, i = 0;
 		struct cb_data *d = NULL;
 	PPCODE:
-		len = av_len(cert_chain) + 1;
-		for(i = 0; i < len; i++) {
+		len = av_len(cert_chain);
+		for(i = 0; i <= len; i++) {
 			SV **sv = av_fetch(cert_chain, i, 0);
 			if(!sv || !purple_perl_is_ref_object(*sv)) {
 				g_list_free(l);
--- a/libpurple/plugins/perl/common/Conversation.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/plugins/perl/common/Conversation.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -336,7 +336,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(users));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(users), i, 0)));
 
 	for (l = purple_conv_chat_set_users(chat, t_GL); l != NULL; l = l->next) {
@@ -374,7 +374,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(ignored));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(ignored), i, 0)));
 
 	for (l = purple_conv_chat_set_ignored(chat, t_GL); l != NULL; l = l->next) {
@@ -431,19 +431,19 @@
 	t_GL_users = NULL;
 	t_len = av_len((AV *)SvRV(users));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL_users = g_list_append(t_GL_users, SvPVutf8_nolen(*av_fetch((AV *)SvRV(users), i, 0)));
 
 	t_GL_flags = NULL;
 	t_len = av_len((AV *)SvRV(flags));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL_flags = g_list_append(t_GL_flags, SvPVutf8_nolen(*av_fetch((AV *)SvRV(flags), i, 0)));
 
 	t_GL_extra_msgs = NULL;
 	t_len = av_len((AV *)SvRV(extra_msgs));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL_extra_msgs = g_list_append(t_GL_extra_msgs, SvPVutf8_nolen(*av_fetch((AV *)SvRV(extra_msgs), i, 0)));
 
 	purple_conv_chat_add_users(chat, t_GL_users, t_GL_extra_msgs, t_GL_flags, new_arrivals);
--- a/libpurple/plugins/perl/common/Prefs.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/plugins/perl/common/Prefs.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -53,7 +53,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_add_string_list(name, t_GL);
@@ -75,7 +75,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_add_path_list(name, t_GL);
@@ -204,7 +204,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_set_string_list(name, t_GL);
@@ -226,7 +226,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(value));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(value), i, 0)));
 
 	purple_prefs_set_path_list(name, t_GL);
--- a/libpurple/plugins/perl/common/Roomlist.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/plugins/perl/common/Roomlist.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -80,7 +80,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(fields));
 
-	for (i = 0; i < t_len; i++)
+	for (i = 0; i <= t_len; i++)
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(fields), i, 0)));
 
 	purple_roomlist_set_fields(list, t_GL);
--- a/libpurple/plugins/perl/common/Status.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/plugins/perl/common/Status.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -85,7 +85,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(source_list));
 
-	for (i = 0; i < t_len; i++) {
+	for (i = 0; i <= t_len; i++) {
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(source_list), i, 0)));
 	}
 	purple_presence_add_list(presence, t_GL);
@@ -381,7 +381,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(status_types));
 
-	for (i = 0; i < t_len; i++) {
+	for (i = 0; i <= t_len; i++) {
 		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(status_types), i, 0)));
 	}
 	RETVAL = (PurpleStatusType *)purple_status_type_find_with_id(t_GL, id);
--- a/libpurple/protocols/bonjour/bonjour.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Mon Dec 15 08:39:08 2008 +0000
@@ -376,20 +376,20 @@
 	}
 
 	/* Only show first/last name if there is a nickname set (to avoid duplication) */
-	if (bb->nick != NULL) {
-		if (bb->first != NULL)
+	if (bb->nick != NULL && *bb->nick != '\0') {
+		if (bb->first != NULL && *bb->first != '\0')
 			purple_notify_user_info_add_pair(user_info, _("First name"), bb->first);
-		if (bb->first != NULL)
+		if (bb->last != NULL && *bb->last != '\0')
 			purple_notify_user_info_add_pair(user_info, _("Last name"), bb->last);
 	}
 
-	if (bb->email != NULL)
+	if (bb->email != NULL && *bb->email != '\0')
 		purple_notify_user_info_add_pair(user_info, _("Email"), bb->email);
 
-	if (bb->AIM != NULL)
+	if (bb->AIM != NULL && *bb->AIM != '\0')
 		purple_notify_user_info_add_pair(user_info, _("AIM Account"), bb->AIM);
 
-	if (bb->jid!= NULL)
+	if (bb->jid != NULL && *bb->jid != '\0')
 		purple_notify_user_info_add_pair(user_info, _("XMPP Account"), bb->jid);
 }
 
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Mon Dec 15 08:39:08 2008 +0000
@@ -411,8 +411,10 @@
 	BonjourData *bd;
 	PurpleXfer *xfer;
 
-	if(pc == NULL || packet == NULL || pb == NULL)
-		return;
+	g_return_if_fail(pc != NULL);
+	g_return_if_fail(packet != NULL);
+	g_return_if_fail(pb != NULL);
+
 	bd = (BonjourData*) pc->proto_data;
 	if(bd == NULL)
 		return;
@@ -488,8 +490,9 @@
 	xmlnode *query;
 	BonjourData *bd;
 
-	if(pc == NULL || packet == NULL || pb == NULL)
-		return;
+	g_return_if_fail(pc != NULL);
+	g_return_if_fail(packet != NULL);
+	g_return_if_fail(pb != NULL);
 
 	bd = (BonjourData*) pc->proto_data;
 	if(bd == NULL)
--- a/libpurple/protocols/bonjour/bonjour_ft.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.h	Mon Dec 15 08:39:08 2008 +0000
@@ -27,7 +27,7 @@
 typedef enum {
 	XEP_BYTESTREAMS = 1,
 	XEP_IBB = 2,
-	XEP_UNKNOWN = 4,
+	XEP_UNKNOWN = 4
 } XepSiMode;
 
 struct _XepXfer
--- a/libpurple/protocols/bonjour/buddy.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Mon Dec 15 08:39:08 2008 +0000
@@ -161,7 +161,7 @@
 	name = purple_buddy_get_name(buddy);
 
 	/* Create the alias for the buddy using the first and the last name */
-	if (bonjour_buddy->nick)
+	if (bonjour_buddy->nick && *bonjour_buddy->nick)
 		serv_got_alias(purple_account_get_connection(account), name, bonjour_buddy->nick);
 	else {
 		gchar *alias = NULL;
--- a/libpurple/protocols/bonjour/jabber.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Mon Dec 15 08:39:08 2008 +0000
@@ -77,7 +77,7 @@
 };
 
 static void
-xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
+xep_iq_parse(xmlnode *packet, PurpleBuddy *pb);
 
 static BonjourJabberConversation *
 bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
@@ -128,7 +128,7 @@
 
 	if (contents) {
 		char *bodystart = strchr(contents, '>');
-		char *bodyend = strrchr(bodystart, '<');
+		char *bodyend = bodystart ? strrchr(bodystart, '<') : NULL;
 		if (bodystart && bodyend && (bodystart + 1) != bodyend) {
 			*bodyend = '\0';
 			memmove(contents, bodystart + 1, (bodyend - bodystart));
@@ -364,11 +364,36 @@
 	if (!strcmp(packet->name, "message"))
 		_jabber_parse_and_write_message_to_ui(packet, pb);
 	else if(!strcmp(packet->name, "iq"))
-		xep_iq_parse(packet, NULL, pb);
+		xep_iq_parse(packet, pb);
 	else
 		purple_debug_warning("bonjour", "Unknown packet: %s\n", packet->name ? packet->name : "(null)");
 }
 
+static void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
+
+	/* Inform the user that the conversation has been closed */
+	BonjourBuddy *bb = NULL;
+
+	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)");
+
+	if(bconv->pb != NULL)
+		bb = bconv->pb->proto_data;
+#if 0
+	if(bconv->pb != NULL) {
+		PurpleConversation *conv;
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account);
+		if (conv != NULL) {
+			char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name);
+			purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+			g_free(tmp);
+		}
+	}
+#endif
+	/* Close the socket, clear the watcher and free memory */
+	bonjour_jabber_close_conversation(bconv);
+	if(bb)
+		bb->conversation = NULL;
+}
 
 static void
 _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition)
@@ -414,35 +439,6 @@
 	bonjour_parser_process(bconv, message, message_length);
 }
 
-void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
-
-	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)");
-
-	/* Inform the user that the conversation has been closed */
-	if (bconv != NULL) {
-		BonjourBuddy *bb = NULL;
-
-		if(bconv->pb != NULL)
-			bb = bconv->pb->proto_data;
-#if 0
-		if(bconv->pb != NULL) {
-			PurpleConversation *conv;
-			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account);
-			if (conv != NULL) {
-				char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name);
-				purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
-				g_free(tmp);
-			}
-		}
-#endif
-		/* Close the socket, clear the watcher and free memory */
-		bonjour_jabber_close_conversation(bconv);
-		if(bb)
-			bb->conversation = NULL;
-	}
-}
-
-
 struct _stream_start_data {
 	char *msg;
 };
@@ -1162,14 +1158,12 @@
 check_if_blocked(PurpleBuddy *pb)
 {
 	gboolean blocked = FALSE;
-	GSList *l = NULL;
-	PurpleAccount *acc = NULL;
+	GSList *l;
+	PurpleAccount *acc = purple_buddy_get_account(pb);
 
-	if(pb == NULL)
+	if(acc == NULL)
 		return FALSE;
 
-	acc = pb->account;
-
 	for(l = acc->deny; l != NULL; l = l->next) {
 		if(!purple_utf8_strcasecmp(pb->name, (char *)l->data)) {
 			purple_debug_info("bonjour", "%s has been blocked by %s.\n", pb->name, acc->username);
@@ -1181,25 +1175,19 @@
 }
 
 static void
-xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb)
+xep_iq_parse(xmlnode *packet, PurpleBuddy *pb)
 {
-	xmlnode *child = NULL;
-
-	if(packet == NULL || pb == NULL)
-		return;
-
-	if(connection == NULL) {
-		if(pb->account != NULL)
-			connection = (pb->account)->gc;
-	}
+	xmlnode *child;
 
 	if(check_if_blocked(pb))
 		return;
 
 	if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "error")))
-		xep_si_parse(connection, packet, pb);
+		xep_si_parse(purple_account_get_connection(pb->account),
+			packet, pb);
 	else
-		xep_bytestreams_parse(connection, packet, pb);
+		xep_bytestreams_parse(purple_account_get_connection(pb->account),
+			packet, pb);
 }
 
 int
--- a/libpurple/protocols/bonjour/jabber.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Mon Dec 15 08:39:08 2008 +0000
@@ -79,8 +79,6 @@
 
 void bonjour_jabber_stream_started(BonjourJabberConversation *bconv);
 
-void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv);
-
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet);
 
 void bonjour_jabber_stop(BonjourJabber *data);
--- a/libpurple/protocols/jabber/auth.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/auth.c	Mon Dec 15 08:39:08 2008 +0000
@@ -613,9 +613,7 @@
 	} else if(!strcmp(type, "result")) {
 		query = xmlnode_get_child(packet, "query");
 		if(js->stream_id && xmlnode_get_child(query, "digest")) {
-			unsigned char hashval[20];
-			char *s, h[41], *p;
-			int i;
+			char *s, *hash;
 
 			iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
 			query = xmlnode_get_child(iq->node, "query");
@@ -626,14 +624,9 @@
 
 			x = xmlnode_new_child(query, "digest");
 			s = g_strdup_printf("%s%s", js->stream_id, pw);
-
-			purple_cipher_digest_region("sha1", (guchar *)s, strlen(s),
-									  sizeof(hashval), hashval, NULL);
-
-			p = h;
-			for(i=0; i<20; i++, p+=2)
-				snprintf(p, 3, "%02x", hashval[i]);
-			xmlnode_insert_data(x, h, -1);
+			hash = jabber_calculate_data_sha1sum(s, strlen(s));
+			xmlnode_insert_data(x, hash, -1);
+			g_free(hash);
 			g_free(s);
 			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
 			jabber_iq_send(iq);
--- a/libpurple/protocols/jabber/buddy.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Mon Dec 15 08:39:08 2008 +0000
@@ -19,7 +19,6 @@
  *
  */
 #include "internal.h"
-#include "cipher.h"
 #include "debug.h"
 #include "imgstore.h"
 #include "prpl.h"
@@ -451,9 +450,6 @@
 		gsize avatar_len;
 		xmlnode *photo, *binval, *type;
 		gchar *enc;
-		int i;
-		unsigned char hashval[20];
-		char *p, hash[41];
 
 		if(!vc_node) {
 			vc_node = xmlnode_new("vCard");
@@ -473,16 +469,7 @@
 		binval = xmlnode_new_child(photo, "BINVAL");
 		enc = purple_base64_encode(avatar_data, avatar_len);
 
-		purple_cipher_digest_region("sha1", avatar_data,
-								  avatar_len, sizeof(hashval),
-								  hashval, NULL);
-
-		purple_imgstore_unref(img);
-
-		p = hash;
-		for(i=0; i<20; i++, p+=2)
-			snprintf(p, 3, "%02x", hashval[i]);
-		js->avatar_hash = g_strdup(hash);
+		js->avatar_hash = jabber_calculate_data_sha1sum(avatar_data, avatar_len);
 
 		xmlnode_insert_data(binval, enc, -1);
 		g_free(enc);
@@ -545,19 +532,9 @@
 				char *lengthstring, *widthstring, *heightstring;
 				
 				/* compute the sha1 hash */
-				PurpleCipherContext *ctx;
-				unsigned char digest[20];
-				char *hash;
+				char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
 				char *base64avatar;
 				
-				ctx = purple_cipher_context_new_by_name("sha1", NULL);
-				purple_cipher_context_append(ctx, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
-				purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
-				purple_cipher_context_destroy(ctx);
-				
-				/* convert digest to a string */
-				hash = g_strdup_printf("%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x",digest[0],digest[1],digest[2],digest[3],digest[4],digest[5],digest[6],digest[7],digest[8],digest[9],digest[10],digest[11],digest[12],digest[13],digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]);
-				
 				publish = xmlnode_new("publish");
 				xmlnode_set_attrib(publish,"node",AVATARNAMESPACEDATA);
 				
@@ -1407,31 +1384,25 @@
 						(bintext = xmlnode_get_data(child))) {
 					gsize size;
 					guchar *data;
-					int i;
-					unsigned char hashval[20];
-					char *p, hash[41];
 					gboolean photo = (strcmp(child->name, "PHOTO") == 0);
 
 					data = purple_base64_decode(bintext, &size);
 					if (data) {
 						char *img_text;
+						char *hash;
 
 						jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_add_with_id(g_memdup(data, size), size, "logo.png")));
 						img_text = g_strdup_printf("<img id='%d'>", GPOINTER_TO_INT(jbi->vcard_imgids->data));
 
 						purple_notify_user_info_add_pair(user_info, (photo ? _("Photo") : _("Logo")), img_text);
 
-						purple_cipher_digest_region("sha1", (guchar *)data, size,
-								sizeof(hashval), hashval, NULL);
-						p = hash;
-						for(i=0; i<20; i++, p+=2)
-							snprintf(p, 3, "%02x", hashval[i]);
-
+						hash = jabber_calculate_data_sha1sum(data, size);
 						purple_buddy_icons_set_for_user(js->gc->account, bare_jid,
 								data, size, hash);
-						g_free(bintext);
+						g_free(hash);
 						g_free(img_text);
 					}
+					g_free(bintext);
 				}
 			}
 			g_free(text);
@@ -1525,9 +1496,12 @@
 		purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
 	} else {
 		xmlnode *info, *goodinfo = NULL;
-		
+		gboolean has_children = FALSE;
+
 		/* iterate over all info nodes to get one we can use */
 		for(info = metadata->child; info; info = info->next) {
+			if(info->type == XMLNODE_TYPE_TAG)
+				has_children = TRUE;
 			if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) {
 				const char *type = xmlnode_get_attrib(info,"type");
 				const char *id = xmlnode_get_attrib(info,"id");
@@ -1542,7 +1516,9 @@
 					goodinfo = info;
 			}
 		}
-		if(goodinfo) {
+		if(has_children == FALSE) {
+			purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
+		} else if(goodinfo) {
 			const char *url = xmlnode_get_attrib(goodinfo, "url");
 			const char *id = xmlnode_get_attrib(goodinfo,"id");
 			
--- a/libpurple/protocols/jabber/jabber.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Dec 15 08:39:08 2008 +0000
@@ -209,7 +209,7 @@
 		if (requested_resource != NULL) {
 			resource = xmlnode_new_child(bind, "resource");
 			xmlnode_insert_data(resource, requested_resource, -1);
-			free(requested_resource);
+			g_free(requested_resource);
 		}
 
 		jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL);
--- a/libpurple/protocols/jabber/jutil.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Mon Dec 15 08:39:08 2008 +0000
@@ -20,7 +20,9 @@
  */
 #include "internal.h"
 #include "account.h"
+#include "cipher.h"
 #include "conversation.h"
+#include "debug.h"
 #include "server.h"
 #include "util.h"
 #include "xmlnode.h"
@@ -236,3 +238,29 @@
 	return NULL;
 }
 
+/* The same as purple_util_get_image_checksum, but guaranteed to remain SHA1 */
+char *
+jabber_calculate_data_sha1sum(gconstpointer data, size_t len)
+{
+	PurpleCipherContext *context;
+	static gchar digest[41];
+
+	context = purple_cipher_context_new_by_name("sha1", NULL);
+	if (context == NULL)
+	{
+		purple_debug_error("jabber", "Could not find sha1 cipher\n");
+		g_return_val_if_reached(NULL);
+	}
+
+	/* Hash the data */
+	purple_cipher_context_append(context, data, len);
+	if (!purple_cipher_context_digest_to_str(context, sizeof(digest), digest, NULL))
+	{
+		purple_debug_error("jabber", "Failed to get SHA-1 digest.\n");
+		g_return_val_if_reached(NULL);
+	}
+	purple_cipher_context_destroy(context);
+
+	return g_strdup(digest);
+}
+
--- a/libpurple/protocols/jabber/jutil.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/jutil.h	Mon Dec 15 08:39:08 2008 +0000
@@ -42,4 +42,5 @@
 
 PurpleConversation *jabber_find_unnormalized_conv(const char *name, PurpleAccount *account);
 
+char *jabber_calculate_data_sha1sum(gconstpointer data, size_t len);
 #endif /* _PURPLE_JABBER_JUTIL_H_ */
--- a/libpurple/protocols/jabber/message.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/message.c	Mon Dec 15 08:39:08 2008 +0000
@@ -571,12 +571,39 @@
 
 	for(child = packet->child; child; child = child->next) {
 		const char *xmlns = xmlnode_get_namespace(child);
-		if(!xmlns)
-			xmlns = "";
 		if(child->type != XMLNODE_TYPE_TAG)
 			continue;
 
-		if(!strcmp(child->name, "subject") && !strcmp(xmlns,"jabber:client")) {
+		if(!strcmp(child->name, "error")) {
+			const char *code = xmlnode_get_attrib(child, "code");
+			char *code_txt = NULL;
+			char *text = xmlnode_get_data(child);
+			if (!text) {
+				xmlnode *enclosed_text_node;
+				
+				if ((enclosed_text_node = xmlnode_get_child(child, "text")))
+					text = xmlnode_get_data(enclosed_text_node);
+			}
+
+			if(code)
+				code_txt = g_strdup_printf(_("(Code %s)"), code);
+
+			if(!jm->error)
+				jm->error = g_strdup_printf("%s%s%s",
+						text ? text : "",
+						text && code_txt ? " " : "",
+						code_txt ? code_txt : "");
+
+			g_free(code_txt);
+			g_free(text);
+		} else if (xmlns == NULL) {
+			/* QuLogic: Not certain this is correct, but it would have happened
+			   with the previous code. */
+			if(!strcmp(child->name, "x"))
+				jm->etc = g_list_append(jm->etc, child);
+			/* The following tests expect xmlns != NULL */
+			continue;
+		} else if(!strcmp(child->name, "subject") && !strcmp(xmlns,"jabber:client")) {
 			if(!jm->subject)
 				jm->subject = xmlnode_get_data(child);
 		} else if(!strcmp(child->name, "thread") && !strcmp(xmlns,"jabber:client")) {
@@ -706,46 +733,24 @@
 				jm->eventitems = g_list_append(jm->eventitems, items);
 		} else if(!strcmp(child->name, "attention") && !strcmp(xmlns,"http://www.xmpp.org/extensions/xep-0224.html#ns")) {
 			jm->hasBuzz = TRUE;
-		} else if(!strcmp(child->name, "error")) {
-			const char *code = xmlnode_get_attrib(child, "code");
-			char *code_txt = NULL;
-			char *text = xmlnode_get_data(child);
-			if (!text) {
-				xmlnode *enclosed_text_node;
-				
-				if ((enclosed_text_node = xmlnode_get_child(child, "text")))
-					text = xmlnode_get_data(enclosed_text_node);
-			}
-
-			if(code)
-				code_txt = g_strdup_printf(_("(Code %s)"), code);
-
-			if(!jm->error)
-				jm->error = g_strdup_printf("%s%s%s",
-						text ? text : "",
-						text && code_txt ? " " : "",
-						code_txt ? code_txt : "");
-
-			g_free(code_txt);
-			g_free(text);
-		} else if(!strcmp(child->name, "delay") && xmlns && !strcmp(xmlns,"urn:xmpp:delay")) {
+		} else if(!strcmp(child->name, "delay") && !strcmp(xmlns,"urn:xmpp:delay")) {
 			const char *timestamp = xmlnode_get_attrib(child, "stamp");
 			jm->delayed = TRUE;
 			if(timestamp)
 				jm->sent = purple_str_to_time(timestamp, TRUE, NULL, NULL, NULL);
 		} else if(!strcmp(child->name, "x")) {
-			if(xmlns && !strcmp(xmlns, "jabber:x:event")) {
+			if(!strcmp(xmlns, "jabber:x:event")) {
 				if(xmlnode_get_child(child, "composing")) {
 					if(jm->chat_state == JM_STATE_ACTIVE)
 						jm->chat_state = JM_STATE_COMPOSING;
 					jm->typing_style |= JM_TS_JEP_0022;
 				}
-			} else if(xmlns && !strcmp(xmlns, "jabber:x:delay")) {
+			} else if(!strcmp(xmlns, "jabber:x:delay")) {
 				const char *timestamp = xmlnode_get_attrib(child, "stamp");
 				jm->delayed = TRUE;
 				if(timestamp)
 					jm->sent = purple_str_to_time(timestamp, TRUE, NULL, NULL, NULL);
-			} else if(xmlns && !strcmp(xmlns, "jabber:x:conference") &&
+			} else if(!strcmp(xmlns, "jabber:x:conference") &&
 					jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE &&
 					jm->type != JABBER_MESSAGE_ERROR) {
 				const char *jid = xmlnode_get_attrib(child, "jid");
@@ -754,8 +759,7 @@
 					g_free(jm->to);
 					jm->to = g_strdup(jid);
 				}
-			} else if(xmlns && !strcmp(xmlns,
-						"http://jabber.org/protocol/muc#user") &&
+			} else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user") &&
 					jm->type != JABBER_MESSAGE_ERROR) {
 				xmlnode *invite = xmlnode_get_child(child, "invite");
 				if(invite) {
--- a/libpurple/protocols/jabber/presence.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/presence.c	Mon Dec 15 08:39:08 2008 +0000
@@ -21,7 +21,6 @@
 #include "internal.h"
 
 #include "account.h"
-#include "cipher.h"
 #include "conversation.h"
 #include "debug.h"
 #include "notify.h"
@@ -353,19 +352,12 @@
 				(( (binval = xmlnode_get_child(photo, "BINVAL")) &&
 				(text = xmlnode_get_data(binval))) ||
 				(text = xmlnode_get_data(photo)))) {
-			unsigned char hashval[20];
-			char hash[41], *p;
-			int i;
+			char *hash;
 
 			data = purple_base64_decode(text, &size);
-
-			purple_cipher_digest_region("sha1", data, size,
-					sizeof(hashval), hashval, NULL);
-			p = hash;
-			for(i=0; i<20; i++, p+=2)
-				snprintf(p, 3, "%02x", hashval[i]);
-
+			hash = jabber_calculate_data_sha1sum(data, size);
 			purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash);
+			g_free(hash);
 			g_free(text);
 		}
 	}
@@ -514,17 +506,19 @@
 				priority = atoi(p);
 				g_free(p);
 			}
+		} else if(xmlns == NULL) {
+			/* The rest of the cases used to check xmlns individually. */
+			continue;
 		} else if(!strcmp(y->name, "delay") && !strcmp(xmlns, "urn:xmpp:delay")) {
 			/* XXX: compare the time.  jabber:x:delay can happen on presence packets that aren't really and truly delayed */
 			delayed = TRUE;
-		} else if(xmlns && !strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) {
+		} else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) {
 			caps = y; /* store for later, when creating buddy resource */
 		} else if(!strcmp(y->name, "x")) {
-			const char *xmlns = xmlnode_get_namespace(y);
-			if(xmlns && !strcmp(xmlns, "jabber:x:delay")) {
+			if(!strcmp(xmlns, "jabber:x:delay")) {
 				/* XXX: compare the time.  jabber:x:delay can happen on presence packets that aren't really and truly delayed */
 				delayed = TRUE;
-			} else if(xmlns && !strcmp(xmlns, "http://jabber.org/protocol/muc#user")) {
+			} else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) {
 				xmlnode *z;
 
 				muc = TRUE;
@@ -567,7 +561,7 @@
 							flags |= PURPLE_CBFLAGS_VOICE;
 					}
 				}
-			} else if(xmlns && !strcmp(xmlns, "vcard-temp:x:update")) {
+			} else if(!strcmp(xmlns, "vcard-temp:x:update")) {
 				xmlnode *photo = xmlnode_get_child(y, "photo");
 				if(photo) {
 					g_free(avatar_hash);
--- a/libpurple/protocols/jabber/si.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/jabber/si.c	Mon Dec 15 08:39:08 2008 +0000
@@ -23,7 +23,6 @@
 #include "internal.h"
 
 #include "blist.h"
-#include "cipher.h"
 #include "debug.h"
 #include "ft.h"
 #include "request.h"
@@ -183,9 +182,6 @@
 {
 	JabberSIXfer *jsx = xfer->data;
 	JabberBytestreamsStreamhost *streamhost;
-	char *dstaddr, *p;
-	int i;
-	unsigned char hashval[20];
 	JabberID *dstjid;
 
 	if(!jsx->streamhosts) {
@@ -221,6 +217,7 @@
 	/* TODO: Deal with zeroconf */
 
 	if(dstjid != NULL && streamhost->host && streamhost->port > 0) {
+		char *dstaddr, *hash;
 		jsx->gpi = purple_proxy_info_new();
 		purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
 		purple_proxy_info_set_host(jsx->gpi, streamhost->host);
@@ -234,17 +231,13 @@
 			dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource,
 				jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
 
-		purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
-				sizeof(hashval), hashval, NULL);
-		g_free(dstaddr);
-		dstaddr = g_malloc(41);
-		p = dstaddr;
-		for(i=0; i<20; i++, p+=2)
-			snprintf(p, 3, "%02x", hashval[i]);
+		/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
+		hash = jabber_calculate_data_sha1sum(dstaddr, strlen(dstaddr));
 
 		jsx->connect_data = purple_proxy_connect_socks5(NULL, jsx->gpi,
-				dstaddr, 0,
+				hash, 0,
 				jabber_si_bytestreams_connect_cb, xfer);
+		g_free(hash);
 		g_free(dstaddr);
 
 		/* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
@@ -361,11 +354,9 @@
 {
 	PurpleXfer *xfer = data;
 	JabberSIXfer *jsx = xfer->data;
-	int i;
 	char buffer[256];
 	int len;
-	char *dstaddr, *p;
-	unsigned char hashval[20];
+	char *dstaddr, *hash;
 	const char *host;
 
 	purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");
@@ -421,23 +412,20 @@
 			jsx->js->user->node, jsx->js->user->domain,
 			jsx->js->user->resource, xfer->who);
 
-	purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
-				    sizeof(hashval), hashval, NULL);
-	g_free(dstaddr);
-	dstaddr = g_malloc(41);
-	p = dstaddr;
-	for(i=0; i<20; i++, p+=2)
-		snprintf(p, 3, "%02x", hashval[i]);
+	/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
+	hash = jabber_calculate_data_sha1sum(dstaddr, strlen(dstaddr));
 
-	if(jsx->rxqueue[4] != 40 || strncmp(dstaddr, jsx->rxqueue+5, 40) ||
+	if(jsx->rxqueue[4] != 40 || strncmp(hash, jsx->rxqueue+5, 40) ||
 			jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
 		purple_debug_error("jabber", "someone connected with the wrong info!\n");
 		close(source);
 		purple_xfer_cancel_remote(xfer);
+		g_free(hash);
 		g_free(dstaddr);
 		return;
 	}
 
+	g_free(hash);
 	g_free(dstaddr);
 
 	g_free(jsx->rxqueue);
--- a/libpurple/protocols/msn/contact.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/contact.c	Mon Dec 15 08:39:08 2008 +0000
@@ -176,28 +176,6 @@
 	return 0;
 }
 
-/* get Network */
-/* QuLogic: These names don't really refer to the MsnNetwork,
- *          but I haven't yet written the code to properly use them.
- */
-static MsnNetwork
-msn_get_network(char *type)
-{
-	g_return_val_if_fail(type != NULL, 0);
-
-	if (!strcmp(type,"Regular")) {
-		return MSN_NETWORK_PASSPORT;
-	}
-	if (!strcmp(type,"Live")) {
-		return MSN_NETWORK_PASSPORT;
-	}
-	if (!strcmp(type,"LivePending")) {
-		return MSN_NETWORK_PASSPORT;
-	}
-
-	return MSN_NETWORK_UNKNOWN;
-}
-
 /* Create the AddressBook in the server, if we don't have one */
 static void
 msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
@@ -245,9 +223,31 @@
 	char *type = xmlnode_get_data(xmlnode_get_child(member, "Type"));
 	char *member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId"));
 	MsnUser *user = msn_userlist_find_add_user(session->userlist, passport, NULL);
+	xmlnode *annotation;
+	guint nid = MSN_NETWORK_UNKNOWN;
 
-	purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s\n",
-		node, passport, type, member_id == NULL ? "(null)" : member_id);
+	/* For EmailMembers, the network must be found in the annotations. */
+	if (!strcmp(node, "PassportName")) {
+		nid = MSN_NETWORK_PASSPORT;
+	} else {
+		for (annotation = xmlnode_get_child(member, "Annotations/Annotation");
+		     annotation;
+		     annotation = xmlnode_get_next_twin(annotation)) {
+			char *name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
+			if (name && !strcmp(name, "MSN.IM.BuddyType")) {
+				char *value = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+				if (value != NULL)
+					nid = strtoul(value, NULL, 10);
+				g_free(value);
+			}
+			g_free(name);
+		}
+	}
+ 
+	purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s, NetworkID: %u\n",
+		node, passport, type, member_id == NULL ? "(null)" : member_id, nid);
+
+	msn_user_set_network(user, nid);
 
 	if (member_id) {
 		user->membership_id[list] = atoi(member_id);
@@ -445,16 +445,16 @@
 		if ((groupInfo = xmlnode_get_child(group, "groupInfo")) && (groupname = xmlnode_get_child(groupInfo, "name")))
 			group_name = xmlnode_get_data(groupname);
 
-		msn_group_new(session->userlist, group_id, group_name);
-
-		if (group_id == NULL){
+		if (group_id == NULL) {
 			/* Group of ungroupped buddies */
 			g_free(group_name);
 			continue;
 		}
 
+		msn_group_new(session->userlist, group_id, group_name);
+
 		purple_debug_info("msn", "AB group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)");
-		if ((purple_find_group(group_name)) == NULL){
+		if ((purple_find_group(group_name)) == NULL) {
 			PurpleGroup *g = purple_group_new(group_name);
 			purple_blist_add_group(g, NULL);
 		}
@@ -528,7 +528,6 @@
 		xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user;
 		xmlnode *annotation;
 		MsnUser *user;
-		MsnNetwork networkId;
 
 		if (!(contactId = xmlnode_get_child(contactNode,"contactId"))
 				|| !(contactInfo = xmlnode_get_child(contactNode, "contactInfo"))
@@ -569,7 +568,6 @@
 			g_free(is_messenger_user);
 		}
 
-		networkId = msn_get_network(type);
 		passportName = xmlnode_get_child(contactInfo, "passportName");
 		if (passportName == NULL) {
 			xmlnode *emailsNode, *contactEmailNode, *emailNode;
@@ -600,7 +598,6 @@
 				if (msnEnabled && !strcmp(msnEnabled, "true")) {
 					/*Messenger enabled, Get the Passport*/
 					purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)");
-					networkId = MSN_NETWORK_YAHOO;
 					g_free(msnEnabled);
 					break;
 				} else {
@@ -628,6 +625,15 @@
 			name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
 			if (!strcmp(name, "AB.NickName"))
 				alias = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+			else if (!strcmp(name, "MSN.IM.HasSharedFolder"))
+				; /* Do nothing yet... */
+			else if (!strcmp(name, "AB.Spouse"))
+				; /* Do nothing yet... */
+			else if (!strcmp(name, "MSN.Mobile.ContactId"))
+				; /* Do nothing yet... */
+			else
+				purple_debug_info("msn",
+				                  "Unknown AB contact annotation: %s\n", name);
 			g_free(name);
 		}
 
@@ -639,7 +645,6 @@
 
 		user = msn_userlist_find_add_user(session->userlist, passport, Name);
 		msn_user_set_uid(user, uid);
-		msn_user_set_network(user, networkId);
 		msn_user_set_mobile_phone(user, mobile_number);
 
 		groupIds = xmlnode_get_child(contactInfo, "groupIds");
@@ -870,7 +875,7 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
-	xmlnode *faultcode;
+	xmlnode *fault;
 	char *faultcode_str;
 
 	if (resp == NULL) {
@@ -880,9 +885,9 @@
 		return;
 	}
 
-	faultcode = xmlnode_get_child(resp->xml, "Body/Fault/faultcode");
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
 
-	if (faultcode == NULL) {
+	if (fault == NULL) {
 		/* No errors */
 		if (state->cb)
 			((MsnSoapCallback)state->cb)(req, resp, data);
@@ -890,7 +895,7 @@
 		return;
 	}
 
-	faultcode_str = xmlnode_get_data(faultcode);
+	faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode"));
 
 	if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
 		purple_debug_info("msn",
@@ -903,12 +908,15 @@
 	}
 	else
 	{
-		/* We don't know how to respond to this faultcode, so just log it */
-		/* XXX: Probably should notify the user or undo the change or something? */
-		char *str = xmlnode_to_str(xmlnode_get_child(resp->xml, "Body/Fault"), NULL);
-		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
-		                   msn_contact_operation_str(state->action), str);
-		g_free(str);
+		if (state->cb) {
+			((MsnSoapCallback)state->cb)(req, resp, data);
+		} else {
+			/* We don't know how to respond to this faultcode, so log it */
+			char *str = xmlnode_to_str(fault, NULL);
+			purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+			                   msn_contact_operation_str(state->action), str);
+			g_free(str);
+		}
 		msn_callback_state_free(state);
 	}
 
@@ -943,21 +951,44 @@
 	MsnUser *user;
 	xmlnode *guid;
 
+	xmlnode *fault;
+
 	g_return_if_fail(session != NULL);
+	userlist = session->userlist;
+
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode"));
+		if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) {
+			/* Do something special! */
+			purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n");
 
-	userlist = session->userlist;
+		} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
+			PurpleBuddy *buddy = purple_find_buddy(session->account, state->who);
+			char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who);
+			purple_notify_error(state->session, _("Buddy Add error"), str,
+			                    _("The username specified does not exist."));
+			g_free(str);
+			msn_userlist_rem_buddy(userlist, state->who);
+			if (buddy != NULL)
+				purple_blist_remove_buddy(buddy);
+
+		} else {
+			/* We don't know how to respond to this faultcode, so log it */
+			char *fault_str = xmlnode_to_str(fault, NULL);
+			if (fault_str != NULL) {
+				purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+				                   msn_contact_operation_str(state->action), fault_str);
+				g_free(fault_str);
+			}
+		}
+		return;
+	}
 
 	purple_debug_info("msn", "Contact added successfully\n");
 
-	/* the code this block is replacing didn't send ADL for yahoo contacts,
-	 * but i haven't confirmed this is WLM's behaviour wrt yahoo contacts
-	 */
-	if ( !msn_user_is_yahoo(session->account, state->who) ) {
-		msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
-		msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
-	}
-
-	msn_notification_send_fqy(session, state->who);
+	msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
+	msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
 
 	user = msn_userlist_find_add_user(userlist, state->who, state->who);
 	msn_user_add_group_id(user, state->guid);
@@ -976,6 +1007,7 @@
 void
 msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport)
 {
+	MsnUser *user;
 	gchar *body = NULL;
 	gchar *contact_xml = NULL;
 
@@ -993,7 +1025,21 @@
 
 	purple_debug_info("msn", "Adding contact %s to contact list\n", passport);
 
-	contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+	user = msn_userlist_find_user(session->userlist, passport);
+	if (user == NULL) {
+		purple_debug_warning("msn", "Unable to retrieve user %s from the userlist!\n", passport);
+		return; /* guess this never happened! */
+	}
+
+	if (user->networkid != MSN_NETWORK_PASSPORT) {
+		contact_xml = g_strdup_printf(MSN_CONTACT_EMAIL_XML,
+		                              user->networkid == MSN_NETWORK_YAHOO ?
+		                                  "Messenger2" :
+		                                  "Messenger3",
+		                              passport, 0);
+	} else {
+		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+	}
 	body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml);
 
 	state->body = xmlnode_from_str(body, -1);
@@ -1011,11 +1057,41 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
+	MsnSession *session = state->session;
 	MsnUserList *userlist;
+	xmlnode *fault;
+
+	g_return_if_fail(session != NULL);
+	userlist = session->userlist;
+
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode"));
+		if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) {
+			/* Do something special! */
+			purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n");
 
-	g_return_if_fail(data != NULL);
+		} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
+			PurpleBuddy *buddy = purple_find_buddy(session->account, state->who);
+			char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who);
+			purple_notify_error(session, _("Buddy Add error"), str,
+			                    _("The username specified does not exist."));
+			g_free(str);
+			msn_userlist_rem_buddy(userlist, state->who);
+			if (buddy != NULL)
+				purple_blist_remove_buddy(buddy);
 
-	userlist = state->session->userlist;
+		} else {
+			/* We don't know how to respond to this faultcode, so log it */
+			char *fault_str = xmlnode_to_str(fault, NULL);
+			if (fault_str != NULL) {
+				purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+				                   msn_contact_operation_str(state->action), fault_str);
+				g_free(fault_str);
+			}
+		}
+		return;
+	}
 
 	if (msn_userlist_add_buddy_to_group(userlist, state->who,
 			state->new_group_name)) {
@@ -1036,11 +1112,8 @@
 			g_free(uid);
 		}
 
-		if ( !msn_user_is_yahoo(state->session->account, state->who) ) {
-			msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
-			msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
-		}
-		msn_notification_send_fqy(state->session, state->who);
+		msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
+		msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
 
 		if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
 			msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL);
@@ -1095,8 +1168,14 @@
 		return; /* guess this never happened! */
 	}
 
-	if (user != NULL && user->uid != NULL) {
+	if (user->uid != NULL) {
 		contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
+	} else if (user->networkid != MSN_NETWORK_PASSPORT) {
+		contact_xml = g_strdup_printf(MSN_CONTACT_EMAIL_XML,
+		                              user->networkid == MSN_NETWORK_YAHOO ?
+		                                  "Messenger2" :
+		                                  "Messenger3",
+		                              passport, 0);
 	} else {
 		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
 	}
@@ -1120,6 +1199,17 @@
 	MsnCallbackState *state = data;
 	MsnUserList *userlist = state->session->userlist;
 	MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid);
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	purple_debug_info("msn", "Delete contact successful\n");
 
@@ -1140,8 +1230,8 @@
 		contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
 		purple_debug_info("msn", "Deleting contact with contactId: %s\n", user->uid);
 	} else {
-		contact_id_xml = g_strdup_printf(MSN_CONTACT_XML, user->passport);
-		purple_debug_info("msn", "Deleting contact with passport: %s\n", user->passport);
+		purple_debug_info("msn", "Unable to delete contact %s without a ContactId\n", user->passport);
+		return;
 	}
 
 	state = msn_callback_state_new(session);
@@ -1165,6 +1255,17 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	if (msn_userlist_rem_buddy_from_group(state->session->userlist,
 			state->who, state->old_group_name)) {
@@ -1235,6 +1336,19 @@
 msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
 	gpointer data)
 {
+	MsnCallbackState *state = (MsnCallbackState *)data;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
+
 	purple_debug_info("msn", "Contact updated successfully\n");
 }
 
@@ -1312,6 +1426,17 @@
 {
 	MsnCallbackState *state = data;
 	MsnSession *session = state->session;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	purple_debug_info("msn", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
 
@@ -1339,10 +1464,13 @@
 			  const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
+	const char *type = "PassportMember";
+	gchar *federate = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 	MsnUser *user;
 
 	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->userlist != NULL);
 	g_return_if_fail(passport != NULL);
 	g_return_if_fail(list < 5);
 
@@ -1354,21 +1482,27 @@
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
 
+	user = msn_userlist_find_user(session->userlist, passport);
+	if (user && user->networkid != MSN_NETWORK_PASSPORT) {
+		type = "EmailMember";
+		federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML,
+		                           user->networkid);
+	}
+	
 	if (list == MSN_LIST_PL) {
-		g_return_if_fail(session->userlist != NULL);
-
-		user = msn_userlist_find_user(session->userlist, passport);
-
 		partner_scenario = MSN_PS_CONTACT_API;
-		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]);
+		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
+		                         type, user->membership_id[MSN_LIST_PL],
+		                         federate ? federate : "");
 	} else {
 		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
 		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
-		
-		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+		                         type, passport,
+		                         federate ? federate : "");
 	}
 
-	body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE,
+	body = g_strdup_printf(MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE,
 		MsnSoapPartnerScenarioText[partner_scenario],
 		MsnMemberRole[list], member);
 
@@ -1378,6 +1512,7 @@
 	state->cb = msn_del_contact_from_list_read_cb;
 	msn_contact_request(state);
 
+	g_free(federate);
 	g_free(member);
 	g_free(body);
 }
@@ -1387,8 +1522,18 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
+	xmlnode *fault;
 
-	g_return_if_fail(state != NULL);
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
+
 	g_return_if_fail(state->session != NULL);
 
 	purple_debug_info("msn", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
@@ -1415,7 +1560,10 @@
 			const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
+	const char *type = "PassportMember";
+	gchar *federate = NULL;
 	MsnSoapPartnerScenario partner_scenario;
+	MsnUser *user;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(passport != NULL);
@@ -1429,9 +1577,16 @@
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
 
-	partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
+	user = msn_userlist_find_user(session->userlist, passport);
+	if (user && user->networkid != MSN_NETWORK_PASSPORT) {
+		type = "EmailMember";
+		federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML,
+		                           user->networkid);
+	}
 
-	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, state->who);
+	partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
+	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+	                         type, state->who, federate ? federate : "");
 
 	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
 		MsnSoapPartnerScenarioText[partner_scenario],
@@ -1443,6 +1598,7 @@
 	state->cb = msn_add_contact_to_list_read_cb;
 	msn_contact_request(state);
 
+	g_free(federate);
 	g_free(member);
 	g_free(body);
 }
@@ -1482,6 +1638,17 @@
 	MsnCallbackState *state = data;
 	MsnSession *session;
 	MsnUserList *userlist;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	purple_debug_info("msn", "Group request successful.\n");
 
@@ -1661,3 +1828,4 @@
 	g_free(escaped_group_name);
 	g_free(body);
 }
+
--- a/libpurple/protocols/msn/contact.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/contact.h	Mon Dec 15 08:39:08 2008 +0000
@@ -211,6 +211,27 @@
 		"</contactInfo>"\
 	"</Contact>"
 
+#define MSN_CONTACT_ID_XML \
+	"<Contact>"\
+		"<contactId>%s</contactId>"\
+	"</Contact>"
+
+#define MSN_CONTACT_EMAIL_XML \
+	"<Contact>"\
+		"<contactInfo>"\
+			"<emails>"\
+				"<ContactEmail>"\
+					"<contactEmailType>%s</contactEmailType>"\
+					"<email>%s</email>"\
+					"<isMessengerEnabled>true</isMessengerEnabled>"\
+					"<Capability>%d</Capability>"\
+					"<MessengerEnabledExternally>false</MessengerEnabledExternally>"\
+					"<propertiesChanged/>"\
+				"</ContactEmail>"\
+			"</emails>"\
+		"</contactInfo>"\
+	"</Contact>"
+
 #define MSN_ADD_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
 	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
@@ -275,7 +296,6 @@
 
 /* Delete a contact from the Contact List */
 #define MSN_CONTACT_DEL_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactDelete"
-#define MSN_CONTACT_ID_XML		"<Contact><contactId>%s</contactId></Contact>"
 #define MSN_DEL_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
 "<soap:Envelope"\
 	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
@@ -376,22 +396,32 @@
 #define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/DeleteMember"
 
 #define MSN_MEMBER_PASSPORT_XML	\
-	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\
 		"<Type>Passport</Type>"\
 		"<State>Accepted</State>"\
 		"<PassportName>%s</PassportName>"\
+		"%s"\
 	"</Member>"
 
 #define MSN_MEMBER_MEMBERSHIPID_XML	\
-	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\
 		"<Type>Passport</Type>"\
 		"<MembershipId>%u</MembershipId>"\
 		"<State>Accepted</State>"\
+		"%s"\
 	"</Member>"
 
+#define MSN_MEMBER_FEDERATED_ANNOTATION_XML \
+	"<Annotations>"\
+		"<Annotation>"\
+			"<Name>MSN.IM.BuddyType</Name>"\
+			"<Value>%02d:</Value>"\
+		"</Annotation>"\
+	"</Annotations>"
+
 /* first delete contact from allow list */
 
-#define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+#define MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
 "<soap:Envelope"\
 	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
 	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
@@ -586,7 +616,7 @@
 	MSN_ADD_GROUP       = 0x10,
 	MSN_DEL_GROUP       = 0x20,
 	MSN_RENAME_GROUP    = 0x40,
-	MSN_UPDATE_INFO     = 0x80,
+	MSN_UPDATE_INFO     = 0x80
 } MsnCallbackAction;
 
 typedef struct _MsnCallbackState MsnCallbackState;
--- a/libpurple/protocols/msn/msg.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/msg.c	Mon Dec 15 08:39:08 2008 +0000
@@ -231,6 +231,25 @@
 	{
 		const char *key, *value;
 
+		/* If this line starts with whitespace, it's been folded from the
+		   previous line and won't have ':'. */
+		if ((**cur == ' ') || (**cur == '\t')) {
+			tokens = g_strsplit(g_strchug(*cur), "=\"", 2);
+			key = tokens[0];
+			value = tokens[1];
+
+			/* The only one I care about is 'boundary' (which is folded from
+			   the key 'Content-Type'), so only process that. */
+			if (!strcmp(key, "boundary")) {
+				char *end = strchr(value, '\"');
+				*end = '\0';
+				msn_message_set_attr(msg, key, value);
+			}
+
+			g_strfreev(tokens);
+			continue;
+		}
+
 		tokens = g_strsplit(*cur, ": ", 2);
 
 		key = tokens[0];
--- a/libpurple/protocols/msn/msn.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Mon Dec 15 08:39:08 2008 +0000
@@ -647,25 +647,41 @@
 	presence = purple_buddy_get_presence(buddy);
 	status = purple_presence_get_active_status(presence);
 
-	/* I think status message should take precedence over media */
-	msg = purple_status_get_attr_string(status, "message");
-	if (msg && *msg)
-		return g_markup_escape_text(msg, -1);
-
 	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
-		const char *title, *artist;
+		const char *title, *game, *office;
 		char *media, *esc;
 		status = purple_presence_get_status(presence, "tune");
 		title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
-		artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
-
-		media = g_strdup_printf("%s%s%s", title, artist ? " - " : "",
-				artist ? artist : "");
+
+		game = purple_status_get_attr_string(status, "game");
+		office = purple_status_get_attr_string(status, "office");
+
+		if (title && *title) {
+			const char *artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+			const char *album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+			media = g_strdup_printf("%s%s%s%s%s%s", title,
+			                        (artist && *artist) ? " - " : "",
+			                        (artist && *artist) ? artist : "",
+			                        (album && *album) ? " (" : "",
+			                        (album && *album) ? album : "",
+			                        (album && *album) ? ")" : "");
+		}
+		else if (game && *game)
+			media = g_strdup_printf("Playing %s", game);
+		else if (office && *office)
+			media = g_strdup_printf("Editing %s", office);
+		else
+			return NULL;
 		esc = g_markup_escape_text(media, -1);
 		g_free(media);
 		return esc;
 	}
 
+	/* Official client says media takes precedence over message */
+	msg = purple_status_get_attr_string(status, "message");
+	if (msg && *msg)
+		return g_markup_escape_text(msg, -1);
+
 	return NULL;
 }
 
@@ -681,6 +697,7 @@
 	if (purple_presence_is_online(presence))
 	{
 		const char *psm, *name;
+		const char *mediatype = NULL;
 		char *currentmedia = NULL;
 		char *tmp;
 
@@ -688,10 +705,20 @@
 		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
 			PurpleStatus *tune = purple_presence_get_status(presence, "tune");
 			const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
-			const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
-			const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
-			currentmedia = purple_util_format_song_info(title, artist, album, NULL);
-			/* We could probably just use user->media.title etc. here */
+			const char *game = purple_status_get_attr_string(tune, "game");
+			const char *office = purple_status_get_attr_string(tune, "office");
+			if (title && *title) {
+				const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
+				const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
+				mediatype = _("Now Listening");
+				currentmedia = purple_util_format_song_info(title, artist, album, NULL);
+			} else if (game && *game) {
+				mediatype = _("Playing a game");
+				currentmedia = g_strdup(game);
+			} else if (office && *office) {
+				mediatype = _("Working");
+				currentmedia = g_strdup(office);
+			}
 		}
 
 		if (!purple_status_is_available(status)) {
@@ -745,7 +772,7 @@
 		}
 
 		if (currentmedia) {
-			purple_notify_user_info_add_pair(user_info, _("Now Listening"), currentmedia);
+			purple_notify_user_info_add_pair(user_info, mediatype, currentmedia);
 			g_free(currentmedia);
 		}
 	}
@@ -840,6 +867,8 @@
 			PURPLE_TUNE_ARTIST, _("Artist"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_ALBUM, _("Album"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_TITLE, _("Title"), purple_value_new(PURPLE_TYPE_STRING),
+			"game", _("Game Title"), purple_value_new(PURPLE_TYPE_STRING),
+			"office", _("Office Title"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
 	types = g_list_append(types, status);
 
@@ -1389,6 +1418,7 @@
 	MsnSession *session;
 	MsnUserList *userlist;
 	const char *who;
+	MsnUser *user;
 
 	session = gc->proto_data;
 	userlist = session->userlist;
@@ -1414,21 +1444,19 @@
 		purple_debug_info("msn", "msn_add_buddy: %s\n", who);
 #endif
 
-#if 0
-	/* Which is the max? */
-	if (session->fl_users_count >= 150)
-	{
-		purple_debug_info("msn", "Too many buddies\n");
-		/* Buddy list full */
-		/* TODO: purple should be notified of this */
-		return;
-	}
-#endif
-
 	/* XXX - Would group ever be NULL here?  I don't think so...
 	 * shx: Yes it should; MSN handles non-grouped buddies, and this is only
 	 * internal. */
-	msn_userlist_add_buddy(userlist, who, group ? group->name : NULL);
+	user = msn_userlist_find_user(userlist, who);
+	if ((user != NULL) && (user->networkid != MSN_NETWORK_UNKNOWN)) {
+		/* We already know this buddy and their network. This function knows
+		   what to do with users already in the list and stuff... */
+		msn_userlist_add_buddy(userlist, who, group ? group->name : NULL);
+	} else {
+		/* We need to check the network for this buddy first */
+		msn_userlist_save_pending_buddy(userlist, who, group ? group->name : NULL);
+		msn_notification_send_fqy(session, who);
+	}
 }
 
 static void
@@ -1811,7 +1839,7 @@
 		if (b->server_alias)
 		{
 			char *nicktext = g_markup_escape_text(b->server_alias, -1);
-			tmp = g_strdup_printf("<font sml=\"msn\">%s</font><br>", nicktext);
+			tmp = g_strdup_printf("<font sml=\"msn\">%s</font>", nicktext);
 			purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
 			g_free(tmp);
 			g_free(nicktext);
@@ -1917,9 +1945,8 @@
 
 	if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0)
 	{
-		tmp = g_strdup_printf("<b>%s</b>", _("Error retrieving profile"));
-		purple_notify_user_info_add_pair(user_info, NULL, tmp);
-		g_free(tmp);
+		purple_notify_user_info_add_pair(user_info,
+				_("Error retrieving profile"), NULL);
 
 		purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
 		purple_notify_user_info_destroy(user_info);
@@ -2260,21 +2287,24 @@
 		char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http&#58;&#47;&#47;spaces.live.com&#47;profile.aspx&#63;cid&#61;0\"");
 		PurpleBuddy *b = purple_find_buddy
 				(purple_connection_get_account(info_data->gc), info_data->name);
-		purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"),
-									   ((p && b) ? _("The user has not created a public profile.") :
-										(p ? _("MSN reported not being able to find the user's profile. "
-											   "This either means that the user does not exist, "
-											   "or that the user exists "
-											   "but has not created a public profile.") :
-										 _("Could not find "	/* This should never happen */
-										   "any information in the user's profile. "
-										   "The user most likely does not exist."))));
+		purple_notify_user_info_add_pair(user_info,
+				_("Error retrieving profile"), NULL);
+		purple_notify_user_info_add_pair(user_info, NULL,
+				((p && b) ? _("The user has not created a public profile.") :
+					(p ? _("MSN reported not being able to find the user's profile. "
+							"This either means that the user does not exist, "
+							"or that the user exists "
+							"but has not created a public profile.") :
+						_("Could not find "	/* This should never happen */
+							"any information in the user's profile. "
+							"The user most likely does not exist."))));
 	}
 
 	/* put a link to the actual profile URL */
-	tmp = g_strdup_printf("<a href=\"%s%s\">%s%s</a>",
-					PROFILE_URL, info_data->name, PROFILE_URL, info_data->name);
-	purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp);
+	purple_notify_user_info_add_section_break(user_info);
+	tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>",
+			PROFILE_URL, info_data->name, _("View web profile"));
+	purple_notify_user_info_add_pair(user_info, NULL, tmp);
 	g_free(tmp);
 
 #if PHOTO_SUPPORT
--- a/libpurple/protocols/msn/notification.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/notification.c	Mon Dec 15 08:39:08 2008 +0000
@@ -849,10 +849,35 @@
 fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
 			 size_t len)
 {
-	purple_debug_info("msn", "FQY payload:\n%s\n", payload);
-	g_return_if_fail(cmdproc->session != NULL);
-/*	msn_notification_post_adl(cmdproc, payload, len); */
-/*	msn_get_address_book(cmdproc->session, MSN_AB_SAVE_CONTACT, NULL, NULL); */
+	MsnUserList *userlist;
+	xmlnode *ml, *d, *c;
+	const char *domain;
+	const char *local;
+	const char *type;
+	char *passport;
+	MsnNetwork network = MSN_NETWORK_PASSPORT;
+
+	userlist = cmdproc->session->userlist;
+
+	/* FQY response:
+	    <ml><d n="domain.com"><c n="local-node" t="network" /></d></ml> */
+	ml = xmlnode_from_str(payload, len);
+	d = xmlnode_get_child(ml, "d");
+	c = xmlnode_get_child(d, "c");
+	domain = xmlnode_get_attrib(d, "n");
+	local = xmlnode_get_attrib(c, "n");
+	type = xmlnode_get_attrib(c, "t");
+
+	passport = g_strdup_printf("%s@%s", local, domain);
+
+	if (type != NULL)
+		network = (MsnNetwork)strtoul(type, NULL, 10);
+	purple_debug_info("msn", "FQY response says %s is from network %d\n",
+	                  passport, network);
+	msn_userlist_add_pending_buddy(userlist, passport, network);
+
+	g_free(passport);
+	xmlnode_free(ml);
 }
 
 static void
@@ -1578,7 +1603,7 @@
 	MsnUser *user;
 	const char *passport;
 	char *psm_str, *str;
-	CurrentMedia media = {NULL, NULL, NULL};
+	CurrentMedia media = {CURRENT_MEDIA_UNKNOWN, NULL, NULL, NULL};
 
 	session = cmdproc->session;
 	account = session->account;
@@ -1933,7 +1958,7 @@
 
 void
 msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list_id,
-							  const char *who)
+							  MsnUser *user)
 {
 	MsnCmdProc *cmdproc;
 	MsnListOp list_op = 1 << list_id;
@@ -1946,8 +1971,8 @@
 	adl_node = xmlnode_new("ml");
 	adl_node->child = NULL;
 
-	msn_add_contact_xml(notification->session, adl_node, who, list_op,
-						MSN_NETWORK_PASSPORT);
+	msn_add_contact_xml(notification->session, adl_node, user->passport,
+	                    list_op, user->networkid);
 
 	payload = xmlnode_to_str(adl_node,&payload_len);
 	xmlnode_free(adl_node);
@@ -1959,7 +1984,7 @@
 
 void
 msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId list_id,
-						   const char *who)
+						   MsnUser *user)
 {
 	MsnCmdProc *cmdproc;
 	MsnTransaction *trans;
@@ -1973,7 +1998,8 @@
 	rml_node = xmlnode_new("ml");
 	rml_node->child = NULL;
 
-	msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_NETWORK_PASSPORT);
+	msn_add_contact_xml(notification->session, rml_node, user->passport,
+	                    list_op, user->networkid);
 
 	payload = xmlnode_to_str(rml_node, &payload_len);
 	xmlnode_free(rml_node);
--- a/libpurple/protocols/msn/notification.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/notification.h	Mon Dec 15 08:39:08 2008 +0000
@@ -42,6 +42,7 @@
 #include "session.h"
 #include "servconn.h"
 #include "cmdproc.h"
+#include "user.h"
 
 struct _MsnNotification
 {
@@ -64,9 +65,9 @@
 void msn_notification_init(void);
 
 void msn_notification_add_buddy_to_list(MsnNotification *notification,
-					MsnListId list_id, const char *who);
+					MsnListId list_id, MsnUser *user);
 void msn_notification_rem_buddy_from_list(MsnNotification *notification,
-					  MsnListId list_id, const char *who);
+					  MsnListId list_id, MsnUser *user);
 
 void msn_notification_send_fqy(MsnSession *session, const char *passport);
 
--- a/libpurple/protocols/msn/oim.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/oim.c	Mon Dec 15 08:39:08 2008 +0000
@@ -594,47 +594,91 @@
 msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
 {
 	MsnMessage *message;
-	char *date,*from,*decode_msg;
+	const char *date;
+	const char *from;
+	char *decode_msg = NULL;
 	gsize body_len;
 	char **tokens;
-	char *start,*end;
-	int has_nick = 0;
-	char *passport_str, *passport;
+	char *passport = NULL;
 	time_t stamp;
 
 	message = msn_message_new(MSN_MSG_UNKNOWN);
 
 	msn_message_parse_payload(message, msg_str, strlen(msg_str),
-							  MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
+	                          MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
 	purple_debug_info("msn", "oim body:{%s}\n", message->body);
-	decode_msg = (char *)purple_base64_decode(message->body,&body_len);
-	date =	(char *)g_hash_table_lookup(message->attr_table, "Date");
-	from =	(char *)g_hash_table_lookup(message->attr_table, "From");
-	if (strstr(from," ")) {
-		has_nick = 1;
-	}
-	if (has_nick) {
-		tokens = g_strsplit(from , " " , 2);
-		passport_str = g_strdup(tokens[1]);
-		purple_debug_info("msn", "oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n",
-							date, tokens[0], tokens[1], passport_str);
+
+	if (!strcmp(msn_message_get_attr(message, "X-OIMProxy"), "MOSMS")) {
+		char *boundary;
+		char **part;
+
+		from = msn_message_get_attr(message, "X-OIM-originatingSource");
+
+		/* Match number to user's mobile number, FROM is a phone number
+		   if the other side pages you using your phone number */
+		if (!strncmp(from, "tel:+", 5)) {
+			MsnUser *user =	msn_userlist_find_user_with_mobile_phone(
+					rdata->oim->session->userlist, from + 4);
+
+			if (user && user->passport)
+				passport = g_strdup(user->passport);
+		}
+		if (passport == NULL)
+			passport = g_strdup(from);
+
+		boundary = g_strdup_printf("--%s" MSG_OIM_LINE_DEM,
+		                           msn_message_get_attr(message, "boundary"));
+		tokens = g_strsplit(message->body, boundary, 0);
+
+		/* tokens+1 to skip the "This is a multipart message..." text */
+		for (part = tokens+1; *part != NULL; part++) {
+			MsnMessage *multipart;
+			const char *type;
+			multipart = msn_message_new(MSN_MSG_UNKNOWN);
+			msn_message_parse_payload(multipart, *part, strlen(*part),
+			                          MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
+
+			type = msn_message_get_content_type(multipart);
+			if (type && !strcmp(type, "text/plain")) {
+				decode_msg = (char *)purple_base64_decode(multipart->body, &body_len);
+				msn_message_destroy(multipart);
+				break;
+			}
+			msn_message_destroy(multipart);
+		}
+
 		g_strfreev(tokens);
+		g_free(boundary);
+
+		if (decode_msg == NULL) {
+			purple_debug_error("msn", "Couldn't find text/plain OIM message.\n");
+			g_free(passport);
+			return;
+		}
 	} else {
-		passport_str = g_strdup(from);
-		purple_debug_info("msn", "oim Date:{%s},passport{%s}\n",
-					date, passport_str);
+		char *start, *end;
+
+		from = msn_message_get_attr(message, "From");
+		decode_msg = (char *)purple_base64_decode(message->body, &body_len);
+
+		tokens = g_strsplit(from, " ", 2);
+		if (tokens[1] != NULL)
+			from = (const char *)tokens[1];
+
+		start = strchr(from, '<') + 1;
+		end = strchr(from, '>');
+		passport = g_strndup(start, end - start);
+
+		g_strfreev(tokens);
 	}
-	start = strstr(passport_str,"<");
-	start += 1;
-	end = strstr(passport_str,">");
-	passport = g_strndup(start,end - start);
-	g_free(passport_str);
-	purple_debug_info("msn", "oim Date:{%s},passport{%s}\n", date, passport);
 
+	date = msn_message_get_attr(message, "Date");
 	stamp = msn_oim_parse_timestamp(date);
+	purple_debug_info("msn", "oim Date:{%s},passport{%s}\n",
+	                  date, passport);
 
 	serv_got_im(rdata->oim->session->account->gc, passport, decode_msg, 0,
-		stamp);
+	            stamp);
 
 	/*Now get the oim message ID from the oim_list.
 	 * and append to read list to prepare for deleting the Offline Message when sign out
--- a/libpurple/protocols/msn/servconn.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/servconn.h	Mon Dec 15 08:39:08 2008 +0000
@@ -40,7 +40,7 @@
 	MSN_SERVCONN_ERROR_NONE,
 	MSN_SERVCONN_ERROR_CONNECT,
 	MSN_SERVCONN_ERROR_WRITE,
-	MSN_SERVCONN_ERROR_READ,
+	MSN_SERVCONN_ERROR_READ
 
 } MsnServConnError;
 
--- a/libpurple/protocols/msn/session.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/session.c	Mon Dec 15 08:39:08 2008 +0000
@@ -448,25 +448,23 @@
 	PurpleConnection *gc;
 	PurpleStoredImage *img;
 
-	if (session->logged_in)
-		return;
-
-	account = session->account;
-	gc = purple_account_get_connection(account);
+	if (!session->logged_in) {
+		account = session->account;
+		gc = purple_account_get_connection(account);
 
-	img = purple_buddy_icons_find_account_icon(session->account);
-	/* TODO: Do we really want to call this if img is NULL? */
-	msn_user_set_buddy_icon(session->user, img);
-	if (img != NULL)
-		purple_imgstore_unref(img);
+		img = purple_buddy_icons_find_account_icon(session->account);
+		/* TODO: Do we really want to call this if img is NULL? */
+		msn_user_set_buddy_icon(session->user, img);
+		if (img != NULL)
+			purple_imgstore_unref(img);
 
-	session->logged_in = TRUE;
+		session->logged_in = TRUE;
+		purple_connection_set_state(gc, PURPLE_CONNECTED);
+
+		/* Sync users */
+		msn_session_sync_users(session);
+	}
 
 	msn_change_status(session);
-
-	purple_connection_set_state(gc, PURPLE_CONNECTED);
-
-	/* Sync users */
-	msn_session_sync_users(session);
 }
 
--- a/libpurple/protocols/msn/slpcall.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/slpcall.h	Mon Dec 15 08:39:08 2008 +0000
@@ -37,7 +37,7 @@
 typedef enum
 {
 	MSN_SLPCALL_ANY,
-	MSN_SLPCALL_DC,
+	MSN_SLPCALL_DC
 
 } MsnSlpCallType;
 
--- a/libpurple/protocols/msn/soap.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/soap.c	Mon Dec 15 08:39:08 2008 +0000
@@ -505,7 +505,7 @@
 		purple_debug_info("soap", "read: %s\n", g_strerror(perrno));
 
 #ifndef MSN_UNSAFE_DEBUG
-	if (conn->current_request->secure)
+	if (conn->current_request && conn->current_request->secure)
 		purple_debug_misc("soap", "Received secure request.\n");
 	else
 #endif
--- a/libpurple/protocols/msn/state.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/state.c	Mon Dec 15 08:39:08 2008 +0000
@@ -100,14 +100,15 @@
 	cmedia_array = g_strsplit(cmedia, "\\0", 0);
 
 	/*
-	 * 0: Media Player
-	 * 1: 'Music'
+	 * 0: Application
+	 * 1: 'Music'/'Games'/'Office'
 	 * 2: '1' if enabled, '0' if not
 	 * 3: Format (eg. {0} by {1})
 	 * 4: Title
-	 * 5: Artist
-	 * 6: Album
-	 * 7: ?
+	 * If 'Music':
+	 *  5: Artist
+	 *  6: Album
+	 *  7: ?
 	 */
 #if GLIB_CHECK_VERSION(2,6,0)
 	strings  = g_strv_length(cmedia_array);
@@ -118,6 +119,15 @@
 	if (strings >= 4 && !strcmp(cmedia_array[2], "1")) {
 		parsed = TRUE;
 
+		if (!strcmp(cmedia_array[1], "Music"))
+			media->type = CURRENT_MEDIA_MUSIC;
+		else if (!strcmp(cmedia_array[1], "Games"))
+			media->type = CURRENT_MEDIA_GAMES;
+		else if (!strcmp(cmedia_array[1], "Office"))
+			media->type = CURRENT_MEDIA_OFFICE;
+		else
+			media->type = CURRENT_MEDIA_UNKNOWN;
+
 		g_free(media->title);
 		if (strings == 4) {
 			media->title = g_strdup(cmedia_array[3]);
@@ -199,21 +209,33 @@
 static char *
 create_media_string(PurplePresence *presence)
 {
-	const char *artist, *title, *album;
+	const char *title, *game, *office;
 	char *ret;
 	PurpleStatus *status = purple_presence_get_status(presence, "tune");
 	if (!status || !purple_status_is_active(status))
-		return g_strdup_printf("WMP\\0Music\\00\\0{0} - {1}\\0\\0\\0\\0\\0");
+		return g_strdup_printf("\\0Music\\00\\0\\0");
 
-	artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
 	title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
-	album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+	game = purple_status_get_attr_string(status, "game");
+	office = purple_status_get_attr_string(status, "office");
 
-	ret = g_strdup_printf("WMP\\0Music\\0%c\\0{0} - {1}\\0%s\\0%s\\0%s\\0\\0",
-			(title && *title) ? '1' : '0',
-			title ? title : "",
-			artist ? artist : "",
-			album ? album : "");
+	if (title && *title) {
+		const char *artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+		const char *album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
+		ret = g_strdup_printf("WMP\\0Music\\01\\0{0}%s%s\\0%s\\0%s\\0%s\\0",
+		                      artist ? " - {1}" : "",
+		                      album ? " ({2})" : "",
+		                      title,
+		                      artist ? artist : "",
+		                      album ? album : "");
+	}
+	else if (game && *game)
+		ret = g_strdup_printf("\\0Games\\01\\0Playing {0}\\0%s\\0", game);
+	else if (office && *office)
+		ret = g_strdup_printf("\\0Office\\01\\0Editing {0}\\0%s\\0", office);
+	else
+		ret = g_strdup_printf("\\0Music\\00\\0\\0");
+
 	return ret;
 }
 
--- a/libpurple/protocols/msn/switchboard.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/switchboard.h	Mon Dec 15 08:39:08 2008 +0000
@@ -57,7 +57,7 @@
 typedef enum
 {
 	MSN_SB_FLAG_IM = 0x01, /**< This switchboard is being used for a conversation. */
-	MSN_SB_FLAG_FT = 0x02, /**< This switchboard is being used for file transfer. */
+	MSN_SB_FLAG_FT = 0x02  /**< This switchboard is being used for file transfer. */
 
 } MsnSBFlag;
 
--- a/libpurple/protocols/msn/user.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/user.c	Mon Dec 15 08:39:08 2008 +0000
@@ -106,12 +106,25 @@
 		purple_prpl_got_user_status_deactive(account, user->passport, "mobile");
 	}
 
-	if (!offline && user->media.title) {
-		purple_prpl_got_user_status(account, user->passport, "tune",
-				PURPLE_TUNE_ARTIST, user->media.artist,
-				PURPLE_TUNE_ALBUM, user->media.album,
-				PURPLE_TUNE_TITLE, user->media.title,
-				NULL);
+	if (!offline && user->media.type != CURRENT_MEDIA_UNKNOWN) {
+		if (user->media.type == CURRENT_MEDIA_MUSIC) {
+			purple_prpl_got_user_status(account, user->passport, "tune",
+			                            PURPLE_TUNE_ARTIST, user->media.artist,
+			                            PURPLE_TUNE_ALBUM, user->media.album,
+			                            PURPLE_TUNE_TITLE, user->media.title,
+			                            NULL);
+		} else if (user->media.type == CURRENT_MEDIA_GAMES) {
+			purple_prpl_got_user_status(account, user->passport, "tune",
+			                            "game", user->media.title,
+			                            NULL);
+		} else if (user->media.type == CURRENT_MEDIA_OFFICE) {
+			purple_prpl_got_user_status(account, user->passport, "tune",
+			                            "office", user->media.title,
+			                            NULL);
+		} else {
+			purple_debug_warning("msn", "Got CurrentMedia with unknown type %d.\n",
+			                     user->media.type);
+		}
 	} else {
 		purple_prpl_got_user_status_deactive(account, user->passport, "tune");
 	}
@@ -191,6 +204,7 @@
 	g_free(user->media.album);
 	g_free(user->media.artist);
 
+	user->media.type   = media ? media->type : CURRENT_MEDIA_UNKNOWN;
 	user->media.title  = media ? g_strdup(media->title) : NULL;
 	user->media.artist = media ? g_strdup(media->artist) : NULL;
 	user->media.album  = media ? g_strdup(media->album) : NULL;
@@ -326,6 +340,20 @@
 }
 
 void
+msn_user_set_pending_group(MsnUser *user, const char *group)
+{
+	user->pending_group = g_strdup(group);
+}
+
+char *
+msn_user_remove_pending_group(MsnUser *user)
+{
+	char *group = user->pending_group;
+	user->pending_group = NULL;
+	return group;
+}
+
+void
 msn_user_set_home_phone(MsnUser *user, const char *number)
 {
 	g_return_if_fail(user != NULL);
--- a/libpurple/protocols/msn/user.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/user.h	Mon Dec 15 08:39:08 2008 +0000
@@ -45,11 +45,20 @@
 /**
  * Current media.
  */
+typedef enum
+{
+	CURRENT_MEDIA_UNKNOWN,
+	CURRENT_MEDIA_MUSIC,
+	CURRENT_MEDIA_GAMES,
+	CURRENT_MEDIA_OFFICE
+} CurrentMediaType;
+
 typedef struct _CurrentMedia
 {
+	CurrentMediaType type;     /**< Type.   */
+	char *title;    /**< Title.  */
 	char *artist;   /**< Artist. */
 	char *album;    /**< Album.  */
-	char *title;    /**< Title.  */
 } CurrentMedia;
 
 /**
@@ -82,6 +91,7 @@
 	gboolean mobile;        /**< Signed up with MSN Mobile.     */
 
 	GList *group_ids;       /**< The group IDs.                 */
+	char *pending_group;    /**< A pending group to add.        */
 
 	MsnObject *msnobj;      /**< The user's MSN Object.         */
 
@@ -204,6 +214,23 @@
 void msn_user_remove_group_id(MsnUser *user, const char * id);
 
 /**
+ * Sets the pending group for a user.
+ *
+ * @param user  The user.
+ * @param group The group name.
+ */
+void msn_user_set_pending_group(MsnUser *user, const char *group);
+
+/**
+ * Removes the pending group from a user.
+ *
+ * @param user The user.
+ *
+ * @return Returns the pending group name.
+ */
+char *msn_user_remove_pending_group(MsnUser *user);
+
+/**
  * Sets the home phone number for a user.
  *
  * @param user   The user.
--- a/libpurple/protocols/msn/userlist.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/userlist.c	Mon Dec 15 08:39:08 2008 +0000
@@ -184,10 +184,6 @@
 		{
 			msn_user_add_group_id(user, group_id);
 		}
-		else
-		{
-			/* session->sync->fl_users_count++; */
-		}
 	}
 	else if (list_id == MSN_LIST_AL)
 	{
@@ -253,10 +249,6 @@
 			msn_user_remove_group_id(user, group_id);
 			return;
 		}
-		else
-		{
-			/* session->sync->fl_users_count--; */
-		}
 	}
 	else if (list_id == MSN_LIST_AL)
 	{
@@ -676,7 +668,7 @@
 
 	msn_user_unset_op(user, list_op);
 
-	msn_notification_rem_buddy_from_list(userlist->session->notification, list_id, who);
+	msn_notification_rem_buddy_from_list(userlist->session->notification, list_id, user);
 }
 
 /*add buddy*/
@@ -756,6 +748,68 @@
 	msn_add_contact_to_group(userlist->session, state, who, group_id);
 }
 
+/*
+ * Save a buddy address/group until we get back response from FQY
+ */
+void
+msn_userlist_save_pending_buddy(MsnUserList *userlist,
+                               const char *who,
+                               const char *group_name)
+{
+	MsnUser *user;
+
+	g_return_if_fail(userlist != NULL);
+
+	user = msn_user_new(userlist, who, NULL);
+	msn_user_set_pending_group(user, group_name);
+	msn_user_set_network(user, MSN_NETWORK_UNKNOWN);
+	userlist->pending = g_list_prepend(userlist->pending, user);
+}
+
+/*
+ * Actually adds a buddy once we have the response from FQY
+ */
+void
+msn_userlist_add_pending_buddy(MsnUserList *userlist,
+                               const char *who,
+                               /*MsnNetwork*/ int network)
+{
+	MsnUser *user = NULL;
+	MsnUser *user2;
+	GList *l;
+	char *group;
+
+	for (l = userlist->pending; l != NULL; l = l->next)
+	{
+		user = (MsnUser *)l->data;
+
+		if (!g_strcasecmp(who, user->passport)) {
+			userlist->pending = g_list_delete_link(userlist->pending, l);
+			break;
+		}
+	}
+
+	if (user == NULL) {
+		purple_debug_error("msn", "Attempting to add a pending user that does not exist.\n");
+		return;
+	}
+
+	group = msn_user_remove_pending_group(user);
+
+	user2 = msn_userlist_find_user(userlist, who);
+	if (user2 != NULL) {
+		/* User already in userlist, so just update it. */
+		msn_user_destroy(user);
+		user = user2;
+	} else {
+		msn_userlist_add_user(userlist, user);
+	}
+
+	msn_user_set_network(user, network);
+	msn_userlist_add_buddy(userlist, who, group);
+	g_free(group);
+}
+
 void
 msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
 							MsnListId list_id)
@@ -781,7 +835,7 @@
 
 	msn_user_set_op(user, list_op);
 
-	msn_notification_add_buddy_to_list(userlist->session->notification, list_id, who);
+	msn_notification_add_buddy_to_list(userlist->session->notification, list_id, user);
 }
 
 gboolean
--- a/libpurple/protocols/msn/userlist.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/msn/userlist.h	Mon Dec 15 08:39:08 2008 +0000
@@ -47,13 +47,12 @@
 
 	GList *users; /* Contains MsnUsers */
 	GList *groups; /* Contains MsnGroups */
+	GList *pending; /* MsnUsers pending addition (waiting for FQY response) */
 
 	GQueue *buddy_icon_requests;
 	int buddy_icon_window;
 	guint buddy_icon_request_timer;
 
-	int fl_users_count;
-
 };
 
 gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id);
@@ -93,6 +92,12 @@
 void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who);
 void msn_userlist_add_buddy(MsnUserList *userlist,
 			    const char *who, const char *group_name);
+void msn_userlist_save_pending_buddy(MsnUserList *userlist,
+                                     const char *who,
+                                     const char *group_name);
+void msn_userlist_add_pending_buddy(MsnUserList *userlist,
+                                    const char *who,
+                                    /*MsnNetwork*/ int network);
 void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
 						    const char *old_group_name,
 						    const char *new_group_name);
--- a/libpurple/protocols/myspace/CHANGES	Sun Dec 14 23:43:52 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-2007-08-28 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.17
-* Get server-side contact list from server on sign-on (partly implements
-  server-side contacts, ticket #2658).
-* Set local alias to username on sign-on, if not already set. This fixes
-  #2793, though this may not be the best way, other fixes under consideration.
-* Support myim:sendIM and addContact URLs with cID and uID parameters.
-* Fix #2722, only check for mail if "New mail notifications" is enabled.
-* Modularize msimprpl.
-* Pidgin 2.1.1 only.
-
-2007-08-23 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.16
-* Add option to add all friends from myspace.com to your buddy list (#2660)
-* If a user doesn't have a picture, don't display an icon (instead of
-  displaying MySpace's "no photo" icon)
-* Fix #2725, a common crash related to buddy icon data
-* Fix #2752, which led to duplicate groups
-* Fix #2720, crash/disconnect when adding a buddy that doesn't exist
-  (You'll now receive an error when looking up invalid usernames).
-* Source-code release only.
-
-2007-08-22 Jeff Connelly <pidgin@xyzzy.cjb.net> - 0.15
-* Incomplete implementation of adding friends from myspace.com.
-* Change msim_msg_get() to start at the given node instead of the beginning.
-* Add msim_msg_get_*_from_element() to access data in MsimMessagElement *'s.
-* Use MsimMessage dictionaries everywhere in incoming messages, instead of
-  the old GHashTable method. Dictionary type is now fully implemented.
-* Add functions to loop over MsimMessages.
-* Link to myspace.com profile in Get Info.
-* Conditionally use my proposed attention API if defined.
-* Propagate to im.pidgin.pidgin branch for 2.1.2.
-* GSoC ended on 2007-08-20. The code in this release hasn't changed since
-  then. I did, however, bump the version number to 0.15 to distinguish this
-  release from the previous one. But there were no code changes. I updated
-  the text files, too.
-* Note: msimprpl will continue to be developed as time permits.
-
-2007-08-12 Jeff Connelly <jeff2@soc.pidgin.im> - 0.14
-* Full emoticon support (except no difference between nerd and geek emoticons),
-  thanks to a number of new icons from Hylke Bons.
-* Package Win32 release archive so that it can easily be extracted directly
-  into the folder Pidgin was installed to.
-* Better password handling, may now support Unicode passwords.
-* Much general clean-up and restructuring of the code.
-* Resolve user ID from buddy list, if it exists. Greatly improves speed of 
-  receiving messages from user IDs.
-* Support sending and receiving hyperlinks.
-* Fix #2521 by reimplementing protocol message escaping to work correctly.
-* Fix #2520 by indicating sign-on at the correct time.
-
-2007-08-04 Jeff Connelly <jeff2@soc.pidgin.im> - 0.13
-* Fix crash when deleting buddies, on Windows.
-* Disable sending client version to oncoming buddies (compile-time option).
-* Updated login process (more closely resembles official client).
-* Zaps, sending and receiving
-* Emoticons, mapped to Pidgin-supported smileys
-* Show official client build in buddy profiles.
-
-2007-07-15 Jeff Connelly <jeff2@soc.pidgin.im> - 0.12
-* Allow logging in with passwords containing uppercase letters (bug #2066)
-* Add /3 -> | translation to escaping.
-* Allow setting status string.
-* Disable keepalive timeout.
-* Remove faking self online, instead show real status (now that it exists).
-* Support font sizes in incoming instant messages.
-* Add support for mail notifications.
-
-2007-07-09 Jeff Connelly <jeff2@soc.pidgin.im> - 0.11
-* Allow going idle (tested with I'dle Ma'ker) and viewing idle status of 
-  buddies (thanks to Scott Ellis, developing a MySpaceIM plugin for Miranda IM,
-  for finding the idle status code.)
-* Time out if no data from server within a certain amount of time 
-  (keep alives).
-* Remove "Sign on as hidden" option, and always set status to current status
-  when signing on.
-* Some support for sending formatted text.
-* Fix build process on Unix, bug #2086.
-
-2007-07-03 Jeff Connelly <jeff2@soc.pidgin.im> - 0.10
-* On incoming instant messages, add support for:
- * Text color
- * Font face
-* Add option to sign on as hidden, default off (previously, always was hidden)
-* Add ability to change status to hidden, available, away
-* Increase password length limit to 10 to match official client (bug #2010)
-
-2007-07-01 Jeff Connelly <jeff2@soc.pidgin.im> - 0.9
-* Fix crash on Windows when logging in (bug #1990)
-* Fix crash on Windows when viewing tooltip text (bug #1999)
-
-2007-06-30 Jeff Connelly <jeff2@soc.pidgin.im> - 0.8
-* Allow "Get Info" on all users, by uid or username
-* Fix crash when re-logging in, if login failed.
-* Show descriptive error message if login password is too long.
-* Fake self from being online, since can't add self to buddy list.
-* Update for Libpurple 2.0.2.
-* Partial support for formatting on incoming instant messages.
-
-2007-06-14 Jeff Connelly <jeff2@soc.pidgin.im> - 0.7
-* Add/delete buddy now functional (required many other code improvements).
-* Show improved buddy information in tooltip text.
-* Show user profile (in "Get Info" option) for buddies on buddy list.
-* Fix crash when re-logging in, if login succeeded.
-
-2007-06-12 Jeff Connelly <jeff2@soc.pidgin.im> - 0.6
-* Use RC4 code from Libpurple 2.0.1
-* Use a new implementation for sending and receiving messages (MsimMessage).
-  This infrastructural change significantly improves extensibility.
-* Show online buddies as online.
-* Send and receive typing notifications (along with other required changes).
-
-2007-05-22 Jeff Connelly <jeff2@soc.pidgin.im> - 0.5
-* Add protocol escaping, so can now send and receive / and \ characters
-* Designed Pidgin 2.0.0beta7
-* Use RC4 code from Samba
-* Use translations (_ macro)
-* No major changes to code, still getting familiar with tools & community
-
-2007-04-29 Jeff Connelly <jeff2@soc.pidgin.im> 
-
-* NOTE: This code is now being developed under Monotone, in the
-  im.pidgin.soc.2007.msimprpl branch on my local computer, which
-  is periodically sync'd with pidgin.im's Monotone database.
-
-  Changes will be logged to Monotone.
-
-2007-04-15 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.4
-
-* Gracefully handle a full receive buffer
-* Handle fatal errors
-* Last version for Gaim 2.0.0beta6
-
-2007-04-14 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.3
-
-* Win32 support
-* Add a large number of precondition checks and a handful of assertions
-* Add documentation to each function, for doxygen.
-
-2007-04-10 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.2
-
-* Add ability to IM by email address.
-* Show usernames on buddy list instead of userids.
-* Show incoming messages as coming from username, instead of userid.
-* Add status messages and tooltip text.
-
-2007-04-09 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.1
-
-* Parsing most of the protocol.
-* Logging in using RC4/SHA1-based authentication.
-* Sending messages, by numeric userid or username.
-* Receiving messages, currently only by numeric userid.
-* Some buddy list support (show all users on buddy list as online, by uid).
-
-2007-04-07 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.0
-
-* Initial version. Login only. Not publicly released.
-
--- a/libpurple/protocols/myspace/ChangeLog	Sun Dec 14 23:43:52 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-
-2007-04-29 Jeff Connelly <jeff2@soc.pidgin.com> 
-
-* NOTE: This code is now being developed under Monotone, in the
-  im.pidgin.soc.2007.msimprpl branch on my local computer, which
-  is periodically sync'd with pidgin.im's Monotone database.
-
-  Changes will be logged to Monotone.
-
-2007-04-15 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.4
-
-* Gracefully handle a full receive buffer
-* Handle fatal errors
-
-2007-04-14 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.3
-
-* Win32 support
-* Add a large number of precondition checks and a handful of assertions
-* Add documentation to each function, for doxygen.
-
-2007-04-10 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.2
-
-* Add ability to IM by email address.
-* Show usernames on buddy list instead of userids.
-* Show incoming messages as coming from username, instead of userid.
-* Add status messages and tooltip text.
-
-2007-04-09 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.1
-
-* Parsing most of the protocol.
-* Logging in using RC4/SHA1-based authentication.
-* Sending messages, by numeric userid or username.
-* Receiving messages, currently only by numeric userid.
-* Some buddy list support (show all users on buddy list as online, by uid).
-
-2007-04-07 Jeff Connelly <myspaceim@xyzzy.cjb.net> - 0.0
-
-* Initial version. Login only. Not publicly released.
-
--- a/libpurple/protocols/myspace/LICENSE	Sun Dec 14 23:43:52 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,339 +0,0 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
--- a/libpurple/protocols/myspace/markup.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/myspace/markup.c	Mon Dec 15 08:39:08 2008 +0000
@@ -21,23 +21,9 @@
 
 typedef int (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **);
 
-/* Internal functions */
-
-static guint msim_point_to_purple_size(MsimSession *session, guint point);
-static guint msim_purple_size_to_point(MsimSession *session, guint size);
-static guint msim_height_to_point(MsimSession *session, guint height);
-static guint msim_point_to_height(MsimSession *session, guint point);
-
-static int msim_markup_tag_to_html(MsimSession *, xmlnode *root, gchar **begin, gchar **end);
-static int html_tag_to_msim_markup(MsimSession *, xmlnode *root, gchar **begin, gchar **end);
-static gchar *msim_convert_xml(MsimSession *, const gchar *raw, MSIM_XMLNODE_CONVERT f);
-static gchar *msim_convert_smileys_to_markup(gchar *before);
-static double msim_round(double round);
-
-
 /* Globals */
 
-/* The names in in emoticon_names (for <i n=whatever>) map to corresponding 
+/* The names in in emoticon_names (for <i n=whatever>) map to corresponding
  * entries in emoticon_symbols (for the ASCII representation of the emoticon).
  *
  * Multiple emoticon symbols in Pidgin can map to one name. List the
@@ -90,23 +76,23 @@
 	{ NULL, NULL }
 };
 
-
-
 /* Indexes of this array + 1 map HTML font size to scale of normal font size. *
- * Based on _point_sizes from libpurple/gtkimhtml.c 
+ * Based on _point_sizes from libpurple/gtkimhtml.c
  *                                 1    2  3    4     5      6       7 */
 static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 };
 
-#define MAX_FONT_SIZE                   7       /* Purple maximum font size */
+/* Purple maximum font size.  Equivalent to sizeof(_font_scale) / sizeof(_font_scale[0]) */
+#define MAX_FONT_SIZE                   7
+
 #define POINTS_PER_INCH                 72      /* How many pt's in an inch */
 
 /* Text formatting bits for <f s=#> */
 #define MSIM_TEXT_BOLD                  1
-#define MSIM_TEXT_ITALIC                2   
+#define MSIM_TEXT_ITALIC                2
 #define MSIM_TEXT_UNDERLINE             4
 
-/* Default baseline size of purple's fonts, in points. What is size 3 in points. 
- * _font_scale specifies scaling factor relative to this point size. Note this 
+/* Default baseline size of purple's fonts, in points. What is size 3 in points.
+ * _font_scale specifies scaling factor relative to this point size. Note this
  * is only the default; it is configurable in account options. */
 #define MSIM_BASE_FONT_POINT_SIZE       8
 
@@ -114,11 +100,10 @@
  * in account options. */
 #define MSIM_DEFAULT_DPI                96
 
-
 /* round is part of C99, but sometimes is unavailable before then.
  * Based on http://forums.belution.com/en/cpp/000/050/13.shtml
  */
-double msim_round(double value)
+static double msim_round(double value)
 {
 	if (value < 0) {
 		return -(floor(-value + 0.5));
@@ -127,22 +112,17 @@
 	}
 }
 
-
-/** Convert typographical font point size to HTML font size. 
+/** Convert typographical font point size to HTML font size.
  * Based on libpurple/gtkimhtml.c */
 static guint
 msim_point_to_purple_size(MsimSession *session, guint point)
 {
 	guint size, this_point, base;
-	gdouble scale;
-	
+
 	base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE);
-   
-	for (size = 0; 
-			size < sizeof(_font_scale) / sizeof(_font_scale[0]);
-			++size) {
-		scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1];
-		this_point = (guint)msim_round(scale * base);
+
+	for (size = 0; size < MAX_FONT_SIZE; ++size) {
+		this_point = (guint)msim_round(base * _font_scale[size]);
 
 		if (this_point >= point) {
 			purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n",
@@ -176,7 +156,7 @@
 }
 
 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */
-static guint 
+static guint
 msim_height_to_point(MsimSession *session, guint height)
 {
 	guint dpi;
@@ -201,7 +181,7 @@
 }
 
 /** Convert the msim markup <f> (font) tag into HTML. */
-static void 
+static void
 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
 {
 	const gchar *face, *height_str, *decor_str;
@@ -212,35 +192,38 @@
 	height_str = xmlnode_get_attrib(root, "h");
 	decor_str = xmlnode_get_attrib(root, "s");
 
-	if (height_str) {
-		height = atol(height_str);
+	/* Validate the font face, to avoid constructing invalid HTML later */
+	if (strchr(face, '\'') != NULL)
+		face = NULL;
+
+	height = height_str != NULL ? atol(height_str) : 12;
+	decor = decor_str != NULL ? atol(decor_str) : 0;
+
+	/*
+	 * The HTML we're constructing here is a bit redudant.  Ideally we
+	 * would use only the font-family and font-size CSS span, but Pidgin
+	 * doesn't support it (it's included for other UIs).  For Pidgin we
+	 * wrap the whole thing in an ugly font tag, and Pidgin will happily
+	 * ignore the <span>.
+	 */
+	gs_begin = g_string_new("");
+	if (height && !face) {
+		guint point_size = msim_height_to_point(session, height);
+		g_string_printf(gs_begin,
+				"<font size='%d'><span style='font-size: %dpt'>",
+				msim_point_to_purple_size(session, point_size),
+				point_size);
+	} else if (height && face) {
+		guint point_size = msim_height_to_point(session, height);
+		g_string_printf(gs_begin,
+				"<font face='%s' size='%d'><span style='font-family: %s; font-size: %dpt'>",
+				face, msim_point_to_purple_size(session, point_size),
+				face, point_size);
 	} else {
-		height = 12;
-	}
-
-	if (decor_str) {
-		decor = atol(decor_str);
-	} else {
-		decor = 0;
+		g_string_printf(gs_begin, "<font><span>");
 	}
 
-	gs_begin = g_string_new("");
-	/* TODO: get font size working */
-	if (height && !face) {
-		g_string_printf(gs_begin, "<font size='%d'>", 
-				msim_point_to_purple_size(session, msim_height_to_point(session, height)));
-	} else if (height && face) {
-		g_string_printf(gs_begin, "<font face='%s' size='%d'>", face,  
-				msim_point_to_purple_size(session, msim_height_to_point(session, height)));
-	} else {
-		g_string_printf(gs_begin, "<font>");
-	}
-
-	/* No support for font-size CSS? */
-	/* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face, 
-			msim_height_to_point(height)); */
-
-	gs_end = g_string_new("</font>");
+	gs_end = g_string_new("</span></font>");
 
 	if (decor & MSIM_TEXT_BOLD) {
 		g_string_append(gs_begin, "<b>");
@@ -257,17 +240,16 @@
 		g_string_append(gs_end, "</u>");
 	}
 
-
 	*begin = g_string_free(gs_begin, FALSE);
 	*end = g_string_free(gs_end, FALSE);
 }
 
 /** Convert a msim markup color to a color suitable for libpurple.
-  *
-  * @param msim Either a color name, or an rgb(x,y,z) code.
-  *
-  * @return A new string, either a color name or #rrggbb code. Must g_free(). 
-  */
+ *
+ * @param msim Either a color name, or an rgb(x,y,z) code.
+ *
+ * @return A new string, either a color name or #rrggbb code. Must g_free().
+ */
 static char *
 msim_color_to_purple(const char *msim)
 {
@@ -287,7 +269,7 @@
 }
 
 /** Convert the msim markup <a> (anchor) tag into HTML. */
-static void 
+static void
 msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
 {
 	const gchar *href;
@@ -302,10 +284,10 @@
 }
 
 /** Convert the msim markup <p> (paragraph) tag into HTML. */
-static void 
+static void
 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
 {
-	/* Just pass through unchanged. 
+	/* Just pass through unchanged.
 	 *
 	 * Note: attributes currently aren't passed, if there are any. */
 	*begin = g_strdup("<p>");
@@ -313,7 +295,7 @@
 }
 
 /** Convert the msim markup <c> tag (text color) into HTML. TODO: Test */
-static void 
+static void
 msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
 {
 	const gchar *color;
@@ -330,7 +312,7 @@
 
 	purple_color = msim_color_to_purple(color);
 
-	*begin = g_strdup_printf("<font color='%s'>", purple_color); 
+	*begin = g_strdup_printf("<font color='%s'>", purple_color);
 
 	g_free(purple_color);
 
@@ -339,7 +321,7 @@
 }
 
 /** Convert the msim markup <b> tag (background color) into HTML. TODO: Test */
-static void 
+static void
 msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
 {
 	const gchar *color;
@@ -357,7 +339,7 @@
 	purple_color = msim_color_to_purple(color);
 
 	/* TODO: find out how to set background color. */
-	*begin = g_strdup_printf("<span style='background-color: %s'>", 
+	*begin = g_strdup_printf("<span style='background-color: %s'>",
 			purple_color);
 	g_free(purple_color);
 
@@ -365,7 +347,7 @@
 }
 
 /** Convert the msim markup <i> tag (emoticon image) into HTML. */
-static void 
+static void
 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end)
 {
 	const gchar *name;
@@ -396,8 +378,8 @@
 }
 
 /** Convert an individual msim markup tag to HTML. */
-static int 
-msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, 
+static int
+msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin,
 		gchar **end)
 {
 	g_return_val_if_fail(root != NULL, 0);
@@ -416,7 +398,7 @@
 		msim_markup_i_to_html(session, root, begin, end);
 	} else {
 		purple_debug_info("msim", "msim_markup_tag_to_html: "
-				"unknown tag name=%s, ignoring", 
+				"unknown tag name=%s, ignoring",
 				root->name ? root->name : "(NULL)");
 		*begin = g_strdup("");
 		*end = g_strdup("");
@@ -425,8 +407,8 @@
 }
 
 /** Convert an individual HTML tag to msim markup. */
-static int 
-html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, 
+static int
+html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin,
 		gchar **end)
 {
 	int ret = 0;
@@ -437,7 +419,7 @@
 		*end = g_strdup("");
 	/* TODO: Coalesce nested tags into one <f> tag!
 	 * Currently, the 's' value will be overwritten when b/i/u is nested
-	 * within another one, and only the inner-most formatting will be 
+	 * within another one, and only the inner-most formatting will be
 	 * applied to the text. */
 	} else if (!purple_utf8_strcasecmp(root->name, "b")) {
 		if (root->child->type == XMLNODE_TYPE_DATA) {
@@ -522,13 +504,13 @@
 		face = xmlnode_get_attrib(root, "face");
 
 		if (face && size) {
-			*begin = g_strdup_printf("<f f='%s' h='%d'>", face, 
+			*begin = g_strdup_printf("<f f='%s' h='%d'>", face,
 					msim_point_to_height(session,
 						msim_purple_size_to_point(session, atoi(size))));
 		} else if (face) {
 			*begin = g_strdup_printf("<f f='%s'>", face);
 		} else if (size) {
-			*begin = g_strdup_printf("<f h='%d'>", 
+			*begin = g_strdup_printf("<f h='%d'>",
 					 msim_point_to_height(session,
 						 msim_purple_size_to_point(session, atoi(size))));
 		} else {
@@ -550,7 +532,7 @@
 #endif
 
 		err = g_strdup_printf("html_tag_to_msim_markup: unrecognized "
-			"HTML tag %s was sent by the IM client; ignoring", 
+			"HTML tag %s was sent by the IM client; ignoring",
 			root->name ? root->name : "(NULL)");
 		msim_unrecognized(NULL, NULL, err);
 		g_free(err);
@@ -564,29 +546,26 @@
  *
  * @return An HTML string. Caller frees.
  */
-static gchar *
-msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed)
+static void
+msim_convert_xmlnode(MsimSession *session, GString *out, xmlnode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed)
 {
 	xmlnode *node;
 	gchar *begin, *inner, *end;
-	GString *final;
 	int descended = nodes_processed;
 
-	if (!root || !root->name) {
-		return g_strdup("");
-	}
+	if (!root || !root->name)
+		return;
 
 	purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n",
 			root->name);
 
 	begin = inner = end = NULL;
 
-	final = g_string_new("");
-
 	if (descended == 0) /* We've not formatted this yet.. :) */
 		descended = f(session, root, &begin, &end); /* Get the value that our format function has already descended for us */
-	
-	g_string_append(final, begin);
+
+	g_string_append(out, begin);
+	g_free(begin);
 
 	/* Loop over all child nodes. */
 	for (node = root->child; node != NULL; node = node->next) {
@@ -597,29 +576,20 @@
 
 			case XMLNODE_TYPE_TAG:
 				/* A tag or tag with attributes. Recursively descend. */
-				inner = msim_convert_xmlnode(session, node, f, descended);
-				g_return_val_if_fail(inner != NULL, NULL);
-		
-				purple_debug_info("msim", " ** node name=%s\n", 
-						(node && node->name) ? node->name : "(NULL)");
+				msim_convert_xmlnode(session, out, node, f, descended);
+
+				purple_debug_info("msim", " ** node name=%s\n",
+						node->name ? node->name : "(NULL)");
 				break;
-		
+
 			case XMLNODE_TYPE_DATA:
 				/* Literal text. */
-				inner = g_strndup(node->data, node->data_sz);
-				purple_debug_info("msim", " ** node data=%s\n", 
-						inner ? inner : "(NULL)");
+				g_string_append_len(out, node->data, node->data_sz);
 				break;
-		
+
 			default:
-				purple_debug_info("msim",
-						"msim_convert_xmlnode: strange node\n");
-		}
-
-		if (inner) {
-			g_string_append(final, inner);
-			g_free(inner);
-			inner = NULL;
+				purple_debug_warning("msim",
+						"msim_convert_xmlnode: unknown node type\n");
 		}
 	}
 
@@ -627,15 +597,8 @@
 	 * a paragraph and will display each on its own line. You actually have
 	 * to _nest_ <f> tags to intersperse different text in one paragraph!
 	 * Comment out this line below to see. */
-	g_string_append(final, end);
-
-	g_free(begin);
+	g_string_append(out, end);
 	g_free(end);
-
-	purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n",
-			(final && final->str) ? final->str : "(NULL)");
-
-	return g_string_free(final, FALSE);
 }
 
 /** Convert XML to something based on MSIM_XMLNODE_CONVERT. */
@@ -643,7 +606,7 @@
 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f)
 {
 	xmlnode *root;
-	gchar *str;
+	GString *str;
 	gchar *enclosed_raw;
 
 	g_return_val_if_fail(raw != NULL, NULL);
@@ -654,7 +617,7 @@
 	root = xmlnode_from_str(enclosed_raw, -1);
 
 	if (!root) {
-		purple_debug_info("msim", "msim_markup_to_html: couldn't parse "
+		purple_debug_warning("msim", "msim_markup_to_html: couldn't parse "
 				"%s as XML, returning raw: %s\n", enclosed_raw, raw);
 		/* TODO: msim_unrecognized */
 		g_free(enclosed_raw);
@@ -663,13 +626,13 @@
 
 	g_free(enclosed_raw);
 
-	str = msim_convert_xmlnode(session, root, f, 0);
-	g_return_val_if_fail(str != NULL, NULL);
-	purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str);
-
+	str = g_string_new(NULL);
+	msim_convert_xmlnode(session, str, root, f, 0);
 	xmlnode_free(root);
 
-	return str;
+	purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str->str);
+
+	return g_string_free(str, FALSE);
 }
 
 /** Convert plaintext smileys to <i> markup tags.
@@ -696,10 +659,10 @@
 		replacement = g_strdup_printf("<i n=\"%s\"/>", name);
 
 		purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n",
-				symbol ? symbol : "(NULL)", 
+				symbol ? symbol : "(NULL)",
 				replacement ? replacement : "(NULL)");
 		new = purple_strreplace(old, symbol, replacement);
-		
+
 		g_free(replacement);
 		g_free(old);
 
@@ -708,16 +671,14 @@
 
 	return new;
 }
-	
 
-/** High-level function to convert MySpaceIM markup to Purple (HTML) markup. 
+/** High-level function to convert MySpaceIM markup to Purple (HTML) markup.
  *
  * @return Purple markup string, must be g_free()'d. */
 gchar *
 msim_markup_to_html(MsimSession *session, const gchar *raw)
 {
-	return msim_convert_xml(session, raw, 
-			(MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html));
+	return msim_convert_xml(session, raw, msim_markup_tag_to_html);
 }
 
 /** High-level function to convert Purple (HTML) to MySpaceIM markup.
@@ -730,9 +691,8 @@
 {
 	gchar *markup;
 
-	markup = msim_convert_xml(session, raw,
-			(MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup));
-	
+	markup = msim_convert_xml(session, raw, html_tag_to_msim_markup);
+
 	if (purple_account_get_bool(session->account, "emoticons", TRUE)) {
 		/* Frees markup and allocates a new one. */
 		markup = msim_convert_smileys_to_markup(markup);
@@ -740,5 +700,3 @@
 
 	return markup;
 }
-
-
--- a/libpurple/protocols/myspace/message.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/myspace/message.c	Mon Dec 15 08:39:08 2008 +0000
@@ -19,17 +19,15 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "message.h"
 #include "myspace.h"
-#include "message.h"
 
-static void msim_msg_free_element(gpointer data, gpointer user_data);
 static void msim_msg_debug_string_element(gpointer data, gpointer user_data);
-static gchar *msim_msg_pack_using(MsimMessage *msg, GFunc gf, const gchar *sep, const gchar *begin, const gchar *end);
-static GList *msim_msg_get_node(MsimMessage *msg, const gchar *name);
-static MsimMessage *msim_msg_new_v(gchar *first_key, va_list argp);
 
-/* Escape codes and associated replacement text, used for protocol message
- * escaping and unescaping. */
+/**
+ * Escape codes and associated replacement text, used for protocol message
+ * escaping and unescaping.
+ */
 static struct MSIM_ESCAPE_REPLACEMENT {
 	gchar *code;
 	gchar text;
@@ -53,7 +51,7 @@
 	guint msg_len;
 
 	gs = g_string_new("");
-	msg_len = strlen(msg);	
+	msg_len = strlen(msg);
 
 	for (i = 0; i < msg_len; ++i) {
 		struct MSIM_ESCAPE_REPLACEMENT *replacement;
@@ -97,7 +95,7 @@
 	guint msg_len;
 
 	gs = g_string_new("");
-	msg_len = strlen(msg);	
+	msg_len = strlen(msg);
 
 	for (i = 0; i < msg_len; ++i) {
 		struct MSIM_ESCAPE_REPLACEMENT *replacement;
@@ -126,27 +124,8 @@
 	return g_string_free(gs, FALSE);
 }
 
-/** Create a new MsimMessage. 
- * 
- * @param first_key The first key in the sequence, or NULL for an empty message.
- * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. 
- *
- * See msim_msg_append() documentation for details on types.
- */
-MsimMessage *
-msim_msg_new(gchar *first_key, ...)
-{
-	va_list argp;
-
-	if (first_key) {
-	va_start(argp, first_key);
-		return msim_msg_new_v(first_key, argp);
-	} else {
-		return NULL;
-	}
-}
-
-/** Create a new message from va_list and its first argument.
+/**
+ * Create a new message from va_list and its first argument.
  *
  * @param first_key The first argument (a key), or NULL to take all arguments
  *    from argp.
@@ -189,11 +168,11 @@
 
 		/* Interpret variadic arguments. */
 		switch (type) {
-			case MSIM_TYPE_INTEGER: 
-			case MSIM_TYPE_BOOLEAN: 
+			case MSIM_TYPE_INTEGER:
+			case MSIM_TYPE_BOOLEAN:
 				msg = msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int)));
 				break;
-				
+
 			case MSIM_TYPE_STRING:
 				value = va_arg(argp, char *);
 
@@ -210,7 +189,7 @@
 				/* msim_msg_free() will free this GString the caller created. */
 				msg = msim_msg_append(msg, key, type, gs);
 				break;
-			
+
 			case MSIM_TYPE_LIST:
 				gl = va_arg(argp, GList *);
 
@@ -237,9 +216,310 @@
 	return msg;
 }
 
-/** Perform a deep copy on a GList * of gchar * strings. Free with msim_msg_list_free(). */
-GList *
-msim_msg_list_copy(GList *old)
+/**
+ * Create a new MsimMessage.
+ *
+ * @param first_key The first key in the sequence, or NULL for an empty message.
+ * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL.
+ *
+ * See msim_msg_append() documentation for details on types.
+ */
+MsimMessage *
+msim_msg_new(gchar *first_key, ...)
+{
+	va_list argp;
+
+	if (first_key) {
+	va_start(argp, first_key);
+		return msim_msg_new_v(first_key, argp);
+	} else {
+		return NULL;
+	}
+}
+
+/**
+ * Pack a string using the given GFunc and seperator.
+ * Used by msim_msg_dump() and msim_msg_pack().
+ */
+static gchar *
+msim_msg_pack_using(MsimMessage *msg,
+		GFunc gf,
+		const gchar *sep,
+		const gchar *begin, const gchar *end)
+{
+	int num_items;
+	gchar **strings;
+	gchar **strings_tmp;
+	gchar *joined;
+	gchar *final;
+	int i;
+
+	g_return_val_if_fail(msg != NULL, NULL);
+
+	num_items = g_list_length(msg);
+
+	/* Add one for NULL terminator for g_strjoinv(). */
+	strings = (gchar **)g_new0(gchar *, num_items + 1);
+
+	strings_tmp = strings;
+	g_list_foreach(msg, gf, &strings_tmp);
+
+	joined = g_strjoinv(sep, strings);
+	final = g_strconcat(begin, joined, end, NULL);
+	g_free(joined);
+
+	/* Clean up. */
+	for (i = 0; i < num_items; ++i) {
+		g_free(strings[i]);
+	}
+
+	g_free(strings);
+
+	return final;
+}
+
+/**
+ * Return a human-readable string of the message.
+ *
+ * @return A new gchar *, must be g_free()'d.
+ */
+static gchar *
+msim_msg_dump_to_str(MsimMessage *msg)
+{
+	gchar *debug_str;
+
+	if (!msg) {
+		debug_str = g_strdup("<MsimMessage: empty>");
+	} else {
+		debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element,
+				"\n", "<MsimMessage: \n", "\n/MsimMessage>");
+	}
+
+	return debug_str;
+}
+
+/**
+ * Store a human-readable string describing the element.
+ *
+ * @param data Pointer to an MsimMessageElement.
+ * @param user_data
+ */
+static void
+msim_msg_debug_string_element(gpointer data, gpointer user_data)
+{
+	MsimMessageElement *elem;
+	gchar *string;
+	GString *gs;
+	gchar *binary;
+	gchar ***items;  /* wow, a pointer to a pointer to a pointer */
+
+	gchar *s;
+	GList *gl;
+	guint i;
+
+	elem = (MsimMessageElement *)data;
+	items = user_data;
+
+	switch (elem->type) {
+		case MSIM_TYPE_INTEGER:
+			string = g_strdup_printf("%s(integer): %d", elem->name,
+					GPOINTER_TO_UINT(elem->data));
+			break;
+
+		case MSIM_TYPE_RAW:
+			string = g_strdup_printf("%s(raw): %s", elem->name,
+					elem->data ? (gchar *)elem->data : "(NULL)");
+			break;
+
+		case MSIM_TYPE_STRING:
+			string = g_strdup_printf("%s(string): %s", elem->name,
+					elem->data ? (gchar *)elem->data : "(NULL)");
+			break;
+
+		case MSIM_TYPE_BINARY:
+			gs = (GString *)elem->data;
+			binary = purple_base64_encode((guchar*)gs->str, gs->len);
+			string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary);
+			g_free(binary);
+			break;
+
+		case MSIM_TYPE_BOOLEAN:
+			string = g_strdup_printf("%s(boolean): %s", elem->name,
+					elem->data ? "TRUE" : "FALSE");
+			break;
+
+		case MSIM_TYPE_DICTIONARY:
+			if (!elem->data) {
+				s = g_strdup("(NULL)");
+			} else {
+				s = msim_msg_dump_to_str((MsimMessage *)elem->data);
+			}
+
+			if (!s) {
+				s = g_strdup("(NULL, couldn't msim_msg_dump_to_str)");
+			}
+
+			string = g_strdup_printf("%s(dict): %s", elem->name, s);
+
+			g_free(s);
+			break;
+
+		case MSIM_TYPE_LIST:
+			gs = g_string_new("");
+			g_string_append_printf(gs, "%s(list): \n", elem->name);
+
+			i = 0;
+			for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) {
+				g_string_append_printf(gs, " %d. %s\n", i, (gchar *)(gl->data));
+				++i;
+			}
+
+			string = g_string_free(gs, FALSE);
+			break;
+
+		default:
+			string = g_strdup_printf("%s(unknown type %d",
+					elem->name ? elem->name : "(NULL)", elem->type);
+			break;
+	}
+
+	**items = string;
+	++(*items);
+}
+
+/**
+ * Search for and return the node in msg, matching name, or NULL.
+ *
+ * @param msg Message to search within.
+ * @param name Field name to search for.
+ *
+ * @return The GList * node for the MsimMessageElement with the given name, or NULL if not found or name is NULL.
+ *
+ * For internal use - users probably want to use msim_msg_get() to
+ * access the MsimMessageElement *, instead of the GList * container.
+ *
+ */
+static GList *
+msim_msg_get_node(MsimMessage *msg, const gchar *name)
+{
+	GList *node;
+
+	if (!name || !msg) {
+		return NULL;
+	}
+
+	/* Linear search for the given name. O(n) but n is small. */
+	for (node = msg; node != NULL; node = g_list_next(node)) {
+		MsimMessageElement *elem;
+
+		elem = (MsimMessageElement *)node->data;
+
+		g_return_val_if_fail(elem != NULL, NULL);
+		g_return_val_if_fail(elem->name != NULL, NULL);
+
+		if (strcmp(elem->name, name) == 0) {
+			return node;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * Create a new MsimMessageElement * - must be g_free()'d.
+ *
+ * For internal use; users probably want msim_msg_append() or msim_msg_insert_before().
+ *
+ * @param dynamic_name Whether 'name' should be freed when the message is destroyed.
+ */
+static MsimMessageElement *
+msim_msg_element_new(const gchar *name, MsimMessageType type, gpointer data, gboolean dynamic_name)
+{
+	MsimMessageElement *elem;
+
+	elem = g_new0(MsimMessageElement, 1);
+
+	elem->name = name;
+	elem->dynamic_name = dynamic_name;
+	elem->type = type;
+	elem->data = data;
+
+	return elem;
+}
+
+/**
+ * Append a new element to a message.
+ *
+ * @param name Textual name of element (static string, neither copied nor freed).
+ * @param type An MSIM_TYPE_* code.
+ * @param data Pointer to data, see below.
+ *
+ * @return The new message - must be assigned to as with GList*. For example:
+ *
+ *     msg = msim_msg_append(msg, ...)
+ *
+ * The data parameter depends on the type given:
+ *
+ * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x).
+ *
+ * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE.
+ *
+ * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed.
+ *
+ * * MSIM_TYPE_RAW: gchar *. The data WILL BE FREED - use g_strdup() if needed.
+ *
+ * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data AND GString will be freed.
+ *
+ * * MSIM_TYPE_DICTIONARY: An MsimMessage *. Freed when message is destroyed.
+ *
+ * * MSIM_TYPE_LIST: GList * of gchar *. Again, everything will be freed.
+ *
+ * */
+MsimMessage *
+msim_msg_append(MsimMessage *msg, const gchar *name,
+		MsimMessageType type, gpointer data)
+{
+	return g_list_append(msg, msim_msg_element_new(name, type, data, FALSE));
+}
+
+/**
+ * Append a new element, but with a dynamically-allocated name.
+ * Exactly the same as msim_msg_append(), except 'name' will be freed when
+ * the message is destroyed. Normally, it isn't, because a static string is given.
+ */
+static MsimMessage *
+msim_msg_append_dynamic_name(MsimMessage *msg, gchar *name,
+		MsimMessageType type, gpointer data)
+{
+	return g_list_append(msg, msim_msg_element_new(name, type, data, TRUE));
+}
+
+/**
+ * Insert a new element into a message, before the given element name.
+ *
+ * @param name_before Name of the element to insert the new element before. If
+ *                    could not be found or NULL, new element will be inserted at end.
+ *
+ * See msim_msg_append() for usage of other parameters, and an important note about return value.
+ */
+MsimMessage *
+msim_msg_insert_before(MsimMessage *msg, const gchar *name_before,
+		const gchar *name, MsimMessageType type, gpointer data)
+{
+	MsimMessageElement *new_elem;
+	GList *node_before;
+
+	new_elem = msim_msg_element_new(name, type, data, FALSE);
+
+	node_before = msim_msg_get_node(msg, name_before);
+
+	return g_list_insert_before(msg, node_before, new_elem);
+}
+
+/**
+ * Perform a deep copy on a GList * of gchar * strings. Free with msim_msg_list_free().
+ */
+static GList *
+msim_msg_list_copy(const GList *old)
 {
 	GList *new_list;
 
@@ -253,65 +533,13 @@
 	return new_list;
 }
 
-/** Free a GList * of MsimMessageElement *'s. */
-void
-msim_msg_list_free(GList *l)
-{
-
-	for (; l != NULL; l = g_list_next(l)) {
-		MsimMessageElement *elem;
-
-		elem = (MsimMessageElement *)l->data;
-
-		/* Note that name is almost never dynamically allocated elsewhere;
-		 * it is usually a static string, but not in lists. So cast it. */
-		g_free((gchar *)elem->name);
-		g_free(elem->data);
-		g_free(elem);
-	}
-	g_list_free(l);
-}
-
-/** Parse a |-separated string into a new GList. Free with msim_msg_list_free(). */
-GList *
-msim_msg_list_parse(const gchar *raw)
-{
-	gchar **array;
-	GList *list;
-	guint i;
-
-	array = g_strsplit(raw, "|", 0);
-	list = NULL;
-
-	/* TODO: escape/unescape /3 <-> | within list elements */
-	
-	for (i = 0; array[i] != NULL; ++i) {
-		MsimMessageElement *elem;
-
-		/* Freed in msim_msg_list_free() */
-		elem = g_new0(MsimMessageElement, 1);
-
-		/* Give the element a name for debugging purposes.
-		 * Not supposed to be looked up by this name; instead,
-		 * lookup the elements by indexing the array. */
-		elem->name = g_strdup_printf("(list item #%d)", i);
-		elem->type = MSIM_TYPE_RAW;
-		elem->data = g_strdup(array[i]);
-
-		list = g_list_append(list, elem);
-	}
-
-	g_strfreev(array);
-
-	return list;
-}
-
-/** Clone an individual element.
+/**
+ * Clone an individual element.
  *
  * @param data MsimMessageElement * to clone.
  * @param user_data Pointer to MsimMessage * to add cloned element to.
  */
-static void 
+static void
 msim_msg_clone_element(gpointer data, gpointer user_data)
 {
 	MsimMessageElement *elem;
@@ -357,10 +585,14 @@
 
 	/* Append cloned data. Note that the 'name' field is a static string, so it
 	 * never needs to be copied nor freed. */
-	*new = msim_msg_append(*new, elem->name, elem->type, new_data);
+	if (elem->dynamic_name)
+		*new = msim_msg_append_dynamic_name(*new, g_strdup(elem->name), elem->type, new_data);
+	else
+		*new = msim_msg_append(*new, elem->name, elem->type, new_data);
 }
 
-/** Clone an existing MsimMessage. 
+/**
+ * Clone an existing MsimMessage.
  *
  * @return Cloned message; caller should free with msim_msg_free().
  */
@@ -380,7 +612,8 @@
 	return new;
 }
 
-/** Free the data of a message element.
+/**
+ * Free the data of a message element.
  *
  * @param elem The MsimMessageElement *
  *
@@ -411,7 +644,7 @@
 		case MSIM_TYPE_DICTIONARY:
 			msim_msg_free((MsimMessage *)elem->data);
 			break;
-			
+
 		case MSIM_TYPE_LIST:
 			g_list_free((GList *)elem->data);
 			break;
@@ -423,7 +656,29 @@
 	}
 }
 
-/** Free an individual message element.
+/**
+ * Free a GList * of MsimMessageElement *'s.
+ */
+void
+msim_msg_list_free(GList *l)
+{
+
+	for (; l != NULL; l = g_list_next(l)) {
+		MsimMessageElement *elem;
+
+		elem = (MsimMessageElement *)l->data;
+
+		/* Note that name is almost never dynamically allocated elsewhere;
+		 * it is usually a static string, but not in lists. So cast it. */
+		g_free((gchar *)elem->name);
+		g_free(elem->data);
+		g_free(elem);
+	}
+	g_list_free(l);
+}
+
+/**
+ * Free an individual message element.
  *
  * @param data MsimMessageElement * to free.
  * @param user_data Not used; required to match g_list_foreach() callback prototype.
@@ -431,7 +686,7 @@
  * Frees both the element data and the element itself.
  * Also frees the name if dynamic_name is TRUE.
  */
-static void 
+static void
 msim_msg_free_element(gpointer data, gpointer user_data)
 {
 	MsimMessageElement *elem;
@@ -449,8 +704,10 @@
 	g_free(elem);
 }
 
-/** Free a complete message. */
-void 
+/**
+ * Free a complete message.
+ */
+void
 msim_msg_free(MsimMessage *msg)
 {
 	if (!msg) {
@@ -466,422 +723,8 @@
 	g_list_free(msg);
 }
 
-/** Send an existing MsimMessage. */
-gboolean 
-msim_msg_send(MsimSession *session, MsimMessage *msg)
-{
-	gchar *raw;
-	gboolean success;
-	
-	raw = msim_msg_pack(msg);
-	g_return_val_if_fail(raw != NULL, FALSE);
-	success = msim_send_raw(session, raw);
-	g_free(raw);
-
-	msim_msg_dump("msim_msg_send()ing %s\n", msg);
-	
-	return success;
-}
-
 /**
- *
- * Send a message to the server, whose contents is specified using 
- * variable arguments.
- *
- * @param session
- * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. 
- *
- * This function exists for coding convenience: it allows a message to be created
- * and sent in one line of code. Internally it calls msim_msg_send(). 
- *
- * IMPORTANT: See msim_msg_append() documentation for details on element types.
- *
- */
-gboolean 
-msim_send(MsimSession *session, ...)
-{
-	gboolean success;
-	MsimMessage *msg;
-	va_list argp;
-	
-	va_start(argp, session);
-	msg = msim_msg_new_v(NULL, argp);
-
-	/* Actually send the message. */
-	success = msim_msg_send(session, msg);
-
-	/* Cleanup. */
-	msim_msg_free(msg);
-
-	return success;
-}
-
-/** Create a new MsimMessageElement * - must be g_free()'d. 
- *
- * For internal use; users probably want msim_msg_append() or msim_msg_insert_before(). 
- *
- * @param dynamic_name Whether 'name' should be freed when the message is destroyed.
- */
-static MsimMessageElement *
-msim_msg_element_new(const gchar *name, MsimMessageType type, gpointer data, gboolean dynamic_name)
-{
-	MsimMessageElement *elem;
-
-	elem = g_new0(MsimMessageElement, 1);
-
-	elem->name = name;
-	elem->dynamic_name = dynamic_name;
-	elem->type = type;
-	elem->data = data;
-
-	return elem;
-}
-
-
-/** Append a new element to a message. 
- *
- * @param name Textual name of element (static string, neither copied nor freed).
- * @param type An MSIM_TYPE_* code.
- * @param data Pointer to data, see below.
- *
- * @return The new message - must be assigned to as with GList*. For example:
- *
- *     msg = msim_msg_append(msg, ...)
- *
- * The data parameter depends on the type given:
- *
- * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x).
- *
- * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE.
- *
- * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed.
- *
- * * MSIM_TYPE_RAW: gchar *. The data WILL BE FREED - use g_strdup() if needed.
- *
- * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data AND GString will be freed.
- *
- * * MSIM_TYPE_DICTIONARY: An MsimMessage *. Freed when message is destroyed.
- *
- * * MSIM_TYPE_LIST: GList * of gchar *. Again, everything will be freed.
- *
- * */
-MsimMessage *
-msim_msg_append(MsimMessage *msg, const gchar *name, 
-		MsimMessageType type, gpointer data)
-{
-	return g_list_append(msg, msim_msg_element_new(name, type, data, FALSE));
-}
-
-/** Append a new element, but with a dynamically-allocated name.
- * Exactly the same as msim_msg_append(), except 'name' will be freed when
- * the message is destroyed. Normally, it isn't, because a static string is given.
- */
-static MsimMessage *
-msim_msg_append_dynamic_name(MsimMessage *msg, gchar *name,
-		MsimMessageType type, gpointer data)
-{
-	return g_list_append(msg, msim_msg_element_new(name, type, data, TRUE));
-}
-
-/** Insert a new element into a message, before the given element name.
- *
- * @param name_before Name of the element to insert the new element before. If 
- *                    could not be found or NULL, new element will be inserted at end.
- *
- * See msim_msg_append() for usage of other parameters, and an important note about return value.
- */
-MsimMessage *
-msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, 
-		const gchar *name, MsimMessageType type, gpointer data)
-{
-	MsimMessageElement *new_elem;
-	GList *node_before;
-
-	new_elem = msim_msg_element_new(name, type, data, FALSE);
-
-	node_before = msim_msg_get_node(msg, name_before);
-
-	return g_list_insert_before(msg, node_before, new_elem);
-}
-
-/** Pack a string using the given GFunc and seperator.
- * Used by msim_msg_dump() and msim_msg_pack().
- */
-gchar *
-msim_msg_pack_using(MsimMessage *msg, 
-		GFunc gf, 
-		const gchar *sep, 
-		const gchar *begin, const gchar *end)
-{
-	int num_items;
-	gchar **strings;
-	gchar **strings_tmp;
-	gchar *joined;
-	gchar *final;
-	int i;
-
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	num_items = g_list_length(msg);
-
-	/* Add one for NULL terminator for g_strjoinv(). */
-	strings = (gchar **)g_new0(gchar *, num_items + 1);
-
-	strings_tmp = strings;
-	g_list_foreach(msg, gf, &strings_tmp);
-
-	joined = g_strjoinv(sep, strings);
-	final = g_strconcat(begin, joined, end, NULL);
-	g_free(joined);
-
-	/* Clean up. */
-	for (i = 0; i < num_items; ++i) {
-		g_free(strings[i]);
-	}
-
-	g_free(strings);
-
-	return final;
-}
-/** Store a human-readable string describing the element.
- *
- * @param data Pointer to an MsimMessageElement.
- * @param user_data 
- */
-static void 
-msim_msg_debug_string_element(gpointer data, gpointer user_data)
-{
-	MsimMessageElement *elem;
-	gchar *string;
-	GString *gs;
-	gchar *binary;
-	gchar ***items;  /* wow, a pointer to a pointer to a pointer */
-	
-	gchar *s;
-	GList *gl;
-	guint i;
-
-	elem = (MsimMessageElement *)data;
-	items = user_data;
-
-	switch (elem->type) {
-		case MSIM_TYPE_INTEGER:
-			string = g_strdup_printf("%s(integer): %d", elem->name, 
-					GPOINTER_TO_UINT(elem->data));
-			break;
-
-		case MSIM_TYPE_RAW:
-			string = g_strdup_printf("%s(raw): %s", elem->name, 
-					elem->data ? (gchar *)elem->data : "(NULL)");
-			break;
-
-		case MSIM_TYPE_STRING:
-			string = g_strdup_printf("%s(string): %s", elem->name, 
-					elem->data ? (gchar *)elem->data : "(NULL)");
-			break;
-
-		case MSIM_TYPE_BINARY:
-			gs = (GString *)elem->data;
-			binary = purple_base64_encode((guchar*)gs->str, gs->len);
-			string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary);
-			g_free(binary);
-			break;
-
-		case MSIM_TYPE_BOOLEAN:
-			string = g_strdup_printf("%s(boolean): %s", elem->name,
-					elem->data ? "TRUE" : "FALSE");
-			break;
-
-		case MSIM_TYPE_DICTIONARY:
-			if (!elem->data) {
-				s = g_strdup("(NULL)");
-			} else {
-				s = msim_msg_dump_to_str((MsimMessage *)elem->data);
-			}
-
-			if (!s) {
-				s = g_strdup("(NULL, couldn't msim_msg_dump_to_str)");
-			}
-
-			string = g_strdup_printf("%s(dict): %s", elem->name, s);
-
-			g_free(s);
-			break;
-			
-		case MSIM_TYPE_LIST:
-			gs = g_string_new("");
-			g_string_append_printf(gs, "%s(list): \n", elem->name);
-
-			i = 0;
-			for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) {
-				g_string_append_printf(gs, " %d. %s\n", i, (gchar *)(gl->data));
-				++i;
-			}
-			
-			string = g_string_free(gs, FALSE);
-			break;
-
-		default:
-			string = g_strdup_printf("%s(unknown type %d", 
-					elem->name ? elem->name : "(NULL)", elem->type);
-			break;
-	}
-
-	**items = string;
-	++(*items);
-}
-
-/** Print a human-readable string of the message to Purple's debug log.
- *
- * @param fmt_string A static string, in which '%s' will be replaced.
- */
-void 
-msim_msg_dump(const gchar *fmt_string, MsimMessage *msg)
-{
-	gchar *debug_str;
-
-	g_return_if_fail(fmt_string != NULL);
-
-	debug_str = msim_msg_dump_to_str(msg);
-	
-	g_return_if_fail(debug_str != NULL);
-
-	purple_debug_info("msim", fmt_string, debug_str);
-
-	g_free(debug_str);
-}
-
-/** Return a human-readable string of the message.
- *
- * @return A new gchar *, must be g_free()'d.
- */
-gchar *
-msim_msg_dump_to_str(MsimMessage *msg)
-{
-	gchar *debug_str;
-
-	if (!msg) {
-		debug_str = g_strdup("<MsimMessage: empty>");
-	} else {
-		debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element, 
-				"\n", "<MsimMessage: \n", "\n/MsimMessage>");
-	}
-
-	return debug_str;
-}
-
-/** Return a message element data as a new string for a raw protocol message, converting from other types (integer, etc.) if necessary.
- *
- * @return const gchar * The data as a string, or NULL. Caller must g_free().
- *
- * Returns a string suitable for inclusion in a raw protocol message, not necessarily
- * optimal for human consumption. For example, strings are escaped. Use 
- * msim_msg_get_string() if you want a string, which in some cases is same as this.
- */
-gchar *
-msim_msg_pack_element_data(MsimMessageElement *elem)
-{
-	GString *gs;
-	GList *gl;
-
-	g_return_val_if_fail(elem != NULL, NULL);
-
-	switch (elem->type) {
-		case MSIM_TYPE_INTEGER:
-			return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data));
-
-		case MSIM_TYPE_RAW:
-			/* Not un-escaped - this is a raw element, already escaped if necessary. */
-			return (gchar *)g_strdup((gchar *)elem->data);
-
-		case MSIM_TYPE_STRING:
-			/* Strings get escaped. msim_escape() creates a new string. */
-			g_return_val_if_fail(elem->data != NULL, NULL);
-			return elem->data ? msim_escape((gchar *)elem->data) :
-				g_strdup("(NULL)");
-
-		case MSIM_TYPE_BINARY:
-			gs = (GString *)elem->data;
-			/* Do not escape! */
-			return purple_base64_encode((guchar *)gs->str, gs->len);
-
-		case MSIM_TYPE_BOOLEAN:
-			/* Not used by messages in the wire protocol * -- see msim_msg_pack_element.
-			 * Only used by dictionaries, see msim_msg_pack_element_dict. */
-			return elem->data ? g_strdup("On") : g_strdup("Off");
-
-		case MSIM_TYPE_DICTIONARY:
-			return msim_msg_pack_dict((MsimMessage *)elem->data);
-			
-		case MSIM_TYPE_LIST:
-			/* Pack using a|b|c|d|... */
-			gs = g_string_new("");
-
-			for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) {
-				g_string_append_printf(gs, "%s", (gchar*)(gl->data));
-				
-				/* All but last element is separated by a bar. */
-				if (g_list_next(gl)) 
-					g_string_append(gs, "|");
-			}
-			
-			return g_string_free(gs, FALSE);
-
-		default:
-			purple_debug_info("msim", "field %s, unknown type %d\n", 
-					elem->name ? elem->name : "(NULL)", 
-					elem->type);
-			return NULL;
-	}
-}
-
-/** Pack an element into its protcol representation inside a dictionary.
- *
- * See msim_msg_pack_element().
- */
-static void
-msim_msg_pack_element_dict(gpointer data, gpointer user_data)
-{
-	MsimMessageElement *elem;
-	gchar *string, *data_string, ***items;
-
-	elem = (MsimMessageElement *)data;
-	items = (gchar ***)user_data;
-
-	/* Exclude elements beginning with '_' from packed protocol messages. */
-	if (elem->name[0] == '_') {
-		return;
-	}
-
-	data_string = msim_msg_pack_element_data(elem);
-
-	g_return_if_fail(data_string != NULL);
-
-	switch (elem->type) {
-		/* These types are represented by key name/value pairs (converted above). */
-		case MSIM_TYPE_INTEGER:
-		case MSIM_TYPE_RAW:
-		case MSIM_TYPE_STRING:
-		case MSIM_TYPE_BINARY:
-		case MSIM_TYPE_DICTIONARY:
-		case MSIM_TYPE_LIST:
-		case MSIM_TYPE_BOOLEAN: /* Boolean is On or Off */
-			string = g_strconcat(elem->name, "=", data_string, NULL);
-			break;
-
-		default:
-			g_free(data_string);
-			g_return_if_fail(FALSE);
-			break;
-	}
-
-	g_free(data_string);
-
-	**items = string;
-	++(*items);
-}
-
-/** Pack an element into its protocol representation. 
+ * Pack an element into its protocol representation.
  *
  * @param data Pointer to an MsimMessageElement.
  * @param user_data Pointer to a gchar ** array of string items.
@@ -891,7 +734,7 @@
  * is responsible for creating array to correct dimensions, and
  * freeing each string element of the array added by this function.
  */
-static void 
+static void
 msim_msg_pack_element(gpointer data, gpointer user_data)
 {
 	MsimMessageElement *elem;
@@ -942,8 +785,55 @@
 	++(*items);
 }
 
+/**
+ * Pack an element into its protcol representation inside a dictionary.
+ *
+ * See msim_msg_pack_element().
+ */
+static void
+msim_msg_pack_element_dict(gpointer data, gpointer user_data)
+{
+	MsimMessageElement *elem;
+	gchar *string, *data_string, ***items;
 
-/** Return a packed string of a message suitable for sending over the wire.
+	elem = (MsimMessageElement *)data;
+	items = (gchar ***)user_data;
+
+	/* Exclude elements beginning with '_' from packed protocol messages. */
+	if (elem->name[0] == '_') {
+		return;
+	}
+
+	data_string = msim_msg_pack_element_data(elem);
+
+	g_return_if_fail(data_string != NULL);
+
+	switch (elem->type) {
+		/* These types are represented by key name/value pairs (converted above). */
+		case MSIM_TYPE_INTEGER:
+		case MSIM_TYPE_RAW:
+		case MSIM_TYPE_STRING:
+		case MSIM_TYPE_BINARY:
+		case MSIM_TYPE_DICTIONARY:
+		case MSIM_TYPE_LIST:
+		case MSIM_TYPE_BOOLEAN: /* Boolean is On or Off */
+			string = g_strconcat(elem->name, "=", data_string, NULL);
+			break;
+
+		default:
+			g_free(data_string);
+			g_return_if_fail(FALSE);
+			break;
+	}
+
+	g_free(data_string);
+
+	**items = string;
+	++(*items);
+}
+
+/**
+ * Return a packed string of a message suitable for sending over the wire.
  *
  * @return A string. Caller must g_free().
  */
@@ -955,11 +845,12 @@
 	return msim_msg_pack_using(msg, msim_msg_pack_element, "\\", "\\", "\\final\\");
 }
 
-/** Return a packed string of a dictionary, suitable for embedding in MSIM_TYPE_DICTIONARY.
+/**
+ * Return a packed string of a dictionary, suitable for embedding in MSIM_TYPE_DICTIONARY.
  *
  * @return A string; caller must g_free().
  */
-gchar *
+static gchar *
 msim_msg_pack_dict(MsimMessage *msg)
 {
 	g_return_val_if_fail(msg != NULL, NULL);
@@ -967,7 +858,146 @@
 	return msim_msg_pack_using(msg, msim_msg_pack_element_dict, "\034", "", "");
 }
 
-/** 
+/**
+ * Send an existing MsimMessage.
+ */
+gboolean
+msim_msg_send(MsimSession *session, MsimMessage *msg)
+{
+	gchar *raw;
+	gboolean success;
+
+	raw = msim_msg_pack(msg);
+	g_return_val_if_fail(raw != NULL, FALSE);
+	success = msim_send_raw(session, raw);
+	g_free(raw);
+
+	msim_msg_dump("msim_msg_send()ing %s\n", msg);
+
+	return success;
+}
+
+/**
+ * Return a message element data as a new string for a raw protocol message,
+ * converting from other types (integer, etc.) if necessary.
+ *
+ * @return const gchar * The data as a string, or NULL. Caller must g_free().
+ *
+ * Returns a string suitable for inclusion in a raw protocol message, not necessarily
+ * optimal for human consumption. For example, strings are escaped. Use
+ * msim_msg_get_string() if you want a string, which in some cases is same as this.
+ */
+gchar *
+msim_msg_pack_element_data(MsimMessageElement *elem)
+{
+	GString *gs;
+	GList *gl;
+
+	g_return_val_if_fail(elem != NULL, NULL);
+
+	switch (elem->type) {
+		case MSIM_TYPE_INTEGER:
+			return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data));
+
+		case MSIM_TYPE_RAW:
+			/* Not un-escaped - this is a raw element, already escaped if necessary. */
+			return (gchar *)g_strdup((gchar *)elem->data);
+
+		case MSIM_TYPE_STRING:
+			/* Strings get escaped. msim_escape() creates a new string. */
+			g_return_val_if_fail(elem->data != NULL, NULL);
+			return elem->data ? msim_escape((gchar *)elem->data) :
+				g_strdup("(NULL)");
+
+		case MSIM_TYPE_BINARY:
+			gs = (GString *)elem->data;
+			/* Do not escape! */
+			return purple_base64_encode((guchar *)gs->str, gs->len);
+
+		case MSIM_TYPE_BOOLEAN:
+			/* Not used by messages in the wire protocol * -- see msim_msg_pack_element.
+			 * Only used by dictionaries, see msim_msg_pack_element_dict. */
+			return elem->data ? g_strdup("On") : g_strdup("Off");
+
+		case MSIM_TYPE_DICTIONARY:
+			return msim_msg_pack_dict((MsimMessage *)elem->data);
+
+		case MSIM_TYPE_LIST:
+			/* Pack using a|b|c|d|... */
+			gs = g_string_new("");
+
+			for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) {
+				g_string_append_printf(gs, "%s", (gchar*)(gl->data));
+
+				/* All but last element is separated by a bar. */
+				if (g_list_next(gl))
+					g_string_append(gs, "|");
+			}
+
+			return g_string_free(gs, FALSE);
+
+		default:
+			purple_debug_info("msim", "field %s, unknown type %d\n",
+					elem->name ? elem->name : "(NULL)",
+					elem->type);
+			return NULL;
+	}
+}
+
+/**
+ * Send a message to the server, whose contents is specified using
+ * variable arguments.
+ *
+ * @param session
+ * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL.
+ *
+ * This function exists for coding convenience: it allows a message to be created
+ * and sent in one line of code. Internally it calls msim_msg_send().
+ *
+ * IMPORTANT: See msim_msg_append() documentation for details on element types.
+ *
+ */
+gboolean
+msim_send(MsimSession *session, ...)
+{
+	gboolean success;
+	MsimMessage *msg;
+	va_list argp;
+
+	va_start(argp, session);
+	msg = msim_msg_new_v(NULL, argp);
+
+	/* Actually send the message. */
+	success = msim_msg_send(session, msg);
+
+	/* Cleanup. */
+	msim_msg_free(msg);
+
+	return success;
+}
+
+/**
+ * Print a human-readable string of the message to Purple's debug log.
+ *
+ * @param fmt_string A static string, in which '%s' will be replaced.
+ */
+void
+msim_msg_dump(const gchar *fmt_string, MsimMessage *msg)
+{
+	gchar *debug_str;
+
+	g_return_if_fail(fmt_string != NULL);
+
+	debug_str = msim_msg_dump_to_str(msg);
+
+	g_return_if_fail(debug_str != NULL);
+
+	purple_debug_info("msim", fmt_string, debug_str);
+
+	g_free(debug_str);
+}
+
+/**
  * Parse a raw protocol message string into a MsimMessage *.
  *
  * @param raw The raw message string to parse, will be g_free()'d.
@@ -1002,7 +1032,7 @@
 
 	msg = msim_msg_new(FALSE);
 
-	for (tokens = g_strsplit(raw + 1, "\\", 0), i = 0; 
+	for (tokens = g_strsplit(raw + 1, "\\", 0), i = 0;
 			(token = tokens[i]);
 			i++) {
 #ifdef MSIM_DEBUG_PARSE
@@ -1012,7 +1042,7 @@
 			/* Odd-numbered ordinal is a value. */
 
 			value = token;
-		
+
 			/* Incoming protocol messages get tagged as MSIM_TYPE_RAW, which
 			 * represents an untyped piece of data. msim_msg_get_* will
 			 * convert to appropriate types for caller, and handle unescaping if needed. */
@@ -1033,43 +1063,8 @@
 	return msg;
 }
 
-/** Search for and return the node in msg, matching name, or NULL. 
- *
- * @param msg Message to search within.
- * @param name Field name to search for.
- *
- * @return The GList * node for the MsimMessageElement with the given name, or NULL if not found or name is NULL.
- *
- * For internal use - users probably want to use msim_msg_get() to
- * access the MsimMessageElement *, instead of the GList * container.
- *
- */
-static GList *
-msim_msg_get_node(MsimMessage *msg, const gchar *name)
-{
-	GList *node;
-
-	if (!name || !msg) {
-		return NULL;
-	}
-
-	/* Linear search for the given name. O(n) but n is small. */
-	for (node = msg; node != NULL; node = g_list_next(node)) {
-		MsimMessageElement *elem;
-
-		elem = (MsimMessageElement *)node->data;
-
-		g_return_val_if_fail(elem != NULL, NULL);
-		g_return_val_if_fail(elem->name != NULL, NULL);
-
-		if (strcmp(elem->name, name) == 0) {
-			return node;
-		}
-	}
-	return NULL;
-}
-
-/** Return the first MsimMessageElement * with given name in the MsimMessage *. 
+/**
+ * Return the first MsimMessageElement * with given name in the MsimMessage *.
  *
  * @param name Name to search for.
  *
@@ -1092,30 +1087,6 @@
 	}
 }
 
-/** Return the data of an element of a given name, as a string.
- *
- * @param name Name of element.
- *
- * @return gchar * The data as a string, or NULL if not found. 
- *     Caller must g_free().
- *
- * Note that msim_msg_pack_element_data() is similar, but returns a string
- * for inclusion into a raw protocol string (escaped and everything).
- * This function unescapes the string for you, if needed.
- */
-gchar *
-msim_msg_get_string(MsimMessage *msg, const gchar *name)
-{
-	MsimMessageElement *elem;
-
-	elem = msim_msg_get(msg, name);
-	if (!elem) {
-		return NULL;
-	}
-
-	return msim_msg_get_string_from_element(elem);
-}
-
 gchar *
 msim_msg_get_string_from_element(MsimMessageElement *elem)
 {
@@ -1140,9 +1111,20 @@
 	}
 }
 
-/** Return an element as a new list. Caller frees with msim_msg_list_free(). */
-GList *
-msim_msg_get_list(MsimMessage *msg, const gchar *name)
+/**
+ * Return the data of an element of a given name, as a string.
+ *
+ * @param name Name of element.
+ *
+ * @return gchar * The data as a string, or NULL if not found.
+ *     Caller must g_free().
+ *
+ * Note that msim_msg_pack_element_data() is similar, but returns a string
+ * for inclusion into a raw protocol string (escaped and everything).
+ * This function unescapes the string for you, if needed.
+ */
+gchar *
+msim_msg_get_string(MsimMessage *msg, const gchar *name)
 {
 	MsimMessageElement *elem;
 
@@ -1151,10 +1133,46 @@
 		return NULL;
 	}
 
-	return msim_msg_get_list_from_element(elem);
+	return msim_msg_get_string_from_element(elem);
 }
 
-GList *
+/**
+ * Parse a |-separated string into a new GList. Free with msim_msg_list_free().
+ */
+static GList *
+msim_msg_list_parse(const gchar *raw)
+{
+	gchar **array;
+	GList *list;
+	guint i;
+
+	array = g_strsplit(raw, "|", 0);
+	list = NULL;
+
+	/* TODO: escape/unescape /3 <-> | within list elements */
+
+	for (i = 0; array[i] != NULL; ++i) {
+		MsimMessageElement *elem;
+
+		/* Freed in msim_msg_list_free() */
+		elem = g_new0(MsimMessageElement, 1);
+
+		/* Give the element a name for debugging purposes.
+		 * Not supposed to be looked up by this name; instead,
+		 * lookup the elements by indexing the array. */
+		elem->name = g_strdup_printf("(list item #%d)", i);
+		elem->type = MSIM_TYPE_RAW;
+		elem->data = g_strdup(array[i]);
+
+		list = g_list_append(list, elem);
+	}
+
+	g_strfreev(array);
+
+	return list;
+}
+
+static GList *
 msim_msg_get_list_from_element(MsimMessageElement *elem)
 {
 	g_return_val_if_fail(elem != NULL, NULL);
@@ -1173,6 +1191,22 @@
 }
 
 /**
+ * Return an element as a new list. Caller frees with msim_msg_list_free().
+ */
+GList *
+msim_msg_get_list(MsimMessage *msg, const gchar *name)
+{
+	MsimMessageElement *elem;
+
+	elem = msim_msg_get(msg, name);
+	if (!elem) {
+		return NULL;
+	}
+
+	return msim_msg_get_list_from_element(elem);
+}
+
+/**
  * Parse a \x1c-separated "dictionary" of key=value pairs into a hash table.
  *
  * @param raw The text of the dictionary to parse. Often the
@@ -1192,8 +1226,8 @@
 	g_return_val_if_fail(raw != NULL, NULL);
 
 	dict = msim_msg_new(NULL);
- 
-	for (items = g_strsplit(raw, "\x1c", 0), i = 0; 
+
+	for (items = g_strsplit(raw, "\x1c", 0), i = 0;
 		(item = items[i]);
 		i++) {
 		gchar *key, *value;
@@ -1202,7 +1236,7 @@
 
 		key = elements[0];
 		if (!key) {
-			purple_debug_info("msim", "msim_msg_parse_dictionary(%s): null key\n", 
+			purple_debug_info("msim", "msim_msg_dictionary_parse(%s): null key\n",
 					raw);
 			g_strfreev(elements);
 			break;
@@ -1210,14 +1244,14 @@
 
 		value = elements[1];
 		if (!value) {
-			purple_debug_info("msim", "msim_msg_parse_dictionary(%s): null value\n", 
+			purple_debug_info("msim", "msim_msg_dictionary_prase(%s): null value\n",
 					raw);
 			g_strfreev(elements);
 			break;
 		}
 
 #ifdef MSIM_DEBUG_PARSE
-		purple_debug_info("msim_msg_parse_dictionary","-- %s: %s\n", key ? key : "(NULL)", 
+		purple_debug_info("msim_msg_dictionary_parse","-- %s: %s\n", key ? key : "(NULL)",
 				value ? value : "(NULL)");
 #endif
 		/* Append with _dynamic_name since g_strdup(key) is dynamic, and
@@ -1232,7 +1266,27 @@
 	return dict;
 }
 
-/** Return an element as a new dictionary. Caller frees with msim_msg_free(). */
+static MsimMessage *
+msim_msg_get_dictionary_from_element(MsimMessageElement *elem)
+{
+	g_return_val_if_fail(elem != NULL, NULL);
+	switch (elem->type) {
+		case MSIM_TYPE_DICTIONARY:
+			return msim_msg_clone((MsimMessage *)elem->data);
+
+		case MSIM_TYPE_RAW:
+			return msim_msg_dictionary_parse((gchar *)elem->data);
+
+		default:
+			purple_debug_info("msim_msg_get_dictionary", "type %d unknown, name %s\n",
+					elem->type, elem->name ? elem->name : "(NULL)");
+			return NULL;
+	}
+}
+
+/**
+ * Return an element as a new dictionary. Caller frees with msim_msg_free().
+ */
 MsimMessage *
 msim_msg_get_dictionary(MsimMessage *msg, const gchar *name)
 {
@@ -1246,49 +1300,6 @@
 	return msim_msg_get_dictionary_from_element(elem);
 }
 
-MsimMessage *
-msim_msg_get_dictionary_from_element(MsimMessageElement *elem)
-{
-	g_return_val_if_fail(elem != NULL, NULL);
-	switch (elem->type) {
-		case MSIM_TYPE_DICTIONARY:
-			return msim_msg_clone((MsimMessage *)elem->data);
-		
-		case MSIM_TYPE_RAW:
-			return msim_msg_dictionary_parse((gchar *)elem->data);
-
-		default:
-			purple_debug_info("msim_msg_get_dictionary", "type %d unknown, name %s\n",
-					elem->type, elem->name ? elem->name : "(NULL)");
-			return NULL;
-	}
-}
-
-/** Return the data of an element of a given name, as an unsigned integer.
- *
- * @param name Name of element.
- *
- * @return guint Numeric representation of data, or 0 if could not be converted / not found.
- *
- * Useful to obtain an element's data if you know it should be an integer,
- * even if it is not stored as an MSIM_TYPE_INTEGER. MSIM_TYPE_STRING will
- * be converted handled correctly, for example.
- */
-guint 
-msim_msg_get_integer(MsimMessage *msg, const gchar *name)
-{
-	MsimMessageElement *elem;
-
-	elem = msim_msg_get(msg, name);
-
-	if (!elem) {
-		return 0;
-	}
-
-	return msim_msg_get_integer_from_element(elem);
-}
-
-
 guint
 msim_msg_get_integer_from_element(MsimMessageElement *elem)
 {
@@ -1307,29 +1318,32 @@
 	}
 }
 
-/** Return the data of an element of a given name, as a binary GString.
+/**
+ * Return the data of an element of a given name, as an unsigned integer.
  *
- * @param binary_data A pointer to a new pointer, which will be filled in with the binary data. CALLER MUST g_free().
+ * @param name Name of element.
  *
- * @param binary_length A pointer to an integer, which will be set to the binary data length.
+ * @return guint Numeric representation of data, or 0 if could not be converted / not found.
  *
- * @return TRUE if successful, FALSE if not.
+ * Useful to obtain an element's data if you know it should be an integer,
+ * even if it is not stored as an MSIM_TYPE_INTEGER. MSIM_TYPE_STRING will
+ * be converted handled correctly, for example.
  */
-gboolean 
-msim_msg_get_binary(MsimMessage *msg, const gchar *name, 
-		gchar **binary_data, gsize *binary_length)
+guint
+msim_msg_get_integer(MsimMessage *msg, const gchar *name)
 {
 	MsimMessageElement *elem;
-				
+
 	elem = msim_msg_get(msg, name);
+
 	if (!elem) {
-		return FALSE;
+		return 0;
 	}
 
-	return msim_msg_get_binary_from_element(elem, binary_data, binary_length);
+	return msim_msg_get_integer_from_element(elem);
 }
 
-gboolean
+static gboolean
 msim_msg_get_binary_from_element(MsimMessageElement *elem, gchar **binary_data, gsize *binary_length)
 {
 	GString *gs;
@@ -1344,7 +1358,7 @@
 			 * by msimprpl code for things like instant messages - stuff that should be
 			 * escaped if needed). DWIM.
 			 */
-	
+
 			/* Previously, incoming messages were stored as MSIM_TYPE_STRING.
 			 * This was fine for integers and strings, since they can easily be
 			 * converted in msim_get_*, as desirable. However, it does not work
@@ -1372,7 +1386,7 @@
 
 
 			/* Rejected because if it isn't already a GString, have to g_new0 it and
-			 * then caller has to ALSO free the GString! 
+			 * then caller has to ALSO free the GString!
 			 *
 			 * return (GString *)elem->data; */
 
@@ -1382,3 +1396,26 @@
 			return FALSE;
 	}
 }
+
+/**
+ * Return the data of an element of a given name, as a binary GString.
+ *
+ * @param binary_data A pointer to a new pointer, which will be filled in with the binary data. CALLER MUST g_free().
+ *
+ * @param binary_length A pointer to an integer, which will be set to the binary data length.
+ *
+ * @return TRUE if successful, FALSE if not.
+ */
+gboolean
+msim_msg_get_binary(MsimMessage *msg, const gchar *name,
+		gchar **binary_data, gsize *binary_length)
+{
+	MsimMessageElement *elem;
+
+	elem = msim_msg_get(msg, name);
+	if (!elem) {
+		return FALSE;
+	}
+
+	return msim_msg_get_binary_from_element(elem, binary_data, binary_length);
+}
--- a/libpurple/protocols/myspace/message.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/myspace/message.h	Mon Dec 15 08:39:08 2008 +0000
@@ -24,17 +24,20 @@
 
 #include <glib.h>
 
+#define MsimMessage GList               /* #define instead of typedef to avoid casting */
+typedef gchar MsimMessageType;
+typedef struct _MsimMessageElement MsimMessageElement;
+
+#include "session.h"
+
 /* Types */
-#define MsimMessage GList               /* #define instead of typedef to avoid casting */
-typedef struct _MsimMessageElement
+struct _MsimMessageElement
 {
 	const gchar *name;              /**< Textual name of element. */
 	gboolean dynamic_name;          /**< TRUE if 'name' is a dynamic string to be freed, not static. */
 	guint type;                     /**< MSIM_TYPE_* code. */
 	gpointer data;                  /**< Pointer to data, or GUINT_TO_POINTER for int/bool. */
-} MsimMessageElement;
-
-typedef gchar MsimMessageType;
+};
 
 #define msim_msg_get_next_element_node(msg)    ((MsimMessage *)(msg->next))
 
@@ -58,20 +61,13 @@
 void msim_msg_free(MsimMessage *msg);
 MsimMessage *msim_msg_append(MsimMessage *msg, const gchar *name, MsimMessageType type, gpointer data);
 MsimMessage *msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, const gchar *name, MsimMessageType type, gpointer data);
-gchar *msim_msg_dump_to_str(MsimMessage *msg);
 gchar *msim_msg_pack_element_data(MsimMessageElement *elem);
 void msim_msg_dump(const char *fmt_string, MsimMessage *msg);
 gchar *msim_msg_pack(MsimMessage *msg);
-gchar *msim_msg_pack_dict(MsimMessage *msg);
 
-GList *msim_msg_list_copy(GList *old);
 void msim_msg_list_free(GList *l);
-GList *msim_msg_list_parse(const gchar *raw);
 
-/* Defined in myspace.h */
-struct _MsimSession;
-
-/* Based on http://permalink.gmane.org/gmane.comp.parsers.sparse/695 
+/* Based on http://permalink.gmane.org/gmane.comp.parsers.sparse/695
  * Define macros for useful gcc attributes. */
 #ifdef __GNUC__
 #define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
@@ -87,7 +83,7 @@
 	#define FORMAT_ATTR(pos)
 	#define NORETURN_ATTR
 	#define SENTINEL_ATTR
-#endif 
+#endif
 
 /* Cause gcc to emit "a missing sentinel in function call" if forgot
  * to write NULL as last, terminating parameter. */
@@ -109,10 +105,6 @@
 
 /* Retrieve data by element (MsimMessageElement *), returned from msim_msg_get() */
 gchar *msim_msg_get_string_from_element(MsimMessageElement *elem);
-GList *msim_msg_get_list_from_element(MsimMessageElement *elem);
-MsimMessage *msim_msg_get_dictionary_from_element(MsimMessageElement *elem);
 guint msim_msg_get_integer_from_element(MsimMessageElement *elem);
-gboolean msim_msg_get_binary_from_element(MsimMessageElement *elem, 
-		gchar **binary_data, gsize *binary_length);
 
 #endif /* _MYSPACE_MESSAGE_H */
--- a/libpurple/protocols/myspace/myspace.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Mon Dec 15 08:39:08 2008 +0000
@@ -980,15 +980,8 @@
 
 	if (!user) {
 		/* User isn't on blist, create a temporary user to store info. */
-		/* TODO: is this legit, or is it somehow responsible for #3444? */
-		PurpleBuddy *buddy;
-
 		user = g_new0(MsimUser, 1);
 		user->temporary_user = TRUE;
-
-		buddy = purple_buddy_new(session->account, username, NULL);
-		user->buddy = buddy;
-		buddy->proto_data = (gpointer)user;
 	}
 
 	/* Update user structure with new information */
@@ -1005,7 +998,6 @@
 	purple_notify_user_info_destroy(user_info);
 
 	if (user->temporary_user) {
-		purple_blist_remove_buddy(user->buddy);
 		g_free(user->client_info);
 		g_free(user->gender);
 		g_free(user->location);
@@ -1026,7 +1018,6 @@
 {
 	MsimSession *session;
 	MsimUser *user;
-	guint uid;
 	gchar *user_to_lookup;
 	MsimMessage *user_msg;
 
@@ -1041,8 +1032,8 @@
 	user = msim_find_user(session, username);
 
 	/* If is on buddy list, lookup by uid since it is faster. */
-	if (user && (uid = purple_blist_node_get_int(&user->buddy->node, "UserID"))) {
-		user_to_lookup = g_strdup_printf("%d", uid);
+	if (user && user->id) {
+		user_to_lookup = g_strdup_printf("%d", user->id);
 	} else {
 		/* Looking up buddy not on blist. Lookup by whatever user entered. */
 		user_to_lookup = g_strdup(username);
@@ -1334,28 +1325,21 @@
 {
 	MsimSession *session;
 	time_t delta;
-	gchar *errmsg;
 
 	session = (MsimSession *)data;
 
 	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 
 	delta = time(NULL) - session->last_comm;
+
 	/* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */
 	if (delta >= MSIM_KEEPALIVE_INTERVAL) {
-	        errmsg = g_strdup_printf(ngettext("Connection to server lost (no data received within %d second)",
-						  "Connection to server lost (no data received within %d seconds)",
-						  (int)delta),
-					 (int)delta);
-
-		purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n",
-				errmsg, MSIM_KEEPALIVE_INTERVAL);
+		purple_debug_info("msim",
+				"msim_check_alive: %zu > interval of %d, presumed dead\n",
+				delta, MSIM_KEEPALIVE_INTERVAL);
 		purple_connection_error_reason(session->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg);
-
-		purple_notify_error(session->gc, NULL, errmsg, NULL);
-
-		g_free(errmsg);
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Lost connection with server"));
 
 		return FALSE;
 	}
@@ -1985,10 +1969,11 @@
 		purple_blist_add_buddy(buddy, NULL, NULL, NULL);
 
 		user = msim_get_user_from_buddy(buddy);
-
-		/* All buddies on list should have a UserID integer associated with them. */
-		purple_blist_node_set_int(&buddy->node, "UserID", msim_msg_get_integer(msg, "f"));
-		
+		user->id = msim_msg_get_integer(msg, "f");
+
+		/* Keep track of the user ID across sessions */
+		purple_blist_node_set_int(&buddy->node, "UserID", user->id);
+
 		msim_store_user_info(session, msg, NULL);
 	} else {
 		purple_debug_info("msim", "msim_status: found buddy %s\n", username);
@@ -2881,7 +2866,8 @@
 	/* 3. Update buddy information */
 	user = msim_get_user_from_buddy(buddy);
 
-	/* All buddies on list should have 'uid' integer associated with them. */
+	user->id = uid;
+	/* Keep track of the user ID across sessions */
 	purple_blist_node_set_int(&buddy->node, "UserID", uid);
 
 	/* Stores a few fields in the MsimUser, relevant to the buddy itself.
--- a/libpurple/protocols/myspace/release.sh	Sun Dec 14 23:43:52 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-#!/bin/bash
-# Created:20070618
-# By Jeff Connelly
-
-# Package a new msimprpl for release. Must be run with bash.
-
-VERSION=0.18
-make
-# Include 'myspace' directory in archive, so it can easily be unextracted
-# into ~/pidgin/libpurple/protocols at the correct location.
-# (if this command fails, run it manually).
-# This convenient command requires bash.
-cd ../../..
-tar -cf libpurple/protocols/msimprpl-$VERSION.tar libpurple/protocols/myspace/{CHANGES,ChangeLog,LICENSE,Makefile.*,*.c,*.h,README,release.sh,.deps/*} autogen.sh configure.ac
-cd libpurple/protocols/myspace
-gzip ../msimprpl-$VERSION.tar
-
-mv ~/pidgin/config.h ~/pidgin/config.h-
-make -f Makefile.mingw
-mv ~/pidgin/config.h- ~/pidgin/config.h
-cp ~/pidgin/win32-install-dir/plugins/libmyspace.dll .
-# Zip is more common with Win32 users. Just include a few files in this archive,
-# but (importantly) preserve the install directory structure!
-mkdir -p win32-archive/plugins
-cp libmyspace.dll win32-archive/plugins
-mkdir -p win32-archive/pixmaps/pidgin/protocols/{48,22,16}
-cp ~/pidgin/win32-install-dir/pixmaps/pidgin/protocols/48/myspace.png \
-                win32-archive/pixmaps/pidgin/protocols/48/
-cp ~/pidgin/win32-install-dir/pixmaps/pidgin/protocols/22/myspace.png \
-                win32-archive/pixmaps/pidgin/protocols/22/
-cp ~/pidgin/win32-install-dir/pixmaps/pidgin/protocols/16/myspace.png \
-                win32-archive/pixmaps/pidgin/protocols/16/
-mkdir -p win32-archive/pixmaps/pidgin/emotes/default
-cp ~/pidgin/win32-install-dir/pixmaps/pidgin/emotes/default/theme \
-        win32-archive/pixmaps/pidgin/emotes/default/theme
-# Emoticons in MySpaceIM but not Pidgin 2.1.0
-cp ~/pidgin/win32-install-dir/pixmaps/pidgin/emotes/default/{sinister,sidefrown,pirate,mohawk,messed,bulgy-eyes}.png \
-	win32-archive/pixmaps/pidgin/emotes/default/
-
-# Use DOS line endings and .txt file extension for convenience
-u2d < README > win32-archive/msimprpl-README.txt
-u2d < LICENSE > win32-archive/msimprpl-LICENSE.txt
-u2d < CHANGES > win32-archive/msimprpl-CHANGES.txt
-cd win32-archive
-zip -r ../../msimprpl-$VERSION-win32.zip *
-cd ..
-rm -rf win32-archive
-ls -l ../msimprpl-$VERSION*
--- a/libpurple/protocols/myspace/session.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/myspace/session.h	Mon Dec 15 08:39:08 2008 +0000
@@ -20,6 +20,8 @@
 #ifndef _MYSPACE_SESSION_H
 #define _MYSPACE_SESSION_H
 
+#include "account.h"
+
 /* Random number in every MsimSession, to ensure it is valid. */
 #define MSIM_SESSION_STRUCT_MAGIC       0xe4a6752b
 
--- a/libpurple/protocols/myspace/user.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Mon Dec 15 08:39:08 2008 +0000
@@ -63,6 +63,7 @@
 		/* TODO: where is this freed? */
 		user = g_new0(MsimUser, 1);
 		user->buddy = buddy;
+		user->id = purple_blist_node_get_int(&buddy->node, "UserID");
 		buddy->proto_data = (gpointer)user;
 	} 
 
@@ -96,7 +97,6 @@
 {
 	PurplePresence *presence;
 	gchar *str;
-	guint uid;
 	guint cv;
 
 	/* Useful to identify the account the tooltip refers to. 
@@ -105,19 +105,6 @@
 		purple_notify_user_info_add_pair(user_info, _("User"), user->username);
 	}
 
-	uid = purple_blist_node_get_int(&user->buddy->node, "UserID");
-
-	if (full) {
-		/* TODO: link to username, if available */
-		if (uid) {
-			char *profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">http://myspace.com/%d</a>",
-											uid, uid);
-			purple_notify_user_info_add_pair(user_info, _("Profile"), profile);
-			g_free(profile);
-		}
-	}
-
-
 	/* a/s/l...the vitals */
 	if (user->age) {
 		char age[16];
@@ -138,21 +125,23 @@
 		purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline);
 	}
 
-	presence = purple_buddy_get_presence(user->buddy);
+	if (user->buddy != NULL) {
+		presence = purple_buddy_get_presence(user->buddy);
+
+		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
+			PurpleStatus *status;
+			const char *artist, *title;
 
-	if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
-		PurpleStatus *status;
-		const char *artist, *title;
-		
-		status = purple_presence_get_status(presence, "tune");
-		title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
-		artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
+			status = purple_presence_get_status(presence, "tune");
+			title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
+			artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
 
-		str = msim_format_now_playing(artist, title);
-		if (str && *str) {
-			purple_notify_user_info_add_pair(user_info, _("Song"), str);
+			str = msim_format_now_playing(artist, title);
+			if (str && *str) {
+				purple_notify_user_info_add_pair(user_info, _("Song"), str);
+			}
+			g_free(str);
 		}
-		g_free(str);
 	}
 
 	/* Note: total friends only available if looked up by uid, not username. */
@@ -180,6 +169,16 @@
 			purple_notify_user_info_add_pair(user_info, _("Client Version"), client);
 		g_free(client);
 	}
+
+	if (full && user->id) {
+		/* TODO: link to username, if available */
+		char *profile;
+		purple_notify_user_info_add_section_break(user_info);
+		profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">%s</a>",
+				user->id, _("View web profile"));
+		purple_notify_user_info_add_pair(user_info, NULL, profile);
+		g_free(profile);
+	}
 }
 
 /** Set the currently playing song artist and or title.
@@ -200,12 +199,16 @@
 	PurplePresence *presence;
 	const char *prev_artist, *prev_title;
 
+	if (user->buddy == NULL)
+		/* User not on buddy list so nothing to do */
+		return;
+
 	prev_artist = NULL;
 	prev_title = NULL;
 
-	if (new_artist && !strlen(new_artist))
+	if (new_artist && !*new_artist)
 		new_artist = NULL;
-	if (new_title && !strlen(new_title))
+	if (new_title && !*new_title)
 		new_title = NULL;
 
 	if (!new_artist && !new_title) {
@@ -247,10 +250,11 @@
 {
 	if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) {
 		/* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */
+		user->id = atol(value_str);
 		if (user->buddy)
 		{
 			purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name);
-			purple_blist_node_set_int(&user->buddy->node, "UserID", atol(value_str));
+			purple_blist_node_set_int(&user->buddy->node, "UserID", user->id);
 		}
 		/* Need to store in MsimUser, too? What if not on blist? */
 	} else if (g_str_equal(key_str, "Age")) {
--- a/libpurple/protocols/myspace/user.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/myspace/user.h	Mon Dec 15 08:39:08 2008 +0000
@@ -25,6 +25,7 @@
 typedef struct _MsimUser
 {
 	PurpleBuddy *buddy;
+	int id;
 	guint client_cv;
 	gchar *client_info;
 	guint age;
--- a/libpurple/protocols/null/nullprpl.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Mon Dec 15 08:39:08 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/oscar/bstream.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/oscar/bstream.c	Mon Dec 15 08:39:08 2008 +0000
@@ -302,3 +302,12 @@
 
 	return len;
 }
+
+int byte_stream_putuid(ByteStream *bs, OscarData *od)
+{
+	PurpleAccount *account;
+
+	account = purple_connection_get_account(od->gc);
+
+	return byte_stream_putle32(bs, atoi(purple_account_get_username(account)));
+}
--- a/libpurple/protocols/oscar/family_auth.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Mon Dec 15 08:39:08 2008 +0000
@@ -295,10 +295,9 @@
 	/*
 	 * No matter what, we should have a screen name.
 	 */
-	memset(od->sn, 0, sizeof(od->sn));
 	if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
 		info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1);
-		strncpy(od->sn, info->sn, sizeof(od->sn));
+		purple_connection_set_display_name(od->gc, info->sn);
 	}
 
 	/*
@@ -598,18 +597,18 @@
 {
 	if (od->authinfo != NULL)
 	{
-		free(od->authinfo->sn);
-		free(od->authinfo->bosip);
-		free(od->authinfo->errorurl);
-		free(od->authinfo->email);
-		free(od->authinfo->chpassurl);
-		free(od->authinfo->latestrelease.name);
-		free(od->authinfo->latestrelease.url);
-		free(od->authinfo->latestrelease.info);
-		free(od->authinfo->latestbeta.name);
-		free(od->authinfo->latestbeta.url);
-		free(od->authinfo->latestbeta.info);
-		free(od->authinfo);
+		g_free(od->authinfo->sn);
+		g_free(od->authinfo->bosip);
+		g_free(od->authinfo->errorurl);
+		g_free(od->authinfo->email);
+		g_free(od->authinfo->chpassurl);
+		g_free(od->authinfo->latestrelease.name);
+		g_free(od->authinfo->latestrelease.url);
+		g_free(od->authinfo->latestrelease.info);
+		g_free(od->authinfo->latestbeta.name);
+		g_free(od->authinfo->latestbeta.url);
+		g_free(od->authinfo->latestbeta.info);
+		g_free(od->authinfo);
 	}
 }
 
--- a/libpurple/protocols/oscar/family_icbm.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Mon Dec 15 08:39:08 2008 +0000
@@ -1214,7 +1214,7 @@
 	/*
 	 * Your UIN
 	 */
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 
 	/*
 	 * TLV t(type) l(strlen(message)+1) v(message+NULL)
--- a/libpurple/protocols/oscar/family_icq.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Mon Dec 15 08:39:08 2008 +0000
@@ -36,7 +36,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
-	purple_debug_info("oscar", "Requesting offline messages from %s", od->sn);
+	purple_debug_info("oscar", "Requesting offline messages\n");
 
 	bslen = 2 + 4 + 2 + 2;
 
@@ -49,7 +49,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x003c); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
@@ -70,7 +70,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
-	purple_debug_info("oscar", "Acknowledged receipt of offline messages from %s", od->sn);
+	purple_debug_info("oscar", "Acknowledged receipt of offline messages\n");
 
 	bslen = 2 + 4 + 2 + 2;
 
@@ -83,7 +83,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x003e); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
@@ -117,7 +117,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x0c3a); /* shrug. */
@@ -172,7 +172,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x042e); /* shrug. */
@@ -212,7 +212,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x04b2); /* shrug. */
@@ -259,7 +259,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x04ba); /* shrug. */
@@ -303,7 +303,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x051f); /* shrug. */
@@ -341,7 +341,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x0998); /* shrug. */
@@ -377,11 +377,12 @@
 int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias)
 {
 	FlapConnection *conn;
+	PurpleAccount *account;
 	ByteStream bs;
 	aim_snacid_t snacid;
 	int bslen, xmllen;
 	char *xml;
-	const char *timestr;
+	const char *timestr, *username;
 	time_t t;
 	struct tm *tm;
 	gchar *stripped;
@@ -392,6 +393,9 @@
 	if (!name || !msg || !alias)
 		return -EINVAL;
 
+	account = purple_connection_get_account(od->gc);
+	username = purple_account_get_username(account);
+
 	time(&t);
 	tm = gmtime(&t);
 	timestr = purple_utf8_strftime("%a, %d %b %Y %T %Z", tm);
@@ -399,7 +403,7 @@
 	stripped = purple_markup_strip_html(msg);
 
 	/* The length of xml included the null terminating character */
-	xmllen = 209 + strlen(name) + strlen(stripped) + strlen(od->sn) + strlen(alias) + strlen(timestr) + 1;
+	xmllen = 209 + strlen(name) + strlen(stripped) + strlen(username) + strlen(alias) + strlen(timestr) + 1;
 
 	xml = g_new(char, xmllen);
 	snprintf(xml, xmllen, "<icq_sms_message>"
@@ -411,7 +415,7 @@
 		"<delivery_receipt>Yes</delivery_receipt>"
 		"<time>%s</time>"
 		"</icq_sms_message>",
-		name, stripped, od->sn, alias, timestr);
+		name, stripped, username, alias, timestr);
 
 	bslen = 36 + xmllen;
 
@@ -424,7 +428,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
@@ -481,7 +485,7 @@
 	byte_stream_put16(&bs, bslen);
 
 	byte_stream_putle16(&bs, bslen - 2);
-	byte_stream_putle32(&bs, atoi(od->sn));
+	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 	byte_stream_putle16(&bs, 0x0fa0); /* shrug. */
--- a/libpurple/protocols/oscar/family_locate.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Mon Dec 15 08:39:08 2008 +0000
@@ -203,11 +203,9 @@
 	 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
 	  0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
 
-	/*
-	{OSCAR_CAPABILITY_ICQ2GO,
+	{OSCAR_CAPABILITY_TYPING,
 	 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
 	  0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
-	*/
 
 	/*
 	 * Chat is oddball.
--- a/libpurple/protocols/oscar/oscar.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Dec 15 08:39:08 2008 +0000
@@ -68,7 +68,7 @@
 
 static OscarCapability purple_caps = (OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM |
 									  OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE |
-									  OSCAR_CAPABILITY_SHORTCAPS);
+									  OSCAR_CAPABILITY_SHORTCAPS | OSCAR_CAPABILITY_TYPING);
 
 static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
 static guint8 features_icq[] = {0x01, 0x06};
@@ -3187,6 +3187,12 @@
 		}
 	}
 
+	purple_notify_user_info_add_section_break(user_info);
+	tmp = g_strdup_printf("<a href=\"http://profiles.aim.com/%s\">%s</a>",
+			purple_normalize(account, userinfo->sn), _("View web profile"));
+	purple_notify_user_info_add_pair(user_info, NULL, tmp);
+	g_free(tmp);
+
 	purple_notify_userinfo(gc, userinfo->sn, user_info, NULL, NULL);
 	purple_notify_user_info_destroy(user_info);
 
--- a/libpurple/protocols/oscar/oscar.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Mon Dec 15 08:39:08 2008 +0000
@@ -362,8 +362,9 @@
 	OSCAR_CAPABILITY_LIVEVIDEO            = 0x02000000,
 	OSCAR_CAPABILITY_CAMERA               = 0x04000000,
 	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x08000000,
-	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x10000000,
-	OSCAR_CAPABILITY_LAST                 = 0x20000000
+	OSCAR_CAPABILITY_TYPING               = 0x10000000,
+	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x20000000,
+	OSCAR_CAPABILITY_LAST                 = 0x40000000
 } OscarCapability;
 
 /*
@@ -488,16 +489,8 @@
 		guint maxawaymsglen; /* max size (bytes) of posted away message */
 	} rights;
 
-	/* ---- Client Accessible ------------------------ */
-
-	/* Our screen name. */
-	/* TODO: Get rid of this and use purple_account_get_username() everywhere? */
-	char sn[MAXSNLEN+1];
-
 	PurpleConnection *gc;
 
-	/* ---- Internal Use Only ------------------------ */
-
 	void *modlistv;
 
 	/*
@@ -1587,6 +1580,7 @@
 int byte_stream_putraw(ByteStream *bs, const guint8 *v, int len);
 int byte_stream_putstr(ByteStream *bs, const char *str);
 int byte_stream_putbs(ByteStream *bs, ByteStream *srcbs, int len);
+int byte_stream_putuid(ByteStream *bs, OscarData *od);
 int byte_stream_putcaps(ByteStream *bs, guint32 caps);
 
 /*
--- a/libpurple/protocols/qq/ChangeLog	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Mon Dec 15 08:39:08 2008 +0000
@@ -58,7 +58,7 @@
 
 #define QQ_PUBLISH_SIZE 3
 static const gchar *publish_types[] = {
-	N_("Visible"), N_("Firend Only"), N_("Private")
+	N_("Visible"), N_("Friend Only"), N_("Private")
 };
 
 #define QQ_GENDER_SIZE 3
@@ -87,7 +87,7 @@
 	QQ_INFO_UNKNOW3, QQ_INFO_UNKNOW4, QQ_INFO_UNKNOW5,
 	QQ_INFO_IS_PUB_MOBILE, QQ_INFO_IS_PUB_CONTACT, QQ_INFO_COLLEGE, QQ_INFO_HOROSCOPE,
 	QQ_INFO_ZODIAC, QQ_INFO_BLOOD, QQ_INFO_SHOW, QQ_INFO_UNKNOW6,
-	QQ_INFO_LAST_2007, QQ_INFO_LAST,
+	QQ_INFO_LAST_2007, QQ_INFO_LAST
 };
 
 enum {
@@ -95,7 +95,7 @@
 };
 
 enum {
-	QQ_FIELD_LABEL = 0, QQ_FIELD_STRING, QQ_FIELD_MULTI, QQ_FIELD_BOOL, QQ_FIELD_CHOICE,
+	QQ_FIELD_LABEL = 0, QQ_FIELD_STRING, QQ_FIELD_MULTI, QQ_FIELD_BOOL, QQ_FIELD_CHOICE
 };
 
 typedef struct {
@@ -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."));
 	}
 }
 
@@ -553,7 +553,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 +564,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 +592,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 +614,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 +652,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 +790,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 +825,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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Mon Dec 15 08:39:08 2008 +0000
@@ -71,7 +71,7 @@
 	QQ_BUDDY_INFO_MODIFY_BASE,
 	QQ_BUDDY_INFO_MODIFY_EXT,
 	QQ_BUDDY_INFO_MODIFY_ADDR,
-	QQ_BUDDY_INFO_MODIFY_CONTACT,
+	QQ_BUDDY_INFO_MODIFY_CONTACT
 };
 
 gchar *qq_get_icon_name(gint face);
@@ -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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Mon Dec 15 08:39:08 2008 +0000
@@ -46,7 +46,7 @@
 enum {
 	QQ_MY_AUTH_APPROVE = 0x30,	/* ASCII value of "0" */
 	QQ_MY_AUTH_REJECT = 0x31,	/* ASCII value of "1" */
-	QQ_MY_AUTH_REQUEST = 0x32,	/* ASCII value of "2" */
+	QQ_MY_AUTH_REQUEST = 0x32	/* ASCII value of "2" */
 };
 
 typedef struct _qq_buddy_req {
@@ -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,9 +317,9 @@
 	add_req->auth_len = 0;
 
 	who = uid_to_purple_name(uid);
-	msg = g_strdup_printf(_("%d needs Q&A"), uid);
-	purple_request_input(gc, _("Add buddy Q&A"), msg,
-			_("Input answer here"),
+	msg = g_strdup_printf(_("%u requires verification"), uid);
+	purple_request_input(gc, _("Add buddy question"), msg,
+			_("Enter answer here"),
 			NULL,
 			TRUE, FALSE, NULL,
 			_("Send"), G_CALLBACK(add_buddy_question_cb),
@@ -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);
@@ -616,7 +616,7 @@
 	qq_buddy_req *add_req = (qq_buddy_req *)data;
 	gchar *who = uid_to_purple_name(add_req->uid);
 	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
-			NULL, _("Sorry, You are not my style."), TRUE, FALSE, NULL,
+			NULL, _("Sorry, you're not my style."), TRUE, FALSE, NULL,
 			_("OK"), G_CALLBACK(buddy_add_deny_reason_cb),
 			_("Cancel"), G_CALLBACK(buddy_add_deny_noreason_cb),
 			purple_connection_get_account(add_req->gc), who, NULL,
@@ -661,9 +661,9 @@
 	}
 
 	who = uid_to_purple_name(uid);
-	msg = g_strdup_printf(_("%d needs authentication"), uid);
+	msg = g_strdup_printf(_("%u needs authorization"), uid);
 	purple_request_input(gc, _("Add buddy authorize"), msg,
-			_("Input request here"),
+			_("Enter request here"),
 			_("Would you be my friend?"),
 			TRUE, FALSE, NULL,
 			_("Send"), G_CALLBACK(add_buddy_auth_cb),
@@ -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/buddy_opt.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.h	Mon Dec 15 08:39:08 2008 +0000
@@ -38,14 +38,14 @@
 	QQ_AUTH_INFO_TEMP_SESSION = 0x0003,
 	QQ_AUTH_INFO_CLUSTER = 0x0002,
 	QQ_AUTH_INFO_REMOVE_BUDDY = 0x0006,
-	QQ_AUTH_INFO_UPDATE_BUDDY_INFO = 0x0007,
+	QQ_AUTH_INFO_UPDATE_BUDDY_INFO = 0x0007
 };
 
 enum {
 	QQ_QUESTION_GET = 0x01,
 	QQ_QUESTION_SET = 0x02,
 	QQ_QUESTION_REQUEST = 0x03,		/* get question only*/
-	QQ_QUESTION_ANSWER = 0x04,
+	QQ_QUESTION_ANSWER = 0x04
 };
 
 void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
--- a/libpurple/protocols/qq/char_conv.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.h	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group.c	Mon Dec 15 08:39:08 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);
 }
@@ -116,7 +116,7 @@
 	return qd->roomlist;
 }
 
-/* free roomlist space, I have no idea when this one is called ... */
+/* free roomlist space, I have no idea when this one is called... */
 void qq_roomlist_cancel(PurpleRoomlist *list)
 {
 	qq_data *qd;
--- a/libpurple/protocols/qq/group.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group.h	Mon Dec 15 08:39:08 2008 +0000
@@ -37,7 +37,7 @@
 	QQ_ROOM_ROLE_NO = 0x00,	/* default 0x00 means not member */
 	QQ_ROOM_ROLE_YES,
 	QQ_ROOM_ROLE_REQUESTING,
-	QQ_ROOM_ROLE_ADMIN,
+	QQ_ROOM_ROLE_ADMIN
 } qq_room_role;
 
 typedef struct _qq_room_data qq_room_data;
--- a/libpurple/protocols/qq/group_im.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Mon Dec 15 08:39:08 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,226 @@
 		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;
+}
+
+#if 0
+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);
+}
+#endif
+
+/* 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 %" G_GSIZE_FORMAT ":\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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Mon Dec 15 08:39:08 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_info.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group_info.h	Mon Dec 15 08:39:08 2008 +0000
@@ -31,7 +31,7 @@
 
 enum {
 	QQ_ROOM_INFO_UPDATE_ONLY = 0,
-	QQ_ROOM_INFO_DISPLAY,
+	QQ_ROOM_INFO_DISPLAY
 };
 
 gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, gint update_class);
--- a/libpurple/protocols/qq/group_internal.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Mon Dec 15 08:39:08 2008 +0000
@@ -44,7 +44,7 @@
 enum {
 	QQ_ROOM_JOIN_OK = 0x01,
 	QQ_ROOM_JOIN_NEED_AUTH = 0x02,
-	QQ_ROOM_JOIN_DENIED = 0x03,
+	QQ_ROOM_JOIN_DENIED = 0x03
 };
 
 enum {
@@ -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(_("Successfully joined 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, _("Successfully joined Qun"));
 	}
 }
 
@@ -254,29 +254,29 @@
 	g_return_if_fail(rmd != NULL);
 	switch (reply) {
 	case QQ_ROOM_JOIN_OK:
-		purple_debug_info("QQ", "Successed in joining group \"%s\"\n", rmd->title_utf8);
+		purple_debug_info("QQ", "Succeeded in joining group \"%s\"\n", rmd->title_utf8);
 		rmd->my_role = QQ_ROOM_ROLE_YES;
 		/* this must be shown before getting online members */
 		qq_room_conv_open(gc, rmd);
 		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 from joining"), 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"));
+		purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknown 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Mon Dec 15 08:39:08 2008 +0000
@@ -122,7 +122,7 @@
 
 	who = uid_to_purple_name(add_req->member);
 	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
-			NULL, _("Sorry, you are not our style ..."), TRUE, FALSE, NULL,
+			NULL, _("Sorry, you are not our style"), TRUE, FALSE, NULL,
 			_("OK"), G_CALLBACK(member_join_deny_reason_cb),
 			_("Cancel"), G_CALLBACK(member_join_deny_noreason_cb),
 			purple_connection_get_account(add_req->gc), who, NULL,
@@ -202,9 +202,9 @@
 	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);
+	qq_room_got_chat_in(gc, id, 0, _("Successfully changed Qun members"), now);
 }
 
 void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd)
@@ -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 detailed 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>Joining 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Mon Dec 15 08:39:08 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 %" G_GSIZE_FORMAT ":\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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/im.h	Mon Dec 15 08:39:08 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,
@@ -54,18 +45,32 @@
 	QQ_MSG_SYS_30 = 0x0030,
 	QQ_MSG_SYS_4C = 0x004C,
 	QQ_MSG_EXTEND = 0x0084,
-	QQ_MSG_EXTEND_85 = 0x0085,
+	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/packet_parse.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.c	Mon Dec 15 08:39:08 2008 +0000
@@ -32,11 +32,9 @@
 /* note:
  * 1, in these functions, 'b' stands for byte, 'w' stands for word, 'dw' stands for double word.
  * 2, we use '*cursor' and 'buf' as two addresses to calculate the length.
- * 3, change '0' to '1', if want to get more info about the packet parsing. */
+ * 3, change 'undef' to 'define' to get more info about the packet parsing. */
 
-#if 0
-#define PARSER_DEBUG
-#endif
+#undef PARSER_DEBUG
 
 /* read one byte from buf,
  * return the number of bytes read if succeeds, otherwise return -1 */
--- a/libpurple/protocols/qq/qq.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Mon Dec 15 08:39:08 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,11 +99,14 @@
 
 	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));
-		return;
+	custom_server = purple_account_get_string(account, "server", NULL);
+
+	if (custom_server != NULL) {
+		purple_debug_info("QQ", "Select server '%s'\n", custom_server);
+		if (*custom_server != '\0' && g_ascii_strcasecmp(custom_server, "auto") != 0) {
+			qd->servers = g_list_append(qd->servers, g_strdup(custom_server));
+			return;
+		}
 	}
 
 	if (qd->use_tcp) {
@@ -439,110 +440,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)
 {
@@ -599,7 +496,7 @@
 	icon_path = qq_get_icon_path(icon_name);
 	g_free(icon_name);
 
-	purple_debug_info("QQ", "Change prev icon %s to ...\n", icon_path);
+	purple_debug_info("QQ", "Change prev icon %s to...\n", icon_path);
 	purple_request_file(action, _("Select icon..."), icon_path,
 			FALSE,
 			G_CALLBACK(qq_change_icon_cb), NULL,
@@ -760,13 +657,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 +703,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 +722,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 +935,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 +973,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 */
@@ -1172,7 +1070,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");
@@ -1190,7 +1087,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);
@@ -1210,7 +1106,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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq_base.c	Mon Dec 15 08:39:08 2008 +0000
@@ -68,10 +68,11 @@
 	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"));
+				_("Cannot decrypt server reply"));
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
@@ -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;
 }
@@ -159,7 +160,7 @@
 	if (len < 11) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
-				_("Can not decrypt get server reply"));
+				_("Cannot decrypt server reply"));
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
@@ -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);
@@ -423,7 +424,7 @@
 			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
 					">>> [default] decrypt and dump");
 			error = g_strdup_printf(
-						_("Unknow reply code when login (0x%02X)"),
+						_("Unknown reply code when logging in (0x%02X)"),
 						ret );
 			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 			break;
@@ -463,14 +464,16 @@
 	qq_data *qd;
 	gchar **segments;
 
-	g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
+	g_return_val_if_fail(data != NULL, FALSE);
+	g_return_val_if_fail(data_len != 0, FALSE);
 
 	qd = (qq_data *) gc->proto_data;
 
 	/* qq_show_packet("Keep alive reply packet", data, len); */
 
 	/* the last one is 60, don't know what it is */
-	if (NULL == (segments = split_data(data, data_len, "\x1f", 6)))
+	segments = split_data(data, data_len, "\x1f", 6);
+	if (segments == NULL)
 			return TRUE;
 
 	/* segments[0] and segment[1] are all 0x30 ("0") */
@@ -478,7 +481,7 @@
 	if(0 == qd->online_total) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Keep alive error"));
+				_("Lost connection with server"));
 	}
 	qd->my_ip.s_addr = inet_addr(segments[3]);
 	qd->my_port = strtol(segments[4], NULL, 10);
@@ -502,7 +505,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 +524,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"));
+				_("Lost connection with server"));
 	}
-
-	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 +568,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"));
+				_("Lost connection with server"));
 	}
-
-	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;
@@ -652,7 +655,7 @@
 	if (data_len < 15) {
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
-				_("Can not decrypt get server reply"));
+				_("Could not decrypt server reply"));
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
@@ -744,7 +747,7 @@
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
 
-	purple_connection_update_progress(gc, _("Requesting captcha ..."), 3, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Requesting captcha"), 3, QQ_CONNECT_STEPS);
 }
 
 static void request_token_ex_code(PurpleConnection *gc,
@@ -789,7 +792,7 @@
 	qd->send_seq++;
 	qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
 
-	purple_connection_update_progress(gc, _("Checking code of captcha ..."), 3, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Checking captcha"), 3, QQ_CONNECT_STEPS);
 }
 
 typedef struct {
@@ -812,7 +815,7 @@
 
 	purple_connection_error_reason(captcha_req->gc,
 			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
-			_("Failed captcha verify"));
+			_("Failed captcha verification"));
 }
 
 static void captcha_input_ok_cb(qq_captcha_request *captcha_req,
@@ -871,8 +874,8 @@
 	purple_request_field_group_add_field(group, field);
 
 	purple_request_fields(account,
-		_("QQ Captcha Verifing"),
-		_("QQ Captcha Verifing"),
+		_("QQ Captcha Verification"),
+		_("QQ Captcha Verification"),
 		_("Enter the text from the image"),
 		fields,
 		_("OK"), G_CALLBACK(captcha_input_ok_cb),
@@ -1110,7 +1113,7 @@
 			qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
 					">>> [default] decrypt and dump");
 			error = g_strdup_printf(
-						_("Unknow reply code when checking password (0x%02X)"),
+						_("Unknown reply when checking password (0x%02X)"),
 						ret );
 			reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 			break;
@@ -1256,7 +1259,7 @@
 				/* Missing get server before login*/
 			default:
 				error = g_strdup_printf(
-						_("Unknow reply code when login (0x%02X):\n%s"),
+						_("Unknown reply code when logging in (0x%02X):\n%s"),
 						ret, msg_utf8);
 				break;
 		}
@@ -1279,7 +1282,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);
@@ -1445,7 +1448,7 @@
 				break;
 			default:
 				error = g_strdup_printf(
-						_("Unknow reply code when login (0x%02X):\n%s"),
+						_("Unknown reply code when logging in (0x%02X):\n%s"),
 						ret, msg_utf8);
 				break;
 		}
@@ -1468,7 +1471,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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.c	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq_define.h	Mon Dec 15 08:39:08 2008 +0000
@@ -74,7 +74,7 @@
 	QQ_CMD_ADD_BUDDY_NO_AUTH_EX = 0x00A7,			/* add friend without auth */
 	QQ_CMD_ADD_BUDDY_AUTH_EX = 0x00A8, 				/* add buddy with auth */
 	QQ_CMD_BUDDY_CHECK_CODE =  0x00B5,
-	QQ_CMD_BUDDY_QUESTION =  0x00B7,
+	QQ_CMD_BUDDY_QUESTION =  0x00B7
 };
 
 const gchar *qq_get_cmd_desc(gint type);
@@ -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,
 
@@ -104,7 +104,7 @@
 	QQ_ROOM_CMD_TEMP_QUIT = 0x32,
 	QQ_ROOM_CMD_TEMP_GET_INFO = 0x33,
 	QQ_ROOM_CMD_TEMP_SEND_IM = 0x35,
-	QQ_ROOM_CMD_TEMP_GET_MEMBERS = 0x37,
+	QQ_ROOM_CMD_TEMP_GET_MEMBERS = 0x37
 };
 
 const gchar *qq_get_room_cmd_desc(gint room_cmd);
@@ -119,7 +119,7 @@
 	QQ_SERVER_BUDDY_ADDING_EX = 40,
 	QQ_SERVER_BUDDY_ADD_REQUEST_EX = 41,
 	QQ_SERVER_BUDDY_ADDED_ANSWER = 42,
-	QQ_SERVER_BUDDY_ADDED_EX = 43,
+	QQ_SERVER_BUDDY_ADDED_EX = 43
 };
 
 enum {
@@ -128,7 +128,7 @@
 	QQ_BUDDY_CHANGE_TO_OFFLINE = 20,
 	QQ_BUDDY_ONLINE_AWAY = 30,
 	QQ_BUDDY_ONLINE_INVISIBLE = 40,
-	QQ_BUDDY_ONLINE_BUSY = 50,
+	QQ_BUDDY_ONLINE_BUSY = 50
 };
 
 gboolean is_online(guint8 status);
--- a/libpurple/protocols/qq/qq_network.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Mon Dec 15 08:39:08 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:
@@ -381,7 +376,7 @@
 			/* No worries */
 			return;
 
-		error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno));
+		error_msg = g_strdup_printf(_("Lost connection with server:\n%s"), g_strerror(errno));
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				error_msg);
@@ -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));
@@ -776,12 +773,12 @@
 	set_all_keys( gc );
 
 	if (qd->client_version >= 2007) {
-		purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS);
+		purple_connection_update_progress(gc, _("Getting server"), 2, QQ_CONNECT_STEPS);
 		qq_request_get_server(gc);
 		return;
 	}
 
-	purple_connection_update_progress(gc, _("Request token"), 2, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Requesting token"), 2, QQ_CONNECT_STEPS);
 	qq_request_token(gc);
 }
 
@@ -938,7 +935,7 @@
 		return FALSE;
 	}
 
-	purple_connection_update_progress(gc, _("Connecting server ..."), 1, QQ_CONNECT_STEPS);
+	purple_connection_update_progress(gc, _("Connecting to server"), 1, QQ_CONNECT_STEPS);
 
 	purple_debug_info("QQ", "Connect to %s:%d\n", server, port);
 
@@ -954,7 +951,7 @@
 		qd->conn_data = purple_proxy_connect_udp(gc, account, server, port, connect_cb, gc);
 	}
 	if ( qd->conn_data == NULL ) {
-		purple_debug_error("QQ", _("Couldn't create socket"));
+		purple_debug_error("QQ", "Couldn't create socket");
 		return FALSE;
 	}
 #else
@@ -989,7 +986,7 @@
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	purple_debug_info("QQ", "Disconnecting ...\n");
+	purple_debug_info("QQ", "Disconnecting...\n");
 
 	if (qd->network_watcher > 0) {
 		purple_debug_info("QQ", "Remove network watcher\n");
@@ -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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.c	Mon Dec 15 08:39:08 2008 +0000
@@ -87,14 +87,14 @@
 
 	if (data[0] != 0) {
 		purple_debug_warning("QQ", "Failed sent IM\n");
-		purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
+		purple_notify_error(gc, _("Error"), _("Unable to send message."), NULL);
 		return;
 	}
 
 	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;
@@ -496,7 +538,7 @@
 			qq_process_buddy_change_status(data, data_len, gc);
 			break;
 		default:
-			process_unknow_cmd(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq);
+			process_unknow_cmd(gc, _("Unknown SERVER CMD"), data, data_len, cmd, seq);
 			break;
 	}
 }
@@ -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;
@@ -754,7 +796,7 @@
 						seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
 			} else {
 				purple_debug_warning("QQ",
-					   _("Not a member of room \"%s\"\n"), rmd->title_utf8);
+					   "Not a member of room \"%s\"\n", rmd->title_utf8);
 				rmd->my_role = QQ_ROOM_ROLE_NO;
 			}
 			break;
@@ -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;
@@ -903,7 +948,7 @@
 		qq_show_packet("Can not decrypted", rcved, rcved_len);
 		purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
-				_("Can not decrypt login reply"));
+				_("Could not decrypt login reply"));
 		return QQ_LOGIN_REPLY_ERR;
 	}
 
@@ -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 */
 
@@ -974,7 +1019,7 @@
 			qq_update_all(gc, 0);
 			break;
 		default:
-			process_unknow_cmd(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq);
+			process_unknow_cmd(gc, _("Unknown LOGIN CMD"), data, data_len, cmd, seq);
 			return QQ_LOGIN_REPLY_ERR;
 	}
 	return QQ_LOGIN_REPLY_OK;
@@ -1096,7 +1141,7 @@
 			qq_process_buddy_check_code(gc, data, data_len);
 			break;
 		default:
-			process_unknow_cmd(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq);
+			process_unknow_cmd(gc, _("Unknown CLIENT CMD"), data, data_len, cmd, seq);
 			is_unknow = TRUE;
 			break;
 	}
--- a/libpurple/protocols/qq/qq_process.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq_process.h	Mon Dec 15 08:39:08 2008 +0000
@@ -35,7 +35,7 @@
 	QQ_CMD_CLASS_UPDATE_ALL,
 	QQ_CMD_CLASS_UPDATE_ONLINE,
 	QQ_CMD_CLASS_UPDATE_BUDDY,
-	QQ_CMD_CLASS_UPDATE_ROOM,
+	QQ_CMD_CLASS_UPDATE_ROOM
 };
 
 guint8 qq_proc_login_cmds(PurpleConnection *gc,  guint16 cmd, guint16 seq,
--- a/libpurple/protocols/qq/qq_trans.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Mon Dec 15 08:39:08 2008 +0000
@@ -39,7 +39,7 @@
 	QQ_TRANS_IS_SERVER = 0x01,			/* Is server command or client command */
 	QQ_TRANS_IS_IMPORT = 0x02,			/* Only notice if not get reply; or resend, disconn if reties get 0*/
 	QQ_TRANS_REMAINED = 0x04,				/* server command before login*/
-	QQ_TRANS_IS_REPLY = 0x08,				/* server command before login*/
+	QQ_TRANS_IS_REPLY = 0x08				/* server command before login*/
 };
 
 struct _qq_transaction {
--- a/libpurple/protocols/qq/send_file.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/send_file.c	Mon Dec 15 08:39:08 2008 +0000
@@ -612,7 +612,8 @@
 	PurpleConnection *gc;
 	PurpleAccount *account;
 	guint32 to_uid;
-	gchar *filename, *filename_without_path;
+	const gchar *filename;
+	gchar *base_filename;
 
 	g_return_if_fail (xfer != NULL);
 	account = purple_xfer_get_account(xfer);
@@ -621,13 +622,14 @@
 	to_uid = purple_name_to_uid (xfer->who);
 	g_return_if_fail (to_uid != 0);
 
-	filename = (gchar *) purple_xfer_get_local_filename (xfer);
+	filename = purple_xfer_get_local_filename (xfer);
 	g_return_if_fail (filename != NULL);
 
-	filename_without_path = strrchr (filename, '/') + 1;
+	base_filename = g_path_get_basename(filename);
 
-	_qq_send_packet_file_request (gc, to_uid, filename_without_path,
+	_qq_send_packet_file_request (gc, to_uid, base_filename,
 			purple_xfer_get_size(xfer));
+	g_free(base_filename);
 }
 
 /* cancel the transfer of receiving files */
@@ -696,7 +698,7 @@
 		return;
 	}
 	*/
-	filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
+	filename = g_path_get_basename(purple_xfer_get_local_filename(qd->xfer));
 	msg = g_strdup_printf(_("%d has declined the file %s"),
 		 sender_uid, filename);
 
@@ -704,7 +706,8 @@
 	purple_xfer_request_denied(qd->xfer);
 	qd->xfer = NULL;
 
-	g_free (msg);
+	g_free(filename);
+	g_free(msg);
 }
 
 /* process cancel im for file transfer request */
@@ -725,7 +728,7 @@
 		return;
 	}
 	*/
-	filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
+	filename = g_path_get_basename(purple_xfer_get_local_filename(qd->xfer));
 	msg = g_strdup_printf
 		(_("%d canceled the transfer of %s"),
 		 sender_uid, filename);
@@ -734,7 +737,8 @@
 	purple_xfer_cancel_remote(qd->xfer);
 	qd->xfer = NULL;
 
-	g_free (msg);
+	g_free(filename);
+	g_free(msg);
 }
 
 /* process accept im for file transfer request */
--- a/libpurple/protocols/qq/utils.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/qq/utils.h	Mon Dec 15 08:39:08 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/sametime/sametime.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Mon Dec 15 08:39:08 2008 +0000
@@ -158,7 +158,7 @@
   blist_choice_LOCAL = 1, /**< local only */
   blist_choice_MERGE = 2, /**< merge from server */
   blist_choice_STORE = 3, /**< merge from and save to server */
-  blist_choice_SYNCH = 4, /**< sync with server */
+  blist_choice_SYNCH = 4  /**< sync with server */
 };
 
 
@@ -514,6 +514,11 @@
     idle_len = time(NULL) - idle;
     ugly_idle_len = ((time(NULL) * 1000) - idle) / 1000;
 
+	if(idle > ugly_idle_len)
+		ugly_idle_len = 0;
+	else
+		ugly_idle_len = (ugly_idle_len - idle) / 1000;
+
     /* 
        what's the deal here? Well, good clients are smart enough to
        publish their idle time by using an attribute to indicate that
--- a/libpurple/protocols/silc10/wb.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/silc10/wb.c	Mon Dec 15 08:39:08 2008 +0000
@@ -61,14 +61,14 @@
 /* Commands */
 typedef enum {
 	SILCPURPLE_WB_DRAW 	= 0x01,
-	SILCPURPLE_WB_CLEAR	= 0x02,
+	SILCPURPLE_WB_CLEAR	= 0x02
 } SilcPurpleWbCommand;
 
 /* Brush size */
 typedef enum {
 	SILCPURPLE_WB_BRUSH_SMALL = 2,
 	SILCPURPLE_WB_BRUSH_MEDIUM = 5,
-	SILCPURPLE_WB_BRUSH_LARGE = 10,
+	SILCPURPLE_WB_BRUSH_LARGE = 10
 } SilcPurpleWbBrushSize;
 
 /* Brush color (XXX Purple should provide default colors) */
@@ -85,7 +85,7 @@
 	SILCPURPLE_WB_COLOR_TAN		= 12093547,
 	SILCPURPLE_WB_COLOR_BROWN		= 5256485,
 	SILCPURPLE_WB_COLOR_GREY		= 11184810,
-	SILCPURPLE_WB_COLOR_WHITE		= 16777215,
+	SILCPURPLE_WB_COLOR_WHITE		= 16777215
 } SilcPurpleWbColor;
 
 typedef struct {
--- a/libpurple/protocols/simple/simple.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/simple/simple.c	Mon Dec 15 08:39:08 2008 +0000
@@ -1939,6 +1939,13 @@
 		sip->txbuf = purple_circ_buffer_new(0);
 
 	userserver = g_strsplit(username, "@", 2);
+	if (userserver[1] == NULL || userserver[1][0] == '\0') {
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
+			_("SIP connect server not specified"));
+		return;
+	}
+
 	purple_connection_set_display_name(gc, userserver[0]);
 	sip->username = g_strdup(userserver[0]);
 	sip->servername = g_strdup(userserver[1]);
--- a/libpurple/protocols/yahoo/yahoo_aliases.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Mon Dec 15 08:39:08 2008 +0000
@@ -301,7 +301,7 @@
 						  "<ct a=\"1\" yi='%s' nn='%s' />\n</ab>\r\n",
 						  purple_account_get_username(gc->account),
 						  who, converted_alias_jp);
-			free(converted_alias_jp);
+			g_free(converted_alias_jp);
 			g_free(alias_jp);
 		} else {
 			gchar *escaped_alias = g_markup_escape_text(alias, -1);
@@ -321,7 +321,7 @@
 						  "<ct e=\"1\"  yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n",
 						  purple_account_get_username(gc->account),
 						  who, cb->id, converted_alias_jp);
-			free(converted_alias_jp);
+			g_free(converted_alias_jp);
 			g_free(alias_jp);
 		} else {
 			gchar *escaped_alias = g_markup_escape_text(alias, -1);
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Mon Dec 15 08:39:08 2008 +0000
@@ -408,8 +408,10 @@
 	if (ret < 0 && errno == EAGAIN)
 		return;
 	else if (ret <= 0) {
-		purple_debug_info("yahoo", "Buddy icon upload response (%d) bytes (> ~400 indicates failure):\n%.*s\n",
-			d->str->len, d->str->len, d->str->str);
+		/* There are other problems if d->str->len overflows, so shut up the
+		 * warning on 64-bit. */
+		purple_debug_info("yahoo", "Buddy icon upload response (%" G_GSIZE_FORMAT ") bytes (> ~400 indicates failure):\n%.*s\n",
+			d->str->len, (guint)d->str->len, d->str->str);
 
 		yahoo_buddy_icon_upload_data_free(d);
 		return;
@@ -517,7 +519,8 @@
 	g_string_prepend(d->str, header);
 	g_free(header);
 
-	purple_debug_info("yahoo", "Buddy icon upload data:\n%.*s\n", d->str->len, d->str->str);
+	/* There are other problems if we're uploading over 4GB of data */
+	purple_debug_info("yahoo", "Buddy icon upload data:\n%.*s\n", (guint)d->str->len, d->str->str);
 
 	d->fd = source;
 	d->watcher = purple_input_add(d->fd, PURPLE_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d);
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Mon Dec 15 08:39:08 2008 +0000
@@ -807,7 +807,7 @@
 	 */
 	if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0) {
 		purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"), NULL);
-		purple_notify_userinfo(info_data->gc, info_data->name, 
+		purple_notify_userinfo(info_data->gc, info_data->name,
 			user_info, NULL, NULL);
 		purple_notify_user_info_destroy(user_info);
 		g_free(profile_url_text);
@@ -841,10 +841,10 @@
 						 _("If you wish to view this profile, "
 						"you will need to visit this link in your web browser:"),
 						 profile_url_text, profile_url_text);
-		purple_notify_user_info_add_pair(user_info, NULL, tmp);		
+		purple_notify_user_info_add_pair(user_info, NULL, tmp);
 		g_free(tmp);
 
-		purple_notify_userinfo(info_data->gc, info_data->name, 
+		purple_notify_userinfo(info_data->gc, info_data->name,
 				user_info, NULL, NULL);
 
 		g_free(profile_url_text);
@@ -1193,17 +1193,15 @@
 
 	if(!found)
 	{
-		GString *str = g_string_new("");
+		const gchar *str;
 
-		g_string_append_printf(str, "<br><b>");
-		g_string_append_printf(str, _("User information for %s unavailable"),
-				info_data->name);
-		g_string_append_printf(str, "</b><br>");
+		purple_notify_user_info_add_section_break(user_info);
+		purple_notify_user_info_add_pair(user_info,
+				_("Error retrieving profile"), NULL);
 
 		if (profile_state == PROFILE_STATE_UNKNOWN_LANGUAGE) {
-			g_string_append_printf(str, "%s<br><br>",
-					_("Sorry, this profile seems to be in a language "
-					  "or format that is not supported at this time."));
+			str = _("This profile is in a language "
+					  "or format that is not supported at this time.");
 
 		} else if (profile_state == PROFILE_STATE_NOT_FOUND) {
 			PurpleBuddy *b = purple_find_buddy
@@ -1217,27 +1215,26 @@
 				 */
 				f = yahoo_friend_find(b->account->gc, b->name);
 			}
-			g_string_append_printf(str, "%s<br><br>",
-				f?  _("Could not retrieve the user's profile. "
+			str = f ? _("Could not retrieve the user's profile. "
 					  "This most likely is a temporary server-side problem. "
-					  "Please try again later."):
+					  "Please try again later.") :
 					_("Could not retrieve the user's profile. "
 					  "This most likely means that the user does not exist; "
 					  "however, Yahoo! sometimes does fail to find a user's "
 					  "profile. If you know that the user exists, "
-					  "please try again later."));
+					  "please try again later.");
 		} else {
-			g_string_append_printf(str, "%s<br><br>",
-					_("The user's profile is empty."));
+			str = _("The user's profile is empty.");
 		}
-		
-		purple_notify_user_info_add_pair(user_info, NULL, str->str);
-		g_string_free(str, TRUE);
+
+		purple_notify_user_info_add_pair(user_info, NULL, str);
 	}
 
 	/* put a link to the actual profile URL */
-	tmp = g_strdup_printf("<a href=\"%s\">%s</a>", profile_url_text, profile_url_text);
-	purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp);
+	purple_notify_user_info_add_section_break(user_info);
+	tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
+			profile_url_text, _("View web profile"));
+	purple_notify_user_info_add_pair(user_info, NULL, tmp);
 	g_free(tmp);
 
 	g_free(stripped);
--- a/libpurple/protocols/yahoo/yahoochat.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Mon Dec 15 08:39:08 2008 +0000
@@ -1180,7 +1180,7 @@
 
 enum yahoo_room_type {
 	yrt_yahoo,
-	yrt_user,
+	yrt_user
 };
 
 struct yahoo_chatxml_state {
--- a/libpurple/protocols/yahoo/ycht.h	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/yahoo/ycht.h	Mon Dec 15 08:39:08 2008 +0000
@@ -43,7 +43,7 @@
 	YCHT_SERVICE_CHATMSG = 0x41,
 	YCHT_SERVICE_CHATMSG_EMOTE = 0x43,
 	YCHT_SERVICE_PING = 0x62,
-	YCHT_SERVICE_ONLINE_FRIENDS = 0x68,
+	YCHT_SERVICE_ONLINE_FRIENDS = 0x68
 } ycht_service;
 /*
 yahoo: YCHT Service: 0x11 Version: 0x100
--- a/libpurple/protocols/zephyr/zephyr.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Mon Dec 15 08:39:08 2008 +0000
@@ -64,7 +64,7 @@
 	PURPLE_ZEPHYR_NONE, /* Non-kerberized ZEPH0.2 */
 	PURPLE_ZEPHYR_KRB4, /* ZEPH0.2 w/ KRB4 support */
 	PURPLE_ZEPHYR_TZC,  /* tzc executable proxy */
-	PURPLE_ZEPHYR_INTERGALACTIC_KRB4, /* Kerberized ZEPH0.3 */
+	PURPLE_ZEPHYR_INTERGALACTIC_KRB4 /* Kerberized ZEPH0.3 */
 } zephyr_connection_type;
 
 struct _zephyr_account {
@@ -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	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/prpl.h	Mon Dec 15 08:39:08 2008 +0000
@@ -189,7 +189,7 @@
 	 * Used as a hint that unknown commands should not be sent as messages.
 	 * @since 2.1.0
 	 */
-	OPT_PROTO_SLASH_COMMANDS_NATIVE = 0x00000400,
+	OPT_PROTO_SLASH_COMMANDS_NATIVE = 0x00000400
 
 } PurpleProtocolOptions;
 
--- a/libpurple/roomlist.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/roomlist.c	Mon Dec 15 08:39:08 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/smiley.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/smiley.c	Mon Dec 15 08:39:08 2008 +0000
@@ -288,7 +288,7 @@
 {
 	PROP_0,
 	PROP_SHORTCUT,
-	PROP_IMGSTORE,
+	PROP_IMGSTORE
 };
 
 #define PROP_SHORTCUT_S "shortcut"
--- a/libpurple/win32/global.mak	Sun Dec 14 23:43:52 2008 +0000
+++ b/libpurple/win32/global.mak	Mon Dec 15 08:39:08 2008 +0000
@@ -16,11 +16,11 @@
 GTK_BIN ?= $(GTK_TOP)/bin
 BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
 LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.6.30
-MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa1
+MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa2
 NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4
 NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4
 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0
-SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.7
+SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.8
 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
 GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13
 
--- a/pidgin/gtkaccount.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/pidgin/gtkaccount.c	Mon Dec 15 08:39:08 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/gtkblist.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/pidgin/gtkblist.c	Mon Dec 15 08:39:08 2008 +0000
@@ -3687,6 +3687,7 @@
 	const char *name = NULL;
 	char *filename, *path;
 	PurplePresence *p;
+	PurpleStatus *tune;
 
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		if(!gtknode->contact_expanded) {
@@ -3725,7 +3726,21 @@
 		return _pidgin_blist_get_cached_emblem(path);
 	}
 
-	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) {
+	tune = purple_presence_get_status(p, "tune");
+	if (tune && purple_status_is_active(tune)) {
+		/* Only in MSN.
+		 * TODO: Replace "Tune" with generalized "Media" in 3.0. */
+		if (purple_status_get_attr_string(tune, "game") != NULL) {
+			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "game.png", NULL);
+			return _pidgin_blist_get_cached_emblem(path);
+		}
+		/* Only in MSN.
+		 * TODO: Replace "Tune" with generalized "Media" in 3.0. */
+		if (purple_status_get_attr_string(tune, "office") != NULL) {
+			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "office.png", NULL);
+			return _pidgin_blist_get_cached_emblem(path);
+		}
+		/* Regular old "tune" is the only one in all protocols. */
 		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
 		return _pidgin_blist_get_cached_emblem(path);
 	}
--- a/pidgin/gtkimhtml.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/pidgin/gtkimhtml.c	Mon Dec 15 08:39:08 2008 +0000
@@ -3126,9 +3126,15 @@
 								font->size = 6;
 							else if (g_ascii_strcasecmp(size, "xx-large") == 0)
 								font->size = 7;
-							else
-								font->size = 3;
-						    gtk_imhtml_font_set_size(imhtml, font->size);
+
+							/*
+							 * TODO: Handle other values, like percentages, or
+							 * lengths specified as em, ex, px, in, cm, mm, pt
+							 * or pc.  Or even better, use an actual HTML
+							 * renderer like webkit.
+							 */
+							if (font->size > 0)
+							    gtk_imhtml_font_set_size(imhtml, font->size);
 						}
 						else if (oldfont)
 						{
--- a/pidgin/gtkroomlist.c	Sun Dec 14 23:43:52 2008 +0000
+++ b/pidgin/gtkroomlist.c	Mon Dec 15 08:39:08 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	Sun Dec 14 23:43:52 2008 +0000
+++ b/pidgin/gtkutils.c	Mon Dec 15 08:39:08 2008 +0000
@@ -1502,16 +1502,16 @@
 
 		break;
 	}
-	free(data->filename);
-	free(data->who);
-	free(data);
+	g_free(data->filename);
+	g_free(data->who);
+	g_free(data);
 }
 
 static void dnd_image_cancel_callback(_DndData *data, int choice)
 {
-	free(data->filename);
-	free(data->who);
-	free(data);
+	g_free(data->filename);
+	g_free(data->who);
+	g_free(data);
 }
 
 static void dnd_set_icon_ok_cb(_DndData *data)
@@ -1521,9 +1521,9 @@
 
 static void dnd_set_icon_cancel_cb(_DndData *data)
 {
-	free(data->filename);
-	free(data->who);
-	free(data);
+	g_free(data->filename);
+	g_free(data->who);
+	g_free(data);
 }
 
 void
--- a/pidgin/plugins/perl/common/GtkIMHtml.xs	Sun Dec 14 23:43:52 2008 +0000
+++ b/pidgin/plugins/perl/common/GtkIMHtml.xs	Mon Dec 15 08:39:08 2008 +0000
@@ -173,7 +173,7 @@
 	t_GL = NULL;
 	t_len = av_len((AV *)SvRV(unused));
 
-	for (i = 0; i < t_len; i++) {
+	for (i = 0; i <= t_len; i++) {
 		STRLEN t_sl;
 		t_GL = g_slist_append(t_GL, SvPV(*av_fetch((AV *)SvRV(unused), i, 0), t_sl));
 	}
--- a/po/ca.po	Sun Dec 14 23:43:52 2008 +0000
+++ b/po/ca.po	Mon Dec 15 08:39:08 2008 +0000
@@ -3,7 +3,7 @@
 # Copyright (C) unknown, Robert Millan <zeratul2@wanadoo.es>
 # Copyright (C) December 2003 (from 2003-12-12 until 2003-12-18),
 #               January (2004-01-07,12), Xan <dxpublica@telefonica.net>
-# Copyright (c) 2004, 2005, 2006, 2007
+# Copyright (c) 2004, 2005, 2006, 2007, 2008
 #               Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>
 #
 # This file is distributed under the same license as the Pidgin package.
@@ -27,14 +27,14 @@
 #
 # NOTE: adjectives in catalan go [almost] always to the end, for instance:
 # NOTA: els adjectius en català acostumen a anar al final, per exemple:
-#   "New file" --> "Fitxer nou"
+#  "New file" --> "Fitxer nou"
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-01 15:52-0800\n"
-"PO-Revision-Date: 2008-05-11 13:00+0200\n"
+"POT-Creation-Date: 2008-12-14 17:46+0100\n"
+"PO-Revision-Date: 2008-12-15 00:07+0100\n"
 "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n"
 "Language-Team: Catalan <tradgnome@softcatala.net>\n"
 "MIME-Version: 1.0\n"
@@ -96,7 +96,6 @@
 msgid "Remember password"
 msgstr "Recorda la contrasenya"
 
-#, fuzzy
 msgid "There are no protocol plugins installed."
 msgstr "No hi ha connectors de protocol instal·lats."
 
@@ -122,9 +121,8 @@
 msgstr "Àlies:"
 
 #. Register checkbox
-#, fuzzy
 msgid "Create this account on the server"
-msgstr "Crea aquest compte nou al servidor"
+msgstr "Crea aquest compte al servidor"
 
 #. Cancel button
 #. Cancel
@@ -434,9 +432,8 @@
 msgid "View Log..."
 msgstr "Mostra el registre..."
 
-#, fuzzy
 msgid "View All Logs"
-msgstr "Visualitza el registre"
+msgstr "Visualitza tots els registres"
 
 msgid "Show"
 msgstr "Mostra"
@@ -641,21 +638,18 @@
 msgid "Send To"
 msgstr "Envia a"
 
-# FIXME
-#, fuzzy
 msgid "Invite message"
-msgstr "Missatge de sortida"
+msgstr "Missatge d'invitació"
 
 msgid "Invite"
 msgstr "Convida"
 
-#, fuzzy
 msgid ""
 "Please enter the name of the user you wish to invite,\n"
 "along with an optional invite message."
 msgstr ""
-"Introduïu el nom de l'usuari que vulgueu convidar, així com un missatge "
-"d'invitació opcional."
+"Introduïu el nom de l'usuari que vulgueu convidar,\n"
+"així com un missatge d'invitació opcional."
 
 msgid "Conversation"
 msgstr "Conversa"
@@ -670,9 +664,8 @@
 msgid "Add Buddy Pounce..."
 msgstr "Afegeix un avís per a l'amic..."
 
-#, fuzzy
 msgid "Invite..."
-msgstr "Convida"
+msgstr "Convida..."
 
 msgid "Enable Logging"
 msgstr "Habilita el registre"
@@ -683,11 +676,11 @@
 msgid "<AUTO-REPLY> "
 msgstr "<RESPOSTA-AUTOMÀTICA> "
 
-#, fuzzy, c-format
+#, c-format
 msgid "List of %d user:\n"
 msgid_plural "List of %d users:\n"
-msgstr[0] "Llista d'usuaris:\n"
-msgstr[1] "Llista d'usuaris:\n"
+msgstr[0] "Llista d'%d usuari:\n"
+msgstr[1] "Llista de %d usuaris:\n"
 
 msgid "Supported debug options are:  version"
 msgstr "Les opcions de depuració disponibles són:  version"
@@ -707,10 +700,14 @@
 "%s is not a valid message class. See '/help msgcolor' for valid message "
 "classes."
 msgstr ""
+"%s no és una classe de missatge vàlida. Vegeu '/help msgcolor' per a llistar "
+"les classes de missatge vàlides."
 
 #, c-format
 msgid "%s is not a valid color. See '/help msgcolor' for valid colors."
 msgstr ""
+"%s no és un color vàlid. Vegei '/help msgcolor' per a llistar els colors "
+"vàlids."
 
 msgid ""
 "say &lt;message&gt;:  Send a message normally as if you weren't using a "
@@ -763,6 +760,13 @@
 "background&gt;: black, red, green, blue, white, gray, darkgray, magenta, "
 "cyan, default<br><br>EXAMPLE:<br>    msgcolor send cyan default"
 msgstr ""
+"msgcolor &lt;classe&gt; &lt;primer pla&gt; &lt;fons&gt;: estableix el color "
+"de les diferents classes de missatge en les finestres de conversa.<br>    "
+"&lt;classe&gt;: receive (rep), send (envia), highlight (ressalta), action "
+"(acció), timestamp (marca de temps)<br>    &lt;primer pla/fons&gt;: black "
+"(negre), red (vermell), green (verd), blue (blau), white (blanc), gray "
+"(gris), darkgray (gris fosc), magenta, cyan (cian), default (per defecte)"
+"<br><br>EXEMPLE:<br>    msgcolor send cyan default"
 
 msgid "Unable to open file."
 msgstr "No s'ha pogut obrir el fitxer."
@@ -783,10 +787,10 @@
 msgid "Pause"
 msgstr "Fes una pausa"
 
-#, fuzzy, c-format
+#, c-format
 msgid "File Transfers - %d%% of %d file"
 msgid_plural "File Transfers - %d%% of %d files"
-msgstr[0] "Transferència de fitxers - %d%% de %d fitxers"
+msgstr[0] "Transferència de fitxers - %d%% de %d fitxer"
 msgstr[1] "Transferència de fitxers - %d%% de %d fitxers"
 
 #. Create the window.
@@ -904,9 +908,8 @@
 msgid "Conversations with %s"
 msgstr "Converses amb %s"
 
-#, fuzzy
 msgid "All Conversations"
-msgstr "Converses"
+msgstr "Totes les converses"
 
 msgid "System Log"
 msgstr "Registre del sistema"
@@ -1542,10 +1545,10 @@
 msgstr "Sense agrupament"
 
 msgid "Nested Subgroup"
-msgstr ""
+msgstr "Subgrup imbricat"
 
 msgid "Nested Grouping (experimental)"
-msgstr ""
+msgstr "Subgrup imbricat (experimental)"
 
 msgid "Provides alternate buddylist grouping options."
 msgstr "Proporciona opcions alternatives per a l'agrupament d'amics."
@@ -1868,7 +1871,7 @@
 
 #, c-format
 msgid "Resolver process exited without answering our request"
-msgstr ""
+msgstr "El procés resoledor ha acabat sense respondre la nostra sol·licitud"
 
 #, c-format
 msgid "Thread creation failure: %s"
@@ -2177,10 +2180,11 @@
 msgid "ABI version mismatch %d.%d.x (need %d.%d.x)"
 msgstr "Hi ha un error de coincidència de l'ABI %d.%d.x (cal %d.%d.x)"
 
-#, fuzzy
 msgid ""
 "Plugin does not implement all required functions (list_icon, login and close)"
-msgstr "El connector no implementa totes les funcions requerides"
+msgstr ""
+"El connector no implementa totes les funcions requerides (list_icon, login i "
+"close)"
 
 #, c-format
 msgid ""
@@ -2266,9 +2270,8 @@
 "s'hagi transferit completament (només quan no hi ha conversa amb qui\n"
 "l'envia)"
 
-#, fuzzy
 msgid "Create a new directory for each user"
-msgstr "Seleccioneu quin directori d'usuari cercar"
+msgstr "Crea un directori nou per a cada usuari"
 
 msgid "Notes"
 msgstr "Notes"
@@ -2433,7 +2436,7 @@
 msgstr "Temps d'espera d'inactivitat del usuaris (en minuts)"
 
 msgid "Apply hiding rules to buddies"
-msgstr ""
+msgstr "Aplica les normes d'ocultació als amics"
 
 #. *< type
 #. *< ui_requirement
@@ -2846,14 +2849,13 @@
 "No s'ha pogut detectar la instal·lació d'ActiveTCL. Si voleu emprar "
 "connectors TCL, instal·leu l'ActiveTCL de http://www.activestate.com\n"
 
-#, fuzzy
 msgid ""
 "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d."
 "pidgin.im/BonjourWindows for more information."
 msgstr ""
 "No s'ha pogut trobar el joc d'eines per a Windows de l'Apple Bonjour, "
-"consulteu les preguntes més freqüents a http://developer.pidgin.im/wiki/Using"
-"%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging per a més informació."
+"consulteu les preguntes més freqüents a http://developer.pidgin.im/"
+"BonjourWindows per a més informació."
 
 msgid "Unable to listen for incoming IM connections\n"
 msgstr "No es poden escoltar connexions entrants de MI\n"
@@ -3257,7 +3259,7 @@
 msgstr "Codificacions"
 
 msgid "Auto-detect incoming UTF-8"
-msgstr ""
+msgstr "Detecta UTF-8 entrant automàticament"
 
 msgid "Real name"
 msgstr "Nom real"
@@ -3272,9 +3274,9 @@
 msgid "Bad mode"
 msgstr "Mode dolent"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Ban on %s by %s, set %s ago"
-msgstr "Bandejament a %s de %s, establert fa %ld segons"
+msgstr "Bandejament a %s de %s, establert fa %s"
 
 #, c-format
 msgid "Ban on %s"
@@ -3445,7 +3447,7 @@
 "missatge el treu."
 
 msgid "ctcp <nick> <msg>: sends ctcp msg to nick."
-msgstr ""
+msgstr "ctcp <sobrenom> <msg>: envia un missatge ctcp a sobrenom"
 
 msgid "chanserv: Send a command to chanserv"
 msgstr "chanserv: envia una ordre al chanserv"
@@ -3750,9 +3752,9 @@
 msgid "Service Discovery Items"
 msgstr "Elements del servei de descoberta"
 
-#, fuzzy
+# http://xmpp.org/extensions/xep-0033.html
 msgid "Extended Stanza Addressing"
-msgstr "Adreça (cont.)"
+msgstr "Bloc d'adressament estès"
 
 msgid "Multi-User Chat"
 msgstr "Xat multi-usuari"
@@ -3812,17 +3814,18 @@
 msgstr "Negociacions de la sessió xifrada"
 
 # Pot ser també música, cançó... (josep)
-#, fuzzy
+# Vegeu: http://xmpp.org/extensions/xep-0118.html
 msgid "User Tune"
 msgstr "Melodia de l'usuari"
 
-#, fuzzy
+# Nota: "Roster" en XMPP és la llista de contactes (josep)
 msgid "Roster Item Exchange"
-msgstr "MI amb intercanvi de clau"
-
-#, fuzzy
+msgstr "Intercanvi d'element de la llista de contactes"
+
+# Vegeu: http://xmpp.org/extensions/xep-0152.html (josep)
+# Nota: codi mort
 msgid "Reachability Address"
-msgstr "Adreça de correu"
+msgstr "Adreça de localització"
 
 msgid "User Profile"
 msgstr "Perfil de l'usuari"
@@ -3875,8 +3878,11 @@
 msgid "Ping"
 msgstr "Ping"
 
+# Vegeu: http://xmpp.org/extensions/xep-0200.html
+# Nota: Això es refereix a les capabilitats de l'altre interlocutor
+# No apareix a l'aplicació final, és codi mort dins un #if 0 ... #endif (josep)
 msgid "Stanza Encryption"
-msgstr "Xifratge de paràgrafs"
+msgstr "Blocs xifrats"
 
 msgid "Entity Time"
 msgstr "Hora de l'entitat"
@@ -3909,9 +3915,9 @@
 msgid "Resource"
 msgstr "Recurs"
 
-# FIXME
+# Segons la viquipèdia
 msgid "Middle Name"
-msgstr "Primer cognom"
+msgstr "Nom del mig"
 
 msgid "Address"
 msgstr "Adreça"
@@ -4421,8 +4427,9 @@
 msgid "Unsupported Encoding"
 msgstr "La codificació no està implementada"
 
+# FIXME
 msgid "Unsupported Stanza Type"
-msgstr "El tipus de permanència no està implementada"
+msgstr "Aquest tipus de bloc no està implementat"
 
 msgid "Unsupported Version"
 msgstr "Aquesta versió no està implementada"
@@ -4580,7 +4587,6 @@
 
 #. this should probably be part of global smiley theme settings later on,
 #. shared with MSN
-#, fuzzy
 msgid "Show Custom Smileys"
 msgstr "Mostra emoticones personalitzades"
 
@@ -4607,9 +4613,9 @@
 msgid "XMPP Message Error"
 msgstr "Missatge d'error de l'XMPP"
 
-#, fuzzy, c-format
+#, c-format
 msgid "(Code %s)"
-msgstr " (Codi %s)"
+msgstr "(Codi %s)"
 
 msgid "XML Parse error"
 msgstr "Error en l'anàlisi de l'XML"
@@ -4709,6 +4715,19 @@
 msgid "Unable to retrieve MSN Address Book"
 msgstr "No s'ha pogut obtenir la llibreta d'adreces MSN"
 
+#. only notify the user about problems adding to the friends list
+#. * maybe we should do something else for other lists, but it probably
+#. * won't cause too many problems if we just ignore it
+#, c-format
+msgid "Unable to add \"%s\"."
+msgstr "No s'ha pogut afegir «%s»."
+
+msgid "Buddy Add error"
+msgstr "Error en afegir un amic"
+
+msgid "The username specified does not exist."
+msgstr "El nom d'usuari especificat no existeix."
+
 #, c-format
 msgid "Buddy list synchronization issue in %s (%s)"
 msgstr "Problema de sincronització de la llista d'amics a %s (%s)"
@@ -4936,9 +4955,9 @@
 msgid "Passport account not yet verified"
 msgstr "El compte de passaport encara no està verificat"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Passport account suspended"
-msgstr "El compte de passaport encara no està verificat"
+msgstr "El compte de passaport s'ha suspès"
 
 #, c-format
 msgid "Bad ticket"
@@ -4952,13 +4971,11 @@
 msgid "MSN Error: %s\n"
 msgstr "Error d'MSN: %s\n"
 
-#, fuzzy
 msgid "Other Contacts"
-msgstr "Contacte preferit"
-
-#, fuzzy
+msgstr "Altres contactes"
+
 msgid "Non-IM Contacts"
-msgstr "Suprimeix el contacte"
+msgstr "Contactes que no són de MI"
 
 msgid "Nudge"
 msgstr "Donar un cop de colze"
@@ -4971,9 +4988,8 @@
 msgid "Nudging %s..."
 msgstr "S'està donant un cop de colze a %s..."
 
-#, fuzzy
 msgid "Email Address..."
-msgstr "Adreça de correu electrònic"
+msgstr "Correu electrònic..."
 
 msgid "Your new MSN friendly name is too long."
 msgstr "El vostre nom amistós nou d'MSN és massa llarg."
@@ -5009,22 +5025,20 @@
 msgid "Disallow"
 msgstr "Denega"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Blocked Text for %s"
-msgstr "Comentari sobre l'amic %s"
-
-#, fuzzy
+msgstr "Text blocat de %s"
+
 msgid "No text is blocked for this account."
-msgstr "Utilitza aquesta _icona d'amic per a aquest compte:"
+msgstr "No hi ha text blocat per aquest compte."
 
 #, c-format
 msgid ""
 "MSN servers are currently blocking the following regular expressions:<br/>%s"
-msgstr ""
-
-#, fuzzy
+msgstr "Actualment, sevidors MSN bloquen aquestes expressions regulars:<br/>%s"
+
 msgid "This account does not have email enabled."
-msgstr "Pot ser que aquest compte Hotmail no sigui actiu."
+msgstr "Aquest compte no té el correu habilitat."
 
 msgid "Send a mobile message."
 msgstr "Envia un missatge de mòbil."
@@ -5032,6 +5046,12 @@
 msgid "Page"
 msgstr "Pàgina"
 
+msgid "Playing a game"
+msgstr "Jugant a un joc"
+
+msgid "Working"
+msgstr "Treballant"
+
 msgid "Has you"
 msgstr "Us té"
 
@@ -5068,6 +5088,12 @@
 msgid "Album"
 msgstr "Àlbum"
 
+msgid "Game Title"
+msgstr "Títol del joc"
+
+msgid "Office Title"
+msgstr "Títol oficial"
+
 msgid "Set Friendly Name..."
 msgstr "Estableix el nom amistós..."
 
@@ -5087,7 +5113,7 @@
 msgstr "Permet/denega pàgines de mòbil..."
 
 msgid "View Blocked Text..."
-msgstr ""
+msgstr "Mostra el text blocat..."
 
 msgid "Open Hotmail Inbox"
 msgstr "Obre la safata d'entrada de Hotmail"
@@ -5259,8 +5285,8 @@
 "No s'ha pogut trobar informació del perfil de l'usuari. El més segur és que "
 "l'usuari no existeixi."
 
-msgid "Profile URL"
-msgstr "URL del perfil"
+msgid "View web profile"
+msgstr "Mostra el perfil web"
 
 #. *< type
 #. *< ui_requirement
@@ -5289,9 +5315,8 @@
 msgid "Windows Live ID authentication:Unable to connect"
 msgstr "Autenticació amb el Windows Live ID: no s'ha pogut connectar"
 
-#, fuzzy
 msgid "Windows Live ID authentication:Invalid response"
-msgstr "Autenticació amb el Windows Live ID: no s'ha pogut connectar"
+msgstr "Autenticació amb el Windows Live ID: la resposta no és vàlida"
 
 #, c-format
 msgid "%s is not a valid group."
@@ -5316,9 +5341,8 @@
 msgid "Unable to add user"
 msgstr "No s'ha pogut afegir l'usuari"
 
-#, fuzzy
 msgid "The following users are missing from your addressbook"
-msgstr "El que segueix són els resultats de la vostra cerca"
+msgstr "Manquen aquests usuaris a la vostra llista d'amics"
 
 #, c-format
 msgid "Unable to add user on %s (%s)"
@@ -5343,9 +5367,8 @@
 msgid "Service Temporarily Unavailable."
 msgstr "El servei no està disponible temporalment."
 
-#, fuzzy
 msgid "Mobile message was not sent because it was too long."
-msgstr "El missatge no s'ha enviat perquè no esteu connectat."
+msgstr "No s'ha enviat el missatge al mòbil perquè era massa llarg."
 
 msgid "Unable to rename group"
 msgstr "No s'ha pogut canviar el nom del grup"
@@ -5385,21 +5408,21 @@
 "Message was not sent because the system is unavailable. This normally "
 "happens when the user is blocked or does not exist."
 msgstr ""
-
-#, fuzzy
+"No s'ha pogut enviar el missatge perquè el sistema no està disponible. Això "
+"normalment passa quan l'usuari està blocat o no existeix."
+
 msgid "Message was not sent because messages are being sent too quickly."
 msgstr ""
-"El missatge no s'ha pogut enviar perquè s'està enviant massa de pressa:"
-
-#, fuzzy
+"No s'ha pogut enviar el missatge perquè se n'estan enviant massa depressa."
+
 msgid "Message was not sent because an unknown encoding error occurred."
 msgstr ""
-"No s'ha pogut enviar el missatge perquè s'ha produït un error desconegut:"
-
-#, fuzzy
+"No s'ha pogut enviar el missatge perquè s'ha produït un error desconegut en "
+"la codificació."
+
 msgid "Message was not sent because an unknown error occurred."
 msgstr ""
-"No s'ha pogut enviar el missatge perquè s'ha produït un error desconegut:"
+"No s'ha pogut enviar el missatge perquè s'ha produït un error desconegut."
 
 msgid "Unable to connect"
 msgstr "No s'ha pogut connectar"
@@ -5518,20 +5541,11 @@
 msgid "%s has removed you from his or her buddy list."
 msgstr "%s us ha suprimit de la seva llista d'amics."
 
-#, fuzzy
 msgid "Delete Buddy from Address Book?"
-msgstr "Afegeix a la llibreta d'adreces"
-
-#, fuzzy
+msgstr "Voleu suprimir l'amic de la llibreta d'adreces?"
+
 msgid "Do you want to delete this buddy from your address book as well?"
-msgstr "Voleu afegir aquest amic a la vostra llista d'amics?"
-
-#. only notify the user about problems adding to the friends list
-#. * maybe we should do something else for other lists, but it probably
-#. * won't cause too many problems if we just ignore it
-#, c-format
-msgid "Unable to add \"%s\"."
-msgstr "No s'ha pogut afegir «%s»."
+msgstr "Voleu suprimir aquest amic de la vostra llista d'amics també?"
 
 msgid "The username specified is invalid."
 msgstr "El nom d'usuari especificat no és vàlid."
@@ -5539,6 +5553,9 @@
 msgid "This Hotmail account may not be active."
 msgstr "Pot ser que aquest compte Hotmail no sigui actiu."
 
+msgid "Profile URL"
+msgstr "URL del perfil"
+
 #. *< type
 #. *< ui_requirement
 #. *< flags
@@ -5575,13 +5592,8 @@
 msgid "Logging in"
 msgstr "S'està entrant"
 
-#, fuzzy, c-format
-msgid "Connection to server lost (no data received within %d second)"
-msgid_plural "Connection to server lost (no data received within %d seconds)"
-msgstr[0] ""
-"S'ha perdut la connexió al servidor (fa %d segons que no s'hi reben dades)"
-msgstr[1] ""
-"S'ha perdut la connexió al servidor (fa %d segons que no s'hi reben dades)"
+msgid "Lost connection with server"
+msgstr "S'ha perdut la connexió amb el servidor"
 
 #. Can't write _()'d strings in array initializers. Workaround.
 msgid "New mail messages"
@@ -5676,7 +5688,7 @@
 msgid "IM Friends"
 msgstr "Amics de MI"
 
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "%d buddy was added or updated from the server (including buddies already on "
 "the server-side list)"
@@ -5684,8 +5696,8 @@
 "%d buddies were added or updated from the server (including buddies already "
 "on the server-side list)"
 msgstr[0] ""
-"S'han afegit o actualitzat %d amics al servidor (incloent els amics que ja "
-"són a la llista del servidor)"
+"S'ha afegit o actualitzat %d amic al servidor (incloent els amics que ja són "
+"a la llista del servidor)"
 msgstr[1] ""
 "S'han afegit o actualitzat %d amics al servidor (incloent els amics que ja "
 "són a la llista del servidor)"
@@ -5740,9 +5752,6 @@
 msgid "User"
 msgstr "Usuari"
 
-msgid "Profile"
-msgstr "Perfil"
-
 msgid "Headline"
 msgstr "Titular"
 
@@ -6237,9 +6246,8 @@
 msgid "AIM Protocol Plugin"
 msgstr "Connector per al protocol AIM"
 
-#, fuzzy
 msgid "ICQ UIN..."
-msgstr "ICQ UIN"
+msgstr "ICQ UIN..."
 
 #. *< type
 #. *< ui_requirement
@@ -6339,10 +6347,10 @@
 msgstr "En la llista de permès/denegat local"
 
 msgid "Warning level too high (sender)"
-msgstr ""
+msgstr "Nivell d'avís massa alt (remitent)"
 
 msgid "Warning level too high (receiver)"
-msgstr ""
+msgstr "Nivell d'avís massa alt (receptor)"
 
 msgid "User temporarily unavailable"
 msgstr "Usuari no disponible temporalment"
@@ -6446,9 +6454,8 @@
 msgid "Camera"
 msgstr "Càmera"
 
-#, fuzzy
 msgid "Screen Sharing"
-msgstr "Nom d'usuari"
+msgstr "Compartició de pantalla"
 
 #, c-format
 msgid "Free For Chat"
@@ -6697,27 +6704,30 @@
 msgstr[0] "S'ha perdut %hu missatge de %s perquè no era vàlid."
 msgstr[1] "S'han perdut %hu missatges de %s perquè no eren vàlids."
 
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "You missed %hu message from %s because his/her warning level is too high."
 msgid_plural ""
 "You missed %hu messages from %s because his/her warning level is too high."
-msgstr[0] "S'ha perdut %hu missatge de %s perquè ell/ella era massa dolent/a."
+msgstr[0] ""
+"Heu perdut %hu missatge de %s perquè el seu nivell d'avís és massa alt."
 msgstr[1] ""
-"S'han perdut %hu missatges de %s perquè ell/ella era massa dolent/a."
-
-#, fuzzy, c-format
+"Heu perdut %hu missatges de %s perquè el seu nivell d'avís és massa alt."
+
+#, c-format
 msgid "You missed %hu message from %s because your warning level is too high."
 msgid_plural ""
 "You missed %hu messages from %s because your warning level is too high."
-msgstr[0] "S'ha perdut %hu missatge de %s perquè sou massa dolent."
-msgstr[1] "S'han perdut %hu missatges de %s perquè sou massa dolent."
+msgstr[0] ""
+"Heu perdut %hu missatge de %s perquè el vostre nivell d'avís és massa alt."
+msgstr[1] ""
+"Heu perdut %hu missatges de %s perquè el vostre nivell d'avís és massa alt."
 
 #, c-format
 msgid "You missed %hu message from %s for an unknown reason."
 msgid_plural "You missed %hu messages from %s for an unknown reason."
-msgstr[0] "S'ha perdut %hu missatge de %s per motius desconeguts."
-msgstr[1] "S'han perdut %hu missatges de %s per motius desconeguts."
+msgstr[0] "Heu perdut %hu missatge de %s per motius desconeguts."
+msgstr[1] "Heu perdut %hu missatges de %s per motius desconeguts."
 
 #. Data is assumed to be the destination sn
 #, c-format
@@ -6741,6 +6751,9 @@
 msgid "Member Since"
 msgstr "Membre des de"
 
+msgid "Profile"
+msgstr "Perfil"
+
 msgid "Your AIM connection may be lost."
 msgstr "La vostra connexió d'AIM es pot perdre."
 
@@ -6928,11 +6941,9 @@
 "d'usuari han de ser adreces de correu vàlides, o començar amb una lletra i "
 "contenir només lletres, nombres i espais, o només nombres."
 
-#, fuzzy
 msgid "Unable to Add"
 msgstr "No s'ha pogut afegir"
 
-#, fuzzy
 msgid "Unable to Retrieve Buddy List"
 msgstr "No s'ha pogut obtenir la llista d'amics"
 
@@ -7126,8 +7137,9 @@
 "per a la transferència de fitxers\n"
 "(és més lent, però acostuma a funcionar)"
 
+# FIXME: entrades/registres?
 msgid "Allow multiple simultaneous logins"
-msgstr ""
+msgstr "Permet diverses entrades simultànies"
 
 #, c-format
 msgid "Asking %s to connect to us at %s:%hu for Direct IM."
@@ -7230,16 +7242,14 @@
 msgid "Other"
 msgstr "Altres"
 
-#, fuzzy
 msgid "Visible"
-msgstr "Invisible"
-
-msgid "Firend Only"
-msgstr ""
-
-#, fuzzy
+msgstr "Visible"
+
+msgid "Friend Only"
+msgstr "Només amic"
+
 msgid "Private"
-msgstr "Privadesa"
+msgstr "Privat"
 
 msgid "QQ Number"
 msgstr "Número QQ"
@@ -7256,9 +7266,8 @@
 msgid "Phone Number"
 msgstr "Número de telèfon"
 
-#, fuzzy
 msgid "Authorize adding"
-msgstr "Voleu autoritzar l'amic?"
+msgstr "Autoritzar que us afegeixin"
 
 msgid "Cellphone Number"
 msgstr "Número de mòbil"
@@ -7266,131 +7275,110 @@
 msgid "Personal Introduction"
 msgstr "Introducció personal"
 
-#, fuzzy
 msgid "City/Area"
-msgstr "Ciutat"
-
-#, fuzzy
+msgstr "Ciutat/Àrea"
+
 msgid "Publish Mobile"
-msgstr "Mòbil personal"
-
-#, fuzzy
+msgstr "Publica el mòbil"
+
 msgid "Publish Contact"
-msgstr "Posa un àlies al contacte"
+msgstr "Publica el contacte"
 
 msgid "College"
 msgstr "Col·legi"
 
-#, fuzzy
 msgid "Horoscope"
-msgstr "Signe de l'horòscop"
-
-#, fuzzy
+msgstr "Horòscop"
+
 msgid "Zodiac"
-msgstr "Signe del zodíac"
-
-#, fuzzy
+msgstr "Zodíac"
+
 msgid "Blood"
-msgstr "Blocat"
-
-#, fuzzy
+msgstr "Sang"
+
 msgid "True"
-msgstr "Taure"
-
-#, fuzzy
+msgstr "Cert"
+
 msgid "False"
-msgstr "Ha fallat"
-
-#, fuzzy
+msgstr "Fals"
+
 msgid "Modify Contact"
-msgstr "Modifica el compte"
-
-#, fuzzy
+msgstr "Modifica el contacte"
+
 msgid "Modify Address"
-msgstr "Adreça de casa"
-
-#, fuzzy
+msgstr "Modifica l'adreça"
+
 msgid "Modify Extended Information"
-msgstr "Modifica la meva informació"
-
-#, fuzzy
+msgstr "Modifica la informació estesa"
+
 msgid "Modify Information"
-msgstr "Modifica la meva informació"
+msgstr "Modifica la informació"
 
 msgid "Update"
 msgstr "Actualitza"
 
-#, fuzzy
 msgid "Could not change buddy information."
-msgstr "Introduïu informació sobre l'amic."
-
-#, c-format
-msgid "%d needs Q&A"
-msgstr ""
-
-#, fuzzy
-msgid "Add buddy Q&A"
-msgstr "Afegeix un amic"
-
-#, fuzzy
-msgid "Input answer here"
-msgstr "Introduïu la sol·licitud aquí"
+msgstr "No s'ha pogut canviar la informació l'amic."
+
+#, c-format
+msgid "%u requires verification"
+msgstr "Cal verificació per a %u"
+
+# Nota: títol de finestra
+msgid "Add buddy question"
+msgstr "Afegir una pregunta"
+
+msgid "Enter answer here"
+msgstr "Introduïu la resposta aquí"
 
 msgid "Send"
 msgstr "Envia"
 
-#, fuzzy
 msgid "Invalid answer."
-msgstr "La contrasenya no és vàlida"
+msgstr "La resposta no és vàlida"
 
 msgid "Authorization denied message:"
 msgstr "Missatge de denegació de l'autorització:"
 
-#, fuzzy
-msgid "Sorry, You are not my style."
-msgstr "Em sap greu, no sou el meu tipus..."
-
-#, fuzzy, c-format
-msgid "%d needs authentication"
-msgstr "L'usuari %d necessita autenticació"
-
-#, fuzzy
+msgid "Sorry, you're not my style."
+msgstr "Em sap greu, no sou el meu tipus."
+
+#, c-format
+msgid "%u needs authorization"
+msgstr "Cal autorització per a %u"
+
+# Nota: títol de finestra (josep)
 msgid "Add buddy authorize"
-msgstr "Voleu afegir l'amic a la llista?"
-
-msgid "Input request here"
+msgstr "Autorització per a afegir un amic"
+
+msgid "Enter request here"
 msgstr "Introduïu la sol·licitud aquí"
 
 msgid "Would you be my friend?"
 msgstr "Voleu ser el meu amic?"
 
-#, fuzzy
 msgid "QQ Buddy"
-msgstr "Amic"
-
-#, fuzzy
+msgstr "Amic QQ"
+
 msgid "Add buddy"
 msgstr "Afegeix un amic"
 
-#, fuzzy
 msgid "Invalid QQ Number"
-msgstr "El nom de la cara QQ no és vàlida"
-
-#, fuzzy
+msgstr "El nombre QQ no és vàlid"
+
 msgid "Failed sending authorize"
-msgstr "Autoritzeu-me, si us plau."
-
-#, fuzzy, c-format
-msgid "Failed removing buddy %d"
-msgstr "No s'ha pogut suprimir l'amic"
-
-#, fuzzy, c-format
+msgstr "No s'ha pogut enviar l'autorizació"
+
+#, c-format
+msgid "Failed removing buddy %u"
+msgstr "No s'ha pogut suprimir l'amic %u"
+
+#, c-format
 msgid "Failed removing me from %d's buddy list"
-msgstr "Suprimeix l'usuari de la llista d'amics"
-
-#, fuzzy
+msgstr "No us heu pogut suprimir de la llista d'amics de %d"
+
 msgid "No reason given"
-msgstr "No s'ha indicat cap motiu."
+msgstr "No s'ha indicat cap motiu"
 
 #. only need to get value
 #, c-format
@@ -7400,9 +7388,9 @@
 msgid "Would you like to add him?"
 msgstr "Voleu afegir-lo?"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Rejected by %s"
-msgstr "S'ha connectat a %s"
+msgstr "Rebutjat per %s"
 
 #, c-format
 msgid "Message: %s"
@@ -7417,82 +7405,73 @@
 msgid "QQ Qun"
 msgstr "QQ Qun"
 
-#, fuzzy
 msgid "Please enter Qun number"
-msgstr "Introduïu un nou nom per a %s"
-
-#, fuzzy
+msgstr "Introduïu el número Qun"
+
 msgid "You can only search for permanent Qun\n"
-msgstr "Només podeu cercar grups QQ permanents\n"
-
-#, fuzzy
+msgstr "Només podeu cercar Quns permanents\n"
+
+msgid "(Invalid UTF-8 string)"
+msgstr "(No és una cadena UTF-8 vàlida)"
+
 msgid "Not member"
 msgstr "No en sóc membre"
 
-#, fuzzy
 msgid "Member"
-msgstr "Membre des de"
-
-#, fuzzy
+msgstr "Membre"
+
 msgid "Requesting"
-msgstr "Diàleg de sol·licitud"
-
-#, fuzzy
+msgstr "Demanant"
+
 msgid "Admin"
-msgstr "Alerta d'administració"
-
-#, fuzzy
+msgstr "Administrador"
+
 msgid "Notice"
-msgstr "Nota"
-
-#, fuzzy
+msgstr "Avís"
+
 msgid "Detail"
 msgstr "Detalls"
 
 msgid "Creator"
 msgstr "Creador"
 
-#, fuzzy
 msgid "About me"
-msgstr "Quant al %s"
-
-#, fuzzy
+msgstr "Quant a mi"
+
 msgid "Category"
-msgstr "Error en el xat"
-
-#, fuzzy
+msgstr "Categoria"
+
 msgid "The Qun does not allow others to join"
-msgstr "Aquest grup no permet que s'hi afegeixi ningú"
-
-#, fuzzy
+msgstr "Aquest Qun no permet que s'hi afegeixi ningú"
+
 msgid "Join QQ Qun"
-msgstr "Entra a un xat"
-
-#, c-format
-msgid "Successfully joined Qun %s (%d)"
-msgstr ""
-
-#, fuzzy
+msgstr "Entra al Qun QQ"
+
+msgid "Input request here"
+msgstr "Introduïu la sol·licitud aquí"
+
+#, c-format
+msgid "Successfully joined Qun %s (%u)"
+msgstr "S'ha entrat al Qun %s (%u)"
+
 msgid "Successfully joined Qun"
-msgstr "Heu modificat amb èxit el membre del Qun"
-
-#, c-format
-msgid "Qun %d denied to join"
-msgstr ""
+msgstr "S'ha entrat al Qun"
+
+#, c-format
+msgid "Qun %u denied from joining"
+msgstr "No se us ha permès entrar al Qun %u"
 
 msgid "QQ Qun Operation"
 msgstr "Operació Qun QQ"
 
-#, fuzzy
 msgid "Failed:"
-msgstr "Ha fallat"
-
-msgid "Join Qun, Unknow Reply"
-msgstr ""
-
-#, fuzzy
+msgstr "Ha fallat:"
+
+msgid "Join Qun, Unknown Reply"
+msgstr "Resposta desconeguda en entrar al Qun"
+
 msgid "Quit Qun"
-msgstr "QQ Qun"
+msgstr "Surt del Qun"
 
 msgid ""
 "Note, if you are the creator, \n"
@@ -7501,51 +7480,47 @@
 "Nota, si en sou el creador, \n"
 "aquesta operació suprimirà aquest Qun."
 
-#, fuzzy
-msgid "Sorry, you are not our style ..."
-msgstr "Em sap greu, no sou el meu tipus..."
-
-#, fuzzy
-msgid "Successfully changed Qun member"
-msgstr "Heu modificat amb èxit el membre del Qun"
-
-#, fuzzy
+msgid "Sorry, you are not our style"
+msgstr "Em sap greu, no sou el meu tipus"
+
+msgid "Successfully changed Qun members"
+msgstr "S'ha canviat els membres del Qun"
+
 msgid "Successfully changed Qun information"
-msgstr "Heu modificat amb èxit la informació del Qun"
+msgstr "S'ha canviat la iformació del Qun correctament"
 
 msgid "You have successfully created a Qun"
 msgstr "Heu creat un Qun"
 
-#, fuzzy
-msgid "Would you like to set detailed information now?"
-msgstr "Voleu establir-la els detalls del Qun ara?"
+msgid "Would you like to set up detailed information now?"
+msgstr "Voleu establir informació detallada ara?"
 
 msgid "Setup"
-msgstr "Instal·lació"
-
-#, fuzzy, c-format
-msgid "%d requested to join Qun %d for %s"
-msgstr "L'usuari %d ha sol·licitat unir-se al grup %d"
-
-#, fuzzy, c-format
-msgid "%d request to join Qun %d"
-msgstr "L'usuari %d ha sol·licitat unir-se al grup %d"
-
-#, fuzzy, c-format
-msgid "Failed to join Qun %d, operated by admin %d"
-msgstr "No s'ha pogut entrar al xat de l'amic."
-
-#, c-format
-msgid "<b>Joining Qun %d is approved by admin %d for %s</b>"
-msgstr ""
-
-#, fuzzy, c-format
-msgid "<b>Removed buddy %d.</b>"
-msgstr "Suprimeix l'amic"
-
-#, c-format
-msgid "<b>New buddy %d joined.</b>"
-msgstr ""
+msgstr "Configuració"
+
+#, c-format
+msgid "%u requested to join Qun %u for %s"
+msgstr "%u ha sol·licitat unir-se al Qun %u per %s"
+
+#, c-format
+msgid "%u request to join Qun %u"
+msgstr "%u ha sol·licitat unir-se al Qun %u"
+
+#, c-format
+msgid "Failed to join Qun %u, operated by admin %u"
+msgstr "No s'ha pogut entrar al Qun %u, administrat per %u"
+
+#, c-format
+msgid "<b>Joining Qun %u is approved by admin %u for %s</b>"
+msgstr "<b>L'administrador %2$u us ha permès unir-vos al Qun %1$u per %3$s</b>"
+
+#, c-format
+msgid "<b>Removed buddy %u.</b>"
+msgstr "<b>S'ha suprimit l'amic %u.</b>"
+
+#, c-format
+msgid "<b>New buddy %u joined.</b>"
+msgstr "<b>El nou amic %u ha entrat.</b>"
 
 #, c-format
 msgid "Unknown-%d"
@@ -7555,10 +7530,10 @@
 msgstr "Nivell"
 
 msgid " VIP"
-msgstr ""
+msgstr " VIP"
 
 msgid " TCP"
-msgstr ""
+msgstr " TCP"
 
 #, fuzzy
 msgid " FromMobile"
@@ -7568,128 +7543,122 @@
 msgid " BindMobile"
 msgstr "Mòbil"
 
-#, fuzzy
 msgid " Video"
-msgstr "Vídeo en directe"
-
-#, fuzzy
+msgstr " Vídeo"
+
 msgid " Zone"
-msgstr "Cap"
-
+msgstr " Zona"
+
+# Nota: només apareix si es defineix DEBUG
 msgid "Flag"
-msgstr ""
-
+msgstr "Bandera"
+
+# Nota: només apareix si es defineix DEBUG
 msgid "Ver"
-msgstr ""
+msgstr "Ver"
 
 msgid "Invalid name"
 msgstr "QQ: El nom d'usuari no és vàlid"
 
-#, fuzzy
 msgid "Select icon..."
-msgstr "Selecciona una carpeta..."
-
-#, fuzzy, c-format
+msgstr "Selecciona una icona..."
+
+#, c-format
 msgid "<b>Login time</b>: %d-%d-%d, %d:%d:%d<br>\n"
-msgstr "<b>Temps de connexió</b>: %s<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Temps d'entrada</b>: %d-%d-%d, %d:%d:%d<br>\n"
+
+#, c-format
 msgid "<b>Total Online Buddies</b>: %d<br>\n"
-msgstr "<b>En línia ara</b>: %d<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Amic en línia</b>: %d<br>\n"
+
+#, c-format
 msgid "<b>Last Refresh</b>: %d-%d-%d, %d:%d:%d<br>\n"
-msgstr "<b>Actualitzat per darrer cop</b>: %s<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Actualitzat per darrer cop</b>: %d-%d-%d, %d:%d:%d<br>\n"
+
+#, c-format
 msgid "<b>Server</b>: %s<br>\n"
-msgstr "<b>ID del servidor:</b> %s: %d<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Servidor</b>: %s<br>\n"
+
+#, c-format
 msgid "<b>Client Tag</b>: %s<br>\n"
-msgstr "<b>Temps de connexió</b>: %s<br>\n"
+msgstr "<b>Etiqueta del client</b>: %s<br>\n"
 
 #, c-format
 msgid "<b>Connection Mode</b>: %s<br>\n"
 msgstr "<b>Mode de connexió</b>: %s<br>\n"
 
-#, fuzzy, c-format
+#, c-format
 msgid "<b>My Internet IP</b>: %s:%d<br>\n"
-msgstr "<b>Adreça IP:</b> %s<br>"
-
-#, fuzzy, c-format
+msgstr "<b>La meva adreça IP</b>: %s:%d<br>\n"
+
+#, c-format
 msgid "<b>Sent</b>: %lu<br>\n"
-msgstr "<b>En línia ara</b>: %d<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Enviats</b>: %lu<br>\n"
+
+#, c-format
 msgid "<b>Resend</b>: %lu<br>\n"
-msgstr "<b>Usuari:</b> %s<br>"
-
-#, fuzzy, c-format
+msgstr "<b>Reenviats</b>: %lu<br>\n"
+
+#, c-format
 msgid "<b>Lost</b>: %lu<br>\n"
-msgstr "<b>%s:</b> %s<br>"
-
-#, fuzzy, c-format
+msgstr "<b>Perduts</b>: %lu<br>\n"
+
+#, c-format
 msgid "<b>Received</b>: %lu<br>\n"
-msgstr "<b>Actualitzat per darrer cop</b>: %s<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Rebuta</b>: %lu<br>\n"
+
+#, c-format
 msgid "<b>Received Duplicate</b>: %lu<br>\n"
-msgstr "<b>La meva IP pública:</b> %s<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Rebuts duplicats</b>: %lu<br>\n"
+
+#, c-format
 msgid "<b>Time</b>: %d-%d-%d, %d:%d:%d<br>\n"
-msgstr "<b>Temps de connexió</b>: %s<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Temps</b>: %d-%d-%d, %d:%d:%d<br>\n"
+
+#, c-format
 msgid "<b>IP</b>: %s<br>\n"
-msgstr "<b>ID del servidor:</b> %s: %d<br>\n"
+msgstr "<b>IP</b>: %s<br>\n"
 
 msgid "Login Information"
 msgstr "Informació de la connexió"
 
-#, fuzzy
 msgid "<p><b>Original Author</b>:<br>\n"
-msgstr "<b>Usuari extern</b><br>"
+msgstr "<p><b>Autor original</b>:<br>\n"
 
 msgid "<p><b>Code Contributors</b>:<br>\n"
-msgstr ""
-
-#, fuzzy
+msgstr "<p><b>Col·laboradors del codi</b>:<br>\n"
+
 msgid "<p><b>Lovely Patch Writers</b>:<br>\n"
-msgstr "<b>Actualitzat per darrer cop</b>: %s<br>\n"
-
-#, fuzzy
+msgstr "<p><b>Encantadors apedaçadors (de codi)</b>:<br>\n"
+
 msgid "<p><b>Acknowledgement</b>:<br>\n"
-msgstr "<b>En línia ara</b>: %d<br>\n"
-
+msgstr "<p><b>Reconeixement</b>:<br>\n"
+
+# FIXME: ush... traducció lliure... 
 msgid "<p><i>And, all the boys in the backroom...</i><br>\n"
-msgstr ""
+msgstr "<p><i>I tothom que ho ha fet possible...<i><br>\n"
 
 msgid "<i>Feel free to join us!</i> :)"
-msgstr ""
-
-#, fuzzy, c-format
-msgid "About OpenQ r%s"
-msgstr "Quant al %s"
-
-#, fuzzy
+msgstr "<i>No dubteu a col·laborar amb nosaltres!</i> :)"
+
+#, c-format
+msgid "About OpenQ %s"
+msgstr "Quant a l'OpenQ %s"
+
 msgid "Change Icon"
-msgstr "Desa la icona"
+msgstr "Canvia la icona"
 
 msgid "Change Password"
 msgstr "Canvia la contrasenya"
 
-#, fuzzy
 msgid "Account Information"
-msgstr "Informació de la connexió"
+msgstr "Informació del compte"
 
 msgid "Update all QQ Quns"
-msgstr ""
-
-#, fuzzy
+msgstr "Actualitza tots els Quns QQ"
+
 msgid "About OpenQ"
-msgstr "Quant al %s"
+msgstr "Quant a l'OpenQ"
 
 #. *< type
 #. *< ui_requirement
@@ -7701,116 +7670,102 @@
 #. *< version
 #. *  summary
 #. *  description
-#, fuzzy
 msgid "QQ Protocol Plugin"
 msgstr "Connector per al protocol QQ"
 
-#, fuzzy
 msgid "Auto"
-msgstr "Autor"
-
-#, fuzzy
+msgstr "Auto"
+
 msgid "Select Server"
-msgstr "Seleccioneu un usuari"
+msgstr "Seleccioneu un servidor"
 
 msgid "QQ2005"
-msgstr ""
+msgstr "QQ2005"
 
 msgid "QQ2007"
-msgstr ""
+msgstr "QQ2007"
 
 msgid "QQ2008"
-msgstr ""
-
-#. #endif
-#, fuzzy
+msgstr "QQ2008"
+
 msgid "Connect by TCP"
 msgstr "Connecta amb TCP"
 
-#, fuzzy
 msgid "Show server notice"
-msgstr "Mostra menys opcions"
-
-#, fuzzy
+msgstr "Mostra els avisos del servidor"
+
 msgid "Show server news"
-msgstr "Mostra menys opcions"
-
-#, fuzzy
+msgstr "Mostra les notícies del servidor"
+
+# FIXME: keep alive -> permanència
 msgid "Keep alive interval (seconds)"
-msgstr "Error de permanència"
-
-#, fuzzy
+msgstr "Interval de permanència (en segons)"
+
 msgid "Update interval (seconds)"
-msgstr "Error de permanència"
-
-#, fuzzy
-msgid "Can not decrypt server reply"
-msgstr "No s'ha pogut obtenir informació del servidor"
-
-#, fuzzy
-msgid "Can not decrypt get server reply"
-msgstr "No s'ha pogut obtenir informació del servidor"
+msgstr "Interval d'actualització (en segons)"
+
+msgid "Cannot decrypt server reply"
+msgstr "No es pot desxifrar la resposta del servidor"
 
 #, c-format
 msgid "Failed requesting token, 0x%02X"
-msgstr ""
-
-#, fuzzy, c-format
+msgstr "S'ha produït un error en sol·licitar el testimoni, 0x%02X"
+
+#, c-format
 msgid "Invalid token len, %d"
-msgstr "El títol no és vàlid"
+msgstr "La longiud del testimoni no és vàlida, %d"
 
 #. extend redirect used in QQ2006
 msgid "Redirect_EX is not currently supported"
-msgstr ""
+msgstr "Redirect_EX no està implementat"
 
 #. need activation
 #. need activation
 #. need activation
-#, fuzzy
 msgid "Activation required"
-msgstr "Cal registre"
-
-#, c-format
-msgid "Unknow reply code when login (0x%02X)"
-msgstr ""
-
-msgid "Keep alive error"
-msgstr "Error de permanència"
-
-#, fuzzy
-msgid "Requesting captcha ..."
-msgstr "S'està cridant l'atenció de %s..."
-
-msgid "Checking code of captcha ..."
-msgstr ""
-
-msgid "Failed captcha verify"
-msgstr ""
-
-#, fuzzy
+msgstr "Cal activació"
+
+#, c-format
+msgid "Unknown reply code when logging in (0x%02X)"
+msgstr "No s'ha reconegut el codi de resposta en entrar (0x%02X)"
+
+msgid "Could not decrypt server reply"
+msgstr "No s'ha pogut desxifrar la resposta del servidor"
+
+# FIXME: captcha
+msgid "Requesting captcha"
+msgstr "S'està sol·licitant un capcha"
+
+msgid "Checking captcha"
+msgstr "S'està comprovant el captcha"
+
+msgid "Failed captcha verification"
+msgstr "Ha fallat la verificació del captcha"
+
 msgid "Captcha Image"
-msgstr "Desa imatge"
-
-#, fuzzy
+msgstr "Imatge captcha"
+
 msgid "Enter code"
-msgstr "Introduïu la contrasenya"
-
-msgid "QQ Captcha Verifing"
-msgstr ""
-
-#, fuzzy
+msgstr "Introduïu el codi"
+
+msgid "QQ Captcha Verification"
+msgstr "Verificació del captcha QQ"
+
 msgid "Enter the text from the image"
-msgstr "Introduïu el nom del grup"
-
-#, c-format
-msgid "Unknow reply code when checking password (0x%02X)"
-msgstr ""
-
-#, c-format
-msgid ""
-"Unknow reply code when login (0x%02X):\n"
+msgstr "Introduïu el text de la imatge"
+
+#, c-format
+msgid "Unknown reply when checking password (0x%02X)"
+msgstr ""
+"No s'ha reconegut el codi de resposta en comprovar la contrasenya (0x%02X)"
+
+#, c-format
+msgid ""
+"Unknown reply code when logging in (0x%02X):\n"
 "%s"
 msgstr ""
+"No s'ha reconegut el codi de resposta en entrar (0x%02X):\n"
+"%s"
 
 #. we didn't successfully connect. tdt->toc_fd is valid here
 msgid "Unable to connect."
@@ -7819,14 +7774,6 @@
 msgid "Socket error"
 msgstr "Error del sòcol"
 
-#, fuzzy, c-format
-msgid ""
-"Lost connection with server:\n"
-"%d, %s"
-msgstr ""
-"S'ha perdut la connexió amb el servidor:\n"
-"%s"
-
 msgid "Unable to read from socket"
 msgstr "No s'ha pogut llegir el sòcol"
 
@@ -7836,77 +7783,74 @@
 msgid "Connection lost"
 msgstr "S'ha perdut la connexió"
 
-#, fuzzy
-msgid "Get server ..."
-msgstr "Estableix informació d'usuari..."
-
-#, fuzzy
-msgid "Request token"
-msgstr "Petició denegada"
+msgid "Getting server"
+msgstr "S'està obtenint el servidor"
+
+msgid "Requesting token"
+msgstr "S'està sol·licitant un testimoni"
 
 msgid "Couldn't resolve host"
 msgstr "No s'ha pogut obtenir l'adreça de l'ordinador"
 
-#, fuzzy
 msgid "Invalid server or port"
-msgstr "El nom d'usuari o la contrasenya no són vàlides"
-
-#, fuzzy
-msgid "Connecting server ..."
-msgstr "Servidor al qual connectar-se"
-
-#, fuzzy
+msgstr "El servidor o el port no són vàlids"
+
+msgid "Connecting to server"
+msgstr "S'està connectant al servidor"
+
 msgid "QQ Error"
-msgstr "Error del QQid"
-
-msgid "Failed to send IM."
-msgstr "No s'ha pogut enviar la MI."
-
-#, fuzzy, c-format
+msgstr "Error del QQ"
+
+#, c-format
 msgid ""
 "Server News:\n"
 "%s\n"
 "%s\n"
 "%s"
-msgstr "Servidor repetidor d'ICQ"
-
-#, fuzzy, c-format
+msgstr ""
+"Notícies del servidor:\n"
+"%s\n"
+"%s\n"
+"%s"
+
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#, c-format
 msgid "From %s:"
-msgstr "De"
-
-#, fuzzy, c-format
+msgstr "De %s:"
+
+#, c-format
 msgid ""
 "Server notice From %s: \n"
 "%s"
-msgstr "Instruccions del servidor: %s"
-
-msgid "Unknow SERVER CMD"
-msgstr ""
+msgstr ""
+"Avís del servidor de %s: \n"
+"%s"
+
+msgid "Unknown SERVER CMD"
+msgstr "Ordre del servidor desconeguda"
 
 #, c-format
 msgid ""
 "Error reply of %s(0x%02X)\n"
-"Room %d, reply 0x%02X"
-msgstr ""
-
-#, fuzzy
+"Room %u, reply 0x%02X"
+msgstr ""
+"Resposta d'error de %s(0x%02X)\n"
+"Sala %u, resposta 0x%02X"
+
 msgid "QQ Qun Command"
-msgstr "Ordre"
-
-#, c-format
-msgid "Not a member of room \"%s\"\n"
-msgstr ""
-
-msgid "Can not decrypt login reply"
-msgstr ""
-
-#, fuzzy
-msgid "Unknow LOGIN CMD"
-msgstr "Motiu desconegut"
-
-#, fuzzy
-msgid "Unknow CLIENT CMD"
-msgstr "Motiu desconegut"
+msgstr "Ordre QQ Qun"
+
+msgid "Could not decrypt login reply"
+msgstr "No s'ha pogut desxifrar la resposta d'entrada"
+
+msgid "Unknown LOGIN CMD"
+msgstr "Ordre d'entrada desconeguda"
+
+msgid "Unknown CLIENT CMD"
+msgstr "Ordre de client desconeguda"
 
 #, c-format
 msgid "%d has declined the file %s"
@@ -8931,9 +8875,9 @@
 msgid "Error loading SILC key pair"
 msgstr "S'ha produït un error en carregar el parell de claus claus SILC"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Download %s: %s"
-msgstr "Usuaris a %s: %s"
+msgstr "Baixada %s: %s"
 
 msgid "Your Current Mood"
 msgstr "El vostre estat d'ànim actual"
@@ -9195,7 +9139,8 @@
 msgid "Private Key file"
 msgstr "Fitxer de la clau privada"
 
-# FIXME
+# El recull diu "xifra", però aquí no es refereix a una xifra sinó a un
+# mètode de xifratge
 msgid "Cipher"
 msgstr "Codi"
 
@@ -9779,9 +9724,8 @@
 msgid "doodle: Request user to start a Doodle session"
 msgstr "doodle: fa una petició a l'usuari per iniciar una sessió Doodle"
 
-#, fuzzy
 msgid "Yahoo ID..."
-msgstr "ID de Yahoo!"
+msgstr "ID de Yahoo..."
 
 #. *< type
 #. *< ui_requirement
@@ -9891,16 +9835,9 @@
 msgid "Last Update"
 msgstr "Darrera actualització"
 
-#, c-format
-msgid "User information for %s unavailable"
-msgstr "Dades de l'usuari %s no disponibles"
-
-msgid ""
-"Sorry, this profile seems to be in a language or format that is not "
-"supported at this time."
-msgstr ""
-"Disculpeu, aquest perfil deu estar en una llengua que actualment no està "
-"implementada."
+msgid ""
+"This profile is in a language or format that is not supported at this time."
+msgstr "Aquest perfil és en una llengua que actualment no està implementada."
 
 msgid ""
 "Could not retrieve the user's profile. This most likely is a temporary "
@@ -10153,11 +10090,11 @@
 
 #, c-format
 msgid "Requesting %s's attention..."
-msgstr "S'està cridant l'atenció de %s..."
+msgstr "S'està reclamant l'atenció de %s..."
 
 #, c-format
 msgid "%s has requested your attention!"
-msgstr "%s us ha cridat l'atenció!"
+msgstr "%s us ha demanat l'atenció!"
 
 #. *
 #. * A wrapper for purple_request_action() that uses @c Yes and @c No buttons.
@@ -10203,21 +10140,18 @@
 msgstr "Voleu acceptar la invitació al xat?"
 
 #. Shortcut
-#, fuzzy
 msgid "Shortcut"
-msgstr "Ordena"
-
-#, fuzzy
+msgstr "Drecera"
+
 msgid "The text-shortcut for the smiley"
-msgstr "Tema de la drecera de text de GTK+"
+msgstr "La drecera de text per a l'emoticona"
 
 #. Stored Image
-#, fuzzy
 msgid "Stored Image"
-msgstr "Desa imatge"
+msgstr "Imatge desada"
 
 msgid "Stored Image. (that'll have to do for now)"
-msgstr ""
+msgstr "Imatge desada. (de moment hem de passar amb això)"
 
 msgid "SSL Connection Failed"
 msgstr "No s'ha pogut connectar amb SSL"
@@ -10347,9 +10281,11 @@
 msgid "Unable to connect to %s"
 msgstr "No s'ha pogut connectar a %s"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Error reading from %s: response too long (%d bytes limit)"
-msgstr "S'ha produït un error en llegir de %s: %s"
+msgstr ""
+"S'ha produït un error en llegir de %s: la resposta és massa llarga (%d bytes "
+"de límit)"
 
 #, c-format
 msgid ""
@@ -10400,9 +10336,9 @@
 msgstr "S'ha refusat la connexió."
 
 #. 10048
-#, fuzzy, c-format
+#, c-format
 msgid "Address already in use."
-msgstr "Aquest nom de xat ja existeix"
+msgstr "Aquesta adreça ja s'està fent servir"
 
 msgid "Internet Messenger"
 msgstr "Missatger d'Internet"
@@ -10511,9 +10447,8 @@
 msgid "_Basic"
 msgstr "_Bàsic"
 
-#, fuzzy
 msgid "Create _this new account on the server"
-msgstr "Crea aquest compte nou al servidor"
+msgstr "Crea _aquest compte nou al servidor"
 
 msgid "_Advanced"
 msgstr "_Avançat"
@@ -10524,7 +10459,7 @@
 msgid "Protocol"
 msgstr "Protocol"
 
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "<span size='larger' weight='bold'>Welcome to %s!</span>\n"
 "\n"
@@ -10539,12 +10474,12 @@
 "<span size='larger' weight='bold'>Benvingut al %s!</span>\n"
 "\n"
 "No teniu cap compte de MI configurat. Per a connectar-vos amb el %s premeu "
-"el botó <b>Afegeix</b> de sota, i configureu el vostre primer compte. Si "
-"voleu que el %s es connecti amb més comptes de MI, torneu a prémer "
-"<b>Afegeix</b> fins a configurar-los tots.\n"
+"el botó <b>Afegeix</b> d'aquí sota, i configureu el vostre primer compte. Si "
+"voleu que el %s es connecti amb més comptes de missatgeria instantània (MI), "
+"torneu a prémer <b>Afegeix</b> fins a configurar-los tots.\n"
 "\n"
 "Podeu tornar a aquesta finestra per afegir, editar o suprimir comptes, a "
-"partir del menú <b>Comptes->Afegeix/Edita</b> de la finestra de la llista "
+"partir del menú <b>Comptes->Gestiona els comptes</b> a finestra de la llista "
 "d'amics."
 
 #, c-format
@@ -10613,9 +10548,8 @@
 msgid "_Remove"
 msgstr "Sup_rimeix"
 
-#, fuzzy
 msgid "Set Custom Icon"
-msgstr "Estableix una icona personalitzada..."
+msgstr "Estableix una icona personalitzada"
 
 msgid "Remove Custom Icon"
 msgstr "Suprimeix la icona personalitzada"
@@ -10717,9 +10651,8 @@
 msgid "/_Accounts"
 msgstr "/_Comptes"
 
-#, fuzzy
 msgid "/Accounts/Manage Accounts"
-msgstr "/Comptes/Gestió"
+msgstr "/Comptes/Gestió de comptes"
 
 # Accelerador a la "n" com en la resta de programes
 #. Tools
@@ -10736,14 +10669,13 @@
 msgstr "/Eines/_Connectors"
 
 msgid "/Tools/Pr_eferences"
-msgstr "/Eines/Pr_eferències"
+msgstr "/Eines/Preferè_ncies"
 
 msgid "/Tools/Pr_ivacy"
 msgstr "/Eines/_Privadesa"
 
-#, fuzzy
 msgid "/Tools/Smile_y"
-msgstr "/Eines/_Absent"
+msgstr "/Eines/Em_oticona"
 
 msgid "/Tools/_File Transfers"
 msgstr "/Eines/Transferència de _fitxers"
@@ -10904,7 +10836,7 @@
 msgstr "/Comptes"
 
 #. Translators: Please maintain the use of -> and <- to refer to menu heirarchy
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "<span weight='bold' size='larger'>Welcome to %s!</span>\n"
 "\n"
@@ -10915,9 +10847,9 @@
 "<span weight='bold' size='larger'>Benvingut al %s!</span>\n"
 "\n"
 "No teniu cap compte habilitat. Podeu habilitar comptes de MI des del menú "
-"<b>Comptes->Gestió</b> de la finestra de <b>Comptes</b>. Quan hagueu "
-"habilitat algun compte, podreu connectar-vos, establir el vostre estat, i "
-"parlar amb amics."
+"<b>Comptes->Gestió de comptes</b> de la finestra de <b>Comptes</b>. Quan "
+"hagueu habilitat algun compte podreu connectar-vos-hi, establir el vostre "
+"estat, i parlar amb amics."
 
 #. set the Show Offline Buddies option. must be done
 #. * after the treeview or faceprint gets mad. -Robot101
@@ -10970,9 +10902,8 @@
 msgid "Auto_join when account becomes online."
 msgstr "Entra _automàticament quant el compte estigui connectat."
 
-#, fuzzy
 msgid "_Remain in chat after window is closed."
-msgstr "_Oculta el xat quan la finestra es tanqui."
+msgstr "_Continua al xat quan la finestra es tanqui."
 
 msgid "Please enter the name of the group to be added."
 msgstr "Introduïu el nom del grup que vulgueu afegir."
@@ -11348,23 +11279,22 @@
 msgid "Fatal Error"
 msgstr "Error fatal"
 
+# Fixme
 msgid "bug master"
-msgstr ""
-
-#, fuzzy
+msgstr "bug master"
+
 msgid "artist"
-msgstr "Artista"
+msgstr "artista"
 
 #. feel free to not translate this
 msgid "Ka-Hing Cheung"
-msgstr ""
+msgstr "Ka-Hing Cheung"
 
 msgid "support"
 msgstr "suport"
 
-#, fuzzy
 msgid "webmaster"
-msgstr "desenvolupador i mantenidor de la pàgina web"
+msgstr "administrador del web"
 
 msgid "Senior Contributor/QA"
 msgstr "Col·laborador veterà/CQ"
@@ -11387,7 +11317,7 @@
 msgstr "suport/CQ"
 
 msgid "XMPP"
-msgstr ""
+msgstr "XMPP"
 
 msgid "original author"
 msgstr "autor original"
@@ -11466,9 +11396,8 @@
 msgid "French"
 msgstr "Francès"
 
-#, fuzzy
 msgid "Irish"
-msgstr "Kurd"
+msgstr "Irlandès"
 
 msgid "Galician"
 msgstr "Gallec"
@@ -11537,7 +11466,7 @@
 msgstr "Noruec (Nynorsk)"
 
 msgid "Occitan"
-msgstr ""
+msgstr "Occità"
 
 msgid "Punjabi"
 msgstr "Punjabi"
@@ -11779,45 +11708,35 @@
 msgid "Right-click for more unread messages...\n"
 msgstr "Feu clic amb el botó principal per a més missatges per llegir...\n"
 
-#, fuzzy
 msgid "_Change Status"
-msgstr "Canvia d'estat"
-
-#, fuzzy
+msgstr "_Canvia d'estat"
+
 msgid "Show Buddy _List"
-msgstr "Mostra la llista d'amics"
-
-#, fuzzy
+msgstr "Mostra la _llista d'amics"
+
 msgid "_Unread Messages"
-msgstr "Missatges sense llegir"
-
-#, fuzzy
+msgstr "Missatges _sense llegir"
+
 msgid "New _Message..."
-msgstr "Missatge nou..."
-
-#, fuzzy
+msgstr "_Missatge nou..."
+
 msgid "_Accounts"
-msgstr "/_Comptes"
-
-#, fuzzy
+msgstr "_Comptes"
+
 msgid "Plu_gins"
-msgstr "Connectors"
-
-#, fuzzy
+msgstr "Co_nnectors"
+
 msgid "Pr_eferences"
-msgstr "Preferències"
-
-#, fuzzy
+msgstr "Pr_eferències"
+
 msgid "Mute _Sounds"
-msgstr "Inhabilita els sons"
-
-#, fuzzy
+msgstr "Inhabilita els _sons"
+
 msgid "_Blink on New Message"
-msgstr "Parpelleja si hi ha missatges nous"
-
-#, fuzzy
+msgstr "_Parpelleja si hi ha missatges nous"
+
 msgid "_Quit"
-msgstr "Surt"
+msgstr "_Surt"
 
 msgid "Not started"
 msgstr "No s'ha iniciat"
@@ -11900,14 +11819,13 @@
 msgid "Color to draw hyperlinks."
 msgstr "El color amb què pintar els enllaços."
 
-#, fuzzy
 msgid "Hyperlink visited color"
-msgstr "Color dels enllaços"
-
-#, fuzzy
+msgstr "Color dels enllaços visitats"
+
 msgid "Color to draw hyperlinks after it has been visited (or activated)."
 msgstr ""
-"El color amb què es pintaran els enllaços quan el ratolí hi estigui a sobre."
+"El color amb el qual es pintaran els enllaços que ja s'hagin visitat (o "
+"activat)."
 
 # FIXME: prelight? (josep)
 msgid "Hyperlink prelight color"
@@ -11943,13 +11861,11 @@
 msgid "Color to draw the name of an action message."
 msgstr "Color amb el qual es pintaran els missatges d'acció."
 
-#, fuzzy
 msgid "Action Message Name Color for Whispered Message"
-msgstr "Nom del color dels missatges d'acció"
-
-#, fuzzy
+msgstr "Color del nom del missatge d'acció per a missatges xiuxiuejats"
+
 msgid "Whisper Message Name Color"
-msgstr "Nom del color per als missatges enviats"
+msgstr "Color dels missatges xiuxiuejats enviats"
 
 msgid "Typing notification color"
 msgstr "Color per a les notificacions de quan s'escriu"
@@ -12025,9 +11941,9 @@
 msgid "_Save Image..."
 msgstr "_Desa la imatge..."
 
-#, fuzzy, c-format
+#, c-format
 msgid "_Add Custom Smiley..."
-msgstr "Mostra emoticones personalitzades"
+msgstr "_Afegeix una emoticones personalitzada..."
 
 msgid "Select Font"
 msgstr "Selecciona el tipus de lletra"
@@ -12072,13 +11988,15 @@
 "This smiley is disabled because a custom smiley exists for this shortcut:\n"
 " %s"
 msgstr ""
+"Aquesta emoticona està inhabilitada perquè hi ha una emoticona "
+"personalitzada per aquesta drecera:\n"
+" %s"
 
 msgid "Smile!"
 msgstr "Somrieu!"
 
-#, fuzzy
 msgid "_Manage custom smileys"
-msgstr "Mostra emoticones personalitzades"
+msgstr "_Gestiona les emoticones personalitzades"
 
 msgid "This theme has no available smileys."
 msgstr "Aquest tema no disposa d'emoticones."
@@ -12290,7 +12208,7 @@
 "  -v, --version       mostra la versió actual i surt\n"
 
 # FIXME: backtrace -> traça (bug-buddy) ?
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "%s %s has segfaulted and attempted to dump a core file.\n"
 "This is a bug in the software and has happened through\n"
@@ -12311,17 +12229,12 @@
 "\n"
 "Si podeu reproduir aquest error, feu-ho saber als desenvolupadors,\n"
 "enviant-los un error de programació a:\n"
-"%ssimpleticket\n"
+"%ssimpleticket/\n"
 "\n"
 "Assegureu-vos que indiqueu el que estàveu fent, i envieu una traça\n"
 "(backtrace) del fitxer core. Si no sabeu com obtenir una traça,\n"
 "llegiu les instruccions que hi ha a:\n"
 "%swiki/GetABacktrace\n"
-"\n"
-"Si necessiteu ajuda, envieu un missatge instantani a SeanEgn o\n"
-"bé a LSchiere (per AIM). La informació per a contactar en Sean o\n"
-"en Luke a través d'altres protocols és aquí:\n"
-"%swiki/DeveloperPages\n"
 
 #. Translators may want to transliterate the name.
 #. It is not to be translated.
@@ -12329,7 +12242,7 @@
 msgstr "Pidgin"
 
 msgid "Open All Messages"
-msgstr "Obre els _missatges"
+msgstr "Obre tots els missatges"
 
 msgid "<span weight=\"bold\" size=\"larger\">You have mail!</span>"
 msgstr "<span weight=\"bold\" size=\"larger\">Teniu correu electrònic.</span>"
@@ -12360,8 +12273,7 @@
 msgid ""
 "The 'Manual' browser command has been chosen, but no command has been set."
 msgstr ""
-"S'ha triat l'ordre per al navegador 'manualment', però no se n'ha indicat "
-"cap."
+"S'ha triat l'ordre per al navegador «manualment», però no se n'ha indicat cap."
 
 msgid "The following plugins will be unloaded."
 msgstr "Es descarregaran els connectors següents."
@@ -12794,31 +12706,26 @@
 "Ordre per al so\n"
 "(%s per al nom de fitxer)"
 
-#, fuzzy
 msgid "M_ute sounds"
-msgstr "Inhabilita els sons"
+msgstr "In_habilita els sons"
 
 msgid "Sounds when conversation has _focus"
 msgstr "Sons quan la conversa tingui el _focus"
 
-#, fuzzy
 msgid "_Enable sounds:"
-msgstr "Habilita els sons:"
-
-#, fuzzy
+msgstr "_Habilita els sons:"
+
 msgid "V_olume:"
-msgstr "Volum:"
+msgstr "V_olum:"
 
 msgid "Play"
 msgstr "Reprodueix"
 
-#, fuzzy
 msgid "_Browse..."
 msgstr "Nav_ega..."
 
-#, fuzzy
 msgid "_Reset"
-msgstr "Reinicia"
+msgstr "_Reinicia"
 
 msgid "_Report idle time:"
 msgstr "Informa del _temps d'inactivitat:"
@@ -12990,61 +12897,52 @@
 msgid "Status for %s"
 msgstr "Estat per a %s"
 
-#, fuzzy
 msgid "Custom Smiley"
-msgstr "Insereix una emoticona"
+msgstr "Emoticona personalitzada"
 
 msgid "More Data needed"
-msgstr ""
+msgstr "Calen més dades"
 
 msgid "Please provide a shortcut to associate with the smiley."
-msgstr ""
-
-#, fuzzy
+msgstr "Especifiqueu una drecera associada a l'emoticona."
+
 msgid "Duplicate Shortcut"
-msgstr "Duplicació de la correcció"
+msgstr "Drecera duplicada"
 
 msgid ""
 "A custom smiley for the selected shortcut already exists. Please specify a "
 "different shortcut."
 msgstr ""
-
-#, fuzzy
+"Hi ha una emoticona personalitzada per la drecera que heu seleccionat. "
+"Indiqueu-ne una de diferent."
+
 msgid "Please select an image for the smiley."
-msgstr "Seleccioneu l'estat d'ànim de la llista."
-
-#, fuzzy
+msgstr "Seleccioneu una imatge per a l'emoticona."
+
 msgid "Edit Smiley"
-msgstr "Insereix una emoticona"
-
-#, fuzzy
+msgstr "Edita l'emoticona"
+
 msgid "Add Smiley"
-msgstr "_Emoticona"
-
-#, fuzzy
+msgstr "Afegeix una emoticona"
+
 msgid "Smiley _Image"
-msgstr "Desa imatge"
+msgstr "_Imatge de l'emoticona"
 
 #. Smiley shortcut
-#, fuzzy
 msgid "Smiley S_hortcut"
-msgstr "Dreceres de teclat"
-
-#, fuzzy
+msgstr "_Dreceres de l'emoticona"
+
 msgid "Smiley"
-msgstr "_Emoticona"
-
-#, fuzzy
+msgstr "Emoticona"
+
 msgid "Custom Smiley Manager"
-msgstr "Gestor de certificats"
-
-#, fuzzy
+msgstr "Gestor d'emoticones personalitzades"
+
 msgid "Click to change your buddyicon for this account."
-msgstr "Utilitza aquesta _icona d'amic per a aquest compte:"
-
-#, fuzzy
+msgstr "Feu clic per canviar la icona d'amic d'aquest compte."
+
 msgid "Click to change your buddyicon for all accounts."
-msgstr "Utilitza aquesta _icona d'amic per a aquest compte:"
+msgstr "Feu clic per canviar la icona d'amic de tots els comptes."
 
 msgid "Waiting for network connection"
 msgstr "S'està esperant la connexió de xarxa"
@@ -13182,20 +13080,17 @@
 msgid "_Invite"
 msgstr "Conv_ida"
 
-#, fuzzy
 msgid "_Modify..."
-msgstr "_Modifica"
-
-#, fuzzy
+msgstr "_Modifica..."
+
 msgid "_Add..."
-msgstr "_Afegeix"
+msgstr "_Afegeix..."
 
 msgid "_Open Mail"
 msgstr "_Obre el correu"
 
-#, fuzzy
 msgid "_Edit"
-msgstr "Edita"
+msgstr "_Edita"
 
 msgid "Pidgin Tooltip"
 msgstr "Indicador de funció del Pidgin"
@@ -13214,12 +13109,11 @@
 msgid "none"
 msgstr "cap"
 
-#, fuzzy
 msgid "Small"
-msgstr "Correu electrònic"
+msgstr "Petites"
 
 msgid "Smaller versions of the default smilies"
-msgstr ""
+msgstr "Versions més petites de les emoticones per defecte"
 
 msgid "Response Probability:"
 msgstr "Probabilitat de resposta:"
@@ -13693,9 +13587,9 @@
 msgid "Set window manager \"_URGENT\" hint"
 msgstr "Aplica l'opció «_URGENT» del gestor de finestres"
 
-#, fuzzy
+# (gnome)
 msgid "_Flash window"
-msgstr "Finestres de _xat"
+msgstr "Finestres _flash"
 
 #. Raise window method button
 msgid "R_aise conversation window"
@@ -13783,9 +13677,8 @@
 msgid "Hyperlink Color"
 msgstr "Color dels hiperenllaços"
 
-#, fuzzy
 msgid "Visited Hyperlink Color"
-msgstr "Color dels hiperenllaços"
+msgstr "Color dels enllaços visitats"
 
 msgid "Highlighted Message Name Color"
 msgstr "Nom del color per als missatges ressaltats"
@@ -13880,18 +13773,16 @@
 
 #, c-format
 msgid "You can upgrade to %s %s today."
-msgstr ""
+msgstr "Podeu actualitzar-vos a %s %s avui."
 
 msgid "New Version Available"
 msgstr "Nova versió disponible"
 
-#, fuzzy
 msgid "Later"
-msgstr "Data"
-
-#, fuzzy
+msgstr "Més tard"
+
 msgid "Download Now"
-msgstr "Usuaris a %s: %s"
+msgstr "Baixa-la ara"
 
 #. *< type
 #. *< ui_requirement
@@ -14190,24 +14081,64 @@
 
 # FIXME: stanza -> estrofa (literal) (Josep)
 msgid "Insert an <iq/> stanza."
-msgstr "Insereix una estrofa <iq/>"
+msgstr "Insereix un bloc <iq/>."
 
 msgid "Insert a <presence/> stanza."
-msgstr "Insereix una estrofa <presence/>"
+msgstr "Insereix un bloc <presence/>."
 
 msgid "Insert a <message/> stanza."
-msgstr "Insereix una estrofa <message/>"
+msgstr "Insereix un bloc <message/>."
 
 #. *< name
 #. *< version
 #. *  summary
 msgid "Send and receive raw XMPP stanzas."
-msgstr "Envia i rep estrofes XMPP en brut."
+msgstr "Envia i rep blocs XMPP en brut."
 
 #. *  description
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr "Aquest connector és útil per a depurar servidors i clients XMPP."
 
+#~ msgid "Connection to server lost (no data received within %d second)"
+#~ msgid_plural ""
+#~ "Connection to server lost (no data received within %d seconds)"
+#~ msgstr[0] ""
+#~ "S'ha perdut la connexió al servidor (fa %d segon que no s'hi reben dades)"
+#~ msgstr[1] ""
+#~ "S'ha perdut la connexió al servidor (fa %d segons que no s'hi reben dades)"
+
+#~ msgid "Keep alive error"
+#~ msgstr "Error de permanència"
+
+#~ msgid ""
+#~ "Lost connection with server:\n"
+#~ "%d, %s"
+#~ msgstr ""
+#~ "S'ha perdut la connexió amb el servidor:\n"
+#~ "%d, %s"
+
+#~ msgid "Connecting server ..."
+#~ msgstr "S'està connectant al servidor..."
+
+#~ msgid "Failed to send IM."
+#~ msgstr "No s'ha pogut enviar la MI."
+
+#~ msgid "Not a member of room \"%s\"\n"
+#~ msgstr "No sou un membre de la sala «%s»\n"
+
+#~ msgid "User information for %s unavailable"
+#~ msgstr "Dades de l'usuari %s no disponibles"
+
+#~ msgid ""
+#~ "You are using %s version %s.  The current version is %s.  You can get it "
+#~ "from <a href=\"%s\">%s</a><hr>"
+#~ msgstr ""
+#~ "Esteu emprant la versió %s del %s. La versió actual és %s, la podeu "
+#~ "obtenir de <a href=\"%s\">%s</a><hr>"
+
+#~ msgid "<b>ChangeLog:</b><br>%s"
+#~ msgstr "<b>Registre de canvis:</b><br>%s"
+
 #~ msgid "A group with the name already exists."
 #~ msgstr "Ja existeix un grup amb aquest nom."
 
@@ -14217,13 +14148,8 @@
 #~ msgid "Blood Type"
 #~ msgstr "Tipus de sang"
 
-#, fuzzy
 #~ msgid "Update information"
-#~ msgstr "Actualitza la meva informació"
-
-#, fuzzy
-#~ msgid "Successed:"
-#~ msgstr "Velocitat:"
+#~ msgstr "Actualitza la informació"
 
 #~ msgid ""
 #~ "Setting custom faces is not currently supported. Please choose an image "
@@ -14244,14 +14170,6 @@
 #~ msgid "Add buddy with auth request failed"
 #~ msgstr "Ha fallat la sol·licitut per afegir un amic amb autorització"
 
-#, fuzzy
-#~ msgid "Add into %d's buddy list"
-#~ msgstr "No s'ha pogut carregar la llista d'amics"
-
-#, fuzzy
-#~ msgid "QQ Number Error"
-#~ msgstr "Número QQ"
-
 #~ msgid "Group Description"
 #~ msgstr "Descripció del grup"
 
@@ -14261,26 +14179,12 @@
 #~ msgid "Approve"
 #~ msgstr "Aprova"
 
-#, fuzzy
-#~ msgid "Successed to join Qun %d, operated by admin %d"
-#~ msgstr ""
-#~ "L'administrador %2$d ha rebutjat la vostra sol·liciut per entrar al grup %"
-#~ "1$d"
-
-#, fuzzy
-#~ msgid "[%d] removed from Qun \"%d\""
-#~ msgstr "Vós [%d] heu sortit del grup «%d»"
-
-#, fuzzy
-#~ msgid "[%d] added to Qun \"%d\""
-#~ msgstr "Vós [%d] heu estat afegit al grup «%d»"
-
 #~ msgid "I am a member"
 #~ msgstr "En sóc membre"
 
-#, fuzzy
+# FIXME? (hi demano [entrar]?)
 #~ msgid "I am requesting"
-#~ msgstr "Sol·licitud incorrecta"
+#~ msgstr "Demano"
 
 #~ msgid "I am the admin"
 #~ msgstr "En sóc l'administrador"
@@ -14288,10 +14192,6 @@
 #~ msgid "Unknown status"
 #~ msgstr "Estat desconegut"
 
-#, fuzzy
-#~ msgid "Remove from Qun"
-#~ msgstr "Suprimeix el grup"
-
 #~ msgid "You entered a group ID outside the acceptable range"
 #~ msgstr "Heu entrat un identificador de grup fora del rang"
 
@@ -14301,24 +14201,6 @@
 #~ msgid "Do you want to approve the request?"
 #~ msgstr "Voleu aprovar aquesta sol·licitud?"
 
-#, fuzzy
-#~ msgid "Change Qun member"
-#~ msgstr "Número de telèfon"
-
-#, fuzzy
-#~ msgid "Change Qun information"
-#~ msgstr "Informació del canal"
-
-# Gnome_2.6_Extras [rhythmbox.HEAD.ca.po]
-#, fuzzy
-#~ msgid ""
-#~ "%s\n"
-#~ "\n"
-#~ "%s"
-#~ msgstr ""
-#~ "%s:\n"
-#~ "%s"
-
 #~ msgid "System Message"
 #~ msgstr "Missatge del sistema"
 
@@ -14331,64 +14213,47 @@
 #~ msgid "Set My Information"
 #~ msgstr "Estableix la meva informació"
 
-#, fuzzy
-#~ msgid "Leave the QQ Qun"
-#~ msgstr "Surt d'aquest Qun QQ"
-
 #~ msgid "Block this buddy"
 #~ msgstr "Bloca aquest usuari"
 
-#, fuzzy
 #~ msgid "Error password: %s"
-#~ msgstr "S'ha produït un error en canviar la contrasenya"
-
-#, fuzzy
+#~ msgstr "Error en la contrasenya: %s"
+
 #~ msgid "Failed to connect all servers"
-#~ msgstr "No s'ha pogut connectar al servidor."
-
-#, fuzzy
+#~ msgstr "No s'han pogut connectar tots els servidors"
+
 #~ msgid "Connecting server %s, retries %d"
-#~ msgstr ""
-#~ "S'ha produït un error de connexió del servidor %s:\n"
-#~ "%s"
-
-#, fuzzy
+#~ msgstr "S'està connectant al servidor %s, %d reintents"
+
 #~ msgid "Do you approve the requestion?"
 #~ msgstr "Voleu aprovar aquesta sol·licitud?"
 
-#, fuzzy
 #~ msgid "Do you add the buddy?"
 #~ msgstr "Voleu afegir aquest amic?"
 
-#, fuzzy
 #~ msgid "%s added you [%s] to buddy list"
 #~ msgstr "%s us ha afegit [%s] a la seva llista d'amics"
 
-#, fuzzy
 #~ msgid "QQ Budy"
-#~ msgstr "Amic"
+#~ msgstr "Amic QQ"
+
+#~ msgid "Requestion approved by %s"
+#~ msgstr "%s ha aprovat la sol.licitud"
 
 #~ msgid "%s wants to add you [%s] as a friend"
 #~ msgstr "%s us vol afegir [%s] com a amic"
 
-#, fuzzy
 #~ msgid "%s is not in buddy list"
 #~ msgstr "%s no és a la vostra llista d'amics"
 
-#, fuzzy
 #~ msgid "Would you add?"
 #~ msgstr "Voleu afegir-lo?"
 
 #~ msgid "%s"
 #~ msgstr "%s"
 
-#, fuzzy
-#~ msgid "QQ Server Notice"
-#~ msgstr "Port en el servidor"
-
-#, fuzzy
 #~ msgid "Network disconnected"
-#~ msgstr "El remot s'ha desconnectat"
+#~ msgstr "La xarxa s'ha desconnectat"
 
 #~ msgid "developer"
 #~ msgstr "desenvolupador"
@@ -14399,33 +14264,6 @@
 #~ msgid "Artists"
 #~ msgstr "Artistes"
 
-#~ msgid ""
-#~ "You are using %s version %s.  The current version is %s.  You can get it "
-#~ "from <a href=\"%s\">%s</a><hr>"
-#~ msgstr ""
-#~ "Esteu emprant la versió %s del %s. La versió actual és %s, la podeu "
-#~ "obtenir de <a href=\"%s\">%s</a><hr>"
-
-#~ msgid "<b>ChangeLog:</b><br>%s"
-#~ msgstr "<b>Registre de canvis:</b><br>%s"
-
-#~ msgid "EOF while reading from resolver process"
-#~ msgstr ""
-#~ "S'ha arribat al final del fitxer (EOF) en llegir del procés resoledor"
-
-#~ msgid "Error setting socket options"
-#~ msgstr "S'ha produït un error en establir les opcions del sòcol"
-
-#~ msgid ""
-#~ "Windows Live ID authentication: cannot find authenticate token in server "
-#~ "response"
-#~ msgstr ""
-#~ "Autenticació amb el Windows Live ID: no s'ha pogut trobar el testimoni "
-#~ "d'autenticació al servidor"
-
-#~ msgid "Windows Live ID authentication Failed"
-#~ msgstr "No s'ha pogut autenticar amb el Windows Live ID"
-
 #~ msgid "Too evil (sender)"
 #~ msgstr "Massa malvat (remitent)"
 
@@ -14441,86 +14279,6 @@
 #~ msgid "<i>(retrieving)</i>"
 #~ msgstr "<i>(s'està recuperant)</i>"
 
-#~ msgid "Your information has been updated"
-#~ msgstr "S'ha actualitzat la vostra informació"
-
-#~ msgid "Input your reason:"
-#~ msgstr "Indiqueu-ne el motiu:"
-
-#~ msgid "You have successfully removed a buddy"
-#~ msgstr "Heu suprimit amb èxit un amic"
-
-#~ msgid "You have successfully removed yourself from your friend's buddy list"
-#~ msgstr "Us heu suprimit amb èxit de la llista d'amics del vostre amic"
-
-#~ msgid "You have added %d to buddy list"
-#~ msgstr "Heu afegit %d a la llista d'amics"
-
-#~ msgid "Invalid QQid"
-#~ msgstr "QQid invàlid"
-
-#~ msgid "Please enter external group ID"
-#~ msgstr "Introduïu l'ID del grup extern"
-
-#~ msgid "Reason: %s"
-#~ msgstr "Motiu: %s"
-
-#~ msgid "Your request to join group %d has been approved by admin %d"
-#~ msgstr ""
-#~ "L'administrador %2$d ha aprovat la vostra sol·liciud per entrar al grup %1"
-#~ "$d"
-
-#~ msgid "This group has been added to your buddy list"
-#~ msgstr "S'ha afegit aquest grup a la vostra llista d'amics"
-
-#~ msgid "I am applying to join"
-#~ msgstr "Estic sol·licitant entrar-hi"
-
-#~ msgid "You have successfully left the group"
-#~ msgstr "Heu sortit del grup correctament"
-
-#~ msgid "QQ Group Auth"
-#~ msgstr "Autorització de grup QQ"
-
-#~ msgid "Your authorization request has been accepted by the QQ server"
-#~ msgstr "El servidor QQ ha acceptat la sol·licitud d'autorització"
-
-#~ msgid "Code [0x%02X]: %s"
-#~ msgstr "Codi [0x%02X]: %s"
-
-#~ msgid "Group Operation Error"
-#~ msgstr "S'ha produït un error d'orientació del grup"
-
-#~ msgid "Enter your reason:"
-#~ msgstr "Indiqueu-ne el motiu:"
-
-#~ msgid "Error requesting login token"
-#~ msgstr "S'ha produït un error en sol·licitar un testimoni d'entrada"
-
-#~ msgid "Unable to login. Check debug log."
-#~ msgstr "No s'ha pogut connectar, comproveu el registre de depuració."
-
-#~ msgid "Unable to login"
-#~ msgstr "No s'ha connectar"
-
-#~ msgid "TCP Address"
-#~ msgstr "Adreça TCP"
-
-#~ msgid "UDP Address"
-#~ msgstr "Adreça UDP"
-
-#~ msgid "Show Login Information"
-#~ msgstr "Mostra informació de la connexió"
-
-#~ msgid "Login failed, no reply"
-#~ msgstr "Ha fallat l'entrada, no s'ha obtingut resposta"
-
-#~ msgid "User %s rejected your request"
-#~ msgstr "L'usuari %s ha declinat la vostra sol·licitud"
-
-#~ msgid "User %s approved your request"
-#~ msgstr "L'usuari %s ha acceptat la vostra sol·licitud"
-
 #~ msgid "Screen name:"
 #~ msgstr "Nom d'usuari:"
 
@@ -15521,6 +15279,9 @@
 #~ msgid "Reject Call"
 #~ msgstr "Rebutja la trucada"
 
+#~ msgid "Connected to %s"
+#~ msgstr "S'ha connectat a %s"
+
 #~ msgid "_Mute"
 #~ msgstr "_Silencia"
 
@@ -15941,9 +15702,6 @@
 #~ msgid "Away title: "
 #~ msgstr "Motiu de l'absència: "
 
-#~ msgid "Buddy List Error"
-#~ msgstr "Error en la llista d'amics"
-
 #~ msgid ""
 #~ "\n"
 #~ "Mark all accounts as \"away\" with the default message.\n"
--- a/po/de.po	Sun Dec 14 23:43:52 2008 +0000
+++ b/po/de.po	Mon Dec 15 08:39:08 2008 +0000
@@ -11,10 +11,10 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-01 15:52-0800\n"
-"PO-Revision-Date: 2008-10-28 17:46+0100\n"
-"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
-"Language-Team: Deutsch <de@li.org>\n"
+"POT-Creation-Date: 2008-12-14 22:58+0100\n"
+"PO-Revision-Date: 2008-12-14 22:58+0100\n"
+"Last-Translator: Björn Voigt <bjoern@cs.tu-berlin.de>\n"
+"Language-Team: German <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -1127,7 +1127,6 @@
 msgid "%s has sent you a message. (%s)"
 msgstr "%s hat Ihnen eine Nachricht gesendet. (%s)"
 
-#, c-format
 msgid "Unknown pounce event. Please report this!"
 msgstr "Unbekanntes Alarm-Ereignis. Bitte berichten Sie dieses Problem!"
 
@@ -1497,7 +1496,6 @@
 "Wenn eine neue Unterhaltung eröffnet wird, fügt dieses Plugin die letzte "
 "Unterhaltung in die aktuelle Unterhaltung ein."
 
-#, c-format
 msgid "Online"
 msgstr "Online"
 
@@ -1840,7 +1838,6 @@
 "Fehler beim Lesen vom Auflösungsprozess:\n"
 "%s"
 
-#, c-format
 msgid "Resolver process exited without answering our request"
 msgstr "Auflösungsprozess hat sich beendet ohne die Anfrage zu beantworten"
 
@@ -1931,7 +1928,6 @@
 msgid "Transfer of file %s complete"
 msgstr "Übertragung der Datei %s ist komplett"
 
-#, c-format
 msgid "File transfer complete"
 msgstr "Dateiübertragung ist komplett"
 
@@ -1939,7 +1935,6 @@
 msgid "You canceled the transfer of %s"
 msgstr "Sie haben die Dateiübertragung von %s abgebrochen"
 
-#, c-format
 msgid "File transfer cancelled"
 msgstr "Dateiübertragung wurde abgebrochen"
 
@@ -2147,7 +2142,6 @@
 msgid "You are using %s, but this plugin requires %s."
 msgstr "Sie benutzen %s, aber dieses Plugin benötigt %s."
 
-#, c-format
 msgid "This plugin has not defined an ID."
 msgstr "Dieses Plugin hat keine ID definiert."
 
@@ -3043,7 +3037,6 @@
 #. get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
 #. * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message
 #. Away stuff
-#, c-format
 msgid "Away"
 msgstr "Abwesend"
 
@@ -3935,7 +3928,6 @@
 msgid "Extended Away"
 msgstr "Abwesend (erweitert)"
 
-#, c-format
 msgid "Do Not Disturb"
 msgstr "Nicht stören"
 
@@ -4318,7 +4310,7 @@
 msgstr "Server überlastet"
 
 msgid "Service Unavailable"
-msgstr "Dienst nicht erreichbar"
+msgstr "Dienst nicht verfügbar"
 
 msgid "Subscription Required"
 msgstr "Abonnement erforderlich"
@@ -4690,6 +4682,19 @@
 msgid "Unable to retrieve MSN Address Book"
 msgstr "Konnte das MSN-Adressbuch nicht abrufen"
 
+#. only notify the user about problems adding to the friends list
+#. * maybe we should do something else for other lists, but it probably
+#. * won't cause too many problems if we just ignore it
+#, c-format
+msgid "Unable to add \"%s\"."
+msgstr "Kann „%s“ nicht hinzufügen."
+
+msgid "Buddy Add error"
+msgstr "Fehler beim Hinzufügen des Buddys"
+
+msgid "The username specified does not exist."
+msgstr "Der angegebene Benutzername existiert nicht."
+
 #, c-format
 msgid "Buddy list synchronization issue in %s (%s)"
 msgstr "Fehler bei der Buddy-Listen-Synchronisation bei %s (%s)"
@@ -4710,220 +4715,166 @@
 "%s ist auf der lokalen Liste, aber nicht auf der Serverliste. Möchten Sie, "
 "dass der Buddy hinzugefügt wird?"
 
-#, c-format
 msgid "Unable to parse message"
 msgstr "Kann die Nachricht nicht parsen"
 
-#, c-format
 msgid "Syntax Error (probably a client bug)"
 msgstr "Syntaxfehler (wahrscheinlich ein Client-Bug)"
 
-#, c-format
 msgid "Invalid email address"
 msgstr "Ungültige E-Mail-Adresse"
 
-#, c-format
 msgid "User does not exist"
 msgstr "Benutzer existiert nicht"
 
-#, c-format
 msgid "Fully qualified domain name missing"
 msgstr "Der Fully Qualified Domain Name fehlt"
 
-#, c-format
 msgid "Already logged in"
 msgstr "Schon angemeldet"
 
-#, c-format
 msgid "Invalid username"
 msgstr "Ungültiger Benutzername"
 
-#, c-format
 msgid "Invalid friendly name"
 msgstr "Ungültiger Freundesname"
 
-#, c-format
 msgid "List full"
 msgstr "Liste voll"
 
-#, c-format
 msgid "Already there"
 msgstr "Schon da"
 
-#, c-format
 msgid "Not on list"
 msgstr "Nicht auf der Liste"
 
-#, c-format
 msgid "User is offline"
 msgstr "Benutzer ist offline"
 
-#, c-format
 msgid "Already in the mode"
 msgstr "Bereits in diesem Modus"
 
-#, c-format
 msgid "Already in opposite list"
 msgstr "Bereits in der „Gegenteil-Liste“"
 
-#, c-format
 msgid "Too many groups"
 msgstr "Zu viele Gruppen"
 
-#, c-format
 msgid "Invalid group"
 msgstr "Ungültige Gruppe"
 
-#, c-format
 msgid "User not in group"
 msgstr "Benutzer ist nicht in der Gruppe"
 
-#, c-format
 msgid "Group name too long"
 msgstr "Name der Gruppe ist zu lang"
 
-#, c-format
 msgid "Cannot remove group zero"
 msgstr "Kann die Gruppe „Null“ nicht entfernen"
 
-#, c-format
 msgid "Tried to add a user to a group that doesn't exist"
 msgstr ""
 "Versuchte einen Benutzer zu einer nichtexistierenden Gruppe hinzuzufügen"
 
-#, c-format
 msgid "Switchboard failed"
 msgstr "Vermittlung gescheitert"
 
-#, c-format
 msgid "Notify transfer failed"
 msgstr "Übertragung der Benachrichtigung gescheitert"
 
-#, c-format
 msgid "Required fields missing"
 msgstr "Notwendige Felder fehlen"
 
-#, c-format
 msgid "Too many hits to a FND"
 msgstr "Zu viele Treffer zu einem FND"
 
-#, c-format
 msgid "Not logged in"
 msgstr "Nicht angemeldet"
 
-#, c-format
 msgid "Service temporarily unavailable"
 msgstr "Dienst momentan nicht verfügbar"
 
-#, c-format
 msgid "Database server error"
 msgstr "Fehler des Datenbank-Servers"
 
-#, c-format
 msgid "Command disabled"
 msgstr "Kommando abgeschaltet"
 
-#, c-format
 msgid "File operation error"
 msgstr "Dateiverarbeitungsfehler"
 
-#, c-format
 msgid "Memory allocation error"
 msgstr "Fehler bei der Speicheranforderung"
 
-#, c-format
 msgid "Wrong CHL value sent to server"
 msgstr "Falscher CHL-Wert zum Server gesendet"
 
-#, c-format
 msgid "Server busy"
 msgstr "Server beschäftigt"
 
-#, c-format
 msgid "Server unavailable"
 msgstr "Server unerreichbar"
 
-#, c-format
 msgid "Peer notification server down"
 msgstr "Peer-Benachrichtigungsserver nicht erreichbar"
 
-#, c-format
 msgid "Database connect error"
 msgstr "Datenbank-Verbindungsfehler"
 
-#, c-format
 msgid "Server is going down (abandon ship)"
 msgstr "Server fährt runter (melden Sie sich ab)"
 
-#, c-format
 msgid "Error creating connection"
 msgstr "Fehler beim Herstellen der Verbindung"
 
-#, c-format
 msgid "CVR parameters are either unknown or not allowed"
 msgstr "CVR-Parameter sind entweder unbekannt oder nicht erlaubt"
 
-#, c-format
 msgid "Unable to write"
 msgstr "Schreiben nicht möglich"
 
-#, c-format
 msgid "Session overload"
 msgstr "Sitzung überlastet"
 
-#, c-format
 msgid "User is too active"
 msgstr "Benutzer ist zu aktiv"
 
-#, c-format
 msgid "Too many sessions"
 msgstr "Zu viele Sitzungen"
 
-#, c-format
 msgid "Passport not verified"
 msgstr "Passport (MSN Benutzerausweis) wurde nicht überprüft"
 
-#, c-format
 msgid "Bad friend file"
 msgstr "Falsche Friends-Datei"
 
-#, c-format
 msgid "Not expected"
 msgstr "Nicht erwartet"
 
-#, c-format
 msgid "Friendly name changes too rapidly"
 msgstr "Benutzernamen werden zu oft geändert"
 
-#, c-format
 msgid "Server too busy"
 msgstr "Server ist zu beschäftigt"
 
-#, c-format
 msgid "Authentication failed"
 msgstr "Authentifizierung fehlgeschlagen"
 
-#, c-format
 msgid "Not allowed when offline"
 msgstr "Nicht erlaubt im Offline-Modus"
 
-#, c-format
 msgid "Not accepting new users"
 msgstr "Akzeptiert keine neuen Benutzer"
 
-#, c-format
 msgid "Kids Passport without parental consent"
 msgstr "Kinder-Passwort ohne die Zustimmung der Eltern"
 
-#, c-format
 msgid "Passport account not yet verified"
 msgstr "Passport-Konto wurde noch nicht überprüft"
 
-#, c-format
 msgid "Passport account suspended"
 msgstr "Passport-Konto gesperrt"
 
-#, c-format
 msgid "Bad ticket"
 msgstr "Falsches Ticket"
 
@@ -5011,6 +4962,12 @@
 msgid "Page"
 msgstr "Nachricht"
 
+msgid "Playing a game"
+msgstr "Spielt ein Spiel"
+
+msgid "Working"
+msgstr "Arbeitet"
+
 msgid "Has you"
 msgstr "Hat Sie"
 
@@ -5047,6 +5004,13 @@
 msgid "Album"
 msgstr "Album"
 
+msgid "Game Title"
+msgstr "Spieltitel"
+
+#, fuzzy
+msgid "Office Title"
+msgstr "Titel anpassen"
+
 msgid "Set Friendly Name..."
 msgstr "Setze Spitzname..."
 
@@ -5240,8 +5204,8 @@
 "Konnte keinerlei Information im Profil des Benutzers finden. Der Benutzer "
 "existiert wahrscheinlich nicht."
 
-msgid "Profile URL"
-msgstr "URL des Profils"
+msgid "View web profile"
+msgstr "Web-Profil ansehen"
 
 #. *< type
 #. *< ui_requirement
@@ -5508,19 +5472,15 @@
 msgid "Do you want to delete this buddy from your address book as well?"
 msgstr "Möchten Sie diesen Buddy außerdem aus Ihrem Adressbuch löschen?"
 
-#. only notify the user about problems adding to the friends list
-#. * maybe we should do something else for other lists, but it probably
-#. * won't cause too many problems if we just ignore it
-#, c-format
-msgid "Unable to add \"%s\"."
-msgstr "Kann „%s“ nicht hinzufügen."
-
 msgid "The username specified is invalid."
 msgstr "Der angegebene Benutzername ist ungültig."
 
 msgid "This Hotmail account may not be active."
 msgstr "Dieses Hotmail-Konto ist vielleicht nicht aktiv."
 
+msgid "Profile URL"
+msgstr "URL des Profils"
+
 #. *< type
 #. *< ui_requirement
 #. *< flags
@@ -5556,13 +5516,8 @@
 msgid "Logging in"
 msgstr "Logge ein"
 
-#, c-format
-msgid "Connection to server lost (no data received within %d second)"
-msgid_plural "Connection to server lost (no data received within %d seconds)"
-msgstr[0] ""
-"Verbindung zum Server verloren (seit %d Sekunde keine Daten empfangen)"
-msgstr[1] ""
-"Verbindung zum Server verloren (seit %d Sekunden keine Daten empfangen)"
+msgid "Lost connection with server"
+msgstr "Verbindung zum Server verloren"
 
 #. Can't write _()'d strings in array initializers. Workaround.
 msgid "New mail messages"
@@ -5719,9 +5674,6 @@
 msgid "User"
 msgstr "Benutzer"
 
-msgid "Profile"
-msgstr "Profil"
-
 msgid "Headline"
 msgstr "Überschrift"
 
@@ -6154,7 +6106,6 @@
 msgid "Error. SSL support is not installed."
 msgstr "Fehler. SSL ist nicht installiert."
 
-#, c-format
 msgid "This conference has been closed. No more messages can be sent."
 msgstr ""
 "Diese Konferenz wurde geschlossen. Es können keine Nachrichten mehr gesendet "
@@ -6424,23 +6375,18 @@
 msgid "Screen Sharing"
 msgstr "Gemeinsamer Bildschirm"
 
-#, c-format
 msgid "Free For Chat"
 msgstr "Bereit zum Chatten"
 
-#, c-format
 msgid "Not Available"
 msgstr "Nicht verfügbar"
 
-#, c-format
 msgid "Occupied"
 msgstr "Beschäftigt"
 
-#, c-format
 msgid "Web Aware"
 msgstr "In Web"
 
-#, c-format
 msgid "Invisible"
 msgstr "Unsichtbar"
 
@@ -6731,6 +6677,9 @@
 msgid "Member Since"
 msgstr "Mitglied seit"
 
+msgid "Profile"
+msgstr "Profil"
+
 msgid "Your AIM connection may be lost."
 msgstr "Ihre AIM-Verbindung könnte unterbrochen sein."
 
@@ -6925,11 +6874,9 @@
 "beginnen und nur Buchstaben, Ziffern und Leerzeichen enthalten oder nur aus "
 "Ziffern bestehen."
 
-#, fuzzy
 msgid "Unable to Add"
 msgstr "Kann nicht hinzufügen"
 
-#, fuzzy
 msgid "Unable to Retrieve Buddy List"
 msgstr "Konnte Buddy-Liste nicht laden"
 
@@ -7139,7 +7086,6 @@
 msgid "Attempting to connect to %s:%hu."
 msgstr "Verbindungsversuch mit %s:%hu."
 
-#, c-format
 msgid "Attempting to connect via proxy server."
 msgstr "Verbindungsversuch über einen Proxyserver."
 
@@ -7231,16 +7177,14 @@
 msgid "Other"
 msgstr "Andere"
 
-#, fuzzy
 msgid "Visible"
-msgstr "Unsichtbar"
-
-msgid "Firend Only"
-msgstr ""
-
-#, fuzzy
+msgstr "Sichtbar"
+
+msgid "Friend Only"
+msgstr "Nur Freund"
+
 msgid "Private"
-msgstr "Privatsphäre"
+msgstr "Privat"
 
 msgid "QQ Number"
 msgstr "QQ-Nummer"
@@ -7257,9 +7201,8 @@
 msgid "Phone Number"
 msgstr "Telefonnummer"
 
-#, fuzzy
 msgid "Authorize adding"
-msgstr "Buddy autorisieren?"
+msgstr "Hinzufügen autorisieren?"
 
 msgid "Cellphone Number"
 msgstr "Handy-Telefonnummer"
@@ -7267,26 +7210,22 @@
 msgid "Personal Introduction"
 msgstr "Persönliche Vorstellung"
 
-#, fuzzy
 msgid "City/Area"
-msgstr "Stadt"
+msgstr "Stadt/Gegend"
 
 #, fuzzy
 msgid "Publish Mobile"
 msgstr "Handy (privat)"
 
-#, fuzzy
 msgid "Publish Contact"
-msgstr "Kontakt-Alias"
+msgstr "Kontakt veröffentlichen"
 
 msgid "College"
 msgstr "College"
 
-#, fuzzy
 msgid "Horoscope"
-msgstr "Horoskopsymbol"
-
-#, fuzzy
+msgstr "Horoskop"
+
 msgid "Zodiac"
 msgstr "Sternzeichen"
 
@@ -7294,73 +7233,60 @@
 msgid "Blood"
 msgstr "Blockiert"
 
-#, fuzzy
 msgid "True"
-msgstr "Stier"
-
-#, fuzzy
+msgstr "Wahr"
+
 msgid "False"
-msgstr "Gescheitert"
-
-#, fuzzy
+msgstr "Falsch"
+
 msgid "Modify Contact"
-msgstr "Konto bearbeiten"
-
-#, fuzzy
+msgstr "Kontakt bearbeiten"
+
 msgid "Modify Address"
-msgstr "Privatadresse"
-
-#, fuzzy
+msgstr "Adresse bearbeiten"
+
 msgid "Modify Extended Information"
-msgstr "Informationen bearbeiten"
-
-#, fuzzy
+msgstr "Erweiterte Informationen bearbeiten"
+
 msgid "Modify Information"
 msgstr "Informationen bearbeiten"
 
-#, fuzzy
 msgid "Update"
-msgstr "Letzte Aktualisierung"
-
-#, fuzzy
+msgstr "Aktualisieren"
+
 msgid "Could not change buddy information."
-msgstr "Buddy-Informationen bearbeiten"
-
-#, c-format
-msgid "%d needs Q&A"
-msgstr ""
-
-#, fuzzy
-msgid "Add buddy Q&A"
-msgstr "Buddy hinzufügen"
-
-#, fuzzy
-msgid "Input answer here"
-msgstr "Anfrage hier eingeben"
+msgstr "Konnte Buddy-Informationen nicht bearbeiten."
+
+#, c-format
+msgid "%u requires verification"
+msgstr "%u erfordert Autorisierung"
+
+msgid "Add buddy question"
+msgstr "Buddy-Frage hinzufügen"
+
+msgid "Enter answer here"
+msgstr "Antwort hier eingeben"
 
 msgid "Send"
 msgstr "Senden"
 
-#, fuzzy
 msgid "Invalid answer."
-msgstr "Ungültiger Benutzername."
+msgstr "Ungültige Antwort."
 
 msgid "Authorization denied message:"
 msgstr "Nachricht für die Ablehnung der Autorisierung:"
 
-#, fuzzy
-msgid "Sorry, You are not my style."
-msgstr "Tut mir Leid, du bist nicht mein Typ..."
-
-#, c-format
-msgid "%d needs authentication"
-msgstr "%d benötigt Authentifizierung"
-
-#, fuzzy
+msgid "Sorry, you're not my style."
+msgstr "Tut mir Leid, du bist nicht mein Typ."
+
+#, c-format
+msgid "%u needs authorization"
+msgstr "%u benötigt Authorisierung"
+
 msgid "Add buddy authorize"
-msgstr "Benutzer zu Ihrer Buddy-Liste hinzufügen?"
-
-msgid "Input request here"
+msgstr "Buddy-Autorisierung hinzufügen"
+
+msgid "Enter request here"
 msgstr "Anfrage hier eingeben"
 
 msgid "Would you be my friend?"
@@ -7369,28 +7295,25 @@
 msgid "QQ Buddy"
 msgstr "QQ-Buddy"
 
-#, fuzzy
 msgid "Add buddy"
 msgstr "Buddy hinzufügen"
 
 msgid "Invalid QQ Number"
 msgstr "Ungültige QQ-Nummer"
 
-#, fuzzy
 msgid "Failed sending authorize"
-msgstr "Bitte autorisiere mich!"
-
-#, fuzzy, c-format
-msgid "Failed removing buddy %d"
-msgstr "Kontakt konnte nicht entfernt werden"
-
-#, fuzzy, c-format
+msgstr "Senden der Autorisierung fehlgeschlagen"
+
+#, c-format
+msgid "Failed removing buddy %u"
+msgstr "Kontakt %u konnte nicht entfernt werden"
+
+#, c-format
 msgid "Failed removing me from %d's buddy list"
-msgstr "Von der Liste des Buddys entfernen"
-
-#, fuzzy
+msgstr "Entfernen von %ds Buddy-Liste fehlgeschlagen"
+
 msgid "No reason given"
-msgstr "Kein Grund angegeben."
+msgstr "Kein Grund angegeben"
 
 #. only need to get value
 #, c-format
@@ -7400,9 +7323,9 @@
 msgid "Would you like to add him?"
 msgstr "Möchten Sie ihn hinzufügen?"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Rejected by %s"
-msgstr "Anfrage abgelehnt von %s"
+msgstr "Abgelehnt von %s"
 
 #, c-format
 msgid "Message: %s"
@@ -7423,58 +7346,55 @@
 msgid "You can only search for permanent Qun\n"
 msgstr "Sie können nur nach permanenten Qun suchen\n"
 
-#, fuzzy
+msgid "(Invalid UTF-8 string)"
+msgstr "(Ungültige UTF8-Zeichenkette)"
+
 msgid "Not member"
-msgstr "Ich bin kein Mitglied"
+msgstr "Kein Mitglied"
 
 msgid "Member"
 msgstr "Mitglied"
 
-#, fuzzy
 msgid "Requesting"
-msgstr "Anfrage-Dialog"
-
-#, fuzzy
+msgstr "Frage an"
+
 msgid "Admin"
-msgstr "Adium"
-
-#, fuzzy
+msgstr "Admin"
+
 msgid "Notice"
-msgstr "Bemerkung:"
-
-#, fuzzy
+msgstr "Bemerkung"
+
 msgid "Detail"
-msgstr "Standard"
+msgstr "Detail"
 
 msgid "Creator"
 msgstr "Ersteller"
 
-#, fuzzy
 msgid "About me"
-msgstr "Über %s"
-
-#, fuzzy
+msgstr "Über mich"
+
 msgid "Category"
-msgstr "Chatfehler"
+msgstr "Kategorie"
 
 msgid "The Qun does not allow others to join"
 msgstr "Diesen Qun können andere nicht beitreten"
 
-#, fuzzy
 msgid "Join QQ Qun"
-msgstr "Qun betreten"
-
-#, c-format
-msgid "Successfully joined Qun %s (%d)"
-msgstr ""
-
-#, fuzzy
+msgstr "QQ-Qun betreten"
+
+msgid "Input request here"
+msgstr "Anfrage hier eingeben"
+
+#, c-format
+msgid "Successfully joined Qun %s (%u)"
+msgstr "Qun %s (%u) erfolgreich betreten"
+
 msgid "Successfully joined Qun"
-msgstr "Sie haben einen Qun angelegt"
-
-#, c-format
-msgid "Qun %d denied to join"
-msgstr "Qun %d hat Ihren Beitritt abgelehnt"
+msgstr "Qun erfolgreich betreten"
+
+#, c-format
+msgid "Qun %u denied from joining"
+msgstr "Qun %u hat Ihren Beitritt abgelehnt"
 
 msgid "QQ Qun Operation"
 msgstr "QQ-Qun-Operation"
@@ -7482,12 +7402,11 @@
 msgid "Failed:"
 msgstr "Gescheitert:"
 
-msgid "Join Qun, Unknow Reply"
+msgid "Join Qun, Unknown Reply"
 msgstr "Qun-Beitritt, Unbekannte Antwort"
 
-#, fuzzy
 msgid "Quit Qun"
-msgstr "QQ-Qun"
+msgstr "Qun verlassen"
 
 msgid ""
 "Note, if you are the creator, \n"
@@ -7496,53 +7415,49 @@
 "Beachten Sie, dass diese Operation den Qun entfernen könnte, \n"
 "wenn Sie der Ersteller sind."
 
-#, fuzzy
-msgid "Sorry, you are not our style ..."
-msgstr "Tut mir Leid, du bist nicht mein Typ..."
-
-#, fuzzy
-msgid "Successfully changed Qun member"
-msgstr "Qun-Mitglied ändern"
-
-#, fuzzy
+msgid "Sorry, you are not our style"
+msgstr "Tut mir Leid, du bist nicht unser Typ"
+
+msgid "Successfully changed Qun members"
+msgstr "Qun-Mitglieder erfolgreich geändert"
+
 msgid "Successfully changed Qun information"
-msgstr "Qun-Informationen bearbeiten"
+msgstr "Qun-Informationen erfolgreich bearbeitet"
 
 msgid "You have successfully created a Qun"
 msgstr "Sie haben einen Qun angelegt"
 
-#, fuzzy
-msgid "Would you like to set detailed information now?"
+msgid "Would you like to set up detailed information now?"
 msgstr "Möchten Sie jetzt Detail-Informationen einstellen?"
 
 msgid "Setup"
 msgstr "Setup"
 
 #, fuzzy, c-format
-msgid "%d requested to join Qun %d for %s"
-msgstr "%d möchte dem Qun %d beitreten"
-
-#, c-format
-msgid "%d request to join Qun %d"
+msgid "%u requested to join Qun %u for %s"
 msgstr "%d möchte dem Qun %d beitreten"
 
 #, c-format
-msgid "Failed to join Qun %d, operated by admin %d"
-msgstr "Dem Qun %d, moderiert von admin %d, konnte nicht beigetreten werden"
-
-#, c-format
-msgid "<b>Joining Qun %d is approved by admin %d for %s</b>"
-msgstr ""
+msgid "%u request to join Qun %u"
+msgstr "%u möchte dem Qun %u beitreten"
+
+#, c-format
+msgid "Failed to join Qun %u, operated by admin %u"
+msgstr "Dem Qun %u, moderiert von admin %u, konnte nicht beigetreten werden"
+
+#, c-format
+msgid "<b>Joining Qun %u is approved by admin %u for %s</b>"
+msgstr ""
+
+#, c-format
+msgid "<b>Removed buddy %u.</b>"
+msgstr "<b>Buddy %u entfernt</b>"
 
 #, fuzzy, c-format
-msgid "<b>Removed buddy %d.</b>"
+msgid "<b>New buddy %u joined.</b>"
 msgstr "Buddy entfernen"
 
 #, c-format
-msgid "<b>New buddy %d joined.</b>"
-msgstr ""
-
-#, c-format
 msgid "Unknown-%d"
 msgstr "Unbekannt-%d"
 
@@ -7576,9 +7491,8 @@
 msgid "Invalid name"
 msgstr "QQ: Ungültiger Name"
 
-#, fuzzy
 msgid "Select icon..."
-msgstr "Ordner auswählen..."
+msgstr "Icon wählen..."
 
 #, fuzzy, c-format
 msgid "<b>Login time</b>: %d-%d-%d, %d:%d:%d<br>\n"
@@ -7604,9 +7518,9 @@
 msgid "<b>Connection Mode</b>: %s<br>\n"
 msgstr "<b>Verbindungsmodus</b>: %s<br>\n"
 
-#, fuzzy, c-format
+#, c-format
 msgid "<b>My Internet IP</b>: %s:%d<br>\n"
-msgstr "<b>Meine Internet-Adresse</b>: %s<br>\n"
+msgstr "<b>Meine Internet-IP</b>: %s:%d<br>\n"
 
 #, c-format
 msgid "<b>Sent</b>: %lu<br>\n"
@@ -7628,19 +7542,19 @@
 msgid "<b>Received Duplicate</b>: %lu<br>\n"
 msgstr "<b>Duplikat empfangen</b>: %lu<br>\n"
 
-#, fuzzy, c-format
+#, c-format
 msgid "<b>Time</b>: %d-%d-%d, %d:%d:%d<br>\n"
-msgstr "<b>Anmeldezeit</b>: %s<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Zeit</b>: %d-%d-%d, %d:%d:%d<br>\n"
+
+#, c-format
 msgid "<b>IP</b>: %s<br>\n"
-msgstr "<b>Server</b>: %s<br>\n"
+msgstr "<b>IP</b>: %s<br>\n"
 
 msgid "Login Information"
 msgstr "Login-Informationen"
 
 msgid "<p><b>Original Author</b>:<br>\n"
-msgstr ""
+msgstr "<p><b>Original-Autor</b>:<br>\n"
 
 msgid "<p><b>Code Contributors</b>:<br>\n"
 msgstr ""
@@ -7659,13 +7573,12 @@
 msgid "<i>Feel free to join us!</i> :)"
 msgstr ""
 
-#, fuzzy, c-format
-msgid "About OpenQ r%s"
-msgstr "Über %s"
-
-#, fuzzy
+#, c-format
+msgid "About OpenQ %s"
+msgstr "Über OpenQ %s"
+
 msgid "Change Icon"
-msgstr "Icon speichern"
+msgstr "Icon ändern"
 
 msgid "Change Password"
 msgstr "Passwort ändern"
@@ -7674,11 +7587,10 @@
 msgstr "Kontoinformationen"
 
 msgid "Update all QQ Quns"
-msgstr ""
-
-#, fuzzy
+msgstr "Alle QQ-Quns aktualisieren"
+
 msgid "About OpenQ"
-msgstr "Über %s"
+msgstr "Über OpenQ"
 
 #. *< type
 #. *< ui_requirement
@@ -7690,27 +7602,24 @@
 #. *< version
 #. *  summary
 #. *  description
-#, fuzzy
 msgid "QQ Protocol Plugin"
 msgstr "QQ-Protokoll-Plugin"
 
 msgid "Auto"
 msgstr "Auto"
 
-#, fuzzy
 msgid "Select Server"
-msgstr "Benutzer wählen"
+msgstr "Server wählen"
 
 msgid "QQ2005"
-msgstr ""
+msgstr "QQ2005"
 
 msgid "QQ2007"
-msgstr ""
+msgstr "QQ2007"
 
 msgid "QQ2008"
-msgstr ""
-
-#. #endif
+msgstr "QQ2008"
+
 msgid "Connect by TCP"
 msgstr "Über TCP verbinden"
 
@@ -7720,21 +7629,14 @@
 msgid "Show server news"
 msgstr "Server-News anzeigen"
 
-#, fuzzy
 msgid "Keep alive interval (seconds)"
-msgstr "Intervall(e) zum Aufrechterhalten der Verbindung (Keep alive)"
-
-#, fuzzy
+msgstr "Intervall zum Aufrechterhalten der Verbindung (Sekunden)"
+
 msgid "Update interval (seconds)"
-msgstr "Aktualisierungsintervall(e)"
-
-#, fuzzy
-msgid "Can not decrypt server reply"
-msgstr "Kann die Antwort der Anmeldung nicht entschlüsseln"
-
-#, fuzzy
-msgid "Can not decrypt get server reply"
-msgstr "Kann die Antwort der Anmeldung nicht entschlüsseln"
+msgstr "Aktualisierungsintervall (Sekunden)"
+
+msgid "Cannot decrypt server reply"
+msgstr "Kann die Antwort des Servers nicht entschlüsseln"
 
 #, c-format
 msgid "Failed requesting token, 0x%02X"
@@ -7751,51 +7653,48 @@
 #. need activation
 #. need activation
 #. need activation
-#, fuzzy
 msgid "Activation required"
-msgstr "Registrierung erforderlich"
-
-#, fuzzy, c-format
-msgid "Unknow reply code when login (0x%02X)"
-msgstr "Anmeldung nicht möglich, unbekannter Antwort-Code 0x%02X"
-
-msgid "Keep alive error"
-msgstr "Fehler beim Aufrechterhalten der Verbindung (Keep alive)"
-
-#, fuzzy
-msgid "Requesting captcha ..."
-msgstr "Bitte um %ss Aufmerksamkeit..."
-
-msgid "Checking code of captcha ..."
-msgstr ""
-
-msgid "Failed captcha verify"
-msgstr ""
-
-#, fuzzy
+msgstr "Aktivierung erforderlich"
+
+#, c-format
+msgid "Unknown reply code when logging in (0x%02X)"
+msgstr "Unbekannte Antwort bei der Anmeldung (0x%02X)"
+
+msgid "Could not decrypt server reply"
+msgstr "Konnte die Antwort des Servers nicht entschlüsseln"
+
+msgid "Requesting captcha"
+msgstr "Captcha anfordern"
+
+msgid "Checking captcha"
+msgstr "Captcha überprüfen"
+
+msgid "Failed captcha verification"
+msgstr "Captcha-Überprüfung fehlgeschlagen"
+
 msgid "Captcha Image"
-msgstr "Bild speichern"
-
-#, fuzzy
+msgstr "Captcha-Bild"
+
 msgid "Enter code"
-msgstr "Geben Sie ein Passwort ein"
-
-msgid "QQ Captcha Verifing"
-msgstr ""
-
-#, fuzzy
+msgstr "Geben Sie den Code ein"
+
+msgid "QQ Captcha Verification"
+msgstr "QQ-Captcha-Überprüfung"
+
 msgid "Enter the text from the image"
-msgstr "Bitte geben Sie den Namen der Gruppe ein"
-
-#, c-format
-msgid "Unknow reply code when checking password (0x%02X)"
-msgstr ""
-
-#, c-format
-msgid ""
-"Unknow reply code when login (0x%02X):\n"
+msgstr "Bitte geben Sie den Text aus dem Bild ein"
+
+#, c-format
+msgid "Unknown reply when checking password (0x%02X)"
+msgstr "Unbekannte Antwort bei Überprüfungen des Passwortes (0x%02X)"
+
+#, c-format
+msgid ""
+"Unknown reply code when logging in (0x%02X):\n"
 "%s"
 msgstr ""
+"Unbekannte Antwort bei der Anmeldung (0x%02X):\n"
+"%s"
 
 #. we didn't successfully connect. tdt->toc_fd is valid here
 msgid "Unable to connect."
@@ -7804,14 +7703,6 @@
 msgid "Socket error"
 msgstr "Socket-Fehler"
 
-#, c-format
-msgid ""
-"Lost connection with server:\n"
-"%d, %s"
-msgstr ""
-"Verbindung zum Server verloren:\n"
-"%d, %s"
-
 msgid "Unable to read from socket"
 msgstr "Socket kann nicht gelesen werden"
 
@@ -7822,10 +7713,11 @@
 msgstr "Verbindung verloren"
 
 #, fuzzy
-msgid "Get server ..."
+msgid "Getting server"
 msgstr "Benutzer-Info setzen..."
 
-msgid "Request token"
+#, fuzzy
+msgid "Requesting token"
 msgstr "Anfragekürzel"
 
 msgid "Couldn't resolve host"
@@ -7834,62 +7726,62 @@
 msgid "Invalid server or port"
 msgstr "Ungültiger Server oder Port"
 
-#, fuzzy
-msgid "Connecting server ..."
-msgstr "Verbindungsserver"
+msgid "Connecting to server"
+msgstr "Verbinde mit Server"
 
 msgid "QQ Error"
 msgstr "QQ-Fehler"
 
-msgid "Failed to send IM."
-msgstr "Senden der Nachricht fehlgeschlagen."
-
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "Server News:\n"
 "%s\n"
 "%s\n"
 "%s"
-msgstr "QQ-Server-News"
+msgstr ""
+"Server-News:\n"
+"%s\n"
+"%s\n"
+"%s"
+
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
 
 #, c-format
 msgid "From %s:"
 msgstr "Von %s:"
 
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "Server notice From %s: \n"
 "%s"
-msgstr "Anleitung vom Server: %s"
-
-msgid "Unknow SERVER CMD"
+msgstr ""
+"Servernotiz von %s: \n"
+"%s"
+
+msgid "Unknown SERVER CMD"
 msgstr "Unbekanntes SERVER-CMD"
 
 #, c-format
 msgid ""
 "Error reply of %s(0x%02X)\n"
-"Room %d, reply 0x%02X"
-msgstr ""
-"Fehlerantwort %s(0x%02X)\n"
-"Raum %d, Antwort 0x%02X"
+"Room %u, reply 0x%02X"
+msgstr ""
+"Fehlerantwort von %s(0x%02X)\n"
+"Raum %u, Antwort 0x%02X"
 
 msgid "QQ Qun Command"
 msgstr "QQ-Qun-Kommando"
 
-#, fuzzy, c-format
-msgid "Not a member of room \"%s\"\n"
-msgstr "Sie sind kein Mitglied des Qun „%s“\n"
-
-msgid "Can not decrypt login reply"
-msgstr "Kann die Antwort der Anmeldung nicht entschlüsseln"
-
-#, fuzzy
-msgid "Unknow LOGIN CMD"
-msgstr "Unbekanntes Antwort-CMD"
-
-#, fuzzy
-msgid "Unknow CLIENT CMD"
-msgstr "Unbekanntes SERVER-CMD"
+msgid "Could not decrypt login reply"
+msgstr "Konnte die Antwort der Anmeldung nicht entschlüsseln"
+
+msgid "Unknown LOGIN CMD"
+msgstr "Unbekanntes LOGIN-CMD"
+
+msgid "Unknown CLIENT CMD"
+msgstr "Unbekanntes CLIENT-CMD"
 
 #, c-format
 msgid "%d has declined the file %s"
@@ -8502,7 +8394,6 @@
 msgid "<br><b>Channel Topic:</b><br>%s"
 msgstr "<br><b>Thema des Kanals:</b><br>%s"
 
-#, c-format
 msgid "<br><b>Channel Modes:</b> "
 msgstr "<br><b>Kanal-Modi:</b> "
 
@@ -8527,7 +8418,6 @@
 msgid "Channel Public Keys List"
 msgstr "Liste der öffentlichen Schlüssel des Kanals"
 
-#, c-format
 msgid ""
 "Channel authentication is used to secure the channel from unauthorized "
 "access. The authentication may be based on passphrase and digital "
@@ -8932,7 +8822,6 @@
 msgid "Your Current Mood"
 msgstr "Ihre momentane Stimmung"
 
-#, c-format
 msgid "Normal"
 msgstr "Normal"
 
@@ -9318,47 +9207,37 @@
 msgid "No server statistics available"
 msgstr "Keine Serverstatistik verfügbar"
 
-#, c-format
 msgid "Failure: Version mismatch, upgrade your client"
 msgstr "Fehler: Unterschiedliche Version, aktualisieren Sie Ihren Client"
 
-#, c-format
 msgid "Failure: Remote does not trust/support your public key"
 msgstr ""
 "Fehler: Die entfernte Seite vertraut Ihrem öffentlichen Schlüssel nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed KE group"
 msgstr ""
 "Fehler: Entferntes Programm unterstützt nicht die vorgeschlagen KE-Gruppe"
 
-#, c-format
 msgid "Failure: Remote does not support proposed cipher"
 msgstr ""
 "Fehler: Entferntes Programm unterstützt die vorgeschlagene Cipher nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed PKCS"
 msgstr "Fehler: Entferntes Programm unterstützt die vorgeschlagene PKCS nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed hash function"
 msgstr ""
 "Fehler: Entferntes Programm unterstützt die vorgeschlagen Hashfunktion nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed HMAC"
 msgstr "Fehler: Entferntes Programm unterstützt das vorgeschlagene HMAC nicht"
 
-#, c-format
 msgid "Failure: Incorrect signature"
 msgstr "Fehler: Falsche Signatur"
 
-#, c-format
 msgid "Failure: Invalid cookie"
 msgstr "Fehler: Ungültiger Cookie"
 
-#, c-format
 msgid "Failure: Authentication failed"
 msgstr "Fehler: Authentifizierung fehlgeschlagen"
 
@@ -9455,7 +9334,6 @@
 msgid "Warning of %s not allowed."
 msgstr "Verwarnung von %s nicht erlaubt."
 
-#, c-format
 msgid "A message has been dropped, you are exceeding the server speed limit."
 msgstr ""
 "Eine Nachricht ging verloren. Sie überschreiten die Geschwindigkeitsgrenze "
@@ -9479,39 +9357,30 @@
 "Eine Nachricht von %s hat Sie nicht erreicht, da sie zu schnell gesendet "
 "wurde."
 
-#, c-format
 msgid "Failure."
 msgstr "Fehler."
 
-#, c-format
 msgid "Too many matches."
 msgstr "Zu viele Übereinstimmungen."
 
-#, c-format
 msgid "Need more qualifiers."
 msgstr "Benötige mehr Angaben."
 
-#, c-format
 msgid "Dir service temporarily unavailable."
 msgstr "Verzeichnis-Dienst ist zur Zeit nicht verfügbar."
 
-#, c-format
 msgid "Email lookup restricted."
 msgstr "E-Mail-Suche eingeschränkt."
 
-#, c-format
 msgid "Keyword ignored."
 msgstr "Stichwort ignoriert."
 
-#, c-format
 msgid "No keywords."
 msgstr "Keine Stichwörter."
 
-#, c-format
 msgid "User has no directory information."
 msgstr "Der Benutzer hat kein Profil."
 
-#, c-format
 msgid "Country not supported."
 msgstr "Land nicht unterstützt."
 
@@ -9519,19 +9388,15 @@
 msgid "Failure unknown: %s."
 msgstr "Unbekannter Fehler: %s."
 
-#, c-format
 msgid "Incorrect username or password."
 msgstr "Ungültiger Benutzername oder Passwort."
 
-#, c-format
 msgid "The service is temporarily unavailable."
 msgstr "Der Dienst ist zur Zeit nicht verfügbar."
 
-#, c-format
 msgid "Your warning level is currently too high to log in."
 msgstr "Ihre Warnstufe ist zur Zeit zu hoch, um sich anzumelden."
 
-#, c-format
 msgid ""
 "You have been connecting and disconnecting too frequently.  Wait ten minutes "
 "and try again.  If you continue to try, you will need to wait even longer."
@@ -9891,13 +9756,9 @@
 msgid "Last Update"
 msgstr "Letzte Aktualisierung"
 
-#, c-format
-msgid "User information for %s unavailable"
-msgstr "Benutzerinformation für %s nicht verfügbar"
-
-msgid ""
-"Sorry, this profile seems to be in a language or format that is not "
-"supported at this time."
+#, fuzzy
+msgid ""
+"This profile is in a language or format that is not supported at this time."
 msgstr ""
 "Entschuldigung, das Profil enthält eine Sprache, die zur Zeit nicht "
 "unterstützt wird."
@@ -10372,29 +10233,24 @@
 msgstr " (%s)"
 
 #. 10053
-#, c-format
 msgid "Connection interrupted by other software on your computer."
 msgstr ""
 "Die Verbindung wurde von einer anderen Software auf ihrem Computer "
 "unterbrochen."
 
 #. 10054
-#, c-format
 msgid "Remote host closed connection."
 msgstr "Der entfernte Host hat die Verbindung beendet."
 
 #. 10060
-#, c-format
 msgid "Connection timed out."
 msgstr "Verbindungsabbruch wegen Zeitüberschreitung."
 
 #. 10061
-#, c-format
 msgid "Connection refused."
 msgstr "Verbindung abgelehnt."
 
 #. 10048
-#, c-format
 msgid "Address already in use."
 msgstr "Adresse wird bereits benutzt."
 
@@ -10966,9 +10822,8 @@
 msgid "Auto_join when account becomes online."
 msgstr "Automatisch _beitreten, wenn das Konto online geht."
 
-#, fuzzy
 msgid "_Remain in chat after window is closed."
-msgstr "_Chat verstecken, wenn das Fenster geschlossen wird."
+msgstr "In _Chat bleiben, nachdem das Fenster geschlossen wird."
 
 msgid "Please enter the name of the group to be added."
 msgstr "Bitte geben Sie den Namen der Gruppe ein, die hinzugefügt werden soll."
@@ -11626,7 +11481,6 @@
 "geschützt.  Die Datei 'COPYRIGHT' enthält die komplette Liste der "
 "Mitwirkenden.  Wir übernehmen keine Haftung für dieses Programm.<BR><BR>"
 
-#, c-format
 msgid "<FONT SIZE=\"4\">IRC:</FONT> #pidgin on irc.freenode.net<BR><BR>"
 msgstr "<FONT SIZE=\"4\">IRC:</FONT> #pidgin auf irc.freenode.net<BR><BR>"
 
@@ -11994,11 +11848,9 @@
 msgid "Save Image"
 msgstr "Bild speichern"
 
-#, c-format
 msgid "_Save Image..."
 msgstr "Bild _speichern..."
 
-#, c-format
 msgid "_Add Custom Smiley..."
 msgstr "Benutzerdefinierten Smiley _hinzufügen..."
 
@@ -12260,7 +12112,7 @@
 "                      nur das erste Konto aktiviert).\n"
 "  -v, --version       zeigt aktuelle Version und beendet das Programm\n"
 
-#, fuzzy, c-format
+#, c-format
 msgid ""
 "%s %s has segfaulted and attempted to dump a core file.\n"
 "This is a bug in the software and has happened through\n"
@@ -12288,11 +12140,6 @@
 "der Core-Datei. Falls Sie nicht wissen, wie man einen \n"
 "Backtrace erstellt, lesen Sie bitte die Informationen auf \n"
 "%swiki/GetABacktrace\n"
-"\n"
-"Wenn Sie weitere Hilfe benötigen, kontaktieren sie bitte \n"
-"SeanEgn oder LSchiere (über AIM).  Kontaktinformationen \n"
-"für Sean und Luke über andere Protokolle finden Sie unter \n"
-"%swiki/DeveloperPages\n"
 
 #. Translators may want to transliterate the name.
 #. It is not to be translated.
@@ -12718,27 +12565,21 @@
 msgid "Sound Selection"
 msgstr "Klang-Auswahl"
 
-#, c-format
 msgid "Quietest"
 msgstr "Am leisesten"
 
-#, c-format
 msgid "Quieter"
 msgstr "Leiser"
 
-#, c-format
 msgid "Quiet"
 msgstr "Leise"
 
-#, c-format
 msgid "Loud"
 msgstr "Laut"
 
-#, c-format
 msgid "Louder"
 msgstr "Lauter"
 
-#, c-format
 msgid "Loudest"
 msgstr "Am lautesten"
 
@@ -13139,13 +12980,11 @@
 msgid "_Invite"
 msgstr "_Einladen"
 
-#, fuzzy
 msgid "_Modify..."
-msgstr "_Bearbeiten"
-
-#, fuzzy
+msgstr "_Bearbeiten..."
+
 msgid "_Add..."
-msgstr "_Hinzufügen"
+msgstr "_Hinzufügen..."
 
 msgid "_Open Mail"
 msgstr "Mail ö_ffnen"
@@ -13168,12 +13007,11 @@
 msgid "none"
 msgstr "keine"
 
-#, fuzzy
 msgid "Small"
-msgstr "E-Mail"
+msgstr "Klein"
 
 msgid "Smaller versions of the default smilies"
-msgstr ""
+msgstr "Kleinere Versionen der Default-Smileys"
 
 msgid "Response Probability:"
 msgstr "Antwortwahrscheinlichkeit:"
@@ -13760,7 +13598,6 @@
 msgid "Select Color"
 msgstr "Farbe auswählen"
 
-#, c-format
 msgid "Select Interface Font"
 msgstr "Schriftart wählen"
 
@@ -13835,18 +13672,16 @@
 
 #, c-format
 msgid "You can upgrade to %s %s today."
-msgstr ""
+msgstr "Sie können heute auf %s %s aktualisieren."
 
 msgid "New Version Available"
 msgstr "Neue Version verfügbar"
 
-#, fuzzy
 msgid "Later"
-msgstr "Datum"
-
-#, fuzzy
+msgstr "Später"
+
 msgid "Download Now"
-msgstr "Download %s: %s"
+msgstr "Jetzt herunterladen"
 
 #. *< type
 #. *< ui_requirement
@@ -13987,7 +13822,6 @@
 msgid "Timestamp Format Options"
 msgstr "Zeitstempelformat-Optionen"
 
-#, c-format
 msgid "_Force 24-hour time format"
 msgstr "_Erzwinge 24-Stunden Zeitformat"
 
@@ -14158,169 +13992,3 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr ""
 "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients."
-
-#~ msgid "Primary Information"
-#~ msgstr "Primäre Informationen"
-
-#~ msgid "Blood Type"
-#~ msgstr "Blutgruppe"
-
-#~ msgid "Update information"
-#~ msgstr "Informationen aktualisieren"
-
-#~ msgid "Successed:"
-#~ msgstr "Erfolgreich:"
-
-#~ msgid ""
-#~ "Setting custom faces is not currently supported. Please choose an image "
-#~ "from %s."
-#~ msgstr ""
-#~ "Das Setzen von benutzerdefinierten Gesichtern wird momentan nicht "
-#~ "unterstützt. Bitte wählen Sie ein Bild von %s."
-
-#~ msgid "Invalid QQ Face"
-#~ msgstr "Ungültiges QQ-Gesicht"
-
-# c-format
-#~ msgid "You rejected %d's request"
-#~ msgstr "Sie haben die Anfrage von %d abgelehnt"
-
-#~ msgid "Reject request"
-#~ msgstr "Anfrage ablehnen"
-
-#~ msgid "Add buddy with auth request failed"
-#~ msgstr "Benutzer hinzufügen, wenn Autorisierungsanfrage fehlschlug"
-
-#~ msgid "Add into %d's buddy list"
-#~ msgstr "Zu %ds Buddy-Liste hinzufügen"
-
-#~ msgid "QQ Number Error"
-#~ msgstr "Fehler in QQ-Nummer"
-
-#~ msgid "Group Description"
-#~ msgstr "Gruppenbeschreibung"
-
-#~ msgid "Auth"
-#~ msgstr "Autorisieren"
-
-#~ msgid "Approve"
-#~ msgstr "Akzeptieren"
-
-#~ msgid "Successed to join Qun %d, operated by admin %d"
-#~ msgstr "Erfolgreicher Beitritt in den Qun %d, moderiert vom Admin %d"
-
-#~ msgid "[%d] removed from Qun \"%d\""
-#~ msgstr "[%d] vom Qun „%d“ entfernt"
-
-#~ msgid "[%d] added to Qun \"%d\""
-#~ msgstr "[%d] zum Qun „%d“ hinzugefügt"
-
-#~ msgid "I am a member"
-#~ msgstr "Ich bin Mitglied"
-
-#~ msgid "I am requesting"
-#~ msgstr "Ich frage an"
-
-#~ msgid "I am the admin"
-#~ msgstr "Ich bin der Admin"
-
-#~ msgid "Unknown status"
-#~ msgstr "Unbekannter Status"
-
-#~ msgid "Remove from Qun"
-#~ msgstr "vom Qun entfernen"
-
-#~ msgid "You entered a group ID outside the acceptable range"
-#~ msgstr ""
-#~ "Sie haben eine Gruppen-ID außerhalb des erlaubten Bereichs angegeben"
-
-#~ msgid "Are you sure you want to leave this Qun?"
-#~ msgstr "Wollen Sie dieses Qun wirklich verlassen?"
-
-#~ msgid "Do you want to approve the request?"
-#~ msgstr "Wollen sie die Anfrage akzeptieren?"
-
-#~ msgid ""
-#~ "%s\n"
-#~ "\n"
-#~ "%s"
-#~ msgstr ""
-#~ "%s\n"
-#~ "\n"
-#~ "%s"
-
-#~ msgid "System Message"
-#~ msgstr "Systemnachricht"
-
-#~ msgid "<b>Last Login IP</b>: %s<br>\n"
-#~ msgstr "<b>Letzte Anmelde-IP</b>: %s<br>\n"
-
-#~ msgid "<b>Last Login Time</b>: %s\n"
-#~ msgstr "<b>Letzte Anmeldezeit</b>: %s\n"
-
-#~ msgid "Set My Information"
-#~ msgstr "Meine Informationen festlegen"
-
-#~ msgid "Leave the QQ Qun"
-#~ msgstr "Diesen QQ-Qun verlassen"
-
-#~ msgid "Block this buddy"
-#~ msgstr "Diesen Buddy blockieren"
-
-#~ msgid "Invalid token reply code, 0x%02X"
-#~ msgstr "Ungültiger Token-Antwort-Code, 0x%02X"
-
-#~ msgid "Unable login for not support Redirect_EX now"
-#~ msgstr "Anmeldung nicht möglich, Redirect_EX wird noch nicht unterstützt"
-
-#~ msgid "Error password: %s"
-#~ msgstr "Passwort-Fehler: %s"
-
-#~ msgid "Need active: %s"
-#~ msgstr "Brauche aktiv: %s"
-
-#~ msgid "Failed to connect all servers"
-#~ msgstr "Konnte nicht alle Server verbinden"
-
-#~ msgid "Connecting server %s, retries %d"
-#~ msgstr "Verbinde zu Server %s, %d Wiederholungen"
-
-#~ msgid "Do you approve the requestion?"
-#~ msgstr "Wollen sie die Anfrage akzeptieren?"
-
-#~ msgid "Do you add the buddy?"
-#~ msgstr "Möchten Sie diesen Buddy hinzufügen?"
-
-#~ msgid "%s added you [%s] to buddy list"
-#~ msgstr "%s hat Sie [%s] zur Buddy-Liste hinzugefügt"
-
-#~ msgid "QQ Budy"
-#~ msgstr "QQ-Buddy"
-
-#~ msgid "Requestion approved by %s"
-#~ msgstr "Anfrage akzeptiert von %s"
-
-#~ msgid "%s wants to add you [%s] as a friend"
-#~ msgstr "%s möchte Sie [%s] als Freund hinzufügen"
-
-#~ msgid "%s is not in buddy list"
-#~ msgstr "%s ist nicht in der Buddy-Liste"
-
-#~ msgid "Would you add?"
-#~ msgstr "Möchten Sie ihn hinzufügen?"
-
-#~ msgid "%s"
-#~ msgstr "%s"
-
-#~ msgid "QQ Server Notice"
-#~ msgstr "QQ-Server-Nachricht"
-
-#~ msgid ""
-#~ "You are using %s version %s.  The current version is %s.  You can get it "
-#~ "from <a href=\"%s\">%s</a><hr>"
-#~ msgstr ""
-#~ "Sie verwenden gerade %s Version %s.  Die aktuelle Version ist %s.  Sie "
-#~ "können Pidgin von <a href=\"%s\">%s</a> herunterladen.<hr>"
-
-#~ msgid "<b>ChangeLog:</b><br>%s"
-#~ msgstr "<b>Änderungen:</b><br>%s"
--- a/share/ca-certs/Makefile.am	Sun Dec 14 23:43:52 2008 +0000
+++ b/share/ca-certs/Makefile.am	Mon Dec 15 08:39:08 2008 +0000
@@ -3,19 +3,27 @@
 		CAcert_Class3.pem \
 		Equifax_Secure_CA.pem \
 		GTE_CyberTrust_Global_Root.pem \
-		Microsoft_Internet_Authority.pem \
-		Microsoft_Secure_Server_Authority.pem \
 		StartCom_Free_SSL_CA.pem \
 		Verisign_RSA_Secure_Server_CA.pem \
 		Verisign_Class3_Primary_CA.pem \
 		VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem
 
-if INSTALL_SSL_CERTIFICATES
+EXTRA_CERTS = \
+		Microsoft_Internet_Authority.pem \
+		Microsoft_Secure_Server_Authority.pem
+
+
 cacertsdir =	$(datadir)/purple/ca-certs
-cacerts_DATA =	$(CERTIFICATES)
+
+if INSTALL_SSL_CERTIFICATES
+cacerts_DATA = \
+	$(CERTIFICATES) \
+	$(EXTRA_CERTS)
+else
+cacerts_DATA = $(EXTRA_CERTS)
 endif
 
 EXTRA_DIST =	\
 		Makefile.mingw \
-		$(CERTIFICATES)
-
+		$(CERTIFICATES) \
+		$(EXTRA_CERTS)
--- a/share/ca-certs/Microsoft_Internet_Authority.pem	Sun Dec 14 23:43:52 2008 +0000
+++ b/share/ca-certs/Microsoft_Internet_Authority.pem	Mon Dec 15 08:39:08 2008 +0000
@@ -1,24 +1,29 @@
------BEGIN CERTIFICATE-----
-MIIECzCCA3SgAwIBAgIEBAAD/jANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
-UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
-cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
-b2JhbCBSb290MB4XDTA2MDQxOTE0MzUwMFoXDTA5MDQxOTIzNTkwMFowJzElMCMG
-A1UEAxMcTWljcm9zb2Z0IEludGVybmV0IEF1dGhvcml0eTCCASIwDQYJKoZIhvcN
-AQEBBQADggEPADCCAQoCggEBALUIbk0YdsTXnGPswqx8d/Ntrsjy8Wau8cKHBGBu
-KZwAZe5qW+QOU4gRxiKbM/trspEu1lObU77PVtiZSqFQMkXOYNLhlq1a/eqh68RJ
-Jxxev9KWmfppQ388ONqhi8wziHoXc66RUCiqabp751dbmwnnTN6GfIR952Zg+ab1
-wmGL3o7B1efCMCI9LIMKsId16yHiXKbTBHuWnkAe4Qx2BMAgoJQQ21EbTBhyvCfd
-EiRdOdYo1OTe2xih4JUPmXf7xPNDjMCrpjEJ8woxgCnH12z7PNPqwrhnFe/6808t
-axy4iNgObXcZS3ERcZBA9RFT1z3onQ2E2plkaBPmZQPiZu0CAwEAAaOCAXAwggFs
-MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9j
-Z2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwHQYDVR0OBBYEFDNf3Q+3nFzO7ofdcHCL
-X33PIry5MFQGA1UdIARNMEswSQYKKoZIhvhjAQIBBTA7MDkGCCsGAQUFBwIBFi1o
-dHRwOi8vd3d3LnB1YmxpYy10cnVzdC5jb20vQ1BTL09tbmlSb290Lmh0bWwwgYkG
-A1UdIwSBgTB/oXmkdzB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBv
-cmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4x
-IzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290ggIBpTAOBgNVHQ8B
-Af8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBAjANBgkqhkiG9w0BAQUFAAOBgQBj
-SQlU7cXbnngZAIOa4zci+1Z2XFTTyOFc/Tfc0qU/xVWPZPBJdx2UVk2yCwmIHFBY
-OJSQC+7Kn7GE5nE3rBAyBrnB0oymBcBeD8tT3B4B31jHfnFgsC9UYin9uJN144+e
-tbzOegUg4qaNApAaWGre3YY7ALn1y/6XgqIEIEZcCQ==
------END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFCjCCBHOgAwIBAgIEBycWdTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTA4MDIxOTE4MjcwMloXDTExMDIxOTE4MjQ1M1owJzElMCMG
+A1UEAxMcTWljcm9zb2Z0IEludGVybmV0IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBAKiloatvDehDG/rQriel2AC9qmSJdvjKb2fmJf30
+K7SaC3zQu8kGQxENUEFsHsH0jmBejJ9vvn9tHZ8hGL+kORvWUVBskdMzP65rC0V0
+VeVgUYzPZ7MvrLfhh9+v3yJ7qRuf1aNgmzJg5t1AA91X+aRrFT4lRzl9BFWhQ1VS
+XaD7l6qoiyhD8FbrdLRAe61swsRmzWeXoy6NJpOBsGXaCSG1Jooylro+zkWxt97c
+NkNf/wYqoYcIXo02YpFbwreveejW9a0Lh/1z9+e9aiMtC5QnPT57GTqNINt5R0rp
+Iz4g3GJhmjXVoVF/tev5DMJuhRgPoz0W0aA3UnSmTWh2RFvgqawLqSRrKUhVjySi
+/m5s62uG5xxIftO7/6ljzS061CFoV/RBl/I3WghYp04sr4cSXWa/rL449YhBT8BJ
+jltefWCYAOcT1nA4oFXwXbl1qCUIkZ0bqwju2FGW5vl2qh6vmzcQjc3XxD0m2UqC
+yJNFa9SUgVXtUCqeOI+KqgLW01tpqZteG10byWKmppTVAvdPwHoGE0bl6wBwXldE
+f7Pn4lqu6Yp4bedP3Qv1sfp2H7D98cxSwYRt3UcXN2kjTPv+pby2fEHm9GWf3iE/
+7OtzUI7LYhP7+rGyuCYTtZKH1jDWHrTZBpnAPmrAQQHCI8/4TjF9ZQ1mqRi6x9I9
+9XGfAgMBAAGjggFvMIIBazASBgNVHRMBAf8ECDAGAQH/AgEBMFMGA1UdIARMMEow
+SAYJKwYBBAGxPgEAMDswOQYIKwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9t
+bmlyb290LmNvbS9yZXBvc2l0b3J5LmNmbTAOBgNVHQ8BAf8EBAMCAYYwgYkGA1Ud
+IwSBgTB/oXmkdzB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0
+aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAh
+BgNVBAMTGkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8
+MDqgOKA2hjRodHRwOi8vd3d3LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwv
+MjAxOC9jZHAuY3JsMB0GA1UdDgQWBBTG27vA2CAZkvFg/IjxWH+8G06PGjANBgkq
+hkiG9w0BAQUFAAOBgQBnSDXCyiqGmHTAEJOtZYVm/IbzGtzCY423NF6/yuccYZkm
+spJnDoh8nq3nx3P2KBEyPAqoQ1MEFC+ByQjV4AAQ9dMQALUGNRfHhFUhBeeIybYd
+bzvKOxSlIYSwO2T56+oXyDlkbQWKmHec5qzCE0lwGHslsRU/CHwhuFu2nH+CxQ==
+-----END CERTIFICATE-----
--- a/share/ca-certs/Microsoft_Secure_Server_Authority.pem	Sun Dec 14 23:43:52 2008 +0000
+++ b/share/ca-certs/Microsoft_Secure_Server_Authority.pem	Mon Dec 15 08:39:08 2008 +0000
@@ -1,30 +1,35 @@
 -----BEGIN CERTIFICATE-----
-MIIFEzCCA/ugAwIBAgIKYQVOAQADAAAAHDANBgkqhkiG9w0BAQUFADAnMSUwIwYD
-VQQDExxNaWNyb3NvZnQgSW50ZXJuZXQgQXV0aG9yaXR5MB4XDTA3MDkyODIyMDYz
-N1oXDTA5MDQxOTIzNTkwMFowgYsxEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
+MIIGEzCCA/ugAwIBAgIKYRZtLwAEAAAAIDANBgkqhkiG9w0BAQUFADAnMSUwIwYD
+VQQDExxNaWNyb3NvZnQgSW50ZXJuZXQgQXV0aG9yaXR5MB4XDTA4MDQwOTIxMzc1
+NFoXDTExMDIxOTE4MjQ1M1owgYsxEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
 kiaJk/IsZAEZFgltaWNyb3NvZnQxFDASBgoJkiaJk/IsZAEZFgRjb3JwMRcwFQYK
 CZImiZPyLGQBGRYHcmVkbW9uZDEqMCgGA1UEAxMhTWljcm9zb2Z0IFNlY3VyZSBT
 ZXJ2ZXIgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-xcCphW3RnMyuq1EAwlSsUghQUuCSQAaeFBTro1qbeCJEALHQGrXg8SMnY9Om64WG
-PnB9HhOb1LTOdkMVx6a3GU0mcv+LkefcW/CewIMfDXPoeGA4ULmY9wbraPwJY8Ba
-H3PAvOVSTJ0k9ZX6tXjE0D/214kn0+ZN+Y+ZwoqFC5/lgQyUxxgfVA0FsR/NDNF6
-keBsoNz2vPghAsruLhkON+5KGQo0o+T+XcJ7xNs7eJR7MWZn2rNCM1J+qrR0o2vG
-vz8QBSKogUXiPRXYVkNSkBEFZqGvEob1pDwC5fJEP71J6xEJHsSe0pcaa637RU04
-4BsjRHqJE3Rrlurb6Fc8vQIDAQABo4IB2jCCAdYwEgYDVR0TAQH/BAgwBgEB/wIB
-ATAdBgNVHQ4EFgQUmY+l9x6Bb/p5wvAWP7JUsQhoR1UwCwYDVR0PBAQDAgGGMBIG
-CSsGAQQBgjcVAQQFAgMEAAQwIwYJKwYBBAGCNxUCBBYEFBOB4Tms57aqAqhuDUms
-pmqjrALsMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMB8GA1UdIwQYMBaAFDNf
-3Q+3nFzO7ofdcHCLX33PIry5MIGjBgNVHR8EgZswgZgwgZWggZKggY+GNmh0dHA6
-Ly9tc2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3KDMpLmNy
+kYTz6fKXvrdfIr5o3Ue4CRIzhTE+8JE4hrLTQki3emjYn/CfHRPb7hmMiOZmWBdE
+DUEymyXOyZ7Sy2tC6WaBC4onVYotPoSsaOZJv6EJeHPk64RiWTfX+XqufRndYOEC
+DUmotYQNPV/8InioIBf9+gOSsAMdmyGZF6C1PkJqvPZTRxNv6hxuMMb6uOQIPoFX
+/ceQvAOZcJx2qGsAVKsJHylYkC0GgVyFVhOI0vcZZBcP5T+NtOmyjVBWdxS413HL
+D+8w+3wG0bOP8EyOeRnuf0KLXGBangte0ZFIRd28GXpo5UrcA/r5000e2RTHmhC4
+8YPMIoi+q9XZoF5R0Z069QIDAQABo4IB2jCCAdYwEgYDVR0TAQH/BAgwBgEB/wIB
+ADAdBgNVHQ4EFgQUFFXEOeA9LtFVLkiWsNh+FCIGk7wwCwYDVR0PBAQDAgGGMBIG
+CSsGAQQBgjcVAQQFAgMFAAUwIwYJKwYBBAGCNxUCBBYEFM7FoL4P/nlmdZEP8PeS
+WzWYqBWzMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMB8GA1UdIwQYMBaAFMbb
+u8DYIBmS8WD8iPFYf7wbTo8aMIGjBgNVHR8EgZswgZgwgZWggZKggY+GNmh0dHA6
+Ly9tc2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3KDQpLmNy
 bIY0aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3
-KDMpLmNybIYfaHR0cDovL2NvcnBwa2kvY3JsL21zd3d3KDMpLmNybDB5BggrBgEF
+KDQpLmNybIYfaHR0cDovL2NvcnBwa2kvY3JsL21zd3d3KDQpLmNybDB5BggrBgEF
 BQcBAQRtMGswPAYIKwYBBQUHMAKGMGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
-a2kvbXNjb3JwL21zd3d3KDMpLmNydDArBggrBgEFBQcwAoYfaHR0cDovL2NvcnBw
-a2kvYWlhL21zd3d3KDMpLmNydDANBgkqhkiG9w0BAQUFAAOCAQEARSzU1qmSJczv
-IiFrscxgYtzXekBWgm2rYVIqN996H5ZfMDaso0tZXmhWo0zX0z0r0H+IIGtJ+4D9
-t6dMTpB26jV/5EtMbtQu48ryCkMHBKNtCn7pQ5Mq2gqjq84KrVUlBFfPT4z4+Hqp
-Q6gVghMggWNtiujdHh7w4ja3OqHkFZTX/wUSTb+BtH+GBZKYAGU6duakb3D0piFu
-W5W6gZ4j15R12ys3ZYTKThZjW5blolA0lzuCSghoSfJHRA4jChIzDNojkf7OxqJw
-yFl8+++iZwTiulCTgOucJxGCT1tEEwdfX2c+XKyJIS/d5Ndtk91WEWU+TzqX9ZT4
-SAMJpIZ1pg==
+a2kvbXNjb3JwL21zd3d3KDQpLmNydDArBggrBgEFBQcwAoYfaHR0cDovL2NvcnBw
+a2kvYWlhL21zd3d3KDQpLmNydDANBgkqhkiG9w0BAQUFAAOCAgEAempuzk/VLM4N
+H9TAbFtCjKc95iGmyx2bHbEk9m2cbGxXjBre+N4cJoIYYmhLrZ6L712ov1NjM73b
+m8fb2Fy8Yw8Cmwc8VtarPZT2yzGr8MhNUDVuZswaKfjCY3H7RYv/XKc7AOMd25WP
+/M0WTT4Bna6hl9dUaDGwv5SZFFIJ17FLo4FR2H7IkOOI/WcUPAHeDXUewp4qRPE/
+560xZrLSeNH2lKnOAwwXxwnXSo5WOF5AQXh1nRdbBV9Nu7yI6jH1QV6fKf6oFU2Y
+IOjpnJ0FihVB6XoZ0wNOUMzPEEQcTfIoVoc+t0iK02wcmTLgBgbYU703dHvvPTcn
+IfdI2mscx8l9MjUOdklIIve0FhCxRPqHpEeKjM95gllbXmWgQxAXiog+A62fEo5d
+M7nfeEyiweSlhj1cv+2dyhzyS5saKYkk3ocCnOMCyD0M+4gJx4n4b/zT3rcujyN+
+7m20PbBTjcdTT1+AxOs75rON2hhKUqqrk2MDCpnEJsNK4TuRyDUtm9r+AxaZ4XRK
+MT8InY1Xl9hzrIK6MVERYH46kxg6odwpzJ8Urn4dREBiMy6Gzq8mtyXvpYEcmeGL
+zz1aT7qNNbQ0qqbPb6RpOMHlUWOIhVWJC71T5WK1pynAc3P9zOm8BkUYvIyJvCbR
+bufCGVng4FAtVZ1advxSVRoa4GyuFZ8=
 -----END CERTIFICATE-----