changeset 26985:bc2aa262a567

propagate from branch 'im.pidgin.pidgin' (head ca6eb11f6084be37aeabcb2d99df789aaf8b4afd) to branch 'im.pidgin.cpw.malu.client_type' (head 61fe08ebe574f1112f120259839dbc0474a701cb)
author Marcus Lundblad <ml@update.uu.se>
date Mon, 25 May 2009 19:24:17 +0000
parents d2424873c666 (current diff) 0f1b41c2f835 (diff)
children 68d6c6517ef8
files libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/jabber.c
diffstat 44 files changed, 621 insertions(+), 611 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun May 17 19:04:15 2009 +0000
+++ b/COPYRIGHT	Mon May 25 19:24:17 2009 +0000
@@ -1,5 +1,5 @@
 Pidgin, Finch, and libpurple
-Copyright (C) 1998-2008 by the following:
+Copyright (C) 1998-2009 by the following:
 
 If you have contributed to this project then you deserve to be on this
 list.  Contact us (see: AUTHORS) and we'll add you.
--- a/ChangeLog	Sun May 17 19:04:15 2009 +0000
+++ b/ChangeLog	Mon May 25 19:24:17 2009 +0000
@@ -7,18 +7,23 @@
 	* Voice & Video framework in libpurple, thanks to Mike Ruprecht's summer
 	  of code project in 2008.
 	* It should no longer be possible to end up with duplicates of buddies
-	  in a group on the buddy list.
+	  in a group on the buddy list. (Paul Aurich)
 	* Removed the unmaintained and unneeded toc protocol plugin.
 	* Fixed NTLM authentication on big-endian systems.
 
 	libpurple:
 	* Various memory cleanups when unloading libpurple. (Nick Hebner)
 	* Report idle time 'From last message sent' should work properly.
+	* Show the invite message for buddies that requested authorization
+	  from you on MSN.
+	* DNS servers are re-read when DNS queries fail in case the system has
+	  moved to a new network and the old servers are not accessible.
 
 	XMPP:
 	* Voice & Video support with Jingle (XEP-0166, 0167, 0176, & 0177), and
 	  voice support with GTalk and GMail. (Mike "Maiku" Ruprecht)
-	* Support for in-band bytestreams for file transfers (XEP-0047).
+	* Support for in-band bytestreams for file transfers (XEP-0047). (Marcus
+	  Lundblad)
 	* Support for sending and receiving attentions (equivalent to "buzz"
 	  and "nudge") using the command /buzz. (XEP-0224)
 	* Support for connecting using BOSH. (Tobias Markmann)
@@ -38,9 +43,11 @@
 	  contains formatting.
 	* Show when the user was last logged in when doing "Get Info" on an offline
 	  buddy, provided the server supports it.
