changeset 30517:105437d8253f

propagate from branch 'im.pidgin.pidgin' (head d5a2e2287440ac7134c201e66921754bd4187ba9) to branch 'im.pidgin.cpw.malu.xmpp.google_relay' (head 6d5f3264173d13362030c57e5372ffbbfbdef53d)
author Marcus Lundblad <ml@update.uu.se>
date Fri, 08 Jan 2010 23:22:19 +0000
parents d4ec9ba82814 (current diff) b8d97cf37200 (diff)
children d873eeaccfc2
files libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jingle/jingle.c
diffstat 60 files changed, 703 insertions(+), 284 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu Dec 10 21:36:41 2009 +0000
+++ b/COPYRIGHT	Fri Jan 08 23:22:19 2010 +0000
@@ -154,6 +154,7 @@
 David Fiander
 Rob Flynn <gaim@robflynn.com>
 Rob Foehl (rwf)
+Chris Foote
 Alan Ford
 Nathan Fredrickson
 Chris J. Friesen
--- a/ChangeLog	Thu Dec 10 21:36:41 2009 +0000
+++ b/ChangeLog	Fri Jan 08 23:22:19 2010 +0000
@@ -1,6 +1,31 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.6.5 (??/??/20??):
+version 2.6.6 (??/??/20??):
+	Gadu-Gadu:
+	* Fix display of avatars after a server-side change. (Krzysztof
+	  Klinikowski)
+
+	MSN:
+	* File transfer requests will no longer cause a crash if you delete the
+	  file before the other side accepts.
+	* Recieved files will no longer hold an extra lock after completion,
+	  meaning they can be moved or deleted without complaints from your OS.
+	* Buddies who sign in from a second location will no longer cause an
+	  unnecessary chat window to open.
+	* Support setting an animated GIF as a buddy icon.
+
+	XMPP:
+	* Added support for the SCRAM-SHA-1 SASL mechanism.  This is only
+	  available when built without Cyrus SASL support.
+	* When getting info on a domain-only (server) JID, show uptime
+	  (when given by the result of the "last query") and don't show status as
+	  offline.
+
+version 2.6.5 (01/08/2010):
+	libpurple:
+	* TLS certificates are actually stored to the local cache once again
+	  (accepting a name mismatch on a certificate should now be remembered)
+
 	General:
 	* Build-time fixes for Solaris.  (Paul Townsend)
 
@@ -8,12 +33,11 @@
 	* Messages from some mobile clients are no longer displayed as
 	  Chinese characters (broken in 2.6.4)
 
+	MSN:
+	* Fix an issue allowing a remote user to download arbitrary files from
+	  a libpurple client.  (CVE-2010-0013)
+
 	XMPP:
-	* Added support for the SCRAM-SHA-1 SASL mechanism.  This is only
-	  available when built without Cyrus SASL support.
-	* When getting info on a domain-only (server) JID, show uptime
-	  (when given by the result of the "last query") and don't show status as
-	  offline.
 	* Do not crash when attempting to register for a new account on Windows.
 	* Fix file transfer with clients that do not support Entity Capabilities
 	  (e.g. Spark)
--- a/ChangeLog.API	Thu Dec 10 21:36:41 2009 +0000
+++ b/ChangeLog.API	Fri Jan 08 23:22:19 2010 +0000
@@ -1,6 +1,22 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.6.5 (??/??/20??):
+version 2.6.6 (??/??/2010):
+	libpurple:
+		Changed:
+		* purple_xfer_cancel_local is now called instead of
+		  purple_xfer_request_denied if an error is found when selecting
+		  a file to send. Request denied is still used when a receive
+		  request is not allowed.
+	Perl:
+		Changed:
+		* Corrected the package names for the PurpleProxyType and
+		  PurpleLogReadFlags enums to have the correct number of colons
+		  (from Purple::ProxyType::::<type> to Purple::ProxyType::<type>
+		  and Purple::Log:ReadFlags::::<type> to
+		  Purple::Log::ReadFlags::<type>)  (Chris Foote)
+
+version 2.6.5 (01/08/2010):
+	No changes
 
 version 2.6.4 (11/29/2009):
 	No changes
--- a/ChangeLog.win32	Thu Dec 10 21:36:41 2009 +0000
+++ b/ChangeLog.win32	Fri Jan 08 23:22:19 2010 +0000
@@ -1,6 +1,10 @@
-version 2.6.5 (??/??/20??):
+
+version 2.6.6 (??/??/2010):
 	* Installer translations for: Norwegian nynorsk
 
+version 2.6.5 (01/08/2010):
+	* No changes
+
 version 2.6.4 (11/29/2009):
 	* Register URL handlers for everything that Windows knows about.  Still
 	  use the HTTP "open" handler for security reasons.
--- a/NEWS	Thu Dec 10 21:36:41 2009 +0000
+++ b/NEWS	Fri Jan 08 23:22:19 2010 +0000
@@ -2,7 +2,12 @@
 
 Our development blog is available at: http://planet.pidgin.im
 
-2.6.5 (??/??/20??):
+2.6.6 (??/??/2010):
+
+2.6.5 (01/08/2010):
+	Paul: This release fixes a pretty serious bug in the MSN code, so we're
+	releasing this build a little earlier than planned with only major
+	bugs fixed.  See the ChangeLog for details.  Enjoy!
 
 2.6.4 (11/29/2009):
 	John:  It's release time again.  Lots of bug fixes this time around, as
--- a/configure.ac	Thu Dec 10 21:36:41 2009 +0000
+++ b/configure.ac	Fri Jan 08 23:22:19 2010 +0000
@@ -46,7 +46,7 @@
 m4_define([purple_lt_current], [6])
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [6])
-m4_define([purple_micro_version], [5])
+m4_define([purple_micro_version], [6])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
@@ -55,7 +55,7 @@
 m4_define([gnt_lt_current], [6])
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [6])
-m4_define([gnt_micro_version], [5])
+m4_define([gnt_micro_version], [6])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -812,6 +812,10 @@
 fi
 AM_CONDITIONAL(USE_VV, test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farsight" != "xno")
 
+dnl #######################################################################
+dnl # Check for Internationalized Domain Name support
+dnl #######################################################################
+
 AC_ARG_ENABLE(idn,
 	[AC_HELP_STRING([--disable-idn], [compile without IDN support])],
 	[enable_idn="$enableval" force_idn=$enableval], [enable_idn="yes" force_idn=no])
--- a/finch/gntidle.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/gntidle.c	Fri Jan 08 23:22:19 2010 +0000
@@ -21,6 +21,8 @@
  *
  */
 
+#include <internal.h>
+
 #include "finch.h"
 #include "gntidle.h"
 #include "gntwm.h"
--- a/finch/gntrequest.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/gntrequest.c	Fri Jan 08 23:22:19 2010 +0000
@@ -23,6 +23,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#include <internal.h>
+
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -35,7 +37,6 @@
 #include <gnttree.h>
 
 #include "finch.h"
-#include <internal.h>
 #include "gntrequest.h"
 #include "debug.h"
 #include "util.h"
--- a/finch/gntstatus.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/gntstatus.c	Fri Jan 08 23:22:19 2010 +0000
@@ -23,6 +23,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#include <internal.h>
+
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -34,7 +36,6 @@
 #include <gntutils.h>
 
 #include "finch.h"
-#include <internal.h>
 
 #include <notify.h>
 #include <request.h>
--- a/finch/libgnt/gntinternal.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntinternal.h	Fri Jan 08 23:22:19 2010 +0000
@@ -32,6 +32,14 @@
 # define gnt_warning g_warning
 #endif
 
+#ifndef G_GNUC_NULL_TERMINATED
+#	if defined(__GNUC__) && __GNUC__ >= 4
+#		define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+#	else
+#		define G_GNUC_NULL_TERMINATED
+#	endif
+#endif
+
 extern int gnt_need_conversation_to_locale;
 extern const char *C_(const char *x);
 
--- a/finch/libgnt/gntline.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntline.c	Fri Jan 08 23:22:19 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntline.h"
 
 enum
--- a/finch/libgnt/gntmenuitem.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntmenuitem.c	Fri Jan 08 23:22:19 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 
--- a/finch/libgnt/gntmenuitemcheck.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntmenuitemcheck.c	Fri Jan 08 23:22:19 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmenuitemcheck.h"
 
 static GntMenuItemClass *parent_class = NULL;
--- a/finch/libgnt/gntprogressbar.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntprogressbar.c	Fri Jan 08 23:22:19 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  **/
 
+#include "gntinternal.h"
 #include "gntprogressbar.h"
 #include "gntutils.h"
 
--- a/finch/libgnt/gntslider.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntslider.c	Fri Jan 08 23:22:19 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntcolors.h"
 #include "gntkeys.h"
 #include "gntslider.h"
--- a/finch/libgnt/gntwidget.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntwidget.c	Fri Jan 08 23:22:19 2010 +0000
@@ -22,6 +22,7 @@
 
 /* Stuff brutally ripped from Gflib */
 
+#include "gntinternal.h"
 #include "gntwidget.h"
 #include "gntstyle.h"
 #include "gntmarshal.h"
--- a/finch/libgnt/gntwindow.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/gntwindow.c	Fri Jan 08 23:22:19 2010 +0000
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntstyle.h"
 #include "gntwindow.h"
 
--- a/finch/libgnt/wms/irssi.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/finch/libgnt/wms/irssi.c	Fri Jan 08 23:22:19 2010 +0000
@@ -33,6 +33,8 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "gntinternal.h"
+
 #include "gnt.h"
 #include "gntbox.h"
 #include "gntmenu.h"
--- a/libpurple/account.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/account.c	Fri Jan 08 23:22:19 2010 +0000
@@ -1341,7 +1341,8 @@
 
 	handles = g_list_remove(handles, info);
 
-	info->auth_cb(info->userdata);
+	if (info->auth_cb != NULL)
+		info->auth_cb(info->userdata);
 
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-granted", info->account, info->user);
@@ -1356,7 +1357,8 @@
 
 	handles = g_list_remove(handles, info);
 
-	info->deny_cb(info->userdata);
+	if (info->deny_cb != NULL)
+		info->deny_cb(info->userdata);
 
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-denied", info->account, info->user);
@@ -1383,10 +1385,12 @@
 				"account-authorization-requested", account, remote_user));
 
 	if (plugin_return > 0) {
-		auth_cb(user_data);
+		if (auth_cb != NULL)
+			auth_cb(user_data);
 		return NULL;
 	} else if (plugin_return < 0) {
-		deny_cb(user_data);
+		if (deny_cb != NULL)
+			deny_cb(user_data);
 		return NULL;
 	}
 
