changeset 25011:ae544623840b

propagate from branch 'im.pidgin.pidgin' (head f018e11a7ea08e07f22667e6daca2ec7e64f9710) to branch 'org.darkrain42.pidgin.buddy-add' (head e4fae922272cfd510a1f55595c374c2b9f665767)
author Paul Aurich <paul@darkrain42.org>
date Fri, 02 Jan 2009 19:27:02 +0000
parents 6eecd6ba2862 (current diff) b26e8a41a937 (diff)
children 300cf4e16720
files libpurple/protocols/myspace/myspace.c libpurple/protocols/myspace/user.c libpurple/protocols/myspace/user.h pidgin/gtkblist.c
diffstat 45 files changed, 616 insertions(+), 283 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Mon Dec 22 19:40:05 2008 +0000
+++ b/AUTHORS	Fri Jan 02 19:27:02 2009 +0000
@@ -35,6 +35,7 @@
 
 Crazy Patch Writers:
 -------------------
+Paul Aurich
 Felipe 'shx' Contreras
 Marcus 'malu' Lundblad
 Dennis 'EvilDennisR' Ristuccia
--- a/COPYRIGHT	Mon Dec 22 19:40:05 2008 +0000
+++ b/COPYRIGHT	Fri Jan 02 19:27:02 2009 +0000
@@ -63,6 +63,7 @@
 Damien Carbery
 Michael Carlson
 Keegan Carruthers-Smith
+Ludovico Cavedon
 Steve Cavilia
 Julien Cegarra
 Cerulean Studios, LLC
@@ -228,6 +229,7 @@
 Steve Láposi
 Daniel Larsson
 Peter Lawler
+Vadim Lebedev
 Ho-seok Lee
 Jean-Yves Lefort
 Moses Lei
--- a/ChangeLog	Mon Dec 22 19:40:05 2008 +0000
+++ b/ChangeLog	Fri Jan 02 19:27:02 2009 +0000
@@ -6,6 +6,22 @@
 	  Rosinski)
 	* Don't ignore namespace information when parsing XMPP data. (Michal
 	  Witkowski)
+	* Fix a crash that occurred when retrieving certain Offline Messages
+	  on MSN.
+	* Extended purple-url-handler to handle "gtalk" URI's. (Paul Aurich)
+	* Fix the hang on exit in Network Location Awareness for Windows XP
+	  and Windows Vista. (Paul Aurich)
+
+	XMPP:
+	* Support for XEP-0191 blocking.  (Vijay Raghunathan)
+	* Don't put SASL PLAIN or IQ Auth passwords in debug logs. (Paul Aurich)
+
+	Pidgin:
+	* Fix a crash in the Add Account dialog when changing protocols under
+	  certain circumstances.
+
+	Finch:
+	* Redirect stderr outputs to the debug window.
 
 version 2.5.3 (12/20/2008):
 	libpurple:
@@ -127,7 +143,7 @@
 	  if no custom smilies have previously been added using the smiley
 	  manager.
 	* Improved support for some message formatting in conversations.
-	* Allow focusing the coversation history or userlist with F6.
+	* Allow focusing the conversation history or userlist with F6.
 	* Fixed the Send Button plugin to avoid duplicate buttons in a single
 	  conversation.
 	* Double-clicking a saved status will now activate it and close the
--- a/doc/pidgin.1.in	Mon Dec 22 19:40:05 2008 +0000
+++ b/doc/pidgin.1.in	Fri Jan 02 19:27:02 2009 +0000
@@ -641,6 +641,8 @@
 
 Our crazy patch writers include:
 .br
+  Paul Aurich
+.br
   Felipe 'shx' Contreras
 .br
   Marcus 'malu' Lundblad
--- a/finch/finch.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/finch/finch.c	Fri Jan 02 19:27:02 2009 +0000
@@ -229,7 +229,6 @@
 	gboolean opt_nologin = FALSE;
 	gboolean opt_version = FALSE;
 	char *opt_config_dir_arg = NULL;
-	char *opt_session_arg = NULL;
 	gboolean debug_enabled = FALSE;
 
 	struct option long_options[] = {
@@ -237,7 +236,6 @@
 		{"debug",    no_argument,       NULL, 'd'},
 		{"help",     no_argument,       NULL, 'h'},
 		{"nologin",  no_argument,       NULL, 'n'},
-		{"session",  required_argument, NULL, 's'},
 		{"version",  no_argument,       NULL, 'v'},
 		{0, 0, 0, 0}
 	};