-	* Support custom smileys in MUCs (only when all participants supports the
+	* Support custom smileys in MUCs (only when all participants support the
 	  "Bits of Binary" extension, and a maximum of 10 participants are in the
-	  chat (to avoid getting too many fetch requests).
+	  chat to avoid getting too many fetch requests).
+	* Fix an issue with Jabber (pre-XMPP) servers and the user's preference
+	  to require SSL not being respected.
 
 	Yahoo:
 	* P2P file transfers. (Sulabh Mahajan)
@@ -48,6 +55,11 @@
 	  (Sulabh Mahajan)
 	* Sending text messages (address to +<countrycode><phone number>).
 	  (Sulabh Mahajan)
+	* Addition of MSN buddies to Yahoo accounts by adding them as
+	  'msn/buddy@somedomain.com' is now supported.  (Sulabh Mahajan)
+	* Yahoo Protocol 16 support, including new HTTPS login method; this should
+	  fix a number of login problems that have recently cropped up.  (Sulabh
+	  Mahajan, Mike "Maiku" Ruprecht)
 
 	Pidgin:
 	* Added -f command line option to tell Pidgin to ignore NetworkManager
--- a/finch/plugins/gnttinyurl.c	Sun May 17 19:04:15 2009 +0000
+++ b/finch/plugins/gnttinyurl.c	Mon May 25 19:24:17 2009 +0000
@@ -228,12 +228,15 @@
 	if (!(*flags & PURPLE_MESSAGE_RECV) || *flags & PURPLE_MESSAGE_INVISIBLE)
 		return FALSE;
 
-	t = g_string_new(*message);
 	urls = purple_conversation_get_data(conv, "TinyURLs");
 	if (urls != NULL) /* message was cancelled somewhere? Reset. */
 		g_list_foreach(urls, free_urls, NULL);
 	g_list_free(urls);
-	urls = extract_urls(t->str);
+	urls = extract_urls(*message);
+	if (!urls)
+		return FALSE;
+
+	t = g_string_new(*message);
 	g_free(*message);
 	for (iter = urls; iter; iter = iter->next) {
 		if (g_utf8_strlen((char *)iter->data, -1) >= purple_prefs_get_int(PREF_LENGTH)) {
--- a/libpurple/dnsquery.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/dnsquery.c	Mon May 25 19:24:17 2009 +0000
@@ -32,6 +32,10 @@
 #include "prefs.h"
 #include "util.h"
 
+#ifndef _WIN32
+#include <resolv.h>
+#endif
+
 #if (defined(__APPLE__) || defined (__unix__)) && !defined(__osf__)
 #define PURPLE_DNSQUERY_USE_FORK
 #endif
@@ -256,6 +260,9 @@
 		 * library.
 		 */
 		hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_ADDRCONFIG
+		hints.ai_flags |= AI_ADDRCONFIG;
+#endif /* AI_ADDRCONFIG */
 		rc = getaddrinfo(dns_params.hostname, servname, &hints, &res);
 		write_to_parent(child_out, &rc, sizeof(rc));
 		if (rc != 0) {
@@ -568,8 +575,10 @@
 		g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
 				query_data->hostname, err);
 #endif
+		/* Re-read resolv.conf and friends in case DNS servers have changed */
+		res_init();
+
 		purple_dnsquery_failed(query_data, message);
-
 	} else if (rc > 0) {
 		/* Success! */
 		while (rc > 0) {
@@ -706,6 +715,9 @@
 	 * library.
 	 */
 	hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_ADDRCONFIG
+	hints.ai_flags |= AI_ADDRCONFIG;
+#endif /* AI_ADDRCONFIG */
 	if ((rc = getaddrinfo(query_data->hostname, servname, &hints, &res)) == 0) {
 		tmp = res;
 		while(res) {
--- a/libpurple/dnssrv.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/dnssrv.c	Mon May 25 19:24:17 2009 +0000
@@ -141,7 +141,14 @@
 	}
 
 	size = res_query( query.query, C_IN, query.type, (u_char*)&answer, sizeof( answer));
-	
+	if (size == -1) {
+		write(out, &(query.type), sizeof(query.type));
+		write(out, &size, sizeof(int));
+		close(out);
+		close(in);
+		_exit(0);
+	}
+
 	qdcount = ntohs(answer.hdr.qdcount);
 	ancount = ntohs(answer.hdr.ancount);
 	cp = (guchar*)&answer + sizeof(HEADER);
@@ -223,59 +230,73 @@
 	PurpleSrvQueryData *query_data = (PurpleSrvQueryData*)data;
 	int i;
 	int status;
-	
+
 	if (read(source, &type, sizeof(type)) == sizeof(type)) {
-		if (type == T_SRV) {
-			PurpleSrvResponse *res;
-			PurpleSrvResponse *tmp;
-			PurpleSrvCallback cb = query_data->cb.srv;
-			if (read(source, &size, sizeof(int)) == sizeof(int)) {
-				ssize_t red;
-				purple_debug_info("dnssrv","found %d SRV entries\n", size);
-				tmp = res = g_new0(PurpleSrvResponse, size);
-				for (i = 0; i < size; i++) {
-					red = read(source, tmp++, sizeof(PurpleSrvResponse));
-					if (red != sizeof(PurpleSrvResponse)) {
-						purple_debug_error("dnssrv","unable to read srv "
-								"response: %s\n", g_strerror(errno));
-						size = 0;
-						g_free(res);
-						res = NULL;
+		if (read(source, &size, sizeof(size)) == sizeof(size)) {
+			if (size == -1 || size == 0) {
+				if (size == -1) {
+					purple_debug_warning("dnssrv", "res_query returned an error\n");
+					/* Re-read resolv.conf and friends in case DNS servers have changed */
+					res_init();
+				} else
+					purple_debug_info("dnssrv", "Found 0 entries, errno is %i\n", errno);
+
+				if (type == T_SRV) {
+					PurpleSrvCallback cb = query_data->cb.srv;
+					cb(NULL, 0, query_data->extradata);
+				} else if (type == T_TXT) {
+					PurpleTxtCallback cb = query_data->cb.txt;
+					cb(NULL, query_data->extradata);
+				} else {
+					purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno);
+				}
+
+			} else if (size) {
+				if (type == T_SRV) {
+					PurpleSrvResponse *res;
+					PurpleSrvResponse *tmp;
+					PurpleSrvCallback cb = query_data->cb.srv;
+					ssize_t red;
+					purple_debug_info("dnssrv","found %d SRV entries\n", size);
+					tmp = res = g_new0(PurpleSrvResponse, size);
+					for (i = 0; i < size; i++) {
+						red = read(source, tmp++, sizeof(PurpleSrvResponse));
+						if (red != sizeof(PurpleSrvResponse)) {
+							purple_debug_error("dnssrv","unable to read srv "
+									"response: %s\n", g_strerror(errno));
+							size = 0;
+							g_free(res);
+							res = NULL;
+						}
 					}
+
+					cb(res, size, query_data->extradata);
+				} else if (type == T_TXT) {
+					GSList *responses = NULL;
+					PurpleTxtResponse *res;
+					PurpleTxtCallback cb = query_data->cb.txt;
+					ssize_t red;
+					purple_debug_info("dnssrv","found %d TXT entries\n", size);
+					res = g_new0(PurpleTxtResponse, 1);
+					for (i = 0; i < size; i++) {
+						red = read(source, res, sizeof(PurpleTxtResponse));
+						if (red != sizeof(PurpleTxtResponse)) {
+							purple_debug_error("dnssrv","unable to read txt "
+									"response: %s\n", g_strerror(errno));
+							size = 0;
+							g_free(res);
+							g_slist_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
+							g_slist_free(responses);
+							responses = NULL;
+							break;
+						}
+					}
+
+					cb(responses, query_data->extradata);
+				} else {
+					purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno);
 				}
-			} else {
-				purple_debug_info("dnssrv","found 0 SRV entries; errno is %i\n", errno);
-				size = 0;
-				res = NULL;
 			}
-			cb(res, size, query_data->extradata);
-		} else if (type == T_TXT) {
-			GSList *responses = NULL;
-			PurpleTxtResponse *res;
-			PurpleTxtCallback cb = query_data->cb.txt;
-			if (read(source, &size, sizeof(int)) == sizeof(int)) {
-				ssize_t red;
-				purple_debug_info("dnssrv","found %d TXT entries\n", size);
-				res = g_new0(PurpleTxtResponse, 1);
-				for (i = 0; i < size; i++) {
-					red = read(source, res, sizeof(PurpleTxtResponse));
-					if (red != sizeof(PurpleTxtResponse)) {
-						purple_debug_error("dnssrv","unable to read txt "
-								"response: %s\n", g_strerror(errno));
-						size = 0;
-						g_free(res);
-						g_slist_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
-						g_slist_free(responses);
-						responses = NULL;
-						break;
-					}
-				}
-			} else {
-				purple_debug_info("dnssrv","found 0 TXT entries; errno is %i\n", errno);
-			}
-			cb(responses, query_data->extradata);			
-		} else {
-			purple_debug_info("dnssrv","type unknown of DNS result entry; errno is %i\n", errno);
 		}
 	}
 
--- a/libpurple/nat-pmp.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/nat-pmp.c	Mon May 25 19:24:17 2009 +0000
@@ -200,7 +200,7 @@
 
     if (!(buf = malloc(needed)))
 	{
-		purple_debug_warning("nat-pmp", "Failed to malloc %i\n", needed);
+		purple_debug_warning("nat-pmp", "Failed to malloc %" G_GSIZE_FORMAT "\n", needed);
 		return NULL;
     }
 
--- a/libpurple/plugins/perl/common/Certificate.xs	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/plugins/perl/common/Certificate.xs	Mon May 25 19:24:17 2009 +0000
@@ -214,7 +214,7 @@
 		GByteArray *gba = NULL;
 	CODE:
 		gba = purple_certificate_get_fingerprint_sha1(crt);
-		RETVAL = newSVpv(gba->data, gba->len);
+		RETVAL = newSVpv((gchar *)gba->data, gba->len);
 		g_byte_array_free(gba, TRUE);
 	OUTPUT:
 		RETVAL
--- a/libpurple/plugins/perl/common/Cipher.xs	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/plugins/perl/common/Cipher.xs	Mon May 25 19:24:17 2009 +0000
@@ -66,9 +66,9 @@
 		guchar *data = NULL;
 		size_t data_len;
 	CODE:
-		data = SvPV(data_sv, data_len);
+		data = (guchar *)SvPV(data_sv, data_len);
 		SvUPGRADE(digest, SVt_PV);
-		buff = SvGROW(digest, in_len);
+		buff = (guchar *)SvGROW(digest, in_len);
 		ret = purple_cipher_digest_region(name, data, data_len, in_len, buff, &RETVAL);
 		if(!ret) {
 			SvSetSV_nosteal(digest, &PL_sv_undef);
@@ -181,7 +181,7 @@
 		guchar *buff = NULL;
 	CODE:
 		SvUPGRADE(digest, SVt_PV);
-		buff = SvGROW(digest, in_len);
+		buff = (guchar *)SvGROW(digest, in_len);
 		ret = purple_cipher_context_digest(context, in_len, buff, &RETVAL);
 		if(!ret) {
 			SvSetSV_nosteal(digest, &PL_sv_undef);
@@ -225,9 +225,9 @@
 		guchar *buff = NULL;
 		guchar *data = NULL;
 	CODE:
-		data = SvPV(data_sv, datalen);
+		data = (guchar *)SvPV(data_sv, datalen);
 		SvUPGRADE(output, SVt_PV);
-		buff = SvGROW(output, datalen);
+		buff = (guchar *)SvGROW(output, datalen);
 		RETVAL = purple_cipher_context_encrypt(context, data, datalen, buff, &outlen);
 		if(outlen != 0) {
 			SvPOK_only(output);
@@ -249,9 +249,9 @@
 		guchar *buff = NULL;
 		guchar *data = NULL;
 	CODE:
-		data = SvPV(data_sv, datalen);
+		data = (guchar *)SvPV(data_sv, datalen);
 		SvUPGRADE(output, SVt_PV);
-		buff = SvGROW(output, datalen);
+		buff = (guchar *)SvGROW(output, datalen);
 		RETVAL = purple_cipher_context_decrypt(context, data, datalen, buff, &outlen);
 		if(outlen != 0) {
 			SvPOK_only(output);
--- a/libpurple/plugins/perl/common/Util.xs	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/plugins/perl/common/Util.xs	Mon May 25 19:24:17 2009 +0000
@@ -69,7 +69,7 @@
 	PREINIT:
 		char *message = NULL;
 		gboolean ret;
-		gssize len;
+		gsize len;
 	CODE:
 		message = SvPV(msg, len);
 		message = g_strndup(message, len);
@@ -239,7 +239,7 @@
 	CODE:
 		ret = purple_base16_decode(str, &len);
 		if(len) {
-			RETVAL = newSVpv(ret, len);
+			RETVAL = newSVpv((gchar *)ret, len);
 		} else {
 			g_free(ret);
 			XSRETURN_UNDEF;
@@ -257,7 +257,7 @@
 	CODE:
 		ret = purple_base64_decode(str, &len);
 		if(len) {
-			RETVAL = newSVpv(ret, len);
+			RETVAL = newSVpv((gchar *)ret, len);
 		} else {
 			g_free(ret);
 			XSRETURN_UNDEF;
@@ -275,7 +275,7 @@
 	CODE:
 		ret = purple_quotedp_decode(str, &len);
 		if(len) {
-			RETVAL = newSVpv(ret, len);
+			RETVAL = newSVpv((gchar *)ret, len);
 		} else {
 			g_free(ret);
 			XSRETURN_UNDEF;
@@ -499,11 +499,11 @@
 	gpointer unused
 
 const char*
-purple_util_get_image_extension(gconstpointer data, size_t length(data))
+purple_util_get_image_extension(const char *data, size_t length(data))
 	PROTOTYPE: $
 
 gchar_own*
-purple_util_get_image_filename(gconstpointer image_data, size_t length(image_data))
+purple_util_get_image_filename(const char *image_data, size_t length(image_data))
 	PROTOTYPE: $
 
 Purple::XMLNode
--- a/libpurple/plugins/signals-test.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/plugins/signals-test.c	Mon May 25 19:24:17 2009 +0000
@@ -624,6 +624,44 @@
 }
 
 /**************************************************************************
+ * Jabber signals callbacks
+ **************************************************************************/
+static gboolean
+jabber_iq_received(PurpleConnection *pc, const char *type, const char *id,
+                   const char *from, xmlnode *iq)
+{
+	purple_debug_misc("signals test", "jabber IQ (type=%s, id=%s, from=%s) %p\n",
+	                  type, id, from ? from : "(null)", iq);
+
+	/* We don't want the plugin to stop processing */
+	return FALSE;
+}
+
+static gboolean
+jabber_message_received(PurpleConnection *pc, const char *type, const char *id,
+                        const char *from, const char *to, xmlnode *message)
+{
+	purple_debug_misc("signals test", "jabber message (type=%s, id=%s, "
+	                  "from=%s to=%s) %p\n",
+	                  type ? type : "(null)", id ? id : "(null)",
+	                  from ? from : "(null)", to ? to : "(null)", message);
+
+	/* We don't want the plugin to stop processing */
+	return FALSE;
+}
+
+static gboolean
+jabber_presence_received(PurpleConnection *pc, const char *type,
+                         const char *from, xmlnode *presence)
+{
+	purple_debug_misc("signals test", "jabber presence (type=%s, from=%s) %p\n",
+	                  type ? type : "(null)", from ? from : "(null)", presence);
+
+	/* We don't want the plugin to stop processing */
+	return FALSE;
+}
+
+/**************************************************************************
  * Plugin stuff
  **************************************************************************/
 static gboolean
@@ -638,6 +676,7 @@
 	void *ft_handle       = purple_xfers_get_handle();
 	void *sound_handle    = purple_sounds_get_handle();
 	void *notify_handle   = purple_notify_get_handle();
+	void *jabber_handle   = purple_plugins_find_with_id("prpl-jabber");
 
 	/* Accounts subsystem signals */
 	purple_signal_connect(accounts_handle, "account-connecting",
@@ -783,6 +822,24 @@
 	purple_signal_connect(notify_handle, "displaying-emails-notification",
 						plugin, PURPLE_CALLBACK(notify_emails_cb), NULL);
 
+	/* Jabber signals */
+	if (jabber_handle) {
+		purple_signal_connect(jabber_handle, "jabber-receiving-iq", plugin,
+		                      PURPLE_CALLBACK(jabber_iq_received), NULL);
+		purple_signal_connect(jabber_handle, "jabber-receiving-message", plugin,
+		                      PURPLE_CALLBACK(jabber_message_received), NULL);
+		purple_signal_connect(jabber_handle, "jabber-receiving-presence", plugin,
+		                      PURPLE_CALLBACK(jabber_presence_received), NULL);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+plugin_unload(PurplePlugin *plugin)
+{
+	purple_signals_disconnect_by_handle(plugin);
+
 	return TRUE;
 }
 
@@ -808,7 +865,7 @@
 	PURPLE_WEBSITE,                                     /**< homepage       */
 
 	plugin_load,                                      /**< load           */
-	NULL,                                             /**< unload         */
+	plugin_unload,                                    /**< unload         */
 	NULL,                                             /**< destroy        */
 
 	NULL,                                             /**< ui_info        */
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Mon May 25 19:24:17 2009 +0000
@@ -357,14 +357,16 @@
 	AvahiBuddyImplData *idata = buddy->mdns_impl_data;
 
 	switch (event) {
+		case AVAHI_BROWSER_CACHE_EXHAUSTED:
+		case AVAHI_BROWSER_ALL_FOR_NOW:
+			/* Ignore these "meta" informational events */
+			return;
 		case AVAHI_BROWSER_NEW:
 			bonjour_buddy_got_buddy_icon(buddy, rdata, size);
 			break;
 		case AVAHI_BROWSER_REMOVE:
-		case AVAHI_BROWSER_CACHE_EXHAUSTED:
-		case AVAHI_BROWSER_ALL_FOR_NOW:
 		case AVAHI_BROWSER_FAILURE:
-			purple_debug_error("bonjour", "Error rerieving buddy icon record: %s\n",
+			purple_debug_error("bonjour", "Error retrieving buddy icon record: %s\n",
 				avahi_strerror(avahi_client_errno(avahi_record_browser_get_client(b))));
 			break;
 	}
--- a/libpurple/protocols/jabber/auth.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/auth.c	Mon May 25 19:24:17 2009 +0000
@@ -689,6 +689,18 @@
 	JabberIq *iq;
 	xmlnode *query, *username;
 
+	/* We can end up here without encryption if the server doesn't support
+	 * <stream:features/> and we're not using old-style SSL.  If the user
+	 * is requiring SSL/TLS, we need to enforce it.
+	 */
+	if (!jabber_stream_is_ssl(js) &&
+			purple_account_get_bool(purple_connection_get_account(js->gc), "require_tls", FALSE)) {
+		purple_connection_error_reason (js->gc,
+			PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+			_("You require encryption, but it is not available on this server."));
+		return;
+	}
+
 #ifdef HAVE_CYRUS_SASL
 	/* If we have Cyrus SASL, then passwords will have been set
 	 * to OPTIONAL for this protocol. So, we need to do our own
--- a/libpurple/protocols/jabber/bosh.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/bosh.c	Mon May 25 19:24:17 2009 +0000
@@ -57,7 +57,7 @@
 	/* decoded URL */
 	char *host;
 	int port;
-	char *path; 
+	char *path;
 
 	/* Must be big enough to hold 2^53 - 1 */
 	guint64 rid;
@@ -281,7 +281,7 @@
 		 * connection.
 		 */
 		chosen = conn->connections[0];
-	
+
 		if (!chosen->ready)
 			purple_debug_warning("jabber", "First BOSH connection wasn't ready. Bad "
 					"things may happen.\n");
@@ -296,12 +296,12 @@
 		 */
 		if (data) {
 			int len = data ? strlen(data) : 0;
-			purple_circ_buffer_append(conn->pending, data, len); 
+			purple_circ_buffer_append(conn->pending, data, len);
 		}
 		return;
 	}
 
-	packet = g_string_new("");
+	packet = g_string_new(NULL);
 
 	g_string_printf(packet, "<body "
 	                "rid='%" G_GUINT64_FORMAT "' "
@@ -349,7 +349,7 @@
 	const char *type;
 
 	type = xmlnode_get_attrib(node, "type");
-	
+
 	if (type != NULL && !strcmp(type, "terminate")) {
 		conn->ready = FALSE;
 		purple_connection_error_reason (conn->js->gc,
@@ -418,7 +418,7 @@
 			jabber_process_packet(js, &child);
 		}
 	} else {
-		purple_debug_warning("jabber", "Received unexepcted empty BOSH packet.\n");	
+		purple_debug_warning("jabber", "Received unexepcted empty BOSH packet.\n");
 	}
 }
 
@@ -448,11 +448,14 @@
 
 	if (version) {
 		const char *dot = strstr(version, ".");
-		int major = atoi(version);
-		int minor = atoi(dot + 1);
+		int major, minor = 0;
 
 		purple_debug_info("jabber", "BOSH connection manager version %s\n", version);
 
+		major = atoi(version);
+		if (dot)
+			minor = atoi(dot + 1);
+
 		if (major != 1 || minor < 6) {
 			purple_connection_error_reason(conn->js->gc,
 			        PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -484,11 +487,11 @@
 	packet = xmlnode_get_child(node, "features");
 	conn->js->use_bosh = TRUE;
 	conn->receive_cb = auth_response_cb;
-	jabber_stream_features_parse(conn->js, packet);		
+	jabber_stream_features_parse(conn->js, packet);
 }
 
 static void jabber_bosh_connection_boot(PurpleBOSHConnection *conn) {
-	GString *buf = g_string_new("");
+	GString *buf = g_string_new(NULL);
 
 	g_string_printf(buf, "<body content='text/xml; charset=utf-8' "
 	                "secure='true' "
@@ -627,13 +630,24 @@
 
 	if (!conn->headers_done) {
 		const char *content_length = purple_strcasestr(cursor, "\r\nContent-Length");
-		const char *end_of_headers = purple_strcasestr(cursor, "\r\n\r\n");
+		const char *end_of_headers = strstr(cursor, "\r\n\r\n");
 
 		/* Make sure Content-Length is in headers, not body */
-		if (content_length && content_length < end_of_headers) {
-			char *sep = strstr(content_length, ": ");
-			int len = atoi(sep + 2);
-			if (len == 0) 
+		if (content_length && (!end_of_headers || content_length < end_of_headers)) {
+			const char *sep;
+			const char *eol;
+			int len;
+
+			if ((sep = strstr(content_length, ": ")) == NULL ||
+					(eol = strstr(sep, "\r\n")) == NULL)
+				/*
+				 * The packet ends in the middle of the Content-Length line.
+				 * We'll try again later when we have more.
+				 */
+				return;
+
+			len = atoi(sep + 2);
+			if (len == 0)
 				purple_debug_warning("jabber", "Found mangled Content-Length header.\n");
 
 			conn->body_len = len;
@@ -686,26 +700,24 @@
 	int cnt, count = 0;
 
 	if (!conn->buf)
-		conn->buf = g_string_new("");
+		conn->buf = g_string_new(NULL);
 
-	/* Read once to prime cnt before the loop */
-	if (conn->psc)
-		cnt = purple_ssl_read(conn->psc, buffer, sizeof(buffer));
-	else
-		cnt = read(conn->fd, buffer, sizeof(buffer));
-	while (cnt > 0) {
-		count += cnt;
-		g_string_append_len(conn->buf, buffer, cnt);
-
+	do {
 		if (conn->psc)
 			cnt = purple_ssl_read(conn->psc, buffer, sizeof(buffer));
 		else
 			cnt = read(conn->fd, buffer, sizeof(buffer));
-	}
+
+		if (cnt > 0) {
+			count += cnt;
+			g_string_append_len(conn->buf, buffer, cnt);
+		}
+	} while (cnt > 0);
 
 	if (cnt == 0 || (cnt < 0 && errno != EAGAIN)) {
 		if (cnt < 0)
-			purple_debug_info("jabber", "bosh read=%d, errno=%d\n", cnt, errno);
+			purple_debug_info("jabber", "bosh read=%d, errno=%d, error=%s\n",
+			                  cnt, errno, g_strerror(errno));
 		else
 			purple_debug_info("jabber", "bosh server closed the connection\n");
 
@@ -718,8 +730,8 @@
 		/* Process what we do have */
 	}
 
-
-	jabber_bosh_http_connection_process(conn);
+	if (conn->buf->len > 0)
+		jabber_bosh_http_connection_process(conn);
 }
 
 static void
--- a/libpurple/protocols/jabber/buddy.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Mon May 25 19:24:17 2009 +0000
@@ -1044,14 +1044,14 @@
 
 	if (!jbi->jb->resources) {
 		/* the buddy is offline */
-		gchar *status = 
-			g_strdup_printf("%s%s%s",	_("Offline"), 
+		gchar *status =
+			g_strdup_printf("%s%s%s",	_("Offline"),
 				jbi->last_message ? ": " : "",
 				jbi->last_message ? jbi->last_message : "");
 		if (jbi->last_seconds > 0) {
 			char *last = purple_str_seconds_to_string(jbi->last_seconds);
 			gchar *message = g_strdup_printf(_("%s ago"), last);
-			purple_notify_user_info_prepend_pair(user_info, 
+			purple_notify_user_info_prepend_pair(user_info,
 				_("Logged off"), message);
 			g_free(last);
 			g_free(message);
@@ -1460,49 +1460,50 @@
 				if(seconds) {
 					char *end = NULL;
 					long sec = strtol(seconds, &end, 10);
-                    JabberBuddy *jb = NULL;
-                    char *resource = NULL;
-                    char *buddy_name = NULL;
+					JabberBuddy *jb = NULL;
+					char *resource = NULL;
+					char *buddy_name = NULL;
 					JabberBuddyResource *jbr = NULL;
 
-                    if(end != seconds) {
+					if(end != seconds) {
 						JabberBuddyInfoResource *jbir = g_hash_table_lookup(jbi->resources, resource_name);
 						if(jbir) {
 							jbir->idle_seconds = sec;
 						}
 					}
-                    /* Update the idle time of the buddy resource, if we got it. 
-                     This will correct the value when a server doesn't mark 
-                     delayed presence and we got the presence when signing on */
-                    jb = jabber_buddy_find(js, from, FALSE);
-                    if (jb) {
-                        resource = jabber_get_resource(from);
-                        buddy_name = jabber_get_bare_jid(from);
-                        /* if the resource already has an idle time set, we
-                         must have gotten it originally from a presence. In
-                         this case we update it. Otherwise don't update it, to
-                         avoid setting an idle and not getting informed about
-                         the resource getting unidle */
-                        if (resource && buddy_name) {
-                            jbr = jabber_buddy_find_resource(jb, resource);
-                            
-                            if (jbr->idle) {
-                                if (sec) {
-                                    jbr->idle = time(NULL) - sec;
-                                } else {
-                                    jbr->idle = 0;
-                                }
-                            
-                                if (jbr == 
-                                    jabber_buddy_find_resource(jb, NULL)) {
-                                    purple_prpl_got_user_idle(js->gc->account, 
-                                        buddy_name, jbr->idle, jbr->idle);
-                                }
-                            }
-                        }
-                        g_free(resource);
-                        g_free(buddy_name);
-                    }
+					/* Update the idle time of the buddy resource, if we got it.
+					 This will correct the value when a server doesn't mark
+					 delayed presence and we got the presence when signing on */
+					jb = jabber_buddy_find(js, from, FALSE);
+					if (jb) {
+						resource = jabber_get_resource(from);
+						buddy_name = jabber_get_bare_jid(from);
+						/* if the resource already has an idle time set, we
+						 must have gotten it originally from a presence. In
+						 this case we update it. Otherwise don't update it, to
+						 avoid setting an idle and not getting informed about
+						 the resource getting unidle */
+						if (resource && buddy_name) {
+							jbr = jabber_buddy_find_resource(jb, resource);
+							if (jbr) {
+								if (jbr->idle) {
+									if (sec) {
+										jbr->idle = time(NULL) - sec;
+									} else {
+										jbr->idle = 0;
+									}
+
+									if (jbr ==
+										jabber_buddy_find_resource(jb, NULL)) {
+										purple_prpl_got_user_idle(js->gc->account,
+											buddy_name, jbr->idle, jbr->idle);
+									}
+								}
+							}
+						}
+						g_free(resource);
+						g_free(buddy_name);
+					}
 				}
 			}
 		}
@@ -1749,7 +1750,7 @@
 		jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
 		jabber_iq_send(iq);
 	}
-	
+
 	js->pending_buddy_info_requests = g_slist_prepend(js->pending_buddy_info_requests, jbi);
 	jbi->timeout_handle = purple_timeout_add_seconds(30, jabber_buddy_get_info_timeout, jbi);
 }
@@ -2500,7 +2501,7 @@
 	if (node)
 		purple_debug_info("jabber", "Found cap: %s\n", cap);
 	else
-		purple_debug_info("jabber", "Cap %s not found\n", cap); 
+		purple_debug_info("jabber", "Cap %s not found\n", cap);
 
 	return (node != NULL);
 }
--- a/libpurple/protocols/jabber/caps.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/caps.c	Mon May 25 19:24:17 2009 +0000
@@ -41,7 +41,7 @@
 
 /**
  *	Processes a query-node and returns a JabberCapsClientInfo object with all relevant info.
- *	
+ *
  *	@param 	query 	A query object.
  *	@return 		A JabberCapsClientInfo object.
  */
@@ -192,7 +192,7 @@
 		xmlnode *feature = xmlnode_new_child(client, "feature");
 		xmlnode_set_attrib(feature, "var", feat);
 	}
-	
+
 	for(iter = props->forms; iter; iter = g_list_next(iter)) {
 		/* FIXME: See #7814 */
 		xmlnode *xdata = iter->data;
@@ -329,7 +329,7 @@
 
 void jabber_caps_init(void)
 {
-	nodetable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)jabber_caps_node_exts_unref); 
+	nodetable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)jabber_caps_node_exts_unref);
 	capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, NULL, (GDestroyNotify)jabber_caps_client_info_destroy);
 	jabber_caps_load();
 }
@@ -589,7 +589,7 @@
 	}
 
 	userdata = g_new0(jabber_caps_cbplususerdata, 1);
-	/* This ref is given to fetching the basic node#ver info if we need it 
+	/* This ref is given to fetching the basic node#ver info if we need it
 	 * or unrefed at the bottom of this function */
 	cbplususerdata_ref(userdata);
 	userdata->cb = cb;
@@ -659,7 +659,7 @@
 				jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, cbdata);
 				jabber_iq_send(iq);
 
-				++userdata->extOutstanding;	
+				++userdata->extOutstanding;
 			}
 			exts[i] = NULL;
 		}
@@ -681,7 +681,7 @@
 	const JabberIdentity *bc;
 	gint cat_cmp;
 	gint typ_cmp;
-	
+
 	ac = a;
 	bc = b;
 
@@ -723,7 +723,7 @@
 
 	aformtype = jabber_caps_get_formtype(aformtypefield);
 	bformtype = jabber_caps_get_formtype(bformtypefield);
-	
+
 	result = strcmp(aformtype, bformtype);
 	g_free(aformtype);
 	g_free(bformtype);
@@ -737,7 +737,7 @@
 
 	if (!query || strcmp(query->xmlns, "http://jabber.org/protocol/disco#info"))
 		return 0;
-	
+
 	info = g_new0(JabberCapsClientInfo, 1);
 
 	for(child = query->child; child; child = child->next) {
@@ -846,7 +846,7 @@
 		char *type = g_markup_escape_text(id->type, -1);
 		char *lang = NULL;
 		char *name = NULL;
-		
+
 		if (id->lang)
 			lang = g_markup_escape_text(id->lang, -1);
 		if (id->name)
@@ -878,7 +878,7 @@
 
 		while (fields) {
 			GList *value;
-			JabberDataFormField *field = (JabberDataFormField*)fields->data; 
+			JabberDataFormField *field = (JabberDataFormField*)fields->data;
 
 			if (strcmp(field->var, "FORM_TYPE")) {
 				/* Append the "var" attribute */
--- a/libpurple/protocols/jabber/chat.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/chat.c	Mon May 25 19:24:17 2009 +0000
@@ -1204,7 +1204,7 @@
 		data.all_support = &all_support;
 		data.jb = jb;
 
-		g_hash_table_foreach(chat->members, 
+		g_hash_table_foreach(chat->members,
 			jabber_chat_all_participants_have_capability_foreach, &data);
 	} else {
 		all_support = FALSE;
--- a/libpurple/protocols/jabber/data.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/data.c	Mon May 25 19:24:17 2009 +0000
@@ -237,7 +237,7 @@
 	remote_data_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal,
 		g_free, jabber_data_delete);
 
-	jabber_iq_register_handler("data", XEP_0231_NAMESPACE, jabber_data_parse);	
+	jabber_iq_register_handler("data", XEP_0231_NAMESPACE, jabber_data_parse);
 }
 
 void
--- a/libpurple/protocols/jabber/google.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Mon May 25 19:24:17 2009 +0000
@@ -53,16 +53,16 @@
 	GoogleSessionId id;
 	GoogleSessionState state;
 	PurpleMedia *media;
-	JabberStream *js; 
+	JabberStream *js;
 	char *remote_jid;
 } GoogleSession;
 
-static gboolean 
+static gboolean
 google_session_id_equal(gconstpointer a, gconstpointer b)
 {
 	GoogleSessionId *c = (GoogleSessionId*)a;
 	GoogleSessionId *d = (GoogleSessionId*)b;
-	
+
 	return !strcmp(c->id, d->id) && !strcmp(c->initiator, d->initiator);
 }
 
@@ -95,12 +95,12 @@
 	xmlnode_set_attrib(iq->node, "to", session->remote_jid);
 	sess = google_session_create_xmlnode(session, "terminate");
 	xmlnode_insert_child(iq->node, sess);
-	
+
 	jabber_iq_send(iq);
 	google_session_destroy(session);
 }
 
-static void 
+static void
 google_session_send_candidates(PurpleMedia *media, gchar *session_id,
 		gchar *participant, GoogleSession *session)
 {
@@ -383,7 +383,7 @@
 	PurpleMediaCodec *codec;
 	const char *id, *encoding_name,  *clock_rate;
 	GParameter *params;
-	guint num_params;	
+	guint num_params;
 
 	if (session->state != UNINIT) {
 		purple_debug_error("jabber", "Received initiate for active session.\n");
@@ -414,8 +414,8 @@
 
 	desc_element = xmlnode_get_child(sess, "description");
 
-	for (codec_element = xmlnode_get_child(desc_element, "payload-type"); 
-	     codec_element; 
+	for (codec_element = xmlnode_get_child(desc_element, "payload-type");
+	     codec_element;
 	     codec_element = xmlnode_get_next_twin(codec_element)) {
 		encoding_name = xmlnode_get_attrib(codec_element, "name");
 		id = xmlnode_get_attrib(codec_element, "id");
@@ -450,15 +450,15 @@
 	jabber_iq_send(result);
 }
 
-static void 
+static void
 google_session_handle_candidates(JabberStream  *js, GoogleSession *session, xmlnode *sess, const char *iq_id)
 {
 	JabberIq *result;
 	GList *list = NULL;
 	xmlnode *cand;
 	static int name = 0;
-	char n[4];	
-		
+	char n[4];
+
 	for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) {
 		PurpleMediaCandidate *info;
 		const gchar *type = xmlnode_get_attrib(cand, "type");
@@ -667,8 +667,6 @@
 
 			purple_notify_emails(js->gc, count, FALSE, NULL, NULL, default_tos, NULL, NULL, NULL);
 			g_free(bare_jid);
-		} else {
-			purple_notify_emails(js->gc, count, FALSE, NULL, NULL, NULL, NULL, NULL, NULL);
 		}
 
 		return;
@@ -1139,7 +1137,7 @@
 }
 
 static void
-jabber_google_stun_lookup_cb(GSList *hosts, gpointer data, 
+jabber_google_stun_lookup_cb(GSList *hosts, gpointer data,
 	const char *error_message)
 {
 	JabberStream *js = (JabberStream *) data;
@@ -1152,16 +1150,16 @@
 	}
 
 	if (hosts && g_slist_next(hosts)) {
-		struct sockaddr *addr = g_slist_next(hosts)->data; 
+		struct sockaddr *addr = g_slist_next(hosts)->data;
 		char dst[INET6_ADDRSTRLEN];
 		int port;
 
 		if (addr->sa_family == AF_INET6) {
-			inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr, 
+			inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr,
 				dst, sizeof(dst));
 			port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
 		} else {
-			inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr, 
+			inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr,
 				dst, sizeof(dst));
 			port = ntohs(((struct sockaddr_in *) addr)->sin_port);
 		}
@@ -1226,12 +1224,12 @@
 
 			if (host && udp) {
 				int port = atoi(udp);
-				/* if there, would already be an ongoing query, 
+				/* if there, would already be an ongoing query,
 				 cancel it */
 				if (js->stun_query)
 					purple_dnsquery_destroy(js->stun_query);
 
-				js->stun_query = purple_dnsquery_a(host, port, 
+				js->stun_query = purple_dnsquery_a(host, port,
 					jabber_google_stun_lookup_cb, js);
 			}
 		}
@@ -1265,7 +1263,7 @@
 void
 jabber_google_send_jingle_info(JabberStream *js)
 {
-	JabberIq *jingle_info = 
+	JabberIq *jingle_info =
 		jabber_iq_new_query(js, JABBER_IQ_GET, GOOGLE_JINGLE_INFO_NAMESPACE);
 
 	jabber_iq_set_callback(jingle_info, jabber_google_jingle_info_cb,
--- a/libpurple/protocols/jabber/jabber.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon May 25 19:24:17 2009 +0000
@@ -68,7 +68,7 @@
 
 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5)
 
-static PurplePlugin *my_protocol = NULL;
+PurplePlugin *jabber_plugin = NULL;
 GList *jabber_features = NULL;
 GList *jabber_identities = NULL;
 GSList *jabber_cmds = NULL;
@@ -253,7 +253,7 @@
 {
 	const char *xmlns;
 
-	purple_signal_emit(my_protocol, "jabber-receiving-xmlnode", js->gc, packet);
+	purple_signal_emit(jabber_plugin, "jabber-receiving-xmlnode", js->gc, packet);
 
 	/* if the signal leaves us with a null packet, we're done */
 	if(NULL == *packet)
@@ -407,7 +407,7 @@
 	/* If we've got a security layer, we need to encode the data,
 	 * splitting it on the maximum buffer length negotiated */
 
-	purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data);
+	purple_signal_emit(jabber_plugin, "jabber-sending-text", js->gc, &data);
 	if (data == NULL)
 		return;
 
@@ -459,7 +459,7 @@
 	char *txt;
 	int len;
 
-	purple_signal_emit(my_protocol, "jabber-sending-xmlnode", js->gc, &packet);
+	purple_signal_emit(jabber_plugin, "jabber-sending-xmlnode", js->gc, &packet);
 
 	/* if we get NULL back, we're done processing */
 	if(NULL == packet)
@@ -484,7 +484,7 @@
 	JabberStream *js = gc->proto_data;
 
 	if (js->keepalive_timeout == -1) {
-		jabber_ping_jid(js, js->user->domain);
+		jabber_keepalive_ping(js);
 		js->keepalive_timeout = purple_timeout_add_seconds(120,
 				(GSourceFunc)(jabber_keepalive_timeout), gc);
 	}
@@ -596,7 +596,7 @@
 	jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
 }
 
-static void 
+static void
 txt_resolved_cb(GSList *responses, gpointer data)
 {
 	JabberStream *js = data;
@@ -609,7 +609,7 @@
 		purple_connection_error_reason (js->gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
-		return;	
+		return;
 	}
 
 	while (responses) {
@@ -1578,7 +1578,7 @@
 		purple_dnsquery_destroy(js->stun_query);
 		js->stun_query = NULL;
 	}
-		
+
 	g_free(js);
 
 	gc->proto_data = NULL;
@@ -1799,14 +1799,14 @@
 	/* both required according to XEP-0030 */
 	g_return_if_fail(category != NULL);
 	g_return_if_fail(type != NULL);
-	
+
 	for(identity = jabber_identities; identity; identity = identity->next) {
 		JabberIdentity *ident = (JabberIdentity*)identity->data;
 		if (!strcmp(ident->category, category) &&
 		    !strcmp(ident->type, type) &&
 		    ((!ident->lang && !lang) || (ident->lang && lang && !strcmp(ident->lang, lang)))) {
 			return;
-		}	
+		}
 	}
 
 	ident = g_new0(JabberIdentity, 1);
@@ -1920,7 +1920,7 @@
 }
 
 static void
-jabber_tooltip_add_resource_text(JabberBuddyResource *jbr, 
+jabber_tooltip_add_resource_text(JabberBuddyResource *jbr,
 	PurpleNotifyUserInfo *user_info, gboolean multiple_resources)
 {
 	char *text = NULL;
@@ -1953,13 +1953,13 @@
 	g_free(label);
 	g_free(value);
 	g_free(text);
-	
+
 	/* if the resource is idle, show that */
 	/* only show it if there is more than one resource available for
 	the buddy, since the "general" idleness will be shown anyway,
 	this way we can see see the idleness of lower-priority resources */
 	if (jbr->idle && multiple_resources) {
-		gchar *idle_str = 
+		gchar *idle_str =
 			purple_str_seconds_to_string(time(NULL) - jbr->idle);
 		label = g_strdup_printf("%s%s", _("Idle"), (res ? res : ""));
 		purple_notify_user_info_add_pair(user_info, label, idle_str);
@@ -1992,13 +1992,13 @@
 		const char *sub;
 		GList *l;
 		const char *mood;
-		gboolean multiple_resources = 
+		gboolean multiple_resources =
 			jb->resources && g_list_next(jb->resources);
 		JabberBuddyResource *top_jbr = jabber_buddy_find_resource(jb, NULL);
 
 		/* resource-specific info for the top resource */
 		if (top_jbr) {
-			jabber_tooltip_add_resource_text(top_jbr, user_info, 
+			jabber_tooltip_add_resource_text(top_jbr, user_info,
 				multiple_resources);
 		}
 
@@ -2010,7 +2010,7 @@
 					multiple_resources);
 			}
 		}
-		
+
 		if (full) {
 			PurpleStatus *status;
 
@@ -2915,7 +2915,7 @@
 	PurpleAccount *account;
 	gchar *who;
 	PurpleMediaSessionType type;
-	
+
 } JabberMediaRequest;
 
 static void
@@ -2944,7 +2944,7 @@
 #endif
 
 gboolean
-jabber_initiate_media(PurpleAccount *account, const char *who, 
+jabber_initiate_media(PurpleAccount *account, const char *who,
 		      PurpleMediaSessionType type)
 {
 #ifdef USE_VV
@@ -3328,7 +3328,7 @@
 		return FALSE;
 	js = gc->proto_data;
 
-	if (!(resource = jabber_get_resource(jid)) || 
+	if (!(resource = jabber_get_resource(jid)) ||
 	    !(jb = jabber_buddy_find(js, jid, FALSE)) ||
 	    !(jbr = jabber_buddy_find_resource(jb, resource))) {
 		g_free(resource);
@@ -3369,7 +3369,7 @@
 			type = ui_type;
 		}
 	}
-	my_protocol = plugin;
+	jabber_plugin = plugin;
 
 	jabber_add_identity("client", type, NULL, PACKAGE);
 
--- a/libpurple/protocols/jabber/jabber.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Mon May 25 19:24:17 2009 +0000
@@ -75,7 +75,7 @@
 /* Index into attention_types list */
 #define JABBER_BUZZ 0
 
-PurplePlugin *jabber_plugin;
+extern PurplePlugin *jabber_plugin;
 
 typedef enum {
 	JABBER_STREAM_OFFLINE,
@@ -245,7 +245,7 @@
 
 	/* A purple timeout tag for the keepalive */
 	int keepalive_timeout;
-	
+
 	PurpleSrvResponse *srv_rec;
 	guint srv_rec_idx;
 	guint max_srv_rec_idx;
@@ -322,7 +322,7 @@
 void jabber_add_feature(const gchar *namespace, JabberFeatureEnabled cb); /* cb may be NULL */
 void jabber_remove_feature(const gchar *namespace);
 
-/** Adds an identitiy to this jabber library instance. For list of valid values vistit the 
+/** Adds an identitiy to this jabber library instance. For list of valid values vistit the
  *	webiste of the XMPP Registrar ( http://www.xmpp.org/registrar/disco-categories.html#client ).
  *  @param category the category of the identity.
  *  @param type the type of the identity.
--- a/libpurple/protocols/jabber/jutil.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Mon May 25 19:24:17 2009 +0000
@@ -105,6 +105,8 @@
 {
 	char *at;
 	char *slash;
+	char *node = NULL;
+	char *domain;
 	JabberID *jid;
 
 	if(!str || !g_utf8_validate(str, -1, NULL))
@@ -116,22 +118,31 @@
 	slash = g_utf8_strchr(str, -1, '/');
 
 	if(at) {
-		jid->node = g_utf8_normalize(str, at-str, G_NORMALIZE_NFKC);
+		node = g_utf8_normalize(str, at-str, G_NORMALIZE_NFKC);
 		if(slash) {
-			jid->domain = g_utf8_normalize(at+1, slash-(at+1), G_NORMALIZE_NFKC);
+			domain = g_utf8_normalize(at+1, slash-(at+1), G_NORMALIZE_NFKC);
 			jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC);
 		} else {
-			jid->domain = g_utf8_normalize(at+1, -1, G_NORMALIZE_NFKC);
+			domain = g_utf8_normalize(at+1, -1, G_NORMALIZE_NFKC);
 		}
 	} else {
 		if(slash) {
-			jid->domain = g_utf8_normalize(str, slash-str, G_NORMALIZE_NFKC);
+			domain = g_utf8_normalize(str, slash-str, G_NORMALIZE_NFKC);
 			jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC);
 		} else {
-			jid->domain = g_utf8_normalize(str, -1, G_NORMALIZE_NFKC);
+			domain = g_utf8_normalize(str, -1, G_NORMALIZE_NFKC);
 		}
 	}
 
+	if (node) {
+		jid->node = g_utf8_strdown(node, -1);
+		g_free(node);
+	}
+
+	if (domain) {
+		jid->domain = g_utf8_strdown(domain, -1);
+		g_free(domain);
+	}
 
 	if(!jabber_nodeprep_validate(jid->node) ||
 			!jabber_nameprep_validate(jid->domain) ||
@@ -193,32 +204,69 @@
 	JabberStream *js = gc ? gc->proto_data : NULL;
 	static char buf[3072]; /* maximum legal length of a jabber jid */
 	JabberID *jid;
-	char *node, *domain;
 
 	jid = jabber_id_new(in);
 
 	if(!jid)
 		return NULL;
 
-	node = jid->node ? g_utf8_strdown(jid->node, -1) : NULL;
-	domain = g_utf8_strdown(jid->domain, -1);
-
-
-	if(js && node && jid->resource &&
-			jabber_chat_find(js, node, domain))
-		g_snprintf(buf, sizeof(buf), "%s@%s/%s", node, domain,
+	if(js && jid->node && jid->resource &&
+			jabber_chat_find(js, jid->node, jid->domain))
+		g_snprintf(buf, sizeof(buf), "%s@%s/%s", jid->node, jid->domain,
 				jid->resource);
 	else
-		g_snprintf(buf, sizeof(buf), "%s%s%s", node ? node : "",
-				node ? "@" : "", domain);
+		g_snprintf(buf, sizeof(buf), "%s%s%s", jid->node ? jid->node : "",
+				jid->node ? "@" : "", jid->domain);
 
 	jabber_id_free(jid);
-	g_free(node);
-	g_free(domain);
 
 	return buf;
 }
 
+gboolean
+jabber_is_own_server(JabberStream *js, const char *str)
+{
+	JabberID *jid;
+	gboolean equal;
+
+	if (str == NULL)
+		return FALSE;
+
+	g_return_val_if_fail(*str != '\0', FALSE);
+
+	jid = jabber_id_new(str);
+	if (!jid)
+		return FALSE;
+
+	equal = (jid->node == NULL &&
+	         g_str_equal(jid->domain, js->user->domain) &&
+	         jid->resource == NULL);
+	jabber_id_free(jid);
+	return equal;
+}
+
+gboolean
+jabber_is_own_account(JabberStream *js, const char *str)
+{
+	JabberID *jid;
+	gboolean equal;
+
+	if (str == NULL)
+		return TRUE;
+
+	g_return_val_if_fail(*str != '\0', FALSE);
+
+	jid = jabber_id_new(str);
+	if (!jid)
+		return FALSE;
+
+	equal = (g_str_equal(jid->node, js->user->node) &&
+	         g_str_equal(jid->domain, js->user->domain) &&
+	         jid->resource == NULL);
+	jabber_id_free(jid);
+	return equal;
+}
+
 PurpleConversation *
 jabber_find_unnormalized_conv(const char *name, PurpleAccount *account)
 {
--- a/libpurple/protocols/jabber/jutil.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/jutil.h	Mon May 25 19:24:17 2009 +0000
@@ -36,6 +36,12 @@
 
 const char *jabber_normalize(const PurpleAccount *account, const char *in);
 
+/* Returns true if JID is the bare JID of our server. */
+gboolean jabber_is_own_server(JabberStream *js, const char *jid);
+
+/* Returns true if JID is the bare JID of our account. */
+gboolean jabber_is_own_account(JabberStream *js, const char *jid);
+
 gboolean jabber_nodeprep_validate(const char *);
 gboolean jabber_nameprep_validate(const char *);
 gboolean jabber_resourceprep_validate(const char *);
--- a/libpurple/protocols/jabber/libxmpp.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Mon May 25 19:24:17 2009 +0000
@@ -46,8 +46,6 @@
 #include "data.h"
 #include "ibb.h"
 
-PurplePlugin *jabber_plugin = NULL;
-
 static PurplePluginProtocolInfo prpl_info =
 {
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK |
@@ -127,8 +125,6 @@
 
 static gboolean load_plugin(PurplePlugin *plugin)
 {
-	jabber_plugin = plugin;
-
 	purple_signal_register(plugin, "jabber-receiving-xmlnode",
 			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
 			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
@@ -145,7 +141,7 @@
 			     purple_value_new_outgoing(PURPLE_TYPE_STRING));
 
 	purple_signal_register(plugin, "jabber-receiving-message",
-			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
 			purple_value_new(PURPLE_TYPE_BOOLEAN), 6,
 			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
 			purple_value_new(PURPLE_TYPE_STRING), /* type */
@@ -173,13 +169,13 @@
 			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */
 
 	purple_signal_register(plugin, "jabber-register-namespace-watcher",
-			purple_marshal_VOID__POINTER_POINTER_POINTER,
+			purple_marshal_VOID__POINTER_POINTER,
 			NULL, 2,
 			purple_value_new(PURPLE_TYPE_STRING),  /* node */
 			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
 
 	purple_signal_register(plugin, "jabber-unregister-namespace-watcher",
-			purple_marshal_VOID__POINTER_POINTER_POINTER,
+			purple_marshal_VOID__POINTER_POINTER,
 			NULL, 2,
 			purple_value_new(PURPLE_TYPE_STRING),  /* node */
 			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
@@ -219,8 +215,6 @@
 	/* Stay on target...stay on target... Almost there... */
 	jabber_uninit_plugin();
 
-	jabber_plugin = NULL;
-
 	return TRUE;
 }
 
--- a/libpurple/protocols/jabber/message.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/message.c	Mon May 25 19:24:17 2009 +0000
@@ -639,7 +639,7 @@
 					purple_debug_info("jabber", "found %d smileys\n",
 						g_list_length(smiley_refs));
 
-					if (smiley_refs) {		
+					if (smiley_refs) {
 						if (jm->type == JABBER_MESSAGE_GROUPCHAT) {
 							JabberID *jid = jabber_id_new(jm->from);
 							JabberChat *chat = NULL;
@@ -657,7 +657,7 @@
 									who, account);
 							if (!conv) {
 								/* we need to create the conversation here */
-								conv = 
+								conv =
 									purple_conversation_new(PURPLE_CONV_TYPE_IM,
 									account, who);
 							}
@@ -949,7 +949,7 @@
 				/* do not attempt to send custom smileys in a MUC with more than
 				 10 people, to avoid getting too many BoB requests */
 				return jabber_chat_get_num_participants(chat) <= 10 &&
-					jabber_chat_all_participants_have_capability(chat, 
+					jabber_chat_all_participants_have_capability(chat,
 						XEP_0231_NAMESPACE);
 			} else {
 				return FALSE;
@@ -1175,7 +1175,6 @@
 	jm->chat_state = JM_STATE_ACTIVE;
 	jm->to = g_strdup(who);
 	jm->id = jabber_get_next_id(jm->js);
-	jm->chat_state = JM_STATE_ACTIVE;
 
 	if(jbr) {
 		if(jbr->thread_id)
--- a/libpurple/protocols/jabber/ping.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/ping.c	Mon May 25 19:24:17 2009 +0000
@@ -28,7 +28,9 @@
 #include "ping.h"
 #include "iq.h"
 
-static void jabber_keepalive_pong_cb(JabberStream *js)
+static void jabber_keepalive_pong_cb(JabberStream *js, const char *from,
+                                     JabberIqType type, const char *id,
+                                     xmlnode *packet, gpointer data)
 {
 	if (js->keepalive_timeout >= 0) {
 		purple_timeout_remove(js->keepalive_timeout);
@@ -57,16 +59,23 @@
                                   JabberIqType type, const char *id,
                                   xmlnode *packet, gpointer data)
 {
-	if (purple_strequal(from, js->user->domain))
-		/* If the pong is from the server, assume it's a result of the
-		 * keepalive functions */
-		jabber_keepalive_pong_cb(js);
-	else {
-		if (type == JABBER_IQ_RESULT)
-			purple_debug_info("jabber", "PONG!\n");
-		else
-			purple_debug_info("jabber", "ping not supported\n");
-	}
+	if (type == JABBER_IQ_RESULT)
+		purple_debug_info("jabber", "PONG!\n");
+	else
+		purple_debug_info("jabber", "ping not supported\n");
+}
+
+void jabber_keepalive_ping(JabberStream *js)
+{
+	JabberIq *iq;
+	xmlnode *ping;
+
+	iq = jabber_iq_new(js, JABBER_IQ_GET);
+	ping = xmlnode_new_child(iq->node, "ping");
+	xmlnode_set_namespace(ping, "urn:xmpp:ping");
+
+	jabber_iq_set_callback(iq, jabber_keepalive_pong_cb, NULL);
+	jabber_iq_send(iq);
 }
 
 gboolean jabber_ping_jid(JabberStream *js, const char *jid)
--- a/libpurple/protocols/jabber/ping.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/ping.h	Mon May 25 19:24:17 2009 +0000
@@ -29,5 +29,6 @@
 void jabber_ping_parse(JabberStream *js, const char *from,
                        JabberIqType, const char *id, xmlnode *child);
 gboolean jabber_ping_jid(JabberStream *js, const char *jid);
+void jabber_keepalive_ping(JabberStream *js);
 
 #endif /* PURPLE_JABBER_PING_H_ */
--- a/libpurple/protocols/jabber/presence.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/presence.c	Mon May 25 19:24:17 2009 +0000
@@ -100,7 +100,7 @@
 
 	if (!purple_account_is_connected(account))
 		return;
-	
+
 	if (!purple_status_is_active(status))
 		return;
 
@@ -277,7 +277,7 @@
 		xmlnode *query = xmlnode_new_child(presence, "query");
 		gchar seconds[10];
 		g_snprintf(seconds, 10, "%d", (int) (time(NULL) - js->idle));
-		
+
 		xmlnode_set_namespace(query, "jabber:iq:last");
 		xmlnode_set_attrib(query, "seconds", seconds);
 	}
@@ -361,10 +361,10 @@
 		if ((nick = xmlnode_get_child(vcard, "NICKNAME"))) {
 			char *tmp = xmlnode_get_data(nick);
 			char *bare_jid = jabber_get_bare_jid(from);
-			if (strstr(bare_jid, tmp) == NULL) {
+			if (tmp && strstr(bare_jid, tmp) == NULL) {
 				g_free(nickname);
 				nickname = tmp;
-			} else
+			} else if (tmp)
 				g_free(tmp);
 
 			g_free(bare_jid);
@@ -626,7 +626,7 @@
 					avatar_hash = xmlnode_get_data(photo);
 				}
 			}
-		} else if (!strcmp(y->name, "query") && 
+		} else if (!strcmp(y->name, "query") &&
 			!strcmp(xmlnode_get_namespace(y), "jabber:iq:last")) {
 			/* resource has specified idle */
 			const gchar *seconds = xmlnode_get_attrib(y, "seconds");
@@ -644,7 +644,7 @@
 			NULL);
 		purple_debug_info("jabber", "got delay %s yielding %ld s offset\n",
 			stamp, offset);
-		idle += offset; 
+		idle += offset;
 	}
 
 	if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) {
--- a/libpurple/protocols/jabber/si.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Mon May 25 19:24:17 2009 +0000
@@ -441,9 +441,11 @@
 		purple_xfer_cancel_remote(xfer);
 		return;
 	} else if(jsx->rxlen - 5 <  jsx->rxqueue[4] + 2) {
-		purple_debug_info("jabber", "reading %u bytes for DST.ADDR + port num (trying to read %u now)\n",
-				  jsx->rxqueue[4] + 2, jsx->rxqueue[4] + 2 - (jsx->rxlen - 5));
-		len = read(source, buffer, jsx->rxqueue[4] + 2 - (jsx->rxlen - 5));
+		/* Upper-bound of 257 (jsx->rxlen = 5, jsx->rxqueue[4] = 0xFF) */
+		unsigned short to_read = jsx->rxqueue[4] + 2 - (jsx->rxlen - 5);
+		purple_debug_info("jabber", "reading %u bytes for DST.ADDR + port num (trying to read %hu now)\n",
+				  jsx->rxqueue[4] + 2, to_read);
+		len = read(source, buffer, to_read);
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
@@ -586,10 +588,12 @@
 		memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
 		jsx->rxlen += len;
 		return;
-	} else if(jsx->rxlen - 2 <  jsx->rxqueue[1]) {
-		purple_debug_info("jabber", "reading %u bytes for auth methods (trying to read %u now)\n",
-				  jsx->rxqueue[1], jsx->rxqueue[1] - (jsx->rxlen - 2));
-		len = read(source, buffer, jsx->rxqueue[1] - (jsx->rxlen - 2));
+	} else if(jsx->rxlen - 2 < jsx->rxqueue[1]) {
+		/* Has a maximum value of 255 (jsx->rxlen = 2, jsx->rxqueue[1] = 0xFF) */
+		unsigned short to_read = jsx->rxqueue[1] - (jsx->rxlen - 2);
+		purple_debug_info("jabber", "reading %u bytes for auth methods (trying to read %hu now)\n",
+				  jsx->rxqueue[1], to_read);
+		len = read(source, buffer, to_read);
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
@@ -1693,8 +1697,8 @@
 void
 jabber_si_init(void)
 {
-	jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse);	
-	
+	jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse);
+
 	jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
 }
 
--- a/libpurple/protocols/msn/contact.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Mon May 25 19:24:17 2009 +0000
@@ -357,29 +357,36 @@
 	MsnUser *user = msn_userlist_find_add_user(session->userlist, passport, NULL);
 	xmlnode *annotation;
 	guint nid = MSN_NETWORK_UNKNOWN;
+	char *invite = NULL;
 
-	/* For EmailMembers, the network must be found in the annotations. */
-	if (!strcmp(node, "PassportName")) {
+	for (annotation = xmlnode_get_child(member, "Annotations/Annotation");
+	     annotation;
+	     annotation = xmlnode_get_next_twin(annotation)) {
+		char *name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
+		char *value = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+		if (name && value) {
+			if (!strcmp(name, "MSN.IM.BuddyType")) {
+				nid = strtoul(value, NULL, 10);
+			}
+			else if (!strcmp(name, "MSN.IM.InviteMessage")) {
+				invite = value;
+				value = NULL;
+			}
+		}
+		g_free(name);
+		g_free(value);
+	}
+
+	/* For EmailMembers, the network must be found in the annotations, above.
+	   Otherwise, PassportMembers are on the Passport network. */
+	if (!strcmp(node, "PassportName"))
 		nid = MSN_NETWORK_PASSPORT;
-	} else {
-		for (annotation = xmlnode_get_child(member, "Annotations/Annotation");
-		     annotation;
-		     annotation = xmlnode_get_next_twin(annotation)) {
-			char *name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
-			if (name && !strcmp(name, "MSN.IM.BuddyType")) {
-				char *value = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
-				if (value != NULL)
-					nid = strtoul(value, NULL, 10);
-				g_free(value);
-			}
-			g_free(name);
-		}
-	}
 
 	purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s, NetworkID: %u\n",
 		node, passport, type, member_id == NULL ? "(null)" : member_id, nid);
 
 	msn_user_set_network(user, nid);
+	msn_user_set_invite_message(user, invite);
 
 	if (member_id) {
 		user->membership_id[list] = atoi(member_id);
@@ -390,6 +397,7 @@
 	g_free(passport);
 	g_free(type);
 	g_free(member_id);
+	g_free(invite);
 }
 
 static void
@@ -717,8 +725,6 @@
 					g_free(msnEnabled);
 				}
 			}
-			if (passport == NULL) /* Couldn't find anything */
-				continue;
 		} else {
 			xmlnode *messenger_user;
 			/* ignore non-messenger contacts */
@@ -736,6 +742,7 @@
 			passport = xmlnode_get_data(passportName);
 		}
 
+		/* Couldn't find anything */
 		if (passport == NULL)
 			continue;
 
--- a/libpurple/protocols/msn/notification.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Mon May 25 19:24:17 2009 +0000
@@ -149,48 +149,6 @@
 }
 
 /**************************************************************************
- * Util
- **************************************************************************/
-
-static void
-group_error_helper(MsnSession *session, const char *msg, const char *group_id, int error)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	char *reason = NULL;
-	char *title = NULL;
-
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	if (error == 224)
-	{
-		if (group_id == 0)
-		{
-			return;
-		}
-		else
-		{
-			const char *group_name;
-			group_name = msn_userlist_find_group_name(session->userlist,group_id);
-			reason = g_strdup_printf(_("%s is not a valid group."),
-									 group_name ? group_name : "");
-		}
-	}
-	else
-	{
-		reason = g_strdup(_("Unknown error."));
-	}
-
-	title = g_strdup_printf(_("%s on %s (%s)"), msg,
-						  purple_account_get_username(account),
-						  purple_account_get_protocol_name(account));
-	purple_notify_error(gc, NULL, title, reason);
-	g_free(title);
-	g_free(reason);
-}
-
-/**************************************************************************
  * Login
  **************************************************************************/
 
@@ -961,10 +919,10 @@
 
 			passport = g_strdup_printf("%s@%s", local, domain);
 
-			if (!g_ascii_isdigit(cmd->command[0]) && type != NULL)
+			if (g_ascii_isdigit(cmd->command[0]))
+				network = MSN_NETWORK_UNKNOWN;
+			else if (type != NULL)
 				network = (MsnNetwork)strtoul(type, NULL, 10);
-			else
-				network = MSN_NETWORK_UNKNOWN;
 
 			purple_debug_info("msn", "FQY response says %s is from network %d\n",
 			                  passport, network);
@@ -1102,38 +1060,6 @@
 }
 
 static void
-adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	gint group_id;
-	const char *group_name;
-
-	session = cmdproc->session;
-
-	group_id = atoi(cmd->params[3]);
-
-	group_name = purple_url_decode(cmd->params[2]);
-
-	msn_group_new(session->userlist, cmd->params[3], group_name);
-
-	/* There is a user that must be moved to this group */
-	if (cmd->trans->data)
-	{
-		/* msn_userlist_move_buddy(); */
-		MsnUserList *userlist = cmdproc->session->userlist;
-		MsnCallbackState *data = cmd->trans->data;
-
-		if (data->old_group_name != NULL)
-		{
-			msn_userlist_move_buddy(userlist, data->who, data->old_group_name, group_name);
-			g_free(data->old_group_name);
-		} else {
-			/* msn_add_contact_to_group(userlist, data, data->who, group_name); */
-		}
-	}
-}
-
-static void
 qng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	/* TODO: Call PNG after the timeout specified. */
@@ -1468,61 +1394,6 @@
 	}
 }
 
-static void
-reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	const char *group_id, *group_name;
-
-	session = cmdproc->session;
-	group_id = cmd->params[2];
-	group_name = purple_url_decode(cmd->params[3]);
-
-	msn_userlist_rename_group_id(session->userlist, group_id, group_name);
-}
-
-static void
-reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	const char * group_id;
-	char **params;
-
-	params = g_strsplit(trans->params, " ", 0);
-
-	group_id = params[0];
-
-	group_error_helper(cmdproc->session, _("Unable to rename group"), group_id, error);
-
-	g_strfreev(params);
-}
-
-static void
-rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	const char *group_id;
-
-	session = cmdproc->session;
-	group_id = cmd->params[2];
-
-	msn_userlist_remove_group_id(session->userlist, group_id);
-}
-
-static void
-rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	const char *group_id;
-	char **params;
-
-	params = g_strsplit(trans->params, " ", 0);
-
-	group_id = params[0];
-
-	group_error_helper(cmdproc->session, _("Unable to delete group"), group_id, error);
-
-	g_strfreev(params);
-}
-
 /**************************************************************************
  * Misc commands
  **************************************************************************/
@@ -2149,9 +2020,6 @@
 	msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd);
 	msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd);
 	msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd);
-	msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd);
-	msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd);
-	msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd);
 	msn_table_add_cmd(cbs_table, "XFR", "XFR", xfr_cmd);
 
 	/* Asynchronous */
@@ -2187,8 +2055,6 @@
 	msn_table_add_error(cbs_table, "ADD", add_error);
 	msn_table_add_error(cbs_table, "ADL", adl_error);
 	msn_table_add_error(cbs_table, "FQY", fqy_error);
-	msn_table_add_error(cbs_table, "REG", reg_error);
-	msn_table_add_error(cbs_table, "RMG", rmg_error);
 	msn_table_add_error(cbs_table, "USR", usr_error);
 
 	msn_table_add_msg_type(cbs_table,
--- a/libpurple/protocols/msn/slplink.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/msn/slplink.c	Mon May 25 19:24:17 2009 +0000
@@ -592,7 +592,7 @@
 		if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
 		{
 			purple_debug_error("msn",
-				"Oversized slpmsg - msgsize=%lld offset=%" G_GSIZE_FORMAT " len=%" G_GSIZE_FORMAT "\n",
+				"Oversized slpmsg - msgsize=%lld offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n",
 				slpmsg->size, offset, len);
 			g_return_if_reached();
 		}
--- a/libpurple/protocols/msn/user.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/msn/user.c	Mon May 25 19:24:17 2009 +0000
@@ -74,6 +74,7 @@
 	g_free(user->media.title);
 	g_free(user->media.album);
 	g_free(user->statusline);
+	g_free(user->invite_message);
 
 	g_free(user);
 }
@@ -426,6 +427,15 @@
 	user->clientcaps = info;
 }
 