@@ -2311,7 +2315,7 @@
 
 	gc = purple_account_get_connection(account);
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2328,7 +2332,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2367,7 +2371,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2384,7 +2388,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2412,7 +2416,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2432,7 +2436,7 @@
 	purple_account_set_password(account, new_pw);
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
--- a/libpurple/certificate.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/certificate.c	Fri Jan 08 23:22:19 2010 +0000
@@ -1431,9 +1431,8 @@
 	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,
 						 "tls_peers");
 	if (tls_peers) {
-		if (!purple_certificate_pool_contains(tls_peers, vrq->subject_name) &&
-		        !purple_certificate_pool_store(tls_peers,vrq->subject_name,
-		                                       peer_crt)) {
+		if (!purple_certificate_pool_store(tls_peers,vrq->subject_name,
+		                                   peer_crt)) {
 			purple_debug_error("certificate/x509/tls_cached",
 			                   "FAILED to cache peer certificate\n");
 		}
--- a/libpurple/conversation.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/conversation.h	Fri Jan 08 23:22:19 2010 +0000
@@ -368,7 +368,8 @@
  * @param type    The type of conversation.
  * @param account The account opening the conversation window on the purple
  *                user's end.
- * @param name    The name of the conversation.
+ * @param name    The name of the conversation.  For PURPLE_CONV_TYPE_IM,
+ *                this is the name of the buddy.
  *
  * @return The new conversation.
  */
@@ -1025,7 +1026,8 @@
 GList *purple_conv_chat_set_users(PurpleConvChat *chat, GList *users);
 
 /**
- * Returns a list of users in the chat room.
+ * Returns a list of users in the chat room.  The members of the list
+ * are PurpleConvChatBuddy objects.
  *
  * @param chat The chat.
  *
--- a/libpurple/dbus-server.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/dbus-server.c	Fri Jan 08 23:22:19 2010 +0000
@@ -601,7 +601,6 @@
 {
 	static DBusObjectPathVTable vtable = {NULL, &purple_dbus_dispatch, NULL, NULL, NULL, NULL};
 	DBusError error;
-	int result;
 
 	dbus_error_init(&error);
 	purple_dbus_connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
@@ -625,16 +624,15 @@
 		return;
 	}
 
-	dbus_request_name_reply =
-	result = dbus_bus_request_name(purple_dbus_connection,
+	dbus_request_name_reply = dbus_bus_request_name(purple_dbus_connection,
 			DBUS_SERVICE_PURPLE, 0, &error);
 
 	if (dbus_error_is_set(&error))
 	{
 		dbus_connection_unref(purple_dbus_connection);
-		dbus_error_free(&error);
 		purple_dbus_connection = NULL;
 		init_error = g_strdup_printf(N_("Failed to get serv name: %s"), error.name);
+		dbus_error_free(&error);
 		return;
 	}
 
--- a/libpurple/ft.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/ft.c	Fri Jan 08 23:22:19 2010 +0000
@@ -69,6 +69,30 @@
 	g_free(priv);
 }
 
+static const gchar *
+purple_xfer_status_type_to_string(PurpleXferStatusType type)
+{
+	static const struct {
+		PurpleXferStatusType type;
+		const char *name;
+	} type_names[] = {
+		{ PURPLE_XFER_STATUS_UNKNOWN, "unknown" },
+		{ PURPLE_XFER_STATUS_NOT_STARTED, "not started" },
+		{ PURPLE_XFER_STATUS_ACCEPTED, "accepted" },
+		{ PURPLE_XFER_STATUS_STARTED, "started" },
+		{ PURPLE_XFER_STATUS_DONE, "done" },
+		{ PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" },
+		{ PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" }
+	};
+	int i;
+
+	for (i = 0; i < G_N_ELEMENTS(type_names); ++i)
+		if (type_names[i].type == type)
+			return type_names[i].name;
+
+	return "invalid state";
+}
+
 GList *
 purple_xfers_get_all()
 {
@@ -109,6 +133,10 @@
 		ui_ops->new_xfer(xfer);
 
 	xfers = g_list_prepend(xfers, xfer);
+
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "new %p [%d]\n", xfer, xfer->ref);
+
 	return xfer;
 }
 
@@ -119,6 +147,9 @@
 
 	g_return_if_fail(xfer != NULL);
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "destroyed %p [%d]\n", xfer, xfer->ref);
+
 	/* Close the file browser, if it's open */
 	purple_request_close_with_handle(xfer);
 
@@ -148,6 +179,9 @@
 	g_return_if_fail(xfer != NULL);
 
 	xfer->ref++;
+
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "ref'd %p [%d]\n", xfer, xfer->ref);
 }
 
 void
@@ -158,6 +192,9 @@
 
 	xfer->ref--;
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "unref'd %p [%d]\n", xfer, xfer->ref);
+
 	if (xfer->ref == 0)
 		purple_xfer_destroy(xfer);
 }
@@ -167,6 +204,11 @@
 {
 	g_return_if_fail(xfer != NULL);
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
+				xfer, purple_xfer_status_type_to_string(xfer->status),
+				purple_xfer_status_type_to_string(status));
+
 	if (xfer->status == status)
 		return;
 
@@ -268,14 +310,16 @@
 purple_xfer_choose_file_ok_cb(void *user_data, const char *filename)
 {
 	PurpleXfer *xfer;
+	PurpleXferType type;
 	struct stat st;
 	gchar *dir;
 
 	xfer = (PurpleXfer *)user_data;
+	type = purple_xfer_get_type(xfer);
 
 	if (g_stat(filename, &st) != 0) {
 		/* File not found. */
-		if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
+		if (type == PURPLE_XFER_RECEIVE) {
 #ifndef _WIN32
 			int mode = W_OK;
 #else
@@ -297,29 +341,26 @@
 		}
 		else {
 			purple_xfer_show_file_error(xfer, filename);
-			purple_xfer_request_denied(xfer);
+			purple_xfer_cancel_local(xfer);
 		}
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) &&
-			 (st.st_size == 0)) {
+	else if ((type == PURPLE_XFER_SEND) && (st.st_size == 0)) {
 
 		purple_notify_error(NULL, NULL,
 						  _("Cannot send a file of 0 bytes."), NULL);
 
-		purple_xfer_request_denied(xfer);
+		purple_xfer_cancel_local(xfer);
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) &&
-			 S_ISDIR(st.st_mode)) {
+	else if ((type == PURPLE_XFER_SEND) && S_ISDIR(st.st_mode)) {
 		/*
 		 * XXX - Sending a directory should be valid for some protocols.
 		 */
 		purple_notify_error(NULL, NULL,
 						  _("Cannot send a directory."), NULL);
 
-		purple_xfer_request_denied(xfer);
+		purple_xfer_cancel_local(xfer);
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) &&
-			 S_ISDIR(st.st_mode)) {
+	else if ((type == PURPLE_XFER_RECEIVE) && S_ISDIR(st.st_mode)) {
 		char *msg, *utf8;
 		utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
 		msg = g_strdup_printf(
@@ -329,6 +370,23 @@
 		g_free(msg);
 		purple_xfer_request_denied(xfer);
 	}
+	else if (type == PURPLE_XFER_SEND) {
+#ifndef _WIN32
+		int mode = R_OK;
+#else
+		int mode = F_OK;
+#endif
+
+		if (g_access(filename, mode) == 0) {
+			purple_xfer_request_accepted(xfer, filename);
+		} else {
+			purple_xfer_ref(xfer);
+			purple_notify_message(
+				NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
+				_("File is not readable."), NULL,
+				(PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
+		}
+	}
 	else {
 		purple_xfer_request_accepted(xfer, filename);
 	}
@@ -342,7 +400,11 @@
 	PurpleXfer *xfer = (PurpleXfer *)user_data;
 
 	purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
-	purple_xfer_request_denied(xfer);
+	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
+		purple_xfer_cancel_local(xfer);
+	else
+		purple_xfer_request_denied(xfer);
+	purple_xfer_unref(xfer);
 }
 
 static int
@@ -506,6 +568,8 @@
 	type = purple_xfer_get_type(xfer);
 	account = purple_xfer_get_account(xfer);
 
+	purple_debug_misc("xfer", "request accepted for %p\n", xfer); 
+
 	if (!filename && type == PURPLE_XFER_RECEIVE) {
 		xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
 		xfer->ops.init(xfer);
@@ -583,6 +647,8 @@
 {
 	g_return_if_fail(xfer != NULL);
 
+	purple_debug_misc("xfer", "xfer %p denied\n", xfer);
+
 	if (xfer->ops.request_denied != NULL)
 		xfer->ops.request_denied(xfer);
 
@@ -1111,6 +1177,8 @@
 
 			purple_input_remove(xfer->watcher);
 			xfer->watcher = 0;
+
+			purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
 			return;
 		}
 	}
@@ -1173,8 +1241,12 @@
 	priv = g_hash_table_lookup(xfers_data, xfer);
 	priv->ready |= PURPLE_XFER_READY_UI;
 
-	if (0 == (priv->ready & PURPLE_XFER_READY_PRPL))
+	if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) {
+		purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer);
 		return;
+	}
+
+	purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer);
 
 	type = purple_xfer_get_type(xfer);
 	if (type == PURPLE_XFER_SEND)
@@ -1201,8 +1273,12 @@
 	priv->ready |= PURPLE_XFER_READY_PRPL;
 
 	/* I don't think fwrite/fread are ever *not* ready */
-	if (xfer->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI))
+	if (xfer->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) {
+		purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
 		return;
+	}
+
+	purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer);
 
 	priv->ready = PURPLE_XFER_READY_NONE;
 
--- a/libpurple/media.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/media.h	Fri Jan 08 23:22:19 2010 +0000
@@ -75,7 +75,7 @@
 	PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION = 1 << 3,
 	PURPLE_MEDIA_CAPS_AUDIO_VIDEO = 1 << 4,
 	PURPLE_MEDIA_CAPS_MODIFY_SESSION = 1 << 5,
-	PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6,
+	PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6
 } PurpleMediaCaps;
 
 /** Media session types */
@@ -93,7 +93,7 @@
 typedef enum {
 	PURPLE_MEDIA_STATE_NEW = 0,
 	PURPLE_MEDIA_STATE_CONNECTED,
-	PURPLE_MEDIA_STATE_END,
+	PURPLE_MEDIA_STATE_END
 } PurpleMediaState;
 
 /** Media info types */
@@ -106,7 +106,7 @@
 	PURPLE_MEDIA_INFO_PAUSE,
 	PURPLE_MEDIA_INFO_UNPAUSE,
 	PURPLE_MEDIA_INFO_HOLD,