@@ -256,7 +254,7 @@
 	opterr = 1;
 	while ((opt = getopt_long(argc, argv,
 #ifndef _WIN32
-				  "c:dhn::s:v",
+				  "c:dhn::v",
 #else
 				  "c:dhn::v",
 #endif
@@ -275,10 +273,6 @@
 		case 'n':	/* no autologin */
 			opt_nologin = TRUE;
 			break;
-		case 's':	/* use existing session ID */
-			g_free(opt_session_arg);
-			opt_session_arg = g_strdup(optarg);
-			break;
 		case 'v':	/* version */
 			opt_version = TRUE;
 			break;
--- a/finch/gntdebug.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/finch/gntdebug.c	Fri Jan 02 19:27:02 2009 +0000
@@ -43,6 +43,48 @@
 
 #define PREF_ROOT "/finch/debug"
 
+static gboolean
+handle_fprintf_stderr_cb(GIOChannel *source, GIOCondition cond, gpointer null)
+{
+	gssize size;
+	char message[1024];
+
+	size = read(g_io_channel_unix_get_fd(source), message, sizeof(message) - 1);
+	if (size <= 0) {
+		/* Something bad probably happened elsewhere ... let's ignore */
+	} else {
+		message[size] = '\0';
+		g_log("stderr", G_LOG_LEVEL_WARNING, "%s", message);
+	}
+
+	return TRUE;
+}
+
+static void
+handle_fprintf_stderr(gboolean stop)
+{
+	GIOChannel *stderrch;
+	static int readhandle = -1;
+	int pipes[2];
+
+	if (stop) {
+		if (readhandle >= 0) {
+			g_source_remove(readhandle);
+			readhandle = -1;
+		}
+		return;
+	}
+	pipe(pipes);
+	dup2(pipes[1], STDERR_FILENO);
+
+	stderrch = g_io_channel_unix_new(pipes[0]);
+	g_io_channel_set_close_on_unref(stderrch, TRUE);
+	readhandle = g_io_add_watch_full(stderrch, G_PRIORITY_HIGH,
+			G_IO_IN | G_IO_ERR | G_IO_PRI,
+			handle_fprintf_stderr_cb, NULL, NULL);
+	g_io_channel_unref(stderrch);
+}
+
 static struct
 {
 	GntWidget *window;
@@ -143,10 +185,6 @@
 }
 
 static void
-suppress_error_messages(const char *message)
-{}
-
-static void
 toggle_pause(GntWidget *w, gpointer n)
 {
 	debug.paused = !debug.paused;
@@ -348,10 +386,11 @@
 #ifdef USE_GSTREAMER
 	REGISTER_G_LOG_HANDLER("GStreamer");
 #endif
+	REGISTER_G_LOG_HANDLER("stderr");
 
 	g_set_print_handler(print_stderr);   /* Redirect the debug messages to stderr */
 	if (!purple_debug_is_enabled())
-		g_set_printerr_handler(suppress_error_messages);
+		handle_fprintf_stderr(FALSE);
 
 	purple_prefs_add_none(PREF_ROOT);
 	purple_prefs_add_string(PREF_ROOT "/filter", "");
@@ -365,5 +404,6 @@
 
 void finch_debug_uninit()
 {
+	handle_fprintf_stderr(TRUE);
 }
 
--- a/libpurple/blist.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/blist.c	Fri Jan 02 19:27:02 2009 +0000
@@ -1029,7 +1029,7 @@
 		return;
 
 	dest = purple_find_group(new_name);
-	if (dest != NULL) {
+	if (dest != NULL && purple_utf8_strcasecmp(source->name, dest->name) != 0) {
 		/* We're merging two groups */
 		PurpleBlistNode *prev, *child, *next;
 
--- a/libpurple/core.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/core.c	Fri Jan 02 19:27:02 2009 +0000
@@ -219,6 +219,7 @@
 	purple_proxy_uninit();
 	purple_dnsquery_uninit();
 	purple_imgstore_uninit();
+	purple_network_uninit();
 
 	purple_debug_info("main", "Unloading all plugins\n");
 	purple_plugins_destroy_all();
--- a/libpurple/network.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/network.c	Fri Jan 02 19:27:02 2009 +0000
@@ -71,6 +71,11 @@
 
 #elif defined _WIN32
 static int current_network_count;
+
+/* Mutex for the other global vars */
+static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+static gboolean network_initialized;
+static HANDLE network_change_handle;
 #endif
 
 struct _PurpleNetworkListenData {
@@ -535,8 +540,8 @@
 
 static gpointer wpurple_network_change_thread(gpointer data)
 {
-	HANDLE h;
 	WSAQUERYSET qs;
+	WSAEVENT *nla_event;
 	time_t last_trigger = time(NULL);
 
 	int (WSAAPI *MyWSANSPIoctl) (
@@ -549,23 +554,47 @@
 		return NULL;
 	}
 
+	if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) {
+		int errorid = WSAGetLastError();
+		gchar *msg = g_win32_error_message(errorid);
+		purple_debug_warning("network", "Couldn't create WSA event. "
+			"Message: %s (%d).\n", msg, errorid);
+		g_free(msg);
+		g_thread_exit(NULL);
+		return NULL;
+	}
+
 	while (TRUE) {
 		int retval;
 		DWORD retLen = 0;
+		WSACOMPLETION completion;
+		WSAOVERLAPPED overlapped;
+
+		g_static_mutex_lock(&mutex);
+		if (network_initialized == FALSE) {
+			/* purple_network_uninit has been called */
+			WSACloseEvent(nla_event);
+			g_static_mutex_unlock(&mutex);
+			g_thread_exit(NULL);
+			return NULL;
+		}
 
 		memset(&qs, 0, sizeof(WSAQUERYSET));
 		qs.dwSize = sizeof(WSAQUERYSET);
 		qs.dwNameSpace = NS_NLA;
-		if (WSALookupServiceBegin(&qs, 0, &h) == SOCKET_ERROR) {
+		if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) {
 			int errorid = WSAGetLastError();
 			gchar *msg = g_win32_error_message(errorid);
 			purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. "
 				"NLA service is probably not running. Message: %s (%d).\n",
 				msg, errorid);
 			g_free(msg);
+			WSACloseEvent(nla_event);
+			g_static_mutex_unlock(&mutex);
 			g_thread_exit(NULL);
 			return NULL;
 		}
+		g_static_mutex_unlock(&mutex);
 
 		/* Make sure at least 30 seconds have elapsed since the last
 		 * notification so we don't peg the cpu if this keeps changing. */
@@ -574,19 +603,40 @@
 
 		last_trigger = time(NULL);
 
-		/* This will block until there is a network change */
-		if (MyWSANSPIoctl(h, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, NULL) == SOCKET_ERROR) {
+		memset(&completion, 0, sizeof(WSACOMPLETION));
+		completion.Type = NSP_NOTIFY_EVENT;
+		overlapped.hEvent = nla_event;
+		completion.Parameters.Event.lpOverlapped = &overlapped;
+
+		if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) {
 			int errorid = WSAGetLastError();
-			gchar *msg = g_win32_error_message(errorid);
-			purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n",
-				msg, errorid);
-			g_free(msg);
+			/* WSA_IO_PENDING indicates successful async notification will happen */
+			if (errorid != WSA_IO_PENDING) {
+				gchar *msg = g_win32_error_message(errorid);
+				purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n",
+					msg, errorid);
+				g_free(msg);
+			}
 		}
 
-		retval = WSALookupServiceEnd(h);
+		/* This will block until NLA notifies us */
+		retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE);
+
+		g_static_mutex_lock(&mutex);
+		if (network_initialized == FALSE) {
+			/* Time to die */
+			WSACloseEvent(nla_event);
+			g_static_mutex_unlock(&mutex);
+			g_thread_exit(NULL);
+			return NULL;
+		}
+
+		retval = WSALookupServiceEnd(network_change_handle);
+		network_change_handle = NULL;
+		WSAResetEvent(nla_event);
+		g_static_mutex_unlock(&mutex);
 
 		purple_timeout_add(0, wpurple_network_change_thread_cb, NULL);
-
 	}
 
 	g_thread_exit(NULL);
@@ -714,6 +764,7 @@
 	GError *err = NULL;
 	gint cnt = wpurple_get_connected_network_count();
 
+	network_initialized = TRUE;
 	if (cnt < 0) /* Assume there is a network */
 		current_network_count = 1;
 	/* Don't listen for network changes if we can't tell anyway */
@@ -782,6 +833,25 @@
 		dbus_g_connection_unref(nm_conn);
 #endif
 
+#ifdef _WIN32
+	g_static_mutex_lock(&mutex);
+	network_initialized = FALSE;
+	if (network_change_handle != NULL) {
+		int retval;
+		/* Trigger the NLA thread to stop waiting for network changes. Not
+		 * doing this can cause hangs on WSACleanup. */
+		purple_debug_warning("network", "Terminating the NLA thread\n");
+		if ((retval = WSALookupServiceEnd(network_change_handle)) == SOCKET_ERROR) {
+			int errorid = WSAGetLastError();
+			gchar *msg = g_win32_error_message(errorid);
+			purple_debug_warning("network", "Unable to kill NLA thread. Message: %s (%d).\n",
+				msg, errorid);
+			g_free(msg);
+		}
+	}
+	g_static_mutex_unlock(&mutex);
+
+#endif
 	purple_signal_unregister(purple_network_get_handle(),
 	                         "network-configuration-changed");
 }
--- a/libpurple/prefs.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/prefs.c	Fri Jan 02 19:27:02 2009 +0000
@@ -693,12 +693,15 @@
 	char *name;
 	GSList *l;
 
-	if(!pref || pref == &prefs)
+	if(!pref)
 		return;
 
 	while(pref->first_child)
 		remove_pref(pref->first_child);
 
+	if(pref == &prefs)
+		return;
+
 	if(pref->parent->first_child == pref) {
 		pref->parent->first_child = pref->sibling;
 	} else {
@@ -1452,4 +1455,8 @@
 	}
 
 	purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
+	purple_prefs_destroy();
+	g_hash_table_destroy(prefs_hash);
+	prefs_hash = NULL;
+
 }
--- a/libpurple/protocols/bonjour/mdns_win32.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Fri Jan 02 19:27:02 2009 +0000
@@ -167,7 +167,11 @@
 	ResolveCallbackArgs *args = (ResolveCallbackArgs*) data;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 	gboolean delete_buddy = FALSE;
-	PurpleBuddy *pb;
+	PurpleBuddy *pb = NULL;
+
+	/* Make sure that the BonjourBuddy associated with this request is still around */
+	if (g_slist_find(pending_buddies, args->bb) == NULL)
+		goto cleanup;
 
 	if ((pb = purple_find_buddy(args->account, args->bb->name)))
 		if (pb->proto_data != args->bb)
@@ -209,6 +213,8 @@
 
 	}
 
+	cleanup:
+
 	/* free the hosts list*/
 	while (hosts != NULL) {
 		hosts = g_slist_remove(hosts, hosts->data);
--- a/libpurple/protocols/gg/buddylist.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/gg/buddylist.c	Fri Jan 02 19:27:02 2009 +0000
@@ -216,13 +216,14 @@
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleGroup *group;
 	PurpleBuddy *buddy;
-
-	char *buddylist = g_strdup("");
+	GString *buddylist;
 	char *ptr;
 
 	if ((blist = purple_get_blist()) == NULL)
 		return NULL;
 
+	buddylist = g_string_sized_new(1024);
+
 	for (gnode = blist->root; gnode != NULL; gnode = gnode->next) {
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
@@ -247,19 +248,16 @@
 				alias = buddy->alias ? buddy->alias : buddy->name;
 				gname = group->name;
 
-				ptr = buddylist;
-				buddylist = g_strdup_printf(
-						"%s%s;%s;%s;%s;%s;%s;%s;%s%s\r\n",
-						ptr, alias, alias, alias, alias,
+				g_string_append_printf(buddylist,
+						"%s;%s;%s;%s;%s;%s;%s;%s%s\r\n",
+						alias, alias, alias, alias,
 						"", gname, name, "", "");
-
-				g_free(ptr);
 			}
 		}
 	}
 