+void
+msn_user_set_invite_message(MsnUser *user, const char *message)
+{
+	g_return_if_fail(user != NULL);
+
+	g_free(user->invite_message);
+	user->invite_message = g_strdup(message);
+}
+
 const char *
 msn_user_get_passport(const MsnUser *user)
 {
@@ -489,3 +499,12 @@
 
 	return user->clientcaps;
 }
+
+const char *
+msn_user_get_invite_message(const MsnUser *user)
+{
+	g_return_val_if_fail(user != NULL, NULL);
+
+	return user->invite_message;
+}
+
--- a/libpurple/protocols/msn/user.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/msn/user.h	Mon May 25 19:24:17 2009 +0000
@@ -105,6 +105,8 @@
 
 	guint membership_id[5];	/**< The membershipId sent by the contacts server,
 				     indexed by the list it belongs to		*/
+
+	char *invite_message;   /**< Invite message of user request */
 };
 
 /**************************************************************************
@@ -290,6 +292,14 @@
  */
 void msn_user_set_client_caps(MsnUser *user, GHashTable *info);
 
+/**
+ * Sets the invite message for a user.
+ *
+ * @param user    The user.
+ * @param message The invite message for a user.
+ */
+void msn_user_set_invite_message(MsnUser *user, const char *message);
+
 
 /**
  * Returns the passport account for a user.
@@ -373,6 +383,15 @@
 GHashTable *msn_user_get_client_caps(const MsnUser *user);
 
 /**
+ * Returns the invite message for a user.
+ *
+ * @param user The user.
+ *
+ * @return The user's invite message.
+ */
+const char *msn_user_get_invite_message(const MsnUser *user);
+
+/**
  * check to see if user is online
  */
 gboolean