-	PURPLE_MEDIA_INFO_UNHOLD,
+	PURPLE_MEDIA_INFO_UNHOLD
 } PurpleMediaInfoType;
 
 typedef enum {
@@ -114,18 +114,18 @@
 	PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
 	PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
 	PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
-	PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
+	PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST
 } PurpleMediaCandidateType;
 
 typedef enum {
 	PURPLE_MEDIA_COMPONENT_NONE = 0,
 	PURPLE_MEDIA_COMPONENT_RTP = 1,
-	PURPLE_MEDIA_COMPONENT_RTCP = 2,
+	PURPLE_MEDIA_COMPONENT_RTCP = 2
 } PurpleMediaComponentType;
 
 typedef enum {
 	PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
-	PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+	PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
 } PurpleMediaNetworkProtocol;
 
 #include "signals.h"
--- a/libpurple/plugins/perl/common/Log.xs	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/plugins/perl/common/Log.xs	Fri Jan 08 23:22:19 2010 +0000
@@ -6,7 +6,7 @@
 BOOT:
 {
 	HV *type_stash = gv_stashpv("Purple::Log::Type", 1);
-	HV *flags_stash = gv_stashpv("Purple::Log:ReadFlags::", 1);
+	HV *flags_stash = gv_stashpv("Purple::Log::ReadFlags", 1);
 
 	static const constiv *civ, type_const_iv[] = {
 #define const_iv(name) {#name, (IV)PURPLE_LOG_##name}
--- a/libpurple/plugins/perl/common/Proxy.xs	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/plugins/perl/common/Proxy.xs	Fri Jan 08 23:22:19 2010 +0000
@@ -5,7 +5,7 @@
 
 BOOT:
 {
-	HV *stash = gv_stashpv("Purple::ProxyType::", 1);
+	HV *stash = gv_stashpv("Purple::ProxyType", 1);
 
 	static const constiv *civ, const_iv[] = {
 #define const_iv(name) {#name, (IV)PURPLE_PROXY_##name}
--- a/libpurple/plugins/signals-test.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/plugins/signals-test.c	Fri Jan 08 23:22:19 2010 +0000
@@ -547,6 +547,26 @@
 	purple_debug_misc("signals test", "quitting ()\n");
 }
 
+static void
+printhash(gpointer key, gpointer value, gpointer data)
+{
+	char *a = (char *)key;
+	char *b = (char *)value;
+	GString *str = (GString *)data;
+	g_string_append_printf(str, "   [%s] = [%s]\n", a, b ? b : "(null)");
+}
+
+static gboolean
+uri_handler(const char *proto, const char *cmd, GHashTable *params)
+{
+	GString *str = g_string_new("\n{\n");
+	g_hash_table_foreach(params, printhash, str);
+	g_string_append_c(str, '}');
+	purple_debug_misc("signals test", "uri handler (%s, %s, %s)\n", proto, cmd, str->str);
+	g_string_free(str, TRUE);
+	return FALSE;
+}
+
 /**************************************************************************
  * File transfer signal callbacks
  **************************************************************************/
@@ -820,6 +840,8 @@
 	/* Core signals */
 	purple_signal_connect(core_handle, "quitting",
 						plugin, PURPLE_CALLBACK(quitting_cb), NULL);
+	purple_signal_connect(core_handle, "uri-handler",
+						plugin,	PURPLE_CALLBACK(uri_handler), NULL);
 
 	/* File transfer signals */
 	purple_signal_connect(ft_handle, "file-recv-accept",
--- a/libpurple/protocols/bonjour/bonjour.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Fri Jan 08 23:22:19 2010 +0000
@@ -392,11 +392,8 @@
 		purple_notify_user_info_add_pair(user_info, _("XMPP Account"), bb->jid);
 }
 
-static void
-bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
-{
+static void bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group) {
 	PurpleBlistNodeFlags oldflags;
-	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
 
 	if (buddy == NULL)
 		return;
@@ -404,13 +401,38 @@
 	oldflags = purple_blist_node_get_flags((PurpleBlistNode *)buddy);
 
 	/* If we're moving them out of the bonjour group, make them persistent */
-	if (strcmp(new_group, BONJOUR_GROUP_NAME) == 0)
+	if (purple_strequal(new_group, BONJOUR_GROUP_NAME))
 		purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags | PURPLE_BLIST_NODE_FLAG_NO_SAVE);
 	else
 		purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags ^ PURPLE_BLIST_NODE_FLAG_NO_SAVE);
 
 }
 
+static void
+bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
+{
+	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+
+	bonjour_do_group_change(buddy, new_group);
+
+}
+
+static void
+bonjour_rename_group(PurpleConnection *connection, const char *old_name, PurpleGroup *group, GList *moved_buddies)
+{
+	GList *cur;
+	const char *new_group;
+	PurpleBuddy *buddy;
+
+	new_group = purple_group_get_name(group);
+
+	for (cur = moved_buddies; cur; cur = cur->next) {
+		buddy = cur->data;
+		bonjour_do_group_change(buddy, new_group);
+	}
+
+}
+
 static gboolean
 bonjour_can_receive_file(PurpleConnection *connection, const char *who)
 {
@@ -478,7 +500,7 @@
 	NULL,                                                    /* get_cb_away */
 	NULL,                                                    /* alias_buddy */
 	bonjour_group_buddy,                                     /* group_buddy */
-	NULL,                                                    /* rename_group */
+	bonjour_rename_group,                                    /* rename_group */
 	NULL,                                                    /* buddy_free */
 	bonjour_convo_closed,                                    /* convo_closed */
 	NULL,                                                    /* normalize */
--- a/libpurple/protocols/gg/gg.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/gg/gg.c	Fri Jan 08 23:22:19 2010 +0000
@@ -947,7 +947,7 @@
 		if (xmlnode_avatar == NULL)
 			goto out;
 
-		xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "bigAvatar");
+		xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar");
 		if (xmlnode_bigavatar == NULL)
 			goto out;
 
--- a/libpurple/protocols/jabber/buddy.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Fri Jan 08 23:22:19 2010 +0000
@@ -823,7 +823,8 @@
 			const gchar *title = NULL;
 			if (is_domain) {
 				title = _("Uptime");
-				message = g_strdup_printf(_("%s"), last);
+				message = last;
+				last = NULL;
 			} else {
 				title = _("Logged Off");
 				message = g_strdup_printf(_("%s ago"), last);
--- a/libpurple/protocols/jabber/jabber.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Fri Jan 08 23:22:19 2010 +0000
@@ -639,10 +639,11 @@
 	js->srv_query_data = NULL;
 
 	if (responses == NULL) {
+		purple_debug_warning("jabber", "Unable to find alternative XMPP connection "
+				  "methods after failing to connect directly.");
 		purple_connection_error_reason(js->gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Unable to find alternative XMPP connection "
-				  "methods after failing to connect directly."));
+				_("Unable to connect"));
 		return;
 	}
 
--- a/libpurple/protocols/jabber/roster.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/jabber/roster.c	Fri Jan 08 23:22:19 2010 +0000
@@ -193,7 +193,9 @@
                          JabberIqType type, const char *id, xmlnode *query)
 {
 	xmlnode *item, *group;
+#if 0
 	const char *ver;
+#endif
 
 	if (!jabber_is_own_account(js, from)) {
 		purple_debug_warning("jabber", "Received bogon roster push from %s\n",
@@ -266,11 +268,13 @@
 		}
 	}
 
+#if 0
 	ver = xmlnode_get_attrib(query, "ver");
 	if (ver) {
 		 PurpleAccount *account = purple_connection_get_account(js->gc);
 		 purple_account_set_string(account, "roster_ver", ver);
 	}
+#endif
 
 	js->currently_parsing_roster_push = FALSE;
 }
--- a/libpurple/protocols/msn/msn.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/msn.c	Fri Jan 08 23:22:19 2010 +0000
@@ -345,17 +345,23 @@
 msn_show_set_friendly_name(PurplePluginAction *action)
 {
 	PurpleConnection *gc;
+	PurpleAccount *account;
+	char *tmp;
 
 	gc = (PurpleConnection *) action->context;
-
-	purple_request_input(gc, NULL, _("Set your friendly name."),
+	account = purple_connection_get_account(gc);
+
+	tmp = g_strdup_printf(_("Set friendly name for %s."),
+	                      purple_account_get_username(account));
+	purple_request_input(gc, _("Set your friendly name."), tmp,
 					   _("This is the name that other MSN buddies will "
 						 "see you as."),
 					   purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
 					   _("OK"), G_CALLBACK(msn_act_id),
 					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
+					   account, NULL, NULL,
 					   gc);
+	g_free(tmp);
 }
 
 static void
@@ -1537,6 +1543,8 @@
 {
 	const char *bname;
 	MsnAddReqData *data;
+	MsnSession *session;
+	MsnUser *user;
 
 	bname = purple_buddy_get_name(buddy);
 
@@ -1558,12 +1566,18 @@
 	data->buddy = buddy;
 	data->group = group;
 
-	purple_request_input(gc, NULL, _("Authorization Request Message:"),
-	                     NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
-	                     _("_OK"), G_CALLBACK(finish_auth_request),
-	                     _("_Cancel"), G_CALLBACK(cancel_auth_request),
-	                     purple_connection_get_account(gc), bname, NULL,
-	                     data);
+	session = purple_connection_get_protocol_data(gc);
+	user = msn_userlist_find_user(session->userlist, bname);
+	if (user && user->authorized) {
+		finish_auth_request(data, NULL);
+	} else {
+		purple_request_input(gc, NULL, _("Authorization Request Message:"),
+		                     NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
+		                     _("_OK"), G_CALLBACK(finish_auth_request),
+		                     _("_Cancel"), G_CALLBACK(cancel_auth_request),
+		                     purple_connection_get_account(gc), bname, NULL,
+		                     data);
+	}
 }
 
 static void
@@ -2653,7 +2667,7 @@
 	OPT_PROTO_MAIL_CHECK,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
-	{"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND},	/* icon_spec */
+	{"png,gif", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND},	/* icon_spec */
 	msn_list_icon,			/* list_icon */
 	msn_list_emblems,		/* list_emblems */
 	msn_status_text,		/* status_text */
--- a/libpurple/protocols/msn/slp.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slp.c	Fri Jan 08 23:22:19 2010 +0000
@@ -126,22 +126,60 @@
 			g_free(content);
 			msn_slplink_send_queued_slpmsgs(slpcall->slplink);
 
-			msn_slpcall_destroy(slpcall);
+			if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
+				slpcall->wasted = TRUE;
+			else
+				msn_slpcall_destroy(slpcall);
 		}
 	}
 }
 