-	ptr = charset_convert(buddylist, "UTF-8", "CP1250");
-	g_free(buddylist);
+	ptr = charset_convert(buddylist->str, "UTF-8", "CP1250");
+	g_string_free(buddylist, TRUE);
 	return ptr;
 }
 /* }}} */
--- a/libpurple/protocols/jabber/disco.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/jabber/disco.c	Fri Jan 02 19:27:02 2009 +0000
@@ -355,6 +355,11 @@
 		jabber_adhoc_server_get_list(js);
 	}
 
+	/* If the server supports blocking, request the block list */
+	if (js->server_caps & JABBER_CAP_BLOCKING) {
+		jabber_request_block_list(js);
+	}
+
 	/* If there are manually specified bytestream proxies, query them */
 	ft_proxies = purple_account_get_string(js->gc->account, "ft_proxies", NULL);
 	if (ft_proxies) {
@@ -454,6 +459,8 @@
 			jabber_google_roster_init(js);
 		} else if (!strcmp("http://jabber.org/protocol/commands", var)) {
 			js->server_caps |= JABBER_CAP_ADHOC;
+		} else if (!strcmp("urn:xmpp:blocking", var)) {
+			js->server_caps |= JABBER_CAP_BLOCKING;
 		}
 	}
 
--- a/libpurple/protocols/jabber/jabber.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Fri Jan 02 19:27:02 2009 +0000
@@ -31,6 +31,7 @@
 #include "message.h"
 #include "notify.h"
 #include "pluginpref.h"
+#include "privacy.h"
 #include "proxy.h"
 #include "prpl.h"
 #include "request.h"
@@ -150,7 +151,7 @@
 	char hostname[256]; /* current hostname */
 
 	/* Empty resource == don't send any */
-	if (*input == '\0')
+	if (input == NULL || *input == '\0')
 		return NULL;
 
 	if (strstr(input, "__HOSTNAME__") == NULL)
@@ -350,9 +351,33 @@
 {
 
 	/* because printing a tab to debug every minute gets old */
-	if(strcmp(data, "\t"))
-		purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s\n",
-				js->gsc ? " (ssl)" : "", data);
+	if(strcmp(data, "\t")) {
+		char *text = NULL, *last_part = NULL, *tag_start = NULL;
+
+		/* Because debug logs with plaintext passwords make me sad */
+		if(js->state != JABBER_STREAM_CONNECTED &&
+				/* Either <auth> or <query><password>... */
+				(((tag_start = strstr(data, "<auth ")) &&
+					strstr(data, "xmlns='urn:ietf:params:xml:ns:xmpp-sasl'")) ||
+				((tag_start = strstr(data, "<query ")) &&
+					strstr(data, "xmlns='jabber:iq:auth'>") &&
+					(tag_start = strstr(tag_start, "<password>"))))) {
+			char *data_start, *tag_end = strchr(tag_start, '>');
+			text = g_strdup(data);
+
+			data_start = text + (tag_end - data) + 1;
+
+			last_part = strchr(data_start, '<');
+			*data_start = '\0';
+		}
+
+		purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s%s%s\n",
+				js->gsc ? " (ssl)" : "", text ? text : data,
+				last_part ? "password removed" : "",
+				last_part ? last_part : "");
+
+		g_free(text);
+	}
 
 	/* If we've got a security layer, we need to encode the data,
 	 * splitting it on the maximum buffer length negotiated */
@@ -1454,6 +1479,106 @@
 	js->idle = idle ? time(NULL) - idle : idle;
 }
 
+static void jabber_blocklist_parse(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	xmlnode *blocklist, *item;
+	PurpleAccount *account;
+
+	blocklist = xmlnode_get_child_with_namespace(packet,
+			"blocklist", "urn:xmpp:blocking");
+	account = purple_connection_get_account(js->gc);
+
+	if (blocklist == NULL)
+		return;
+
+	item = xmlnode_get_child(blocklist, "item");
+	while (item != NULL) {
+		const char *jid = xmlnode_get_attrib(item, "jid");
+
+		purple_privacy_deny_add(account, jid, TRUE);
+		item = xmlnode_get_next_twin(item);
+	}
+}
+
+void jabber_request_block_list(JabberStream *js)
+{
+	JabberIq *iq;
+	xmlnode *blocklist;
+
+	iq = jabber_iq_new(js, JABBER_IQ_GET);
+
+	blocklist = xmlnode_new_child(iq->node, "blocklist");
+	xmlnode_set_namespace(blocklist, "urn:xmpp:blocking");
+
+	jabber_iq_set_callback(iq, jabber_blocklist_parse, NULL);
+
+	jabber_iq_send(iq);
+}
+
+void jabber_add_deny(PurpleConnection *gc, const char *who)
+{
+	JabberStream *js;
+	JabberIq *iq;
+	xmlnode *block, *item;
+
+	js = gc->proto_data;
+	if (js == NULL)
+		return;
+
+	if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
+	{
+		jabber_google_roster_add_deny(gc, who);
+		return;
+	}
+
+	if (!(js->server_caps & JABBER_CAP_BLOCKING))
+	{
+		purple_notify_error(NULL, _("Server doesn't support blocking"),
+							_("Server doesn't support blocking"), NULL);
+		return;
+	}
+
+	iq = jabber_iq_new(js, JABBER_IQ_SET);
+
+	block = xmlnode_new_child(iq->node, "block");
+	xmlnode_set_namespace(block, "urn:xmpp:blocking");
+
+	item = xmlnode_new_child(block, "item");
+	xmlnode_set_attrib(item, "jid", who);
+
+	jabber_iq_send(iq);
+}
+
+void jabber_rem_deny(PurpleConnection *gc, const char *who)
+{
+	JabberStream *js;
+	JabberIq *iq;
+	xmlnode *unblock, *item;
+
+	js = gc->proto_data;
+	if (js == NULL)
+		return;
+
+	if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
+	{
+		jabber_google_roster_rem_deny(gc, who);
+		return;
+	}
+
+	if (!(js->server_caps & JABBER_CAP_BLOCKING))
+		return;
+
+	iq = jabber_iq_new(js, JABBER_IQ_SET);
+
+	unblock = xmlnode_new_child(iq->node, "unblock");
+	xmlnode_set_namespace(unblock, "urn:xmpp:blocking");
+
+	item = xmlnode_new_child(unblock, "item");
+	xmlnode_set_attrib(item, "jid", who);
+
+	jabber_iq_send(iq);
+}
+
 void jabber_add_feature(const char *shortname, const char *namespace, JabberFeatureEnabled cb) {
 	JabberFeature *feat;
 
--- a/libpurple/protocols/jabber/jabber.h	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Fri Jan 02 19:27:02 2009 +0000
@@ -42,7 +42,8 @@
 
 	JABBER_CAP_PING			  = 1 << 11,
 	JABBER_CAP_ADHOC		  = 1 << 12,
-	
+	JABBER_CAP_BLOCKING       = 1 << 13,
+
 	JABBER_CAP_RETRIEVED      = 1 << 31
 } JabberCapabilities;
 
@@ -294,6 +295,9 @@
 void jabber_login(PurpleAccount *account);
 void jabber_close(PurpleConnection *gc);
 void jabber_idle_set(PurpleConnection *gc, int idle);
+void jabber_request_block_list(JabberStream *js);
+void jabber_add_deny(PurpleConnection *gc, const char *who);
+void jabber_rem_deny(PurpleConnection *gc, const char *who);
 void jabber_keepalive(PurpleConnection *gc);
 void jabber_register_gateway(JabberStream *js, const char *gateway);
 void jabber_register_account(PurpleAccount *account);