--- a/libpurple/protocols/msn/userlist.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/msn/userlist.c	Mon May 25 19:24:17 2009 +0000
@@ -86,7 +86,7 @@
 }
 
 static void
-got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly)
+got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly, const char *message)
 {
 	PurpleAccount *acct;
 	MsnPermitAdd *pa;
@@ -97,7 +97,7 @@
 	pa->gc = gc;
 
 	acct = purple_connection_get_account(gc);
-	purple_account_request_authorization(acct, passport, NULL, friendly, NULL,
+	purple_account_request_authorization(acct, passport, NULL, friendly, message,
 										 purple_find_buddy(acct, passport) != NULL,
 										 msn_accept_add_cb, msn_cancel_add_cb, pa);
 
@@ -142,155 +142,6 @@
  * Server functions
  **************************************************************************/
 
-MsnListId
-msn_get_list_id(const char *list)
-{
-	if (list[0] == 'F')
-		return MSN_LIST_FL;
-	else if (list[0] == 'A')
-		return MSN_LIST_AL;
-	else if (list[0] == 'B')
-		return MSN_LIST_BL;
-	else if (list[0] == 'R')
-		return MSN_LIST_RL;
-
-	return -1;
-}
-
-/* this function msn_got_add_user isn't called anywhere */
-void
-msn_got_add_user(MsnSession *session, MsnUser *user,
-				 MsnListId list_id, const char * group_id)
-{
-	PurpleAccount *account;
-	const char *passport;
-	const char *friendly;
-
-	purple_debug_info("msn", "got add user...\n");
-	account = session->account;
-
-	passport = msn_user_get_passport(user);
-	friendly = msn_user_get_friendly_name(user);
-
-	if (list_id == MSN_LIST_FL)
-	{
-		PurpleConnection *gc;
-
-		gc = purple_account_get_connection(account);
-
-		serv_got_alias(gc, passport, friendly);
-
-		if (group_id != NULL)
-		{
-			msn_user_add_group_id(user, group_id);
-		}
-	}
-	else if (list_id == MSN_LIST_AL)
-	{
-		purple_privacy_permit_add(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_BL)
-	{
-		purple_privacy_deny_add(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_RL)
-	{
-		PurpleConversation *convo;
-
-		purple_debug_info("msn",
-						"%s has added you to his or her buddy list.\n",
-						passport);
-
- 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account);
- 		if (convo) {
- 			PurpleBuddy *buddy;
- 			char *msg;
-
- 			buddy = purple_find_buddy(account, passport);
- 			msg = g_strdup_printf(
- 				_("%s has added you to his or her buddy list."),
- 				buddy ? purple_buddy_get_contact_alias(buddy) : passport);
- 			purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg,
- 				PURPLE_MESSAGE_SYSTEM, time(NULL));
- 			g_free(msg);
- 		}
-
-		if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
-		{
-			/*
-			 * TODO: The friendly name was NULL for me when I
-			 *       looked at this.  Maybe we should use the store
-			 *       name instead? --KingAnt
-			 */
-/*			got_new_entry(gc, passport, friendly); */
-		}
-	}
-
-	user->list_op |= (1 << list_id);
-	/* purple_user_add_list_id (user, list_id); */
-}
-
-void
-msn_got_rem_user(MsnSession *session, MsnUser *user,
-				 MsnListId list_id, const char * group_id)
-{
-	PurpleAccount *account;
-	const char *passport;
-
-	account = session->account;
-
-	passport = msn_user_get_passport(user);
-
-	if (list_id == MSN_LIST_FL)
-	{
-		/* TODO: When is the user totally removed? */
-		if (group_id != NULL)
-		{
-			msn_user_remove_group_id(user, group_id);
-			return;
-		}
-	}
-	else if (list_id == MSN_LIST_AL)
-	{
-		purple_privacy_permit_remove(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_BL)
-	{
-		purple_privacy_deny_remove(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_RL)
-	{
-		PurpleConversation *convo;
-
-		purple_debug_info("msn",
-						"%s has removed you from his or her buddy list.\n",
-						passport);
-
-		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account);
-		if (convo) {
-			PurpleBuddy *buddy;
-			char *msg;
-
-			buddy = purple_find_buddy(account, passport);
-			msg = g_strdup_printf(
-				_("%s has removed you from his or her buddy list."),
-				buddy ? purple_buddy_get_contact_alias(buddy) : passport);
-			purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg,
-				PURPLE_MESSAGE_SYSTEM, time(NULL));
-			g_free(msg);
-		}
-	}
-
-	user->list_op &= ~(1 << list_id);
-	/* purple_user_remove_list_id (user, list_id); */
-
-	if (user->list_op == 0)
-	{
-		purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
-						passport);
-	}
-}
-
 void
 msn_got_lst_user(MsnSession *session, MsnUser *user,
 				 int list_op, GSList *group_ids)
@@ -299,12 +150,14 @@
 	PurpleAccount *account;
 	const char *passport;
 	const char *store;
+	const char *message;
 
 	account = session->account;
 	gc = purple_account_get_connection(account);
 
 	passport = msn_user_get_passport(user);
 	store = msn_user_get_friendly_name(user);
+	message = msn_user_get_invite_message(user);
 
 	msn_user_set_op(user, list_op);
 
@@ -348,13 +201,13 @@
 
 		if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
 		{
-/*			got_new_entry(gc, passport, store); */
+/*			got_new_entry(gc, passport, store, NULL); */
 		}
 	}
 
 	if (list_op & MSN_LIST_PL_OP)
 	{
-		got_new_entry(gc, passport, store);
+		got_new_entry(gc, passport, store, message);
 	}
 }
 