-void
-msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize len, gsize offset)
+gssize
+msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
 {
-	PurpleXfer *xfer;
+	MsnSlpCall *slpcall;
+
+	g_return_val_if_fail(xfer != NULL, -1);
+	g_return_val_if_fail(data != NULL, -1);
+	g_return_val_if_fail(len > 0, -1);
+
+	g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1);
+
+	slpcall = xfer->data;
+	/* Not sure I trust it'll be there */
+	g_return_val_if_fail(slpcall != NULL, -1);
+
+	g_return_val_if_fail(slpcall->xfer_msg != NULL, -1);
+
+	slpcall->u.outgoing.len = len;
+	slpcall->u.outgoing.data = data;
+	msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg);
+	return MIN(1202, len);
+}
 
-	xfer = slpcall->xfer;
+gssize
+msn_xfer_read(guchar **data, PurpleXfer *xfer)
+{
+	MsnSlpCall *slpcall;
+	gsize len;
+
+	g_return_val_if_fail(xfer != NULL, -1);
+	g_return_val_if_fail(data != NULL, -1);
+
+	g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1);
 
-	xfer->bytes_sent = (offset + len);
-	xfer->bytes_remaining = total_length - (offset + len);
+	slpcall = xfer->data;
+	/* Not sure I trust it'll be there */
+	g_return_val_if_fail(slpcall != NULL, -1);
 
-	purple_xfer_update_progress(xfer);
+	/* Just pass up the whole GByteArray. We'll make another. */
+	*data = slpcall->u.incoming_data->data;
+	len = slpcall->u.incoming_data->len;
+
+	g_byte_array_free(slpcall->u.incoming_data, FALSE);
+	slpcall->u.incoming_data = g_byte_array_new();
+
+	return len;
 }
 
 void
@@ -238,6 +276,38 @@
 	msn_slplink_queue_slpmsg(slplink, slpmsg);
 }
 
+/* XXX: this could be improved if we tracked custom smileys
+ * per-protocol, per-account, per-session or (ideally) per-conversation
+ */
+static PurpleStoredImage *
+find_valid_emoticon(PurpleAccount *account, const char *path)
+{
+	GList *smileys;
+
+	if (!purple_account_get_bool(account, "custom_smileys", TRUE))
+		return NULL;
+
+	smileys = purple_smileys_get_all();
+
+	for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
+		PurpleSmiley *smiley;
+		PurpleStoredImage *img;
+
+		smiley = smileys->data;
+		img = purple_smiley_get_stored_image(smiley);
+
+		if (purple_strequal(path, purple_imgstore_get_filename(img))) {
+			g_list_free(smileys);
+			return img;
+		}
+
+		purple_imgstore_unref(img);
+	}
+
+	purple_debug_error("msn", "Received illegal request for file %s\n", path);
+	return NULL;
+}
+
 #define MAX_FILE_NAME_LEN 0x226
 
 static void
@@ -255,7 +325,7 @@
 		MsnSlpMessage *slpmsg;
 		MsnObject *obj;
 		char *msnobj_data;
-		PurpleStoredImage *img;
+		PurpleStoredImage *img = NULL;
 		int type;
 
 		/* Send Ok */
@@ -273,51 +343,38 @@
 		obj = msn_object_new_from_string(msnobj_data);
 		type = msn_object_get_type(obj);
 		g_free(msnobj_data);
-
-		if ((type != MSN_OBJECT_USERTILE) && (type != MSN_OBJECT_EMOTICON))
-		{
-			purple_debug_error("msn", "Wrong object?\n");
-			msn_object_destroy(obj);
-			g_return_if_reached();
-		}
-
 		if (type == MSN_OBJECT_EMOTICON) {
-			char *path;
-			path = g_build_filename(purple_smileys_get_storing_dir(),
-					obj->location, NULL);
-			img = purple_imgstore_new_from_file(path);
-			g_free(path);
-		} else {
+			img = find_valid_emoticon(slplink->session->account, obj->location);
+		} else if (type == MSN_OBJECT_USERTILE) {
 			img = msn_object_get_image(obj);
 			if (img)
 				purple_imgstore_ref(img);
 		}
 		msn_object_destroy(obj);
 
-		if (img == NULL)
-		{
-			purple_debug_error("msn", "Wrong object.\n");
-			g_return_if_reached();
-		}
+		if (img != NULL) {
+			/* DATA PREP */
+			slpmsg = msn_slpmsg_new(slplink);
+			slpmsg->slpcall = slpcall;
+			slpmsg->session_id = slpcall->session_id;
+			msn_slpmsg_set_body(slpmsg, NULL, 4);
+			slpmsg->info = "SLP DATA PREP";
+			msn_slplink_queue_slpmsg(slplink, slpmsg);
 
-		/* DATA PREP */
-		slpmsg = msn_slpmsg_new(slplink);
-		slpmsg->slpcall = slpcall;
-		slpmsg->session_id = slpcall->session_id;
-		msn_slpmsg_set_body(slpmsg, NULL, 4);
-		slpmsg->info = "SLP DATA PREP";
-		msn_slplink_queue_slpmsg(slplink, slpmsg);
+			/* DATA */
+			slpmsg = msn_slpmsg_new(slplink);
+			slpmsg->slpcall = slpcall;
+			slpmsg->flags = 0x20;
+			slpmsg->info = "SLP DATA";
+			msn_slpmsg_set_image(slpmsg, img);
+			msn_slplink_queue_slpmsg(slplink, slpmsg);
+			purple_imgstore_unref(img);
 
-		/* DATA */
-		slpmsg = msn_slpmsg_new(slplink);
-		slpmsg->slpcall = slpcall;
-		slpmsg->flags = 0x20;
-		slpmsg->info = "SLP DATA";
-		msn_slpmsg_set_image(slpmsg, img);
-		msn_slplink_queue_slpmsg(slplink, slpmsg);
-		purple_imgstore_unref(img);
+			accepted = TRUE;
 
-		accepted = TRUE;
+		} else {
+			purple_debug_error("msn", "Wrong object.\n");
+		}
 	}
 
 	else if (!strcmp(euf_guid, MSN_FT_GUID))
@@ -332,9 +389,7 @@
 
 		account = slpcall->slplink->session->account;
 
-		slpcall->cb = msn_xfer_completed_cb;
 		slpcall->end_cb = msn_xfer_end_cb;
-		slpcall->progress_cb = msn_xfer_progress_cb;
 		slpcall->branch = g_strdup(branch);
 
 		slpcall->pending = TRUE;
@@ -357,6 +412,10 @@
 			purple_xfer_set_init_fnc(xfer, msn_xfer_init);
 			purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
 			purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
+			purple_xfer_set_read_fnc(xfer, msn_xfer_read);
+			purple_xfer_set_write_fnc(xfer, msn_xfer_write);
+
+			slpcall->u.incoming_data = g_byte_array_new();
 
 			slpcall->xfer = xfer;
 			purple_xfer_ref(slpcall->xfer);
--- a/libpurple/protocols/msn/slp.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slp.h	Fri Jan 08 23:22:19 2010 +0000
@@ -29,9 +29,6 @@
 #include "internal.h"
 #include "ft.h"
 
-void msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize
-						  len, gsize offset);
-
 MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink,
 							  const char *body);
 
@@ -41,6 +38,9 @@
 						   const guchar *body, gsize size);
 
 void msn_xfer_cancel(PurpleXfer *xfer);
+gssize msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer);
+gssize msn_xfer_read(guchar **data, PurpleXfer *xfer);
+
 void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session);
 
 void msn_queue_buddy_icon_request(MsnUser *user);
--- a/libpurple/protocols/msn/slpcall.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Fri Jan 08 23:22:19 2010 +0000
@@ -105,10 +105,13 @@
 		slpcall->end_cb(slpcall, slpcall->slplink->session);
 
 	if (slpcall->xfer != NULL) {
+		if (purple_xfer_get_type(slpcall->xfer) == PURPLE_XFER_RECEIVE)
+			g_byte_array_free(slpcall->u.incoming_data, TRUE);
 		slpcall->xfer->data = NULL;
 		purple_xfer_unref(slpcall->xfer);
 	}
 
+
 	msn_slplink_remove_slpcall(slpcall->slplink, slpcall);
 
 	g_free(slpcall->id);
@@ -272,7 +275,8 @@
 				slpcall->timer = 0;
 			}
 
-			slpcall->cb(slpcall, body, body_len);
+			if (slpcall->cb)
+				slpcall->cb(slpcall, body, body_len);
 
 			slpcall->wasted = TRUE;
 		}
--- a/libpurple/protocols/msn/slpcall.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.h	Fri Jan 08 23:22:19 2010 +0000
@@ -72,6 +72,14 @@
 	char *data_info;
 
 	PurpleXfer *xfer;
+	union {
+		GByteArray *incoming_data;
+		struct {
+			gsize len;
+			const guchar *data;
+		} outgoing;
+	} u;
+	MsnSlpMessage *xfer_msg; /* A dirty hack */
 
 	MsnSlpCb cb;
 	void (*end_cb)(MsnSlpCall *slpcall, MsnSession *session);
--- a/libpurple/protocols/msn/slplink.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slplink.c	Fri Jan 08 23:22:19 2010 +0000
@@ -232,7 +232,7 @@
 	}
 }
 