--- a/libpurple/protocols/jabber/libxmpp.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Fri Jan 02 19:27:02 2009 +0000
@@ -77,9 +77,9 @@
 	jabber_roster_remove_buddy,		/* remove_buddy */
 	NULL,							/* remove_buddies */
 	NULL,							/* add_permit */
-	jabber_google_roster_add_deny,				/* add_deny */
+	jabber_add_deny,				/* add_deny */
 	NULL,							/* rem_permit */
-	jabber_google_roster_rem_deny,				/* rem_deny */
+	jabber_rem_deny,				/* rem_deny */
 	NULL,							/* set_permit_deny */
 	jabber_chat_join,				/* join_chat */
 	NULL,							/* reject_chat */
--- a/libpurple/protocols/jabber/message.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/jabber/message.c	Fri Jan 02 19:27:02 2009 +0000
@@ -291,27 +291,28 @@
 	PurpleAccount *account;
 	PurpleConversation *c;
 	char *username, *str;
-	
+
 	/* Delayed buzz MUST NOT be accepted */
 	if(jm->delayed)
 		return;
-	
+
 	/* Reject buzz when it's not enabled */
 	if(!jm->js->allowBuzz)
 		return;
-	
+
 	account = purple_connection_get_account(jm->js->gc);
-	
-	if ((buddy = purple_find_buddy(account, jm->from)) != NULL)
-		username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
-	else
+
+	if ((buddy = purple_find_buddy(account, jm->from)) == NULL)
 		return; /* Do not accept buzzes from unknown people */
 
-	c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, jm->from);
+	c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account);
+	if (c == NULL)
+		c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, jm->from);
 
-	str = g_strdup_printf(_("%s has buzzed you!"), username);
-	
-	purple_conversation_write(c, NULL, str, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
+	username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
+	/* xmpp only has 1 attention type, so index is 0 */
+	purple_prpl_got_attention(jm->js->gc, username, 0);
+
 	g_free(username);
 	g_free(str);
 }
--- a/libpurple/protocols/msn/error.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/msn/error.c	Fri Jan 02 19:27:02 2009 +0000
@@ -27,191 +27,178 @@
 const char *
 msn_error_get_text(unsigned int type, gboolean *debug)
 {
-	static char msg[MSN_BUF_LEN];
+	static char msg[256];
+	const char *result;
 	*debug = FALSE;
 
 	switch (type) {
 		case 0:
-			g_snprintf(msg, sizeof(msg),
-			           _("Unable to parse message"));
+			result = _("Unable to parse message");
 			*debug = TRUE;
 			break;
 		case 200:
-			g_snprintf(msg, sizeof(msg),
-			           _("Syntax Error (probably a client bug)"));
+			result = _("Syntax Error (probably a client bug)");
 			*debug = TRUE;
 			break;
 		case 201:
-			g_snprintf(msg, sizeof(msg),
-			           _("Invalid email address"));
+			result = _("Invalid email address");
 			break;
 		case 205:
-			g_snprintf(msg, sizeof(msg), _("User does not exist"));
+			result = _("User does not exist");
 			break;
 		case 206:
-			g_snprintf(msg, sizeof(msg),
-			           _("Fully qualified domain name missing"));
+			result = _("Fully qualified domain name missing");
 			break;
 		case 207:
-			g_snprintf(msg, sizeof(msg), _("Already logged in"));
+			result = _("Already logged in");
 			break;
 		case 208:
-			g_snprintf(msg, sizeof(msg), _("Invalid username"));
+			result = _("Invalid username");
 			break;
 		case 209:
-			g_snprintf(msg, sizeof(msg), _("Invalid friendly name"));
+			result = _("Invalid friendly name");
 			break;
 		case 210:
-			g_snprintf(msg, sizeof(msg), _("List full"));
+			result = _("List full");
 			break;
 		case 215:
-			g_snprintf(msg, sizeof(msg), _("Already there"));
+			result = _("Already there");
 			*debug = TRUE;
 			break;
 		case 216:
-			g_snprintf(msg, sizeof(msg), _("Not on list"));
+			result = _("Not on list");
 			break;
 		case 217:
-			g_snprintf(msg, sizeof(msg), _("User is offline"));
+			result = _("User is offline");
 			break;
 		case 218:
-			g_snprintf(msg, sizeof(msg), _("Already in the mode"));
+			result = _("Already in the mode");
 			*debug = TRUE;
 			break;
 		case 219:
-			g_snprintf(msg, sizeof(msg), _("Already in opposite list"));
+			result = _("Already in opposite list");
 			*debug = TRUE;
 			break;
 		case 223:
-			g_snprintf(msg, sizeof(msg), _("Too many groups"));
+			result = _("Too many groups");
 			break;
 		case 224:
-			g_snprintf(msg, sizeof(msg), _("Invalid group"));
+			result = _("Invalid group");
 			break;
 		case 225:
-			g_snprintf(msg, sizeof(msg), _("User not in group"));
+			result = _("User not in group");
 			break;
 		case 229:
-			g_snprintf(msg, sizeof(msg), _("Group name too long"));
+			result = _("Group name too long");
 			break;
 		case 230:
-			g_snprintf(msg, sizeof(msg), _("Cannot remove group zero"));
+			result = _("Cannot remove group zero");
 			*debug = TRUE;
 			break;
 		case 231:
-			g_snprintf(msg, sizeof(msg),
-			           _("Tried to add a user to a group "
-			             "that doesn't exist"));
+			result = _("Tried to add a user to a group that doesn't exist");
 			break;
 		case 280:
-			g_snprintf(msg, sizeof(msg), _("Switchboard failed"));
+			result = _("Switchboard failed");
 			*debug = TRUE;
 			break;
 		case 281:
-			g_snprintf(msg, sizeof(msg), _("Notify transfer failed"));
+			result = _("Notify transfer failed");
 			*debug = TRUE;
 			break;
 
 		case 300:
-			g_snprintf(msg, sizeof(msg), _("Required fields missing"));
+			result = _("Required fields missing");
 			*debug = TRUE;
 			break;
 		case 301:
-			g_snprintf(msg, sizeof(msg), _("Too many hits to a FND"));
+			result = _("Too many hits to a FND");
 			*debug = TRUE;
 			break;
 		case 302:
-			g_snprintf(msg, sizeof(msg), _("Not logged in"));
+			result = _("Not logged in");
 			break;
 
 		case 500:
-			g_snprintf(msg, sizeof(msg),
-			           _("Service temporarily unavailable"));
+			result = _("Service temporarily unavailable");
 			break;
 		case 501:
-			g_snprintf(msg, sizeof(msg), _("Database server error"));
+			result = _("Database server error");
 			*debug = TRUE;
 			break;
 		case 502:
-			g_snprintf(msg, sizeof(msg), _("Command disabled"));
+			result = _("Command disabled");
 			*debug = TRUE;
 			break;
 		case 510:
-			g_snprintf(msg, sizeof(msg), _("File operation error"));
+			result = _("File operation error");
 			*debug = TRUE;
 			break;
 		case 520:
-			g_snprintf(msg, sizeof(msg), _("Memory allocation error"));
+			result = _("Memory allocation error");
 			*debug = TRUE;
 			break;
 		case 540:
-			g_snprintf(msg, sizeof(msg),
-			           _("Wrong CHL value sent to server"));
+			result = _("Wrong CHL value sent to server");
 			*debug = TRUE;
 			break;
 
 		case 600:
-			g_snprintf(msg, sizeof(msg), _("Server busy"));
+			result = _("Server busy");
 			break;
 		case 601:
-			g_snprintf(msg, sizeof(msg), _("Server unavailable"));
+			result = _("Server unavailable");
 			break;
 		case 602:
-			g_snprintf(msg, sizeof(msg),
-			           _("Peer notification server down"));
+			result = _("Peer notification server down");
 			*debug = TRUE;
 			break;
 		case 603:
-			g_snprintf(msg, sizeof(msg), _("Database connect error"));
+			result = _("Database connect error");
 			*debug = TRUE;
 			break;
 		case 604:
-			g_snprintf(msg, sizeof(msg),
-					   _("Server is going down (abandon ship)"));
+			result = _("Server is going down (abandon ship)");
 			break;
 		case 605:
-			g_snprintf(msg, sizeof(msg), _("Server unavailable"));
+			result = _("Server unavailable");
 			break;
 
 		case 707:
-			g_snprintf(msg, sizeof(msg),
-			           _("Error creating connection"));
+			result = _("Error creating connection");
 			*debug = TRUE;
 			break;
 		case 710:
-			g_snprintf(msg, sizeof(msg),
-			           _("CVR parameters are either unknown "
-			             "or not allowed"));
+			result = _("CVR parameters are either unknown or not allowed");
 			*debug = TRUE;
 			break;
 		case 711:
-			g_snprintf(msg, sizeof(msg), _("Unable to write"));
+			result = _("Unable to write");
 			break;
 		case 712:
-			g_snprintf(msg, sizeof(msg), _("Session overload"));
+			result = _("Session overload");
 			*debug = TRUE;
 			break;
 		case 713:
-			g_snprintf(msg, sizeof(msg), _("User is too active"));
+			result = _("User is too active");
 			break;
 		case 714:
-			g_snprintf(msg, sizeof(msg), _("Too many sessions"));
+			result = _("Too many sessions");
 			break;
 		case 715:
-			g_snprintf(msg, sizeof(msg), _("Passport not verified"));
+			result = _("Passport not verified");
 			break;
 		case 717:
-			g_snprintf(msg, sizeof(msg), _("Bad friend file"));
+			result = _("Bad friend file");
 			*debug = TRUE;
 			break;
 		case 731:
-			g_snprintf(msg, sizeof(msg), _("Not expected"));
+			result = _("Not expected");
 			*debug = TRUE;
 			break;
 
 		case 800:
-			g_snprintf(msg, sizeof(msg),
-			           _("Friendly name changes too rapidly"));
+			result = _("Friendly name changes too rapidly");
 			break;
 
 		case 910:
@@ -220,37 +207,34 @@
 		case 919:
 		case 921:
 		case 922:
-			g_snprintf(msg, sizeof(msg), _("Server too busy"));
+			result = _("Server too busy");
 			break;
 		case 911:
 		case 917:
-			g_snprintf(msg, sizeof(msg), _("Authentication failed"));
+			result = _("Authentication failed");
 			break;
 		case 913:
-			g_snprintf(msg, sizeof(msg), _("Not allowed when offline"));
+			result = _("Not allowed when offline");
 			break;
 		case 914:
 		case 915:
 		case 916:
-			g_snprintf(msg, sizeof(msg), _("Server unavailable"));
+			result = _("Server unavailable");
 			break;
 		case 920:
-			g_snprintf(msg, sizeof(msg), _("Not accepting new users"));
+			result = _("Not accepting new users");
 			break;
 		case 923:
-			g_snprintf(msg, sizeof(msg),
-			           _("Kids Passport without parental consent"));
+			result = _("Kids Passport without parental consent");
 			break;
 		case 924:
-			g_snprintf(msg, sizeof(msg),
-			           _("Passport account not yet verified"));
+			result = _("Passport account not yet verified");
 			break;
 		case 927:
-			g_snprintf(msg, sizeof(msg),
-			           _("Passport account suspended"));
+			result = _("Passport account suspended");
 			break;
 		case 928:
-			g_snprintf(msg, sizeof(msg), _("Bad ticket"));
+			result = _("Bad ticket");
 			*debug = TRUE;
 			break;
 
@@ -258,23 +242,25 @@
 			g_snprintf(msg, sizeof(msg),
 			           _("Unknown Error Code %d"), type);
 			*debug = TRUE;
+			result = msg;
 			break;
 	}
 
-	return msg;
+	return result;
 }
 
 void
 msn_error_handle(MsnSession *session, unsigned int type)
 {
-	char buf[MSN_BUF_LEN];
+	char *buf;
 	gboolean debug;
 
-	g_snprintf(buf, sizeof(buf), _("MSN Error: %s\n"),
-	           msn_error_get_text(type, &debug));
+	buf = g_strdup_printf(_("MSN Error: %s\n"),
+	                      msn_error_get_text(type, &debug));
 	if (debug)
 		purple_debug_warning("msn", "error %d: %s\n", type, buf);
 	else
 		purple_notify_error(session->account->gc, NULL, buf, NULL);
+	g_free(buf);
 }
 