--- a/libpurple/protocols/msn/userlist.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/msn/userlist.h	Mon May 25 19:24:17 2009 +0000
@@ -57,12 +57,7 @@
 
 gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id);
 gboolean msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id);
-MsnListId msn_get_list_id(const char *list);
 
-void msn_got_add_user(MsnSession *session, MsnUser *user,
-					  MsnListId list_id, const char *group_id);
-void msn_got_rem_user(MsnSession *session, MsnUser *user,
-					  MsnListId list_id, const char *group_id);
 void msn_got_lst_user(MsnSession *session, MsnUser *user,
 					  int list_op, GSList *group_ids);
 
--- a/libpurple/protocols/oscar/family_locate.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Mon May 25 19:24:17 2009 +0000
@@ -68,8 +68,12 @@
 	 {0x09, 0x46, 0x00, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
+	{OSCAR_CAPABILITY_VIDEO,
+	 {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
 	/* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */
-	{OSCAR_CAPABILITY_VIDEO,
+	{OSCAR_CAPABILITY_LIVEVIDEO,
 	 {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
--- a/libpurple/protocols/oscar/oscar.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon May 25 19:24:17 2009 +0000
@@ -714,6 +714,9 @@
 			case OSCAR_CAPABILITY_ICHATAV:
 				tmp = _("iChat AV");
 				break;
+			case OSCAR_CAPABILITY_LIVEVIDEO:
+				tmp = _("Live Video");
+				break;
 			case OSCAR_CAPABILITY_CAMERA:
 				tmp = _("Camera");
 				break;
@@ -3563,7 +3566,8 @@
 	alerturl  = va_arg(ap, char *);
 	va_end(ap);
 
-	if (account != NULL && emailinfo != NULL && emailinfo->unread && havenewmail) {
+	if (account != NULL && emailinfo != NULL && purple_account_get_check_mail(account) &&
+			emailinfo->unread && havenewmail) {
 		gchar *to = g_strdup_printf("%s%s%s",
 				purple_account_get_username(account),
 				emailinfo->domain ? "@" : "",
--- a/libpurple/protocols/oscar/oscar.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Mon May 25 19:24:17 2009 +0000
@@ -369,11 +369,12 @@
 	OSCAR_CAPABILITY_SMS                  = 0x00400000,
 	OSCAR_CAPABILITY_VIDEO                = 0x00800000,
 	OSCAR_CAPABILITY_ICHATAV              = 0x01000000,
-	OSCAR_CAPABILITY_CAMERA               = 0x02000000,
-	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x04000000,
-	OSCAR_CAPABILITY_TYPING               = 0x08000000,
-	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x10000000,
-	OSCAR_CAPABILITY_LAST                 = 0x20000000
+	OSCAR_CAPABILITY_LIVEVIDEO            = 0x02000000,
+	OSCAR_CAPABILITY_CAMERA               = 0x04000000,
+	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x08000000,
+	OSCAR_CAPABILITY_TYPING               = 0x10000000,
+	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x20000000,
+	OSCAR_CAPABILITY_LAST                 = 0x40000000
 } OscarCapability;
 
 /*
--- a/libpurple/protocols/yahoo/yahoo.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Mon May 25 19:24:17 2009 +0000
@@ -28,7 +28,7 @@
 #include "circbuffer.h"
 #include "prpl.h"
 
-#define YAHOO_PAGER_HOST "scs.msg.yahoo.com"
+#define YAHOO_PAGER_HOST "scsa.msg.yahoo.com"
 #define YAHOO_PAGER_PORT 5050
 #define YAHOO_PAGER_PORT_P2P 5101
 #define YAHOO_LOGIN_URL "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s"
--- a/libpurple/prpl.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/prpl.h	Mon May 25 19:24:17 2009 +0000
@@ -126,10 +126,10 @@
 typedef enum
 {
 	/**
-	 * Use a unique name, not an alias, for chat rooms.
+	 * User names are unique to a chat and are not shared between rooms.
 	 *
-	 * XMPP lets you choose what name you want for chat.
-	 * So it shouldn't be pulling the alias for when you're in chat;
+	 * XMPP lets you choose what name you want in chats, so it shouldn't
+	 * be pulling the aliases from the buddy list for the chat list;
 	 * it gets annoying.
 	 */
 	OPT_PROTO_UNIQUE_CHATNAME = 0x00000004,
--- a/libpurple/signals.c	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/signals.c	Mon May 25 19:24:17 2009 +0000
@@ -975,6 +975,26 @@
 }
 
 void
+purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER(
+		PurpleCallback cb, va_list args, void *data, void **return_val)
+{
+	gboolean ret_val;
+	void *arg1 = va_arg(args, void *);
+	void *arg2 = va_arg(args, void *);
+	void *arg3 = va_arg(args, void *);
+	void *arg4 = va_arg(args, void *);
+	void *arg5 = va_arg(args, void *);
+	void *arg6 = va_arg(args, void *);
+
+	ret_val =
+		((gboolean (*)(void *, void *, void *, void *, void *, void *, void *))cb)(
+			arg1, arg2, arg3, arg4, arg5, arg6, data);
+
+	if (return_val != NULL)
+		*return_val = GINT_TO_POINTER(ret_val);
+}
+
+void
 purple_marshal_BOOLEAN__INT_POINTER(PurpleCallback cb, va_list args, void *data,
                                   void **return_val)
 {
--- a/libpurple/signals.h	Sun May 17 19:04:15 2009 +0000
+++ b/libpurple/signals.h	Mon May 25 19:24:17 2009 +0000
@@ -349,6 +349,8 @@
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
+void purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER(
+		PurpleCallback cb, va_list args, void *data, void **return_val);
 
 void purple_marshal_BOOLEAN__INT_POINTER(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
--- a/pidgin/Makefile.am	Sun May 17 19:04:15 2009 +0000
+++ b/pidgin/Makefile.am	Mon May 25 19:24:17 2009 +0000
@@ -140,8 +140,6 @@
 	gtkcellrendererprogress.h \
 	gtkcellview.h \
 	gtkcellviewmenuitem.h \
-	gtkcellview.h \
-	gtkcellviewmenuitem.h \
 	gtkcertmgr.h \
 	pidgincombobox.h \
 	gtkconn.h \
--- a/pidgin/gtkconv.c	Sun May 17 19:04:15 2009 +0000
+++ b/pidgin/gtkconv.c	Mon May 25 19:24:17 2009 +0000
@@ -2700,7 +2700,7 @@
 	scale_width = gdk_pixbuf_get_width(buf);
 	scale_height = gdk_pixbuf_get_height(buf);
 
-	gtk_widget_get_size_request(gtkconv->infopane_hbox, NULL, &size);
+	gtk_widget_get_size_request(gtkconv->u.im->icon_container, NULL, &size);
 	size = MIN(size, MIN(scale_width, scale_height));
 	size = CLAMP(size, BUDDYICON_SIZE_MIN, BUDDYICON_SIZE_MAX);
 
@@ -2758,13 +2758,22 @@
 static void
 remove_icon(GtkWidget *widget, PidginConversation *gtkconv)
 {
+	GList *children;
+	GtkWidget *event;
 	PurpleConversation *conv = gtkconv->active_conv;
 	PidginWindow *gtkwin;
 
 	g_return_if_fail(conv != NULL);
 
-	if (gtkconv->u.im->icon_container != NULL)
-		gtk_widget_destroy(gtkconv->u.im->icon_container);
+	gtk_widget_set_size_request(gtkconv->u.im->icon_container, -1, BUDDYICON_SIZE_MIN);
+	children = gtk_container_get_children(GTK_CONTAINER(gtkconv->u.im->icon_container));
+	if (children) {
+		/* We know there's only one child here. It'd be nice to shortcut to the
+		   event box, but we can't change the PidginConversation until 3.0 */
+		event = (GtkWidget *)children->data;
+		gtk_container_remove(GTK_CONTAINER(gtkconv->u.im->icon_container), event);
+		g_list_free(children);
+	}
 
 	if (gtkconv->u.im->anim != NULL)
 		g_object_unref(G_OBJECT(gtkconv->u.im->anim));
@@ -2779,7 +2788,6 @@
 	gtkconv->u.im->icon = NULL;
 	gtkconv->u.im->anim = NULL;
 	gtkconv->u.im->iter = NULL;
-	gtkconv->u.im->icon_container = NULL;
 	gtkconv->u.im->show_icon = FALSE;
 
 	gtkwin = gtkconv->win;
@@ -2840,7 +2848,7 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 	GSList *buddies;
 
-	gtk_widget_get_size_request(gtkconv->infopane_hbox, NULL, &size);
+	gtk_widget_get_size_request(gtkconv->u.im->icon_container, NULL, &size);
 
 	if (size == BUDDYICON_SIZE_MAX) {
 		size = BUDDYICON_SIZE_MIN;
@@ -2848,7 +2856,7 @@
 		size = BUDDYICON_SIZE_MAX;
 	}
 
-	gtk_widget_set_size_request(gtkconv->infopane_hbox, -1, size);
+	gtk_widget_set_size_request(gtkconv->u.im->icon_container, -1, size);
 	pidgin_conv_update_buddy_icon(conv);
 
 	buddies = purple_find_buddies(purple_conversation_get_account(conv),
@@ -4629,6 +4637,7 @@
 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 
 		gtkchat->topic_text = gtk_entry_new();
+		gtk_widget_set_size_request(gtkchat->topic_text, -1, BUDDYICON_SIZE_MIN);
 
 		if(prpl_info->set_chat_topic == NULL) {
 			gtk_editable_set_editable(GTK_EDITABLE(gtkchat->topic_text), FALSE);
@@ -4847,15 +4856,37 @@
 	gtk_cell_view_set_displayed_row(GTK_CELL_VIEW(gtkconv->infopane), path);
 	gtk_tree_path_free(path);
 
-	if ((buddy = purple_find_buddy(purple_conversation_get_account(conv),
-					purple_conversation_get_name(conv))) != NULL) {
-		PurpleContact *contact = purple_buddy_get_contact(buddy);
-		if (contact) {
-			buddyicon_size = purple_blist_node_get_int((PurpleBlistNode*)contact, "pidgin-infopane-iconsize");
-		}
-	}
-	buddyicon_size = CLAMP(buddyicon_size, BUDDYICON_SIZE_MIN, BUDDYICON_SIZE_MAX);
-	gtk_widget_set_size_request(gtkconv->infopane_hbox, -1, buddyicon_size);
+	if (chat) {
+		/* This empty widget is used to ensure that the infopane is consistently
+		   sized for chat windows. The correct fix is to put an icon in the chat
+		   window as well, because that would make "Set Custom Icon" consistent
+		   for both the buddy list and the chat window, but PidginConversation
+		   is pretty much stuck until 3.0. */
+		GtkWidget *sizing_vbox;
+		sizing_vbox = gtk_vbox_new(FALSE, 0);
+		gtk_widget_set_size_request(sizing_vbox, -1, BUDDYICON_SIZE_MIN);
+		gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox), sizing_vbox, FALSE, FALSE, 0);
+		gtk_widget_show(sizing_vbox);
+	}
+	else {
+		gtkconv->u.im->icon_container = gtk_vbox_new(FALSE, 0);
+
+		if ((buddy = purple_find_buddy(purple_conversation_get_account(conv),
+						purple_conversation_get_name(conv))) != NULL) {
+			PurpleContact *contact = purple_buddy_get_contact(buddy);
+			if (contact) {
+				buddyicon_size = purple_blist_node_get_int((PurpleBlistNode*)contact, "pidgin-infopane-iconsize");
+			}
+		}
+		buddyicon_size = CLAMP(buddyicon_size, BUDDYICON_SIZE_MIN, BUDDYICON_SIZE_MAX);
+		gtk_widget_set_size_request(gtkconv->u.im->icon_container, -1, buddyicon_size);
+
+		gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox),
+				   gtkconv->u.im->icon_container, FALSE, FALSE, 0);
+
+		gtk_widget_show(gtkconv->u.im->icon_container);
+	}
+
 	gtk_widget_show(gtkconv->infopane);
 
 	rend = gtk_cell_renderer_pixbuf_new();
@@ -6310,7 +6341,7 @@
 		return;
 
 	smiley->data = g_realloc(smiley->data, smiley->datasize + size);
-	g_memmove(smiley->data + smiley->datasize, data, size);
+	g_memmove((guchar *)smiley->data + smiley->datasize, data, size);
 	smiley->datasize += size;
 
 	loader = smiley->loader;
@@ -6909,6 +6940,7 @@
 
 	GdkPixbuf *buf;
 
+	GList *children;
 	GtkWidget *event;
 	GdkPixbuf *scale;
 	int scale_width, scale_height;
@@ -6936,9 +6968,14 @@
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl);
 
 	/* Remove the current icon stuff */
-	if (gtkconv->u.im->icon_container != NULL)
-		gtk_widget_destroy(gtkconv->u.im->icon_container);
-	gtkconv->u.im->icon_container = NULL;
+	children = gtk_container_get_children(GTK_CONTAINER(gtkconv->u.im->icon_container));
+	if (children) {
+		/* We know there's only one child here. It'd be nice to shortcut to the
+		   event box, but we can't change the PidginConversation until 3.0 */
+		event = (GtkWidget *)children->data;
+		gtk_container_remove(GTK_CONTAINER(gtkconv->u.im->icon_container), event);
+		g_list_free(children);
+	}
 
 	if (gtkconv->u.im->anim != NULL)
 		g_object_unref(G_OBJECT(gtkconv->u.im->anim));
@@ -7026,7 +7063,7 @@
 	scale_width = gdk_pixbuf_get_width(buf);
 	scale_height = gdk_pixbuf_get_height(buf);
 
-	gtk_widget_get_size_request(gtkconv->infopane_hbox, NULL, &size);
+	gtk_widget_get_size_request(gtkconv->u.im->icon_container, NULL, &size);
 	size = MIN(size, MIN(scale_width, scale_height));
 
 	/* Some sanity checks */
@@ -7045,7 +7082,6 @@
 	g_object_unref(buf);
 	if (pidgin_gdk_pixbuf_is_opaque(scale))
 		pidgin_gdk_pixbuf_make_round(scale);
-	gtkconv->u.im->icon_container = gtk_vbox_new(FALSE, 0);
 
 	event = gtk_event_box_new();
 	gtk_container_add(GTK_CONTAINER(gtkconv->u.im->icon_container), event);
@@ -7066,11 +7102,6 @@
 
 	g_object_unref(G_OBJECT(scale));
 
-	gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox),
-			   gtkconv->u.im->icon_container, FALSE, FALSE, 0);
-
-	gtk_widget_show(gtkconv->u.im->icon_container);
-
 	/* The buddy icon code needs badly to be fixed. */
 	if(pidgin_conv_window_is_active_conversation(conv))
 	{