-static void
+void
 msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
 {
 	MsnMessage *msg;
@@ -247,11 +247,11 @@
 
 	if (slpmsg->offset < real_size)
 	{
-		if (slpmsg->fp)
+		if (slpmsg->slpcall && slpmsg->slpcall->xfer && purple_xfer_get_type(slpmsg->slpcall->xfer) == PURPLE_XFER_SEND &&
+				purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
 		{
-			char data[1202];
-			len = fread(data, 1, sizeof(data), slpmsg->fp);
-			msn_message_set_bin_data(msg, data, len);
+			len = MIN(1202, slpmsg->slpcall->u.outgoing.len);
+			msn_message_set_bin_data(msg, slpmsg->slpcall->u.outgoing.data, len);
 		}
 		else
 		{
@@ -309,7 +309,13 @@
 
 	if (slpmsg->offset < real_size)
 	{
-		msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
+		if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
+		{
+			slpmsg->slpcall->xfer_msg = slpmsg;
+			purple_xfer_prpl_ready(slpmsg->slpcall->xfer);
+		}
+		else
+			msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
 	}
 	else
 	{
@@ -448,20 +454,22 @@
 send_file_cb(MsnSlpCall *slpcall)
 {
 	MsnSlpMessage *slpmsg;
-	struct stat st;
 	PurpleXfer *xfer;
 
+	xfer = (PurpleXfer *)slpcall->xfer;
+	purple_xfer_ref(xfer);
+	purple_xfer_start(xfer, -1, NULL, 0);
+	if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) {
+		purple_xfer_unref(xfer);
+		return;
+	}
+	purple_xfer_unref(xfer);
+
 	slpmsg = msn_slpmsg_new(slpcall->slplink);
 	slpmsg->slpcall = slpcall;
 	slpmsg->flags = 0x1000030;
 	slpmsg->info = "SLP FILE";
-
-	xfer = (PurpleXfer *)slpcall->xfer;
-	purple_xfer_start(slpcall->xfer, -1, NULL, 0);
-	slpmsg->fp = xfer->dest_fp;
-	if (g_stat(purple_xfer_get_local_filename(xfer), &st) == 0)
-		slpmsg->size = st.st_size;
-	xfer->dest_fp = NULL; /* Disable double fclose() */
+	slpmsg->size = purple_xfer_get_size(xfer);
 
 	msn_slplink_send_slpmsg(slpcall->slplink, slpmsg);
 }
@@ -489,6 +497,7 @@
 	const char *data;
 	guint64 offset;
 	gsize len;
+	PurpleXfer *xfer = NULL;
 
 	if (purple_debug_is_verbose())
 		msn_slpmsg_show(msg);
@@ -525,12 +534,12 @@
 				if (slpmsg->flags == 0x20 ||
 				    slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030)
 				{
-					PurpleXfer *xfer;
-
 					xfer = slpmsg->slpcall->xfer;
-
 					if (xfer != NULL)
 					{
+						slpmsg->ft = TRUE;
+						slpmsg->slpcall->xfer_msg = slpmsg;
+
 						purple_xfer_ref(xfer);
 						purple_xfer_start(xfer,	-1, NULL, 0);
 
@@ -540,14 +549,12 @@
 							g_return_if_reached();
 						} else {
 							purple_xfer_unref(xfer);
-							slpmsg->fp = xfer->dest_fp;
-							xfer->dest_fp = NULL; /* Disable double fclose() */
 						}
 					}
 				}
 			}
 		}
-		if (!slpmsg->fp && slpmsg->size)
+		if (!slpmsg->ft && slpmsg->size)
 		{
 			slpmsg->buffer = g_try_malloc(slpmsg->size);
 			if (slpmsg->buffer == NULL)
@@ -569,10 +576,12 @@
 		}
 	}
 
-	if (slpmsg->fp)
+	if (slpmsg->ft)
 	{
-		/* fseek(slpmsg->fp, offset, SEEK_SET); */
-		len = fwrite(data, 1, len, slpmsg->fp);
+		xfer = slpmsg->slpcall->xfer;
+		slpmsg->slpcall->u.incoming_data =
+				g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)data, len);
+		purple_xfer_prpl_ready(xfer);
 	}
 	else if (slpmsg->size && slpmsg->buffer)
 	{
@@ -613,29 +622,37 @@
 
 		slpcall = msn_slp_process_msg(slplink, slpmsg);
 
-		if (slpmsg->flags == 0x100)
-		{
-			MsnDirectConn *directconn;
+		if (slpcall == NULL) {
+			msn_slpmsg_destroy(slpmsg);
+			return;
+		}
 
-			directconn = slplink->directconn;
+		if (!slpcall->wasted) {
+			if (slpmsg->flags == 0x100)
+			{
+				MsnDirectConn *directconn;
+
+				directconn = slplink->directconn;
 #if 0
-			if (!directconn->acked)
-				msn_directconn_send_handshake(directconn);
+				if (!directconn->acked)
+					msn_directconn_send_handshake(directconn);
 #endif
-		}
-		else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 ||  
-		         slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||  
-		         slpmsg->flags == 0x1000030)
-		{
-			/* Release all the messages and send the ACK */
+			}
+			else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 ||  
+			         slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||  
+			         slpmsg->flags == 0x1000030)
+			{
+				/* Release all the messages and send the ACK */
 
-			msn_slplink_send_ack(slplink, msg);
-			msn_slplink_send_queued_slpmsgs(slplink);
+				msn_slplink_send_ack(slplink, msg);
+				msn_slplink_send_queued_slpmsgs(slplink);
+			}
+
 		}
 
 		msn_slpmsg_destroy(slpmsg);
 
-		if (slpcall != NULL && slpcall->wasted)
+		if (slpcall->wasted)
 			msn_slpcall_destroy(slpcall);
 	}
 }
@@ -652,9 +669,8 @@
 #define MAX_FILE_NAME_LEN 0x226
 
 static gchar *
-gen_context(const char *file_name, const char *file_path)
+gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
 {
-	struct stat st;
 	gsize size = 0;
 	MsnContextHeader header;
 	gchar *u8 = NULL;
@@ -666,8 +682,7 @@
 	glong uni_len = 0;
 	gsize len;
 
-	if (g_stat(file_path, &st) == 0)
-		size = st.st_size;
+	size = purple_xfer_get_size(xfer);
 
 	if(!file_name) {
 		gchar *basename = g_path_get_basename(file_path);
@@ -732,7 +747,6 @@
 
 	slpcall->session_init_cb = send_file_cb;
 	slpcall->end_cb = msn_xfer_end_cb;
-	slpcall->progress_cb = msn_xfer_progress_cb;
 	slpcall->cb = msn_xfer_completed_cb;
 	slpcall->xfer = xfer;
 	purple_xfer_ref(slpcall->xfer);
@@ -740,10 +754,12 @@
 	slpcall->pending = TRUE;
 
 	purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel);
+	purple_xfer_set_read_fnc(xfer, msn_xfer_read);
+	purple_xfer_set_write_fnc(xfer, msn_xfer_write);
 
 	xfer->data = slpcall;
 
-	context = gen_context(fn, fp);
+	context = gen_context(xfer, fn, fp);
 
 	msn_slpcall_invite(slpcall, MSN_FT_GUID, 2, context);
 
--- a/libpurple/protocols/msn/slplink.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slplink.h	Fri Jan 08 23:22:19 2010 +0000
@@ -84,6 +84,9 @@
 void msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg);
 void msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer);
 
+/* Only exported for msn_xfer_write */
+void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
+
 void msn_slplink_request_object(MsnSlpLink *slplink,
 								const char *info,
 								MsnSlpCb cb,
--- a/libpurple/protocols/msn/slpmsg.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slpmsg.c	Fri Jan 08 23:22:19 2010 +0000
@@ -60,9 +60,6 @@
 
 	slplink = slpmsg->slplink;
 
-	if (slpmsg->fp != NULL)
-		fclose(slpmsg->fp);
-
 	purple_imgstore_unref(slpmsg->img);
 
 	/* We don't want to free the data of the PurpleStoredImage,
@@ -96,7 +93,7 @@
 	/* We can only have one data source at a time. */
 	g_return_if_fail(slpmsg->buffer == NULL);
 	g_return_if_fail(slpmsg->img == NULL);
-	g_return_if_fail(slpmsg->fp == NULL);
+	g_return_if_fail(slpmsg->ft == FALSE);
 
 	if (body != NULL)
 		slpmsg->buffer = g_memdup(body, size);
@@ -112,7 +109,7 @@
 	/* We can only have one data source at a time. */
 	g_return_if_fail(slpmsg->buffer == NULL);
 	g_return_if_fail(slpmsg->img == NULL);
-	g_return_if_fail(slpmsg->fp == NULL);
+	g_return_if_fail(slpmsg->ft == FALSE);
 
 	slpmsg->img = purple_imgstore_ref(img);
 	slpmsg->buffer = (guchar *)purple_imgstore_get_data(img);
--- a/libpurple/protocols/msn/slpmsg.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/slpmsg.h	Fri Jan 08 23:22:19 2010 +0000
@@ -54,7 +54,7 @@
 	gboolean sip; /**< A flag that states if this is a SIP slp message. */
 	long flags;
 
-	FILE *fp;
+	gboolean ft;
 	PurpleStoredImage *img;
 	guchar *buffer;
 	long long offset;
--- a/libpurple/protocols/msn/switchboard.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Fri Jan 08 23:22:19 2010 +0000
@@ -222,13 +222,28 @@
 {
 	MsnCmdProc *cmdproc;
 	PurpleAccount *account;
+	char *semicolon;
+	char *passport;
 
 	g_return_if_fail(swboard != NULL);
 
 	cmdproc = swboard->cmdproc;
 	account = cmdproc->session->account;
 
-	swboard->users = g_list_prepend(swboard->users, g_strdup(user));
+	semicolon = strchr(user, ';');
+	/* We don't really care about the machine ID. */
+	if (semicolon)
+		passport = g_strndup(user, semicolon - user);
+	else
+		passport = g_strdup(user);
+
+	/* Don't add multiple endpoints to the conversation. */
+	if (g_list_find_custom(swboard->users, passport, (GCompareFunc)strcmp)) {
+		g_free(passport);
+		return;
+	}
+
+	swboard->users = g_list_prepend(swboard->users, passport);
 	swboard->current_users++;
 	swboard->empty = FALSE;
 
--- a/libpurple/protocols/msn/userlist.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/msn/userlist.c	Fri Jan 08 23:22:19 2010 +0000
@@ -210,6 +210,7 @@
 
 	if (list_op & MSN_LIST_PL_OP)
 	{
+		user->authorized = TRUE;
 		got_new_entry(gc, passport, store, message);
 	}
 }
--- a/libpurple/protocols/mxit/splashscreen.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/mxit/splashscreen.c	Fri Jan 08 23:22:19 2010 +0000
@@ -24,7 +24,6 @@
  */
 
 #include "internal.h"
-#include <glib/gstdio.h>
 
 #include "purple.h"
 #include "imgstore.h"
--- a/libpurple/protocols/myspace/user.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/myspace/user.c	Fri Jan 08 23:22:19 2010 +0000
@@ -70,6 +70,9 @@
 	if (!user)
 		return;
 
+	if (user->url_data != NULL)
+		purple_util_fetch_url_cancel(user->url_data);
+
 	g_free(user->client_info);
 	g_free(user->gender);
 	g_free(user->location);
@@ -212,6 +215,8 @@
 	const char *name = purple_buddy_get_name(user->buddy);
 	PurpleAccount *account;
 
+	user->url_data = NULL;
+
 	purple_debug_info("msim_downloaded_buddy_icon",
 			"Downloaded %" G_GSIZE_FORMAT " bytes\n", len);
 
@@ -375,7 +380,9 @@
 
 		/* Only download if URL changed */
 		if (!previous_url || !g_str_equal(previous_url, user->image_url)) {
-			purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
+			if (user->url_data != NULL)
+				purple_util_fetch_url_cancel(user->url_data);
+			user->url_data = purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
 		}
 	} else if (g_str_equal(key_str, "LastImageUpdated")) {
 		/* TODO: use somewhere */
--- a/libpurple/protocols/myspace/user.h	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/myspace/user.h	Fri Jan 08 23:22:19 2010 +0000
@@ -40,6 +40,7 @@
 	gchar *image_url;
 	guint last_image_updated;
 	gboolean temporary_user;
+	PurpleUtilFetchUrlData *url_data;
 } MsimUser;
 
 /* Callback function pointer type for when a user's information is received,
--- a/libpurple/protocols/oscar/oscar.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri Jan 08 23:22:19 2010 +0000
@@ -378,12 +378,12 @@
 }
 
 static gchar *
-oscar_utf8_try_convert(PurpleAccount *account, const gchar *msg)
+oscar_utf8_try_convert(PurpleAccount *account, OscarData *od, const gchar *msg)
 {
 	const char *charset = NULL;
 	char *ret = NULL;
 
-	if(oscar_util_valid_name_icq(purple_account_get_username(account)))
+	if (od->icq)
 		charset = purple_account_get_string(account, "encoding", NULL);
 
 	if(charset && *charset)
@@ -798,24 +798,24 @@
 }
 
 static void
-oscar_user_info_convert_and_add_pair(PurpleAccount *account, PurpleNotifyUserInfo *user_info,
+oscar_user_info_convert_and_add_pair(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
 									 const char *name, const char *value)
 {
 	gchar *utf8;
 
-	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
+	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
 		purple_notify_user_info_add_pair(user_info, name, utf8);
 		g_free(utf8);
 	}
 }
 
 static void
-oscar_user_info_convert_and_add(PurpleAccount *account, PurpleNotifyUserInfo *user_info,
+oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
 								const char *name, const char *value)
 {
 	gchar *utf8;
 
-	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
+	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
 		purple_notify_user_info_add_pair(user_info, name, utf8);
 		g_free(utf8);
 	}
@@ -1022,7 +1022,7 @@
 			char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
 			g_free(tmp);
 
-			oscar_user_info_convert_and_add_pair(account, user_info, _("Buddy Comment"), tmp2);
+			oscar_user_info_convert_and_add_pair(account, od, user_info, _("Buddy Comment"), tmp2);
 			g_free(tmp2);
 		}
 	}
@@ -2450,7 +2450,7 @@
 	 * Note: There *may* be some clients which send messages as HTML formatted -
 	 *       they need to be special-cased somehow.
 	 */