--- a/libpurple/protocols/msn/oim.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/msn/oim.c	Fri Jan 02 19:27:02 2009 +0000
@@ -596,6 +596,7 @@
 	MsnMessage *message;
 	const char *date;
 	const char *from;
+	const char *boundary;
 	char *decode_msg = NULL;
 	gsize body_len;
 	char **tokens;
@@ -608,27 +609,14 @@
 	                          MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
 	purple_debug_info("msn", "oim body:{%s}\n", message->body);
 
-	if (!strcmp(msn_message_get_attr(message, "X-OIMProxy"), "MOSMS")) {
-		char *boundary;
+	boundary = msn_message_get_attr(message, "boundary");
+
+	if (boundary != NULL) {
+		char *bounds;
 		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);
+		bounds = g_strdup_printf("--%s" MSG_OIM_LINE_DEM, boundary);
+		tokens = g_strsplit(message->body, bounds, 0);
 
 		/* tokens+1 to skip the "This is a multipart message..." text */
 		for (part = tokens+1; *part != NULL; part++) {
@@ -648,18 +636,33 @@
 		}
 
 		g_strfreev(tokens);
-		g_free(boundary);
+		g_free(bounds);
 
 		if (decode_msg == NULL) {
 			purple_debug_error("msn", "Couldn't find text/plain OIM message.\n");
-			g_free(passport);
+			msn_message_destroy(message);
 			return;
 		}
 	} else {
+		decode_msg = (char *)purple_base64_decode(message->body, &body_len);
+	}
+
+	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) {
 		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)
@@ -687,6 +690,7 @@
 
 	g_free(passport);
 	g_free(decode_msg);
+	msn_message_destroy(message);
 }
 
 /* Parse the XML data,
--- a/libpurple/protocols/myspace/myspace.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Fri Jan 02 19:27:02 2009 +0000
@@ -211,7 +211,6 @@
 			/* Don't have uid offhand - need to ask for it, and wait until hear back before sending. */
 			purple_debug_info("msim", ">>> msim_postprocess_outgoing: couldn't find username %s in blist\n",
 					username ? username : "(NULL)");
-			/* TODO: where is cloned message freed? Should be in _cb. */
 			msim_lookup_user(session, username, msim_postprocess_outgoing_cb, msim_msg_clone(msg));
 			return TRUE;       /* not sure of status yet - haven't sent! */
 		}
@@ -1923,8 +1922,7 @@
 
 	msim_process(session, msg);
 
-	/* TODO: Free copy cloned from  msim_preprocess_incoming(). */
-	/* msim_msg_free(msg); */
+	msim_msg_free(msg);
 	msim_msg_free(body);
 }
 
@@ -2735,9 +2733,15 @@
 	 * doesn't seem like it would be necessary, but the official client
 	 * does it)
 	 */
-	if (!msim_update_blocklist_for_buddy(session, buddy->name, FALSE, FALSE))
+	if (!msim_update_blocklist_for_buddy(session, buddy->name, FALSE, FALSE)) {
 		purple_notify_error(NULL, NULL,
 				_("Failed to remove buddy"), _("blocklist command failed"));
+		return;
+	}
+	if (buddy->proto_data) {
+		msim_user_free(buddy->proto_data);
+		buddy->proto_data = NULL;
+	}
 }
 
 /**
@@ -2827,6 +2831,13 @@
 	msim_update_blocklist_for_buddy(session, name, FALSE, FALSE);
 }
 
+static void
+msim_buddy_free(PurpleBuddy *buddy)
+{
+	msim_user_free(buddy->proto_data);
+	buddy->proto_data = NULL;
+}
+
 /**
  * Returns a string of a username in canonical form. Basically removes all the
  * spaces, lowercases the string, and looks up user IDs to usernames.
@@ -3043,7 +3054,7 @@
 	NULL,              /* alias_buddy */
 	NULL,              /* group_buddy */
 	NULL,              /* rename_group */