-	if (oscar_util_valid_name_icq(purple_account_get_username(account)) && oscar_util_valid_name_icq(userinfo->bn)) {
+	if (od->icq && oscar_util_valid_name_icq(userinfo->bn)) {
 		/* being recevied by ICQ from ICQ - escape HTML so it is displayed as sent */
 		gchar *tmp2 = g_markup_escape_text(tmp, -1);
 		g_free(tmp);
@@ -4173,7 +4173,7 @@
 		bi = NULL;
 
 	purple_notify_user_info_add_pair(user_info, _("UIN"), who);
-	oscar_user_info_convert_and_add(account, user_info, _("Nick"), info->nick);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
 	if ((bi != NULL) && (bi->ipaddr != 0)) {
 		char *tstr =  g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
 						(bi->ipaddr & 0xff000000) >> 24,
@@ -4183,9 +4183,9 @@
 		purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
 		g_free(tstr);
 	}
-	oscar_user_info_convert_and_add(account, user_info, _("First Name"), info->first);
-	oscar_user_info_convert_and_add(account, user_info, _("Last Name"), info->last);
-	if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, info->email))) {
+	oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last);
+	if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, od, info->email))) {
 		buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
 		purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
 		g_free(buf);
@@ -4194,7 +4194,7 @@
 	if (info->numaddresses && info->email2) {
 		int i;
 		for (i = 0; i < info->numaddresses; i++) {
-			if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, info->email2[i]))) {
+			if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, od, info->email2[i]))) {
 				buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
 				purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
 				g_free(buf);
@@ -4202,7 +4202,7 @@
 			}
 		}
 	}
-	oscar_user_info_convert_and_add(account, user_info, _("Mobile Phone"), info->mobile);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile);
 
 	if (info->gender != 0)
 		purple_notify_user_info_add_pair(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
@@ -4222,14 +4222,14 @@
 		 * feel free to remove it.  --rlaager */
 		mktime(tm);
 
-		oscar_user_info_convert_and_add(account, user_info, _("Birthday"), purple_date_format_short(tm));
+		oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
 	}
 	if ((info->age > 0) && (info->age < 255)) {
 		char age[5];
 		snprintf(age, sizeof(age), "%hhd", info->age);
 		purple_notify_user_info_add_pair(user_info, _("Age"), age);
 	}
-	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, info->personalwebpage))) {
+	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->personalwebpage))) {
 		buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
 		purple_notify_user_info_add_pair(user_info, _("Personal Web Page"), buf);
 		g_free(buf);
@@ -4239,33 +4239,33 @@
 	if (buddy != NULL)
 		oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE);
 
-	oscar_user_info_convert_and_add(account, user_info, _("Additional Information"), info->info);
+	oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
 	purple_notify_user_info_add_section_break(user_info);
 
 	if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
 		purple_notify_user_info_add_section_header(user_info, _("Home Address"));
 
-		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->homeaddr);
-		oscar_user_info_convert_and_add(account, user_info, _("City"), info->homecity);
-		oscar_user_info_convert_and_add(account, user_info, _("State"), info->homestate);
-		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->homezip);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
+		oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
+		oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
 	}
 	if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
 		purple_notify_user_info_add_section_header(user_info, _("Work Address"));
 
-		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->workaddr);
-		oscar_user_info_convert_and_add(account, user_info, _("City"), info->workcity);
-		oscar_user_info_convert_and_add(account, user_info, _("State"), info->workstate);
-		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->workzip);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
+		oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
+		oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
 	}
 	if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
 		purple_notify_user_info_add_section_header(user_info, _("Work Information"));
 
-		oscar_user_info_convert_and_add(account, user_info, _("Company"), info->workcompany);
-		oscar_user_info_convert_and_add(account, user_info, _("Division"), info->workdivision);
-		oscar_user_info_convert_and_add(account, user_info, _("Position"), info->workposition);
-
-		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, info->workwebpage))) {
+		oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision);
+		oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition);
+
+		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->workwebpage))) {
 			char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
 			purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
 			g_free(webpage);
@@ -4296,7 +4296,7 @@
 	info = va_arg(ap, struct aim_icq_info *);
 	va_end(ap);
 
-	if (info->uin && info->nick && info->nick[0] && (utf8 = oscar_utf8_try_convert(account, info->nick))) {
+	if (info->uin && info->nick && info->nick[0] && (utf8 = oscar_utf8_try_convert(account, od, info->nick))) {
 		g_snprintf(who, sizeof(who), "%u", info->uin);
 		serv_got_alias(gc, who, utf8);
 		if ((b = purple_find_buddy(account, who))) {
@@ -4751,7 +4751,7 @@
 			/* Messaging an SMS (mobile) user */
 			tmp2 = purple_markup_strip_html(tmp1);
 			is_html = FALSE;
-		} else if (oscar_util_valid_name_icq(purple_account_get_username(account))) {
+		} else if (od->icq) {
 			if (oscar_util_valid_name_icq(name)) {
 				/* From ICQ to ICQ */
 				tmp2 = purple_markup_strip_html(tmp1);
@@ -5047,6 +5047,9 @@
 void
 oscar_set_status(PurpleAccount *account, PurpleStatus *status)
 {
+	PurpleConnection *pc;
+	OscarData *od;
+
 	purple_debug_info("oscar", "Set status to %s\n", purple_status_get_name(status));
 
 	if (!purple_status_is_active(status))
@@ -5055,11 +5058,14 @@
 	if (!purple_account_is_connected(account))
 		return;
 
+	pc = purple_account_get_connection(account);
+	od = purple_connection_get_protocol_data(pc);
+
 	/* Set the AIM-style away message for both AIM and ICQ accounts */
 	oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
 
 	/* Set the ICQ status for ICQ accounts only */
-	if (oscar_util_valid_name_icq(purple_account_get_username(account)))
+	if (od->icq)
 		oscar_set_status_icq(account);
 }
 
@@ -5404,7 +5410,7 @@
 						if (g_utf8_validate(gname, -1, NULL))
 							gname_utf8 = g_strdup(gname);
 						else
-							gname_utf8 = oscar_utf8_try_convert(account, gname);
+							gname_utf8 = oscar_utf8_try_convert(account, od, gname);
 					} else
 						gname_utf8 = NULL;
 
@@ -5419,7 +5425,7 @@
 						if (g_utf8_validate(alias, -1, NULL))
 							alias_utf8 = g_strdup(alias);
 						else
-							alias_utf8 = oscar_utf8_try_convert(account, alias);
+							alias_utf8 = oscar_utf8_try_convert(account, od, alias);
 						g_free(alias);
 					} else
 						alias_utf8 = NULL;
@@ -5468,7 +5474,7 @@
 					if (g_utf8_validate(gname, -1, NULL))
 						gname_utf8 = g_strdup(gname);
 					else
-						gname_utf8 = oscar_utf8_try_convert(account, gname);
+						gname_utf8 = oscar_utf8_try_convert(account, od, gname);
 				} else
 					gname_utf8 = NULL;
 
@@ -5636,7 +5642,7 @@
 		return 1;
 
 	gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
-	gname_utf8 = gname ? oscar_utf8_try_convert(account, gname) : NULL;
+	gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL;
 
 	alias = aim_ssi_getalias(od->ssi.local, gname, name);
 	if (alias != NULL)
@@ -5644,7 +5650,7 @@
 		if (g_utf8_validate(alias, -1, NULL))
 			alias_utf8 = g_strdup(alias);
 		else
-			alias_utf8 = oscar_utf8_try_convert(account, alias);
+			alias_utf8 = oscar_utf8_try_convert(account, od, alias);
 	}
 	else
 		alias_utf8 = NULL;
@@ -6417,7 +6423,7 @@
 	data = g_new(struct name_data, 1);
 
 	comment = aim_ssi_getcomment(od->ssi.local, purple_group_get_name(g), name);
-	comment_utf8 = comment ? oscar_utf8_try_convert(account, comment) : NULL;
+	comment_utf8 = comment ? oscar_utf8_try_convert(account, od, comment) : NULL;
 
 	data->gc = gc;
 	data->name = g_strdup(name);
--- a/libpurple/purple-uninstalled.pc.in	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/purple-uninstalled.pc.in	Fri Jan 08 23:22:19 2010 +0000
@@ -6,9 +6,12 @@
 datadir=@datadir@
 sysconfdir=@sysconfdir@
 
+abs_srcdir=@abs_srcdir@
+abs_builddir=@abs_builddir@
+
 Name: libpurple
 Description: libpurple is a GLib-based instant messenger library.
 Version: @VERSION@
 Requires: glib-2.0
-Cflags: -I${pcfiledir}
-Libs: ${pcfiledir}/libpurple.la
+Cflags: -I${abs_srcdir} -I${abs_builddir}
+Libs: ${abs_builddir}/libpurple.la
--- a/libpurple/tests/Makefile.am	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/tests/Makefile.am	Fri Jan 08 23:22:19 2010 +0000
@@ -27,11 +27,11 @@
 		-DBUILDDIR=\"$(top_builddir)\"
 
 check_libpurple_LDADD=\
-        @CHECK_LIBS@ \
-		$(GLIB_LIBS) \
 		$(top_builddir)/libpurple/protocols/jabber/libjabber.la \
 		$(top_builddir)/libpurple/protocols/qq/libqq.la \
 		$(top_builddir)/libpurple/protocols/yahoo/libymsg.la \
-		$(top_builddir)/libpurple/libpurple.la
+		$(top_builddir)/libpurple/libpurple.la \
+        @CHECK_LIBS@ \
+		$(GLIB_LIBS)
 
 endif
--- a/libpurple/tests/check_libpurple.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/tests/check_libpurple.c	Fri Jan 08 23:22:19 2010 +0000
@@ -44,7 +44,7 @@
 	purple_eventloop_set_ui_ops(&eventloop_ui_ops);
 
 	/* build our fake home directory */
-	home_dir = g_build_path(BUILDDIR, "libpurple", "tests", "home", NULL);
+	home_dir = g_build_path(G_DIR_SEPARATOR_S, BUILDDIR, "libpurple", "tests", "home", NULL);
 	purple_util_set_user_dir(home_dir);
 	g_free(home_dir);
 
@@ -67,6 +67,9 @@
 	int number_failed;
 	SRunner *sr;
 
+	if (g_getenv("PURPLE_CHECK_DEBUG"))
+		purple_debug_set_enabled(TRUE);
+
 	/* Make g_return_... functions fatal, ALWAYS.
 	 * As this is the test code, this is NOT controlled
 	 * by PURPLE_FATAL_ASSERTS. */
--- a/libpurple/tests/test_cipher.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/libpurple/tests/test_cipher.c	Fri Jan 08 23:22:19 2010 +0000
@@ -142,12 +142,13 @@
 	PurpleCipherContext *context = NULL; \
 	gchar cdigest[41]; \
 	gboolean ret = FALSE; \
+	gchar *input = data; \
 	\
 	cipher = purple_ciphers_find_cipher("sha1"); \
 	context = purple_cipher_context_new(cipher, NULL); \
 	\
-	if((data)) { \
-		purple_cipher_context_append(context, (guchar *)(data), strlen((data))); \
+	if (input) { \
+		purple_cipher_context_append(context, (guchar *)input, strlen(input)); \
 	} else { \
 		gint j; \
 		guchar buff[1000]; \
@@ -202,12 +203,13 @@
 	PurpleCipherContext *context = NULL; \
 	gchar cdigest[65]; \
 	gboolean ret = FALSE; \
+	gchar *input = data; \
 	\
 	cipher = purple_ciphers_find_cipher("sha256"); \
 	context = purple_cipher_context_new(cipher, NULL); \
 	\
-	if((data)) { \
-		purple_cipher_context_append(context, (guchar *)(data), strlen((data))); \
+	if (input) { \
+		purple_cipher_context_append(context, (guchar *)input, strlen(input)); \
 	} else { \
 		gint j; \
 		guchar buff[1000]; \
--- a/pidgin/gtkconv.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/pidgin/gtkconv.c	Fri Jan 08 23:22:19 2010 +0000
@@ -5493,10 +5493,12 @@
 	}
 
 	/* Somebody wants to keep this conversation around, so don't time it out */
-	timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
-	if (timer) {
-		purple_timeout_remove(timer);
-		purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(0));
+	if (conv) {
+		timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+		if (timer) {
+			purple_timeout_remove(timer);
+			purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(0));
+		}
 	}
 }
 
@@ -7622,16 +7624,24 @@
 	}
 }
 
+struct _status_timeout_user {
+	gchar *name;
+	PurpleAccount *account;
+};
+
 static gboolean
-update_buddy_status_timeout(PurpleBuddy *buddy)
+update_buddy_status_timeout(struct _status_timeout_user *user)
 {
 	/* To remove the signing-on/off door icon */
 	PurpleConversation *conv;
 
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name, buddy->account);
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, user->name, user->account);
 	if (conv)
 		pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON);
 
+	g_free(user->name);
+	g_free(user);
+
 	return FALSE;
 }
 
@@ -7640,6 +7650,7 @@
 {
 	PidginConversation *gtkconv;
 	PurpleConversation *conv;
+	struct _status_timeout_user *user;
 
 	gtkconv = get_gtkconv_with_contact(purple_buddy_get_contact(buddy));
 	if (gtkconv)
@@ -7652,8 +7663,12 @@
 			pidgin_conv_update_fields(conv, PIDGIN_CONV_MENU);
 	}
 
+	user = g_malloc(sizeof(struct _status_timeout_user));
+	user->name = g_strdup(buddy->name);
+	user->account = buddy->account;
+
 	/* In case a conversation is started after the buddy has signed-on/off */
-	purple_timeout_add_seconds(11, (GSourceFunc)update_buddy_status_timeout, buddy);
+	purple_timeout_add_seconds(11, (GSourceFunc)update_buddy_status_timeout, user);
 }
 
 static void
--- a/pidgin/gtkdialogs.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/pidgin/gtkdialogs.c	Fri Jan 08 23:22:19 2010 +0000
@@ -197,7 +197,6 @@
 	{N_("Kurdish"),             "ku", "Amed Ç. Jiyan", "amedcj@hotmail.com"},
 	{N_("Kurdish"),             "ku", "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
 	{N_("Lao"),                 "lo", "Anousak Souphavah", "anousak@gmail.com"},
-	{N_("Lithuanian"),          "lt", "Laurynas Biveinis", "laurynas.biveinis@gmail.com"},
 	{N_("Macedonian"),          "mk", "Arangel Angov ", "arangel@linux.net.mk"},
 	{N_("Macedonian"),          "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
 	{N_("Macedonian"),          "mk", "Jovan Naumovski", "jovan@lugola.net"},
@@ -276,8 +275,9 @@
 	{N_("Georgian"),            "ka", "Temuri Doghonadze", NULL},
 	{N_("Korean"),              "ko", "Sang-hyun S, A Ho-seok Lee", NULL},
 	{N_("Korean"),              "ko", "Kyeong-uk Son", NULL},
+	{N_("Lithuanian"),          "lt", "Laurynas Biveinis", "laurynas.biveinis@gmail.com"},
+	{N_("Lithuanian"),          "lt", "Gediminas Čičinskas", NULL},
 	{N_("Lithuanian"),          "lt", "Andrius Štikonas", NULL},
-	{N_("Lithuanian"),          "lt", "Gediminas Čičinskas", NULL},
 	{N_("Macedonian"),          "mk", "Tomislav Markovski", NULL},
 	{N_("Bokmål Norwegian"),    "nb", "Hallvard Glad", "hallvard.glad@gmail.com"},
 	{N_("Bokmål Norwegian"),    "nb", "Petter Johan Olsen", NULL},
--- a/pidgin/gtkft.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/pidgin/gtkft.c	Fri Jan 08 23:22:19 2010 +0000
@@ -730,6 +730,7 @@
 	GtkWidget *sw;
 	GtkWidget *button;
 	GtkWidget *expander;
+	GtkWidget *alignment;
 	GtkWidget *table;
 	GtkWidget *checkbox;
 
@@ -787,9 +788,15 @@
 
 	gtk_widget_set_sensitive(expander, FALSE);
 
+	/* Small indent make table fall under GtkExpander's label */
+	alignment = gtk_alignment_new(1, 0, 1, 1);
+	gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 20, 0);
+	gtk_container_add(GTK_CONTAINER(expander), alignment);
+	gtk_widget_show(alignment);
+
 	/* The table of information. */
 	table = make_info_table(dialog);
-	gtk_container_add(GTK_CONTAINER(expander), table);
+	gtk_container_add(GTK_CONTAINER(alignment), table);
 	gtk_widget_show(table);
 
 	/* Open button */
--- a/pidgin/gtkimhtml.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/pidgin/gtkimhtml.c	Fri Jan 08 23:22:19 2010 +0000
@@ -549,6 +549,7 @@
 	imhtml->tip_timer = 0;
 	imhtml->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
 	gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
+	gtk_window_set_title(GTK_WINDOW(imhtml->tip_window), "GtkIMHtml");
 	gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
 	gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
 #if GTK_CHECK_VERSION(2,10,0)
--- a/pidgin/gtklog.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/pidgin/gtklog.c	Fri Jan 08 23:22:19 2010 +0000
@@ -423,6 +423,7 @@
 {
 	PidginLogViewer *viewer = data;
 	gtk_imhtml_search_find(GTK_IMHTML(viewer->imhtml), viewer->search);
+	g_object_steal_data(G_OBJECT(viewer->entry), "search-find-cb");
 	return FALSE;
 }
 
@@ -475,8 +476,11 @@
 	g_free(read);
 
 	if (viewer->search != NULL) {
+		guint source;
 		gtk_imhtml_search_clear(GTK_IMHTML(viewer->imhtml));
-		g_idle_add(search_find_cb, viewer);
+		source = g_idle_add(search_find_cb, viewer);
+		g_object_set_data_full(G_OBJECT(viewer->entry), "search-find-cb",
+		                       GINT_TO_POINTER(source), (GDestroyNotify)g_source_remove);
 	}
 
 	pidgin_clear_cursor(viewer->window);
--- a/pidgin/gtkprefs.c	Thu Dec 10 21:36:41 2009 +0000
+++ b/pidgin/gtkprefs.c	Fri Jan 08 23:22:19 2010 +0000
@@ -70,27 +70,33 @@
 	gchar *original_name;
 };
 
-static int sound_row_sel = 0;
-static GtkWidget *prefsnotebook;
-
+/* Main dialog */
+static GtkWidget *prefs = NULL;
+
+/* Notebook */
+static GtkWidget *prefsnotebook = NULL;
+static int notebook_page = 0;
+
+/* Conversations page */
+static GtkWidget *sample_imhtml = NULL;
+
+/* Themes page */
+static GtkWidget *prefs_sound_themes_combo_box;
+static GtkWidget *prefs_blist_themes_combo_box;
+static GtkWidget *prefs_status_themes_combo_box;
+static GtkWidget *prefs_smiley_themes_combo_box;
+
+/* Sound theme specific */
 static GtkWidget *sound_entry = NULL;