-	NULL,              /* buddy_free */
+	msim_buddy_free,   /* buddy_free */
 	NULL,              /* convo_closed */
 	msim_normalize,    /* normalize */
 	NULL,              /* set_buddy_icon */
--- a/libpurple/protocols/myspace/session.h	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/myspace/session.h	Fri Jan 02 19:27:02 2009 +0000
@@ -35,7 +35,7 @@
 	guint userid;                       /**< This user's numeric user ID */
 	gchar *username;                    /**< This user's unique username */
 	gboolean show_only_to_list;
-	int privacy_mode;
+	int privacy_mode;                   /**< This is a bitmask */
 	int offline_message_mode;
 	gint fd;                            /**< File descriptor to/from server */
 
--- a/libpurple/protocols/myspace/user.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Fri Jan 02 19:27:02 2009 +0000
@@ -55,10 +55,9 @@
 	if (create && !buddy->proto_data) {
 		/* No MsimUser for this buddy; make one. */
 
-		/* TODO: where is this freed? */
 		user = g_new0(MsimUser, 1);
 		user->buddy = buddy;
-		user->id = purple_blist_node_get_int(&buddy->node, "UserID");
+		user->id = purple_blist_node_get_int((PurpleBlistNode*)buddy, "UserID");
 		buddy->proto_data = (gpointer)user;
 	} else {
 		user = (MsimUser *)(buddy->proto_data);
@@ -67,6 +66,23 @@
 	return user;
 }
 
+void msim_user_free(MsimUser *user)
+{
+	if (!user)
+		return;
+
+	g_free(user->client_info);
+	g_free(user->gender);
+	g_free(user->location);
+	g_free(user->headline);
+	g_free(user->display_name);
+	g_free(user->username);
+	g_free(user->band_name);
+	g_free(user->song_name);
+	g_free(user->image_url);
+	g_free(user);
+}
+
 /**
  * Find and return an MsimUser * representing a user on the buddy list, or NULL.
  */
@@ -406,10 +422,11 @@
 	}
 
 	if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN &&
-		msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) {
+		msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID)
+	{
 		/*
 		 * Some of this info will be available on the buddy list if the
-		 * has themselves as their own buddy.
+		 * user has themselves as their own buddy.
 		 *
 		 * Much of the info is already available in MsimSession,
 		 * stored in msim_we_are_logged_on().
@@ -431,7 +448,7 @@
 				"idlist", MSIM_TYPE_STRING,
 						g_strdup_printf("w%d|c%d",
 								session->show_only_to_list ? 1 : 0,
-								session->privacy_mode),
+								session->privacy_mode & 1),
 				NULL);
 	} else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN &&
 			msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) {
--- a/libpurple/protocols/myspace/user.h	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/myspace/user.h	Fri Jan 02 19:27:02 2009 +0000
@@ -46,7 +46,8 @@
  * initiated from a user lookup. */
 typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, const MsimMessage *userinfo, gpointer data);
 
-MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy, gboolean create);
+MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy);
+void msim_user_free(MsimUser *user);
 MsimUser *msim_find_user(MsimSession *session, const gchar *username);
 void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full);
 gboolean msim_store_user_info(MsimSession *session, const MsimMessage *msg, MsimUser *user);
--- a/libpurple/protocols/oscar/odc.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/oscar/odc.c	Fri Jan 02 19:27:02 2009 +0000
@@ -567,6 +567,7 @@
 	{
 		purple_debug_info("oscar", "Unknown ODC frame type 0x%04hx, "
 				"subtype 0x%04hx.\n", frame->type, frame->subtype);
+		g_free(frame);
 		return;
 	}
 
@@ -607,6 +608,7 @@
 			g_free(tmp);
 
 			peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
+			g_free(frame);
 			return;
 		}
 
--- a/libpurple/protocols/qq/AUTHORS	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/qq/AUTHORS	Fri Jan 02 19:27:02 2009 +0000
@@ -1,7 +1,7 @@
 Code Contributors
 =========
 puzzlebird	: original author
-gfhuang		: patches for libpurple 2.0.0beta2, maintainer
+gfhuang(poppyer): patches for libpurple 2.0.0beta2, maintainer
 Yuan Qingyun	: patches for libpurple 1.5.0, maintainer
 henryouly	: file transfer, udp sock5 proxy and qq_show, maintainer
 hzhr		: maintainer
--- a/libpurple/protocols/qq/ChangeLog	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Fri Jan 02 19:27:02 2009 +0000
@@ -1,3 +1,10 @@
+2008.12.25 - flos <lonicerae(at)gmail.com>
+	* References #7821
+	* Updated authors
+
+2008.12.17 - flos <lonicerae(at)gmail.com>
+	* Minor fixed in buddy_info.c
+
 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
--- a/libpurple/protocols/qq/buddy_info.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Fri Jan 02 19:27:02 2009 +0000
@@ -790,7 +790,7 @@
 		bytes += qq_get32(&onlineTime, data + bytes);
 		bytes += qq_get16(&level, data + bytes);
 		bytes += qq_get16(&timeRemainder, data + bytes);
-		purple_debug_info("QQ", "level: %d, uid %d, tmOnline: %d, tmRemainder: %d\n",
+		purple_debug_info("QQ", "level: %d, uid %u, tmOnline: %d, tmRemainder: %d\n",
 				level, uid, onlineTime, timeRemainder);
 
 		bd = qq_buddy_data_find(gc, uid);
@@ -825,12 +825,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, uid %d, tmOnline: %d, tmRemainder: %d\n",
+	purple_debug_info("QQ", "level: %d, uid %u, 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);
 		return;
 	}
 
--- a/libpurple/protocols/qq/group_internal.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Fri Jan 02 19:27:02 2009 +0000
@@ -30,7 +30,7 @@
 #include "group_internal.h"
 #include "utils.h"
 
-static qq_room_data *room_data_new(guint32 id, guint32 ext_id, gchar *title)
+static qq_room_data *room_data_new(guint32 id, guint32 ext_id, const gchar *title)
 {
 	qq_room_data *rmd;
 
@@ -62,9 +62,9 @@
 
 	value = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
 	id = value ? strtoul(value, NULL, 10) : 0;
-	value= g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
+	value = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
 	ext_id = value ? strtoul(value, NULL, 10) : 0;
-	value = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8));
+	value = g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8);
 
 	rmd = room_data_new(id, ext_id, value);
 	rmd->my_role = QQ_ROOM_ROLE_YES;
--- a/libpurple/protocols/qq/group_opt.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Fri Jan 02 19:27:02 2009 +0000
@@ -416,11 +416,6 @@
 
 	bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
 
-	add_req = g_new0(qq_room_req, 1);
-	add_req->gc = gc;
-	add_req->id = id;
-	add_req->member = member_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);
@@ -442,6 +437,11 @@
 	who = uid_to_purple_name(member_id);
 	msg = g_strdup_printf(_("%u request to join Qun %u"), member_id, ext_id);
 