-
-static GtkWidget *prefs = NULL;
-static GtkWidget *debugbutton = NULL;
-static int notebook_page = 0;
-
+static int sound_row_sel = 0;
+static gboolean prefs_sound_themes_loading;
+
+/* These exist outside the lifetime of the prefs dialog */
 static GtkListStore *prefs_sound_themes;
 static GtkListStore *prefs_blist_themes;
 static GtkListStore *prefs_status_icon_themes;
 static GtkListStore *prefs_smiley_themes;
 
-static GtkWidget *prefs_sound_themes_combo_box;
-static GtkWidget *prefs_blist_themes_combo_box;
-static GtkWidget *prefs_status_themes_combo_box;
-static GtkWidget *prefs_smiley_themes_combo_box;
-
-static gboolean prefs_sound_themes_loading;
-
 /*
  * PROTOTYPES
  */
@@ -334,10 +340,21 @@
 	/* Unregister callbacks. */
 	purple_prefs_disconnect_by_handle(prefs);
 
-	prefs = NULL;
+	/* NULL-ify globals */
 	sound_entry = NULL;
-	debugbutton = NULL;
+	sound_row_sel = 0;
+	prefs_sound_themes_loading = FALSE;
+
+	prefs_sound_themes_combo_box = NULL;
+	prefs_blist_themes_combo_box = NULL;
+	prefs_status_themes_combo_box = NULL;
+	prefs_smiley_themes_combo_box = NULL;
+
+	sample_imhtml = NULL;
+
 	notebook_page = 0;
+	prefsnotebook = NULL;
+	prefs = NULL;
 }
 
 static gchar *
@@ -949,6 +966,7 @@
 		gtk_tree_model_get(GTK_TREE_MODEL(prefs_smiley_themes), &new_iter, 2, &new_theme, -1);
 
 		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme);
+		pidgin_themes_smiley_themeize(sample_imhtml);
 
 		g_free(new_theme);
 	}
@@ -1537,7 +1555,7 @@
 					 G_CALLBACK(formatting_toggle_cb), toolbar);
 	g_signal_connect_after(G_OBJECT(imhtml), "format_function_clear",
 					 G_CALLBACK(formatting_clear_cb), NULL);
-
+	sample_imhtml = imhtml;
 
 	gtk_widget_show(ret);
 
@@ -2842,7 +2860,7 @@
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
 
 	/* Smiley Callbacks */
-	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
+	purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
 								smiley_theme_pref_cb, NULL);
 
 	pidgin_prefs_update_old();
--- a/pidgin/pidgin-uninstalled.pc.in	Thu Dec 10 21:36:41 2009 +0000
+++ b/pidgin/pidgin-uninstalled.pc.in	Fri Jan 08 23:22:19 2010 +0000
@@ -6,8 +6,11 @@
 datadir=@datadir@
 sysconfdir=@sysconfdir@
 
+abs_srcdir=@abs_srcdir@
+abs_builddir=@abs_builddir@
+
 Name: Pidgin
 Description: Pidgin is a GTK2-based instant messenger application.
 Version: @VERSION@
 Requires: gtk+-2.0 purple
-Cflags: -I${pcfiledir}
+Cflags: -I${abs_srcdir}
--- a/po/ChangeLog	Thu Dec 10 21:36:41 2009 +0000
+++ b/po/ChangeLog	Fri Jan 08 23:22:19 2010 +0000
@@ -1,8 +1,11 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.6.6
+	* Norwegian Nynorsk win32 translation added (Yngve Spjeld Landro)
+	* Russian translation updated (Антон Самохвалов)
+
 version 2.6.5
-	* Norwegian Nynorsk win32 translation added (Yngve Spjeld Landro)
-	* Russian translation updated (Антон Самохвалов)
+	* No changes
 
 version 2.6.4
 	* Afrikaans translation updated (Friedel Wolff)
--- a/po/de.po	Thu Dec 10 21:36:41 2009 +0000
+++ b/po/de.po	Fri Jan 08 23:22:19 2010 +0000
@@ -2,7 +2,7 @@
 # Pidgin German translation
 # Copyright (C) 2001, Daniel Seifert <Pidgin-translation@dseifert.de>
 # Copyright (C) 2002, Karsten Weiss <knweiss@gmx.de>
-# Copyright (C) 2002-2009, Björn Voigt <bjoern@cs.tu-berlin.de>,
+# Copyright (C) 2002-2010, Björn Voigt <bjoern@cs.tu-berlin.de>,
 #                     Jochen Kemnade <jochenkemnade@web.de>
 #
 # This file is distributed under the same license as the Pidgin package.
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-11-30 11:50+0100\n"
-"PO-Revision-Date: 2009-11-30 11:45+0100\n"
+"POT-Creation-Date: 2010-01-08 16:32+0100\n"
+"PO-Revision-Date: 2010-01-08 16:30+0100\n"
 "Last-Translator: Björn Voigt <bjoern@cs.tu-berlin.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -1960,6 +1960,9 @@
 msgstr ""
 "%s ist keine reguläre Datei. Pidgin wird die Datei nicht überschreiben.\n"
 
+msgid "File is not readable."
+msgstr "Datei ist nicht lesbar."
+
 #, c-format
 msgid "%s wants to send you %s (%s)"
 msgstr "%s möchte Ihnen %s (%s) senden"
@@ -3827,6 +3830,13 @@
 "Der Server erfordert eine Klartext-Authentifizierung über einen "
 "unverschlüsselten Kanal"
 
+#. This should never happen!
+msgid "Invalid response from server"
+msgstr "Ungültige Serverantwort"
+
+msgid "Server does not use any supported authentication method"
+msgstr "Der Server benutzt keine der unterstützten Authentifizierungsmethoden"
+
 #, c-format
 msgid ""
 "%s requires plaintext authentication over an unencrypted connection.  Allow "
@@ -3839,15 +3849,6 @@
 msgid "Plaintext Authentication"
 msgstr "Klartext-Authentifizierung"
 
-msgid "SASL authentication failed"
-msgstr "SASL-Authentifizierung fehlgeschlagen"
-
-msgid "Invalid response from server"
-msgstr "Ungültige Serverantwort"
-
-msgid "Server does not use any supported authentication method"
-msgstr "Der Server benutzt keine der unterstützten Authentifizierungsmethoden"
-
 msgid "You require encryption, but it is not available on this server."
 msgstr ""
 "Sie fordern Verschlüsselung, aber diese ist auf dem Server nicht verfügbar."
@@ -3855,10 +3856,30 @@
 msgid "Invalid challenge from server"
 msgstr "Ungültige Challenge vom Server"
 
+msgid "Server thinks authentication is complete, but client does not"
+msgstr ""
+"Der Server ist der Meinung, dass die Authentifizierung vollständig ist, der "
+"Client aber nicht"
+
+msgid "SASL authentication failed"
+msgstr "SASL-Authentifizierung fehlgeschlagen"
+
 #, c-format
 msgid "SASL error: %s"
 msgstr "SASL-Fehler: %s"
 
+msgid "Unable to canonicalize username"
+msgstr "Benutzername konnte nicht in Normalform gebracht werden"
+
+msgid "Unable to canonicalize password"
+msgstr "Passwort konnte nicht in Normalform gebracht werden"
+
+msgid "Malicious challenge from server"
+msgstr "Bösartige Challenge vom Server"
+
+msgid "Unexpected response from server"
+msgstr "Unerwartete Antwort vom Server"
+
 msgid "The BOSH connection manager terminated your session."
 msgstr "Der BOSH-Verbindungsmanager hat Ihre Sitzung beendet."
 
@@ -3959,13 +3980,16 @@
 msgid "Resource"
 msgstr "Ressource"
 
+msgid "Uptime"
+msgstr "Betriebszeit"
+
+msgid "Logged Off"
+msgstr "Abgemeldet"
+
 #, c-format
 msgid "%s ago"
 msgstr "vor %s"
 
-msgid "Logged Off"
-msgstr "Abgemeldet"
-
 msgid "Middle Name"
 msgstr "Zweiter Name"
 
@@ -4158,13 +4182,6 @@
 msgid "Ping timed out"
 msgstr "Ping-Zeitüberschreitung"
 
-msgid ""
-"Unable to find alternative XMPP connection methods after failing to connect "
-"directly."
-msgstr ""
-"Nach dem Fehlschlagen einer direkten XMPP-Verbindung können keine "
-"alternativen Verbindungsmethoden gefunden werden."
-
 msgid "Invalid XMPP ID"
 msgstr "Ungültige XMPP-ID"
 
@@ -5140,6 +5157,10 @@
 msgid "Your new MSN friendly name is too long."
 msgstr "Ihr neuer MSN-Benutzername zu lang."
 
+#, c-format
+msgid "Set friendly name for %s."
+msgstr "Setze Spitznamen für %s."
+
 msgid "Set your friendly name."
 msgstr "Setze Ihren Spitznamen."
 
@@ -12075,9 +12096,6 @@
 msgid "Lao"
 msgstr "Laotisch"
 
-msgid "Lithuanian"
-msgstr "Litauisch"
-
 msgid "Macedonian"
 msgstr "Makedonisch"
 
@@ -12180,6 +12198,9 @@
 msgid "Amharic"
 msgstr "Amharisch"
 
+msgid "Lithuanian"
+msgstr "Litauisch"
+
 #, c-format
 msgid "About %s"
 msgstr "Über %s"
@@ -12229,7 +12250,12 @@
 "a>)<br/>Wir können nicht bei Problemen mit Drittanbieter-Protokollen oder "
 "Plugins helfen!<br/>Die Hauptsprache dieser Liste ist <b>Englisch</b>.  Sie "
 "können gern in einer anderen Sprache schreiben, aber die Antworten könnten "
-"weniger hilfreich sein.<br/><br/>"
+"weniger hilfreich sein.<br/>Deutschsprachige Benutzer können auch das Portal "
+"<a href=\"http://www.pidgin-im.de/\">Pidgin-IM.de</a> nutzen.  Dort finden "
+"Sie aktuelle Informationen zu Pidgin, können mit anderen Benutzern im <a "
+"href=\"http://forum.pidgin-im.de/\">Forum</a> diskutieren und Hilfe zu "
+"Problemen finden.  Beachten Sie, dass dieses Portal unabhängig vom "
+"offiziellen Pidgin-Projekt ist.<br/><br/>"
 
 #, c-format
 msgid ""