+	add_req = g_new0(qq_room_req, 1);
+	add_req->gc = gc;
+	add_req->id = id;
+	add_req->member = member_id;
+
 	purple_request_action(gc, _("QQ Qun Operation"),
 			msg, reason,
 			PURPLE_DEFAULT_ACTION_NONE,
--- a/libpurple/protocols/qq/qq.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Fri Jan 02 19:27:02 2009 +0000
@@ -626,7 +626,7 @@
 	g_string_append(info, "puzzlebird<br>\n");
 	g_string_append(info, "<br>\n");
 	g_string_append(info, _("<p><b>Code Contributors</b>:<br>\n"));
-	g_string_append(info, "gfhuang : patches for libpurple 2.0.0beta2, maintainer<br>\n");
+	g_string_append(info, "gfhuang(poppyer) : patches for libpurple 2.0.0beta2, maintainer<br>\n");
 	g_string_append(info, "Yuan Qingyun : patches for libpurple 1.5.0, maintainer<br>\n");
 	g_string_append(info, "henryouly : file transfer, udp sock5 proxy and qq_show, maintainer<br>\n");
 	g_string_append(info, "hzhr : maintainer<br>\n");
--- a/libpurple/protocols/qq/qq_network.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Fri Jan 02 19:27:02 2009 +0000
@@ -468,7 +468,7 @@
 
 static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
 {
-	PurpleConnection *gc = (PurpleConnection *) data;
+	PurpleConnection *gc = NULL;
 	qq_data *qd;
 	guint8 *buf;
 	gint buf_len;
--- a/libpurple/protocols/sametime/sametime.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Fri Jan 02 19:27:02 2009 +0000
@@ -3724,7 +3724,6 @@
   gc->flags |= PURPLE_CONNECTION_NO_IMAGES;
 
   user = g_strdup(purple_account_get_username(account));
-  pass = g_strdup(purple_account_get_password(account));
 
   host = strrchr(user, ':');
   if(host) {
@@ -3741,10 +3740,12 @@
   if(! host || ! *host) {
     /* somehow, we don't have a host to connect to. Well, we need one
        to actually continue, so let's ask the user directly. */
+    g_free(user);
     prompt_host(gc);
     return;
   }
 
+  pass = g_strdup(purple_account_get_password(account));
   port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT);
 
   DEBUG_INFO("user: '%s'\n", user);
--- a/libpurple/protocols/yahoo/yahoo.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Fri Jan 02 19:27:02 2009 +0000
@@ -4167,8 +4167,7 @@
 	           "Trying to join %s \n", args[0]);
 
 	comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	g_hash_table_replace(comp, g_strdup("room"),
-	g_strdup_printf("%s", g_ascii_strdown(args[0], strlen(args[0]))));
+	g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1));
 	g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat"));
 
 	yahoo_c_join(gc, comp);
--- a/libpurple/proxy.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/proxy.c	Fri Jan 02 19:27:02 2009 +0000
@@ -621,7 +621,7 @@
 	if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data))
 		return;
 
-	purple_debug_info("proxy", "Connected to %s:%d.\n",
+	purple_debug_info("proxy", "Connecting to %s:%d.\n",
 					connect_data->host, connect_data->port);
 
 	/*
--- a/libpurple/purple-url-handler	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/purple-url-handler	Fri Jan 02 19:27:02 2009 +0000
@@ -299,6 +299,31 @@
     else:
         goim(account, screenname)
 
+def gtalk(uri):
+    protocol = "prpl-jabber"
+    match = re.match(r"^gtalk:([^?]*)(\?(.*))", uri)
+    if not match:
+        print "Invalid gtalk URI: %s" % uri
+        return
+
+    command = urllib.unquote_plus(match.group(1))
+    paramstring = match.group(3)
+    params = {}
+    if paramstring:
+        for param in paramstring.split("&"):
+            key, value = extendlist(param.split("=", 1), 2, "")
+            params[key] = urllib.unquote_plus(value)
+    accountname = params.get("from_jid", "")
+    jid = params.get("jid", "")
+
+    account = findaccount(protocol, accountname)
+
+    if command.lower() == "chat":
+        goim(account, jid)
+    elif command.lower() == "call":
+        # XXX V&V prompt to establish call
+        goim(account, jid)
+
 def ymsgr(uri):
     protocol = "prpl-yahoo"
     match = re.match(r"^ymsgr:([^?]*)(\?([^&]*)(&(.*))?)", uri)
@@ -355,6 +380,8 @@
             sip(uri)
         elif type == "xmpp":
             xmpp(uri)
+        elif type == "gtalk":
+            gtalk(uri)
         elif type == "ymsgr":
             ymsgr(uri)
         else:
--- a/libpurple/server.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/server.c	Fri Jan 02 19:27:02 2009 +0000
@@ -804,7 +804,7 @@
 {
 	PurpleAccount *account;
 	char buf2[BUF_LONG];
-	struct chat_invite_data *cid = g_new0(struct chat_invite_data, 1);
+	struct chat_invite_data *cid;
 	int plugin_return;
 
 	account = purple_connection_get_account(gc);
@@ -817,6 +817,8 @@
 		}
 	}
 
+	cid = g_new0(struct chat_invite_data, 1);
+
 	plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
 					purple_conversations_get_handle(),
 					"chat-invited", account, who, name, message, data));
--- a/libpurple/util.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/util.c	Fri Jan 02 19:27:02 2009 +0000
@@ -1676,20 +1676,18 @@
 							  size = "xx-small";
 							  break;
 							case 2:
-							  size = "x-small";
+							  size = "small";
 							  break;
 							case 3:
-							  size = "small";
+							  size = "medium";
 							  break;
 							case 4:
-							  size = "medium";
+							  size = "large";
 							  break;
 							case 5:
-							  size = "large";
+							  size = "x-large";
 							  break;
 							case 6:
-							  size = "x-large";
-							  break;
 							case 7:
 							  size = "xx-large";
 							  break;
--- a/libpurple/value.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/value.c	Fri Jan 02 19:27:02 2009 +0000
@@ -320,6 +320,7 @@
 purple_value_set_string(PurpleValue *value, const char *data)
 {
 	g_return_if_fail(value != NULL);
+	g_return_if_fail(data == NULL || g_utf8_validate(data, -1, NULL));
 
 	g_free(value->data.string_data);
 	value->data.string_data = g_strdup(data);
--- a/libpurple/win32/libc_interface.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/win32/libc_interface.c	Fri Jan 02 19:27:02 2009 +0000
@@ -31,13 +31,7 @@
 #include "config.h"
 #include "debug.h"
 #include "libc_internal.h"
-#if GLIB_CHECK_VERSION(2,6,0)
-# include <glib/gstdio.h>
-#else
-#define g_remove remove
-#define g_rename rename
-#define g_stat stat
-#endif
+#include <glib/gstdio.h>
 
 #ifdef ENABLE_NLS
 #  include <locale.h>
@@ -58,6 +52,9 @@
 #  define dngettext(Domain, Singular, Plural, Number) ((Number == 1) ? ((const char *)Singular) : ((const char *)Plural))
 #endif
 
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR)
+#endif
 
 static char errbuf[1024];
 
--- a/libpurple/xmlnode.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/libpurple/xmlnode.c	Fri Jan 02 19:27:02 2009 +0000
@@ -662,7 +662,7 @@
 	vsnprintf(errmsg, sizeof(errmsg), msg, args);
 	va_end(args);
 
-	purple_debug_error("xmlnode", "Error parsing xml file: %s\n", errmsg);
+	purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg);
 }
 
 static xmlSAXHandler xmlnode_parser_libxml = {
--- a/pidgin/gtkaccount.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/pidgin/gtkaccount.c	Fri Jan 02 19:27:02 2009 +0000
@@ -286,25 +286,27 @@
 screenname_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
 {
 	GdkColor color = {0, 34952, 35466, 34181};
-	GHashTable *table;
-	const char *label;
-
-	table = dialog->prpl_info->get_account_text_table(NULL);
-	label = g_hash_table_lookup(table, "login_label");
-
-	if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') {
-		/* We have to avoid hitting the screenname_changed_cb function 
-		 * because it enables buttons we don't want enabled yet ;)
-		 */
-		g_signal_handlers_block_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
-		gtk_entry_set_text(GTK_ENTRY(widget), label);
-		/* Make sure we can hit it again */
-		g_signal_handlers_unblock_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
-		gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color);
+	GHashTable *table = NULL;
+	const char *label = NULL;
+
+	if(PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) {
+		table = dialog->prpl_info->get_account_text_table(NULL);
+		label = g_hash_table_lookup(table, "login_label");
+
+		if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') {
+			/* We have to avoid hitting the screenname_changed_cb function 
+			 * because it enables buttons we don't want enabled yet ;)
+			 */
+			g_signal_handlers_block_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
+			gtk_entry_set_text(GTK_ENTRY(widget), label);
+			/* Make sure we can hit it again */
+			g_signal_handlers_unblock_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
+			gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color);
+		}
+
+		g_hash_table_destroy(table);
 	}
 
-	g_hash_table_destroy(table);
-
 	return FALSE;
 }
 
--- a/pidgin/gtkblist.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/pidgin/gtkblist.c	Fri Jan 02 19:27:02 2009 +0000
@@ -602,7 +602,7 @@
 			break;
 		case PURPLE_BLIST_GROUP_NODE:
 			dest = purple_find_group(arg2);
-			if (dest != NULL && strcmp(arg2, ((PurpleGroup*) node)->name)) {
+			if (dest != NULL && purple_utf8_strcasecmp(arg2, ((PurpleGroup*) node)->name)) {
 				pidgin_dialogs_merge_groups((PurpleGroup*) node, arg2);
 			} else
 				purple_blist_rename_group((PurpleGroup*)node, arg2);
@@ -7668,6 +7668,7 @@
 	GtkAccelGroup *accel_group = NULL;
 	GList *l = NULL, *accounts = NULL;
 	gboolean disabled_accounts = FALSE;
+	gboolean enabled_accounts = FALSE;
 
 	if (accountmenu == NULL)
 		return;
@@ -7721,10 +7722,16 @@
 				G_CALLBACK(enable_account_cb), account);
 			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
 			gtk_widget_show(menuitem);
+		} else {
+			enabled_accounts = TRUE;
 		}
 	}
 
+	if (!enabled_accounts)
+		return;
+
 	pidgin_separator(accountmenu);
+	accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu));
 
 	for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
 		char *buf = NULL;
@@ -7736,67 +7743,63 @@
 		PurplePlugin *plugin = NULL;
 
 		account = accounts->data;
-		accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu));
-
-		if(purple_account_get_enabled(account, PIDGIN_UI)) {
-			buf = g_strconcat(purple_account_get_username(account), " (",
-					purple_account_get_protocol_name(account), ")", NULL);
-			menuitem = gtk_image_menu_item_new_with_label(buf);
-			accel_path_buf = g_strconcat(N_("<PurpleMain>/Accounts/"), buf, NULL);
-			g_free(buf);
-			pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
-			if (pixbuf != NULL)
-			{
-				if (!purple_account_is_connected(account))
-					gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf,
-							0.0, FALSE);
-				image = gtk_image_new_from_pixbuf(pixbuf);
-				g_object_unref(G_OBJECT(pixbuf));
-				gtk_widget_show(image);
-				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
-			}
-			gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
+
+		if (!purple_account_get_enabled(account, PIDGIN_UI))
+			continue;
+
+		buf = g_strconcat(purple_account_get_username(account), " (",
+				purple_account_get_protocol_name(account), ")", NULL);
+		menuitem = gtk_image_menu_item_new_with_label(buf);
+		accel_path_buf = g_strconcat(N_("<PurpleMain>/Accounts/"), buf, NULL);
+		g_free(buf);
+		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+		if (pixbuf != NULL) {
+			if (!purple_account_is_connected(account))
+				gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf,
+						0.0, FALSE);
+			image = gtk_image_new_from_pixbuf(pixbuf);
+			g_object_unref(G_OBJECT(pixbuf));
+			gtk_widget_show(image);
+			gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
+		}
+		gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
+		gtk_widget_show(menuitem);
+
+		submenu = gtk_menu_new();
+		gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
+		gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf);
+		g_free(accel_path_buf);
+		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+		gtk_widget_show(submenu);
+
+
+		menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account"));
+		g_signal_connect(G_OBJECT(menuitem), "activate",
+				G_CALLBACK(modify_account_cb), account);
+		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+		gtk_widget_show(menuitem);
+
+		pidgin_separator(submenu);
+
+		gc = purple_account_get_connection(account);
+		plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL;
+		if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
+			build_plugin_actions(submenu, plugin, gc);
+		} else {
+			menuitem = gtk_menu_item_new_with_label(_("No actions available"));
+			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+			gtk_widget_set_sensitive(menuitem, FALSE);
 			gtk_widget_show(menuitem);
-
-			submenu = gtk_menu_new();
-			gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
-			gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf);
-			g_free(accel_path_buf);
-			gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-			gtk_widget_show(submenu);
-
-
-			menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account"));
-			g_signal_connect(G_OBJECT(menuitem), "activate",
-					G_CALLBACK(modify_account_cb), account);
-			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-			gtk_widget_show(menuitem);
-
-			pidgin_separator(submenu);
-
-			gc = purple_account_get_connection(account);
-			plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL;
-			if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
-				build_plugin_actions(submenu, plugin, gc);
-			} else {
-				menuitem = gtk_menu_item_new_with_label(_("No actions available"));
-				gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-				gtk_widget_set_sensitive(menuitem, FALSE);
-				gtk_widget_show(menuitem);
-			}
-
-			pidgin_separator(submenu);
-
-			menuitem = gtk_menu_item_new_with_mnemonic(_("_Disable"));
-			g_signal_connect(G_OBJECT(menuitem), "activate",
-					G_CALLBACK(disable_account_cb), account);
-			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-			gtk_widget_show(menuitem);
-		} else {
-			disabled_accounts = TRUE;
 		}
-	}
-
+
+		pidgin_separator(submenu);
+
+		menuitem = gtk_menu_item_new_with_mnemonic(_("_Disable"));
+		g_signal_connect(G_OBJECT(menuitem), "activate",
+				G_CALLBACK(disable_account_cb), account);
+		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+		gtk_widget_show(menuitem);
+	}
 }
 
 static GList *plugin_submenus = NULL;
--- a/pidgin/gtkdialogs.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/pidgin/gtkdialogs.c	Fri Jan 02 19:27:02 2009 +0000
@@ -100,6 +100,7 @@
 
 /* Order: Alphabetical by Last Name */
 static const struct developer patch_writers[] = {
+	{"Paul Aurich", NULL, NULL },
 	{"Marcus 'malu' Lundblad", NULL, NULL},
 	{"Dennis 'EvilDennisR' Ristuccia",	N_("Senior Contributor/QA"),	NULL},
 	{"Peter 'Fmoo' Ruibal",		NULL,	NULL},
--- a/pidgin/gtkimhtml.c	Mon Dec 22 19:40:05 2008 +0000
+++ b/pidgin/gtkimhtml.c	Fri Jan 02 19:27:02 2009 +0000
@@ -3117,15 +3117,15 @@
 							else if (g_ascii_strcasecmp(size, "smaller") == 0
 								  || g_ascii_strcasecmp(size, "x-small") == 0)
 								font->size = 2;
-							else if (g_ascii_strcasecmp(size, "larger") == 0
-								  || g_ascii_strcasecmp(size, "medium") == 0)
+							else if (g_ascii_strcasecmp(size, "medium") == 0)
+								font->size = 3;
+							else if (g_ascii_strcasecmp(size, "large") == 0
+								  || g_ascii_strcasecmp(size, "larger") == 0)
 								font->size = 4;
-							else if (g_ascii_strcasecmp(size, "large") == 0)
+							else if (g_ascii_strcasecmp(size, "x-large") == 0)
 								font->size = 5;
-							else if (g_ascii_strcasecmp(size, "x-large") == 0)
+							else if (g_ascii_strcasecmp(size, "xx-large") == 0)
 								font->size = 6;
-							else if (g_ascii_strcasecmp(size, "xx-large") == 0)
-								font->size = 7;
 
 							/*
 							 * TODO: Handle other values, like percentages, or
--- a/po/fr.po	Mon Dec 22 19:40:05 2008 +0000
+++ b/po/fr.po	Fri Jan 02 19:27:02 2009 +0000
@@ -4964,7 +4964,7 @@
 msgstr "Erreur MSN : %s\n"
 
 msgid "Other Contacts"
-msgstr "AUtres contacts"
+msgstr "Autres contacts"
 
 msgid "Non-IM Contacts"
 msgstr "Contacts non instantanés"