changeset 25895:a81f75025e14

propagate from branch 'im.pidgin.pidgin.2.5.5.veracode' (head 1744a346c5951c97b5d6ec102b49606f3dd918cc) to branch 'im.pidgin.pidgin.2.5.6' (head 9fe90237b75569f21f8de666d690a38109709df0)
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 14 May 2009 21:17:11 +0000
parents c7ec8f3b39d3 (current diff) b33635aced5c (diff)
children 415803efb8b7
files libpurple/protocols/msn/oim.c libpurple/protocols/msn/soap.c
diffstat 57 files changed, 485 insertions(+), 255 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun May 03 23:18:28 2009 +0000
+++ b/COPYRIGHT	Thu May 14 21:17:11 2009 +0000
@@ -8,6 +8,7 @@
 Dave Ahlswede
 Manuel Amador
 Matt Amato
+Josef Andrysek
 Geoffrey Antos
 Daniel Atallah
 Paul Aurich
@@ -18,6 +19,7 @@
 Christopher Ayoup
 Alex Badea
 John Bailey
+Arunan Balasubramaniam
 R. Tyler Ballance
 Chris Banal
 Luca Barbato
@@ -28,6 +30,7 @@
 Derek Battams
 Martin Bayard
 Curtis Beattie
+Stefan Becker
 Dave Bell
 Igor Belyi
 Brian Bernas
--- a/ChangeLog	Sun May 03 23:18:28 2009 +0000
+++ b/ChangeLog	Thu May 14 21:17:11 2009 +0000
@@ -1,5 +1,33 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.5.6 (??/??/2009):
+	libpurple:
+	* Improve sleep behavior by aggregation of longer timeouts on second
+	  boundaries to allow better power saving.  (Arunan Balasubramaniam)
+	* Fix various crashes on exit.
+	* Make XML parsing more resilient to interactions with other libraries.
+	  This, along with the fix for libxml2 bug 564217, fixes the crashes
+	  on connect in XMPP with recent gst-plugins-bad (see #8830 for details).
+
+	IRC:
+	* Correctly handle WHOIS for users who are joined to a large number of
+	  channels.
+	* Notify the user if a /nick command fails, rather than trying
+	  fallback nicks.
+
+	MSN:
+	* Fix a race condition causing occasional Pidgin crashes.
+	* Fix some errors about the friendly name changing too fast caused
+	  by MSN/Yahoo integration buddies.
+
+	XMPP:
+	* Less likely to pop up a new conversation window in disregard of
+	  the "Hide new IM conversations" preference.
+
+	Yahoo:
+	* Fix a crash when sending very long messages.
+	* Fix a bug where UTF-8 status messages get garbled when going idle.
+
 version 2.5.5 (03/01/2009):
 	libpurple:
 	* Fix a crash when removing an account with an unknown protocol id.
@@ -669,13 +697,13 @@
 version 2.2.0 (09/13/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.2.0
 
-	Libpurple:
+	libpurple:
 	* New protocol plugin: MySpaceIM (Jeff Connelly, Google Summer of
 	  Code)
 	* XMPP enhancements. See
-	  http://www.adiumx.com/blog/2007/07/soc-xmpp-update.php (Andreas 
+	  http://www.adiumx.com/blog/2007/07/soc-xmpp-update.php (Andreas
 	  Monitzer, Google Summer of Code for Adium)
-	* Certificate management. Libpurple will validate certificates on
+	* Certificate management. libpurple will validate certificates on
 	  SSL-encrypted protocols (William Ehlhardt, Google Summer of Code)
 	* Some adjustments were made to fix sending messages when using
 	  the MSN HTTP method. (Laszlo Pandy)
--- a/configure.ac	Sun May 03 23:18:28 2009 +0000
+++ b/configure.ac	Thu May 14 21:17:11 2009 +0000
@@ -46,7 +46,7 @@
 m4_define([purple_lt_current], [5])
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [5])
-m4_define([purple_micro_version], [5])
+m4_define([purple_micro_version], [6])
 m4_define([purple_version_suffix], [])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
@@ -55,7 +55,7 @@
 m4_define([gnt_lt_current], [5])
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [5])
-m4_define([gnt_micro_version], [5])
+m4_define([gnt_micro_version], [6])
 m4_define([gnt_version_suffix], [])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/doc/finch.1.in	Sun May 03 23:18:28 2009 +0000
+++ b/doc/finch.1.in	Thu May 14 21:17:11 2009 +0000
@@ -59,7 +59,7 @@
 Display the version information window.
 
 .SH GNT Shortcuts
-You can use the following shortcuts:
+You can use the following shortcuts (see the "\*QWidget Actions\*U" section for a more complete list):
 .TP
 .B Alt \+ a
 Bring up a list of available actions. You can use this list to access the
@@ -378,6 +378,8 @@
 [GntWidget::binding]
 .br
 f11 = context-menu
+.br
+c-x = context-menu
 
 [GntWindow::binding]
 .br
--- a/finch/gntblist.c	Sun May 03 23:18:28 2009 +0000
+++ b/finch/gntblist.c	Thu May 14 21:17:11 2009 +0000
@@ -61,7 +61,7 @@
 #include <string.h>
 
 #define PREF_ROOT "/finch/blist"
-#define TYPING_TIMEOUT 4000
+#define TYPING_TIMEOUT_S 4
 
 #define SHOW_EMPTY_GROUP_TIMEOUT  60
 
@@ -931,7 +931,7 @@
 	else if (PURPLE_BLIST_NODE_IS_GROUP(node))
 		return purple_group_get_name((PurpleGroup*)node);
 
-	snprintf(text, sizeof(text) - 1, "%s %s", status, name);
+	g_snprintf(text, sizeof(text) - 1, "%s %s", status, name);
 
 	return text;
 }
@@ -2012,7 +2012,7 @@
 	}
 
 	if (ggblist->typing)
-		g_source_remove(ggblist->typing);
+		purple_timeout_remove(ggblist->typing);
 	remove_peripherals(ggblist);
 	if (ggblist->tagged)
 		g_list_free(ggblist->tagged);
@@ -2249,7 +2249,7 @@
 end:
 	g_free(escnewmessage);
 	if (ggblist->typing)
-		g_source_remove(ggblist->typing);
+		purple_timeout_remove(ggblist->typing);
 	ggblist->typing = 0;
 	return FALSE;
 }
@@ -2268,7 +2268,7 @@
 		/* Move the focus to the entry box */
 		/* XXX: Make sure the selected status can have a message */
 		gnt_box_move_focus(GNT_BOX(ggblist->window), 1);
-		ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL);
+		ggblist->typing = purple_timeout_add_seconds(TYPING_TIMEOUT_S, (GSourceFunc)remove_typing_cb, NULL);
 	}
 	else if (now->type == STATUS_SAVED_ALL)
 	{
@@ -2294,7 +2294,7 @@
 		return FALSE;
 
 	if (ggblist->typing)
-		g_source_remove(ggblist->typing);
+		purple_timeout_remove(ggblist->typing);
 	ggblist->typing = 0;
 
 	if (text[0] == '\r' && text[1] == 0)
@@ -2304,7 +2304,7 @@
 		return TRUE;
 	}
 
-	ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL);
+	ggblist->typing = purple_timeout_add_seconds(TYPING_TIMEOUT_S, (GSourceFunc)remove_typing_cb, NULL);
 	return FALSE;
 }
 
@@ -2638,7 +2638,7 @@
 		char menuid[128];
 		FinchBlistManager *manager = iter->data;
 		GntMenuItem *item = gnt_menuitem_new(_(manager->name));
-		snprintf(menuid, sizeof(menuid), "grouping-%s", manager->id);
+		g_snprintf(menuid, sizeof(menuid), "grouping-%s", manager->id);
 		gnt_menuitem_set_id(GNT_MENU_ITEM(item), menuid);
 		gnt_menu_add_item(GNT_MENU(subsub), item);
 		g_object_set_data_full(G_OBJECT(item), "grouping-id", g_strdup(manager->id), g_free);
--- a/finch/gntnotify.c	Sun May 03 23:18:28 2009 +0000
+++ b/finch/gntnotify.c	Thu May 14 21:17:11 2009 +0000
@@ -263,7 +263,7 @@
 userinfo_hash(PurpleAccount *account, const char *who)
 {
 	char key[256];
-	snprintf(key, sizeof(key), "%s - %s", purple_account_get_username(account), purple_normalize(account, who));
+	g_snprintf(key, sizeof(key), "%s - %s", purple_account_get_username(account), purple_normalize(account, who));
 	return g_utf8_strup(key, -1);
 }
 
--- a/finch/libgnt/configure.ac	Sun May 03 23:18:28 2009 +0000
+++ b/finch/libgnt/configure.ac	Thu May 14 21:17:11 2009 +0000
@@ -27,7 +27,7 @@
 m4_define([gnt_lt_current], [5])
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [5])
-m4_define([gnt_micro_version], [0])
+m4_define([gnt_micro_version], [6])
 m4_define([gnt_version_suffix], [])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/finch/plugins/gntgf.c	Sun May 03 23:18:28 2009 +0000
+++ b/finch/plugins/gntgf.c	Thu May 14 21:17:11 2009 +0000
@@ -47,6 +47,7 @@
 #include <blist.h>
 #include <conversation.h>
 #include <debug.h>
+#include <eventloop.h>
 #include <util.h>
 
 #include <gnt.h>
@@ -75,7 +76,7 @@
 {
 	toasters = g_list_remove(toasters, toast);
 	gnt_widget_destroy(toast->window);
-	g_source_remove(toast->timer);
+	purple_timeout_remove(toast->timer);
 	g_free(toast);
 }
 
@@ -220,7 +221,7 @@
 	}
 	gnt_widget_draw(window);
 
-	toast->timer = g_timeout_add(4000, (GSourceFunc)remove_toaster, toast);
+	toast->timer = purple_timeout_add_seconds(4, (GSourceFunc)remove_toaster, toast);
 	toasters = g_list_prepend(toasters, toast);
 }
 
--- a/libpurple/buddyicon.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/buddyicon.c	Thu May 14 21:17:11 2009 +0000
@@ -753,6 +753,8 @@
 	}
 	unref_filename(old_icon);
 
+	old_img = g_hash_table_lookup(pointer_icon_cache, account);
+
 	if (img)
 		g_hash_table_insert(pointer_icon_cache, account, img);
 	else
@@ -770,7 +772,7 @@
 			prpl_info->set_buddy_icon(gc, img);
 	}
 
-	if ((old_img = g_hash_table_lookup(pointer_icon_cache, account)))
+	if (old_img)
 		purple_imgstore_unref(old_img);
 	else if (old_icon)
 	{
--- a/libpurple/circbuffer.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/circbuffer.c	Thu May 14 21:17:11 2009 +0000
@@ -68,7 +68,8 @@
 
 	/* If the fill pointer is wrapped to before the remove
 	 * pointer, we need to shift the data */
-	if (in_offset < out_offset) {
+	if (in_offset < out_offset
+			|| (in_offset == out_offset && buf->bufused > 0)) {
 		int shift_n = MIN(buf->buflen - start_buflen,
 			in_offset);
 		memcpy(buf->buffer + start_buflen, buf->buffer,
--- a/libpurple/conversation.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/conversation.c	Thu May 14 21:17:11 2009 +0000
@@ -33,7 +33,7 @@
 #include "signals.h"
 #include "util.h"
 
-#define SEND_TYPED_TIMEOUT 5000
+#define SEND_TYPED_TIMEOUT_SECONDS 5
 
 static GList *conversations = NULL;
 static GList *ims = NULL;
@@ -1122,8 +1122,9 @@
 {
 	g_return_if_fail(im != NULL);
 
-	im->send_typed_timeout = purple_timeout_add(SEND_TYPED_TIMEOUT, send_typed_cb,
-											  purple_conv_im_get_conversation(im));
+	im->send_typed_timeout = purple_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
+	                                                    send_typed_cb,
+	                                                    purple_conv_im_get_conversation(im));
 }
 
 void
@@ -1476,11 +1477,11 @@
 		return;
 
 	if (!(flags & PURPLE_MESSAGE_WHISPER)) {
-		char *str;
-
-		str = g_strdup(purple_normalize(account, who));
-
-		if (!strcmp(str, purple_normalize(account, chat->nick))) {
+		const char *str;
+
+		str = purple_normalize(account, who);
+
+		if (!strcmp(str, chat->nick)) {
 			flags |= PURPLE_MESSAGE_SEND;
 		} else {
 			flags |= PURPLE_MESSAGE_RECV;
@@ -1488,8 +1489,6 @@
 			if (purple_utf8_has_word(message, chat->nick))
 				flags |= PURPLE_MESSAGE_NICK;
 		}
-
-		g_free(str);
 	}
 
 	/* Pass this on to either the ops structure or the default write func. */
--- a/libpurple/core.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/core.c	Thu May 14 21:17:11 2009 +0000
@@ -206,19 +206,6 @@
 	 */
 	purple_certificate_uninit();
 
-	/* The SSL plugins must be uninit before they're unloaded */
-	purple_ssl_uninit();
-
-	/* Unload all plugins before the UI because UI plugins might call
-	 * UI-specific functions */
-	purple_debug_info("main", "Unloading all plugins\n");
-	purple_plugins_destroy_all();
-
-	/* Shut down the UI before all the subsystems */
-	ops = purple_core_get_ui_ops();
-	if (ops != NULL && ops->quit != NULL)
-		ops->quit();
-
 	/* Save .xml files, remove signals, etc. */
 	purple_smileys_uninit();
 	purple_idle_uninit();
@@ -239,6 +226,16 @@
 	purple_imgstore_uninit();
 	purple_network_uninit();
 
+	/* The SSL plugins must be uninit before they're unloaded */
+	purple_ssl_uninit();
+
+	purple_debug_info("main", "Unloading all plugins\n");
+	purple_plugins_destroy_all();
+
+	ops = purple_core_get_ui_ops();
+	if (ops != NULL && ops->quit != NULL)
+		ops->quit();
+
 	/* Everything after this must not try to read any prefs */
 	purple_prefs_uninit();
 	purple_plugins_uninit();
--- a/libpurple/mime.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/mime.c	Thu May 14 21:17:11 2009 +0000
@@ -113,7 +113,7 @@
 fields_loadline(struct mime_fields *mf, const char *line, gsize len)
 {
 	/* split the line into key: value */
-	char *key, *val;
+	char *key, *newkey, *val;
 	char **tokens;
 
 	/* feh, need it to be NUL terminated */
@@ -129,17 +129,18 @@
 
 	/* normalize whitespace (sorta) and trim on key and value */
 	tokens = g_strsplit(key, "\t\r\n", 0);
-	key = g_strjoinv("", tokens);
-	key = g_strstrip(key);
+	newkey = g_strjoinv("", tokens);
+	g_strstrip(newkey);
 	g_strfreev(tokens);
 
 	tokens = g_strsplit(val, "\t\r\n", 0);
 	val = g_strjoinv("", tokens);
-	val = g_strstrip(val);
+	g_strstrip(val);
 	g_strfreev(tokens);
 
-	fields_set(mf, key, val);
+	fields_set(mf, newkey, val);
 
+	g_free(newkey);
 	g_free(key);
 	g_free(val);
 }
@@ -439,6 +440,8 @@
 
 		b = tail;
 	}
+
+	g_free(bnd);
 }
 
 
--- a/libpurple/plugins/filectl.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/plugins/filectl.c	Thu May 14 21:17:11 2009 +0000
@@ -220,7 +220,7 @@
 plugin_load(PurplePlugin *plugin)
 {
 	init_file();
-	check = purple_timeout_add(5000, (GSourceFunc)check_file, NULL);
+	check = purple_timeout_add_seconds(5, (GSourceFunc)check_file, NULL);
 
 	return TRUE;
 }
--- a/libpurple/plugins/joinpart.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/plugins/joinpart.c	Thu May 14 21:17:11 2009 +0000
@@ -194,7 +194,7 @@
 	                    PURPLE_CALLBACK(received_chat_msg_cb), users);
 
 	/* Cleanup every 5 minutes */
-	id = purple_timeout_add(1000 * 60 * 5, (GSourceFunc)clean_users_hash, users);
+	id = purple_timeout_add_seconds(60 * 5, (GSourceFunc)clean_users_hash, users);
 
 	data = g_new(gpointer, 2);
 	data[0] = users;
--- a/libpurple/protocols/bonjour/mdns_win32.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Thu May 14 21:17:11 2009 +0000
@@ -169,14 +169,17 @@
 	gboolean delete_buddy = FALSE;
 	PurpleBuddy *pb = NULL;
 
+	if ((pb = purple_find_buddy(args->account, args->res_data->name))) {
+		if (pb->proto_data != args->bb) {
+			purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record.",
+				args->res_data->name);
+			goto cleanup;
+		}
 	/* Make sure that the BonjourBuddy associated with this request is still around */
-	if (g_slist_find(pending_buddies, args->bb) == NULL)
+	} else if (g_slist_find(pending_buddies, args->bb) == NULL) {
+		purple_debug_error("bonjour", "host resolution - complete, but buddy no longer pending.\n");
 		goto cleanup;
-
-	if ((pb = purple_find_buddy(args->account, args->bb->name)))
-		if (pb->proto_data != args->bb)
-			purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record. "
-				"This is going to be ugly!.\n", args->bb->name);
+	}
 
 	if (!hosts || !hosts->data) {
 		purple_debug_error("bonjour", "host resolution - callback error.\n");
--- a/libpurple/protocols/bonjour/parser.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Thu May 14 21:17:11 2009 +0000
@@ -156,6 +156,18 @@
 	xmlnode_insert_data(bconv->current, (const char*) text, text_len);
 }
 
+static void
+bonjour_parser_structured_error_handler(void *user_data, xmlErrorPtr error)
+{
+	BonjourJabberConversation *bconv = user_data;
+
+	purple_debug_error("jabber", "XML parser error for BonjourJabberConversation %p: "
+	                             "Domain %i, code %i, level %i: %s",
+	                   bconv,
+	                   error->domain, error->code, error->level,
+	                   (error->message ? error->message : "(null)\n"));
+}
+
 static xmlSAXHandler bonjour_parser_libxml = {
 	NULL,									/*internalSubset*/
 	NULL,									/*isStandalone*/
@@ -188,7 +200,7 @@
 	NULL,									/*_private*/
 	bonjour_parser_element_start_libxml,	/*startElementNs*/
 	bonjour_parser_element_end_libxml,		/*endElementNs*/
-	NULL									/*serror*/
+	bonjour_parser_structured_error_handler /*serror*/
 };
 
 void
--- a/libpurple/protocols/irc/irc.h	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/irc/irc.h	Thu May 14 21:17:11 2009 +0000
@@ -72,7 +72,7 @@
 		char *name;
 		char *server;
 		char *serverinfo;
-		char *channels;
+		GString *channels;
 		int ircop;
 		int identified;
 		int idle;
--- a/libpurple/protocols/irc/msgs.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/irc/msgs.c	Thu May 14 21:17:11 2009 +0000
@@ -117,7 +117,7 @@
 
 	irc_blist_timeout(irc);
 	if (!irc->timer)
-		irc->timer = purple_timeout_add(45000, (GSourceFunc)irc_blist_timeout, (gpointer)irc);
+		irc->timer = purple_timeout_add_seconds(45, (GSourceFunc)irc_blist_timeout, (gpointer)irc);
 }
 
 void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -325,7 +325,11 @@
 		if (args[3])
 			irc->whois.signon = (time_t)atoi(args[3]);
 	} else if (!strcmp(name, "319")) {
-		irc->whois.channels = g_strdup(args[2]);
+		if (irc->whois.channels == NULL) {
+			irc->whois.channels = g_string_new(args[2]);
+		} else {
+			irc->whois.channels = g_string_append(irc->whois.channels, args[2]);
+		}
 	} else if (!strcmp(name, "320")) {
 		irc->whois.identified = 1;
 	}
@@ -380,8 +384,8 @@
 		g_free(irc->whois.serverinfo);
 	}
 	if (irc->whois.channels) {
-		purple_notify_user_info_add_pair(user_info, _("Currently on"), irc->whois.channels);
-		g_free(irc->whois.channels);
+		purple_notify_user_info_add_pair(user_info, _("Currently on"), irc->whois.channels->str);
+		g_string_free(irc->whois.channels, TRUE);
 	}
 	if (irc->whois.idle) {
 		gchar *timex = purple_str_seconds_to_string(irc->whois.idle);
@@ -989,10 +993,25 @@
 void irc_msg_nickused(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
 	char *newnick, *buf, *end;
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
 
 	if (!args || !args[1])
 		return;
 
+	if (gc && purple_connection_get_state(gc) == PURPLE_CONNECTED) {
+		/* We only want to do the following dance if the connection
+		   has not been successfully completed.  If it has, just
+		   notify the user that their /nick command didn't go. */
+		buf = g_strdup_printf("The nickname \"%s\" is already being used.",
+				      irc->reqnick);
+		purple_notify_error(gc, "Nickname in use",
+				    "Nickname in use", buf);
+		g_free(buf);
+		g_free(irc->reqnick);
+		irc->reqnick = NULL;
+		return;
+	}
+
 	if (strlen(args[1]) < strlen(irc->reqnick) || irc->nickused)
 		newnick = g_strdup(args[1]);
 	else
--- a/libpurple/protocols/jabber/buddy.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Thu May 14 21:17:11 2009 +0000
@@ -1775,7 +1775,7 @@
 	}
 
 	js->pending_buddy_info_requests = g_slist_prepend(js->pending_buddy_info_requests, jbi);
-	jbi->timeout_handle = purple_timeout_add(30000, jabber_buddy_get_info_timeout, jbi);
+	jbi->timeout_handle = purple_timeout_add_seconds(30, jabber_buddy_get_info_timeout, jbi);
 }
 
 void jabber_buddy_get_info(PurpleConnection *gc, const char *who)
--- a/libpurple/protocols/jabber/google.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Thu May 14 21:17:11 2009 +0000
@@ -39,7 +39,6 @@
 	char *subject;
 	const char *in_str;
 	char *to_name;
-	char *default_tos[1];
 
 	int i, count = 1, returned_count;
 
@@ -59,14 +58,20 @@
 
 	/* If Gmail doesn't tell us who the mail is to, let's use our JID */
 	to = xmlnode_get_attrib(packet, "to");
-	default_tos[0] = jabber_get_bare_jid(to);
 
 	message = xmlnode_get_child(child, "mail-thread-info");
 
 	if (count == 0 || !message) {
-		if (count > 0)
-			purple_notify_emails(js->gc, count, FALSE, NULL, NULL, (const char**) default_tos, NULL, NULL, NULL);
-		g_free(default_tos[0]);
+		if (count > 0) {
+			char *bare_jid = jabber_get_bare_jid(to);
+			const char *default_tos[2] = { bare_jid };
+
+			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;
 	}
 
@@ -74,10 +79,10 @@
 	 * accordingly */
 	for (returned_count = 0; message; returned_count++, message=xmlnode_get_next_twin(message));
 
-	froms    = g_new0(const char* , returned_count);
-	tos      = g_new0(const char* , returned_count);
-	subjects = g_new0(char* , returned_count);
-	urls     = g_new0(const char* , returned_count);
+	froms    = g_new0(const char* , returned_count + 1);
+	tos      = g_new0(const char* , returned_count + 1);
+	subjects = g_new0(char* , returned_count + 1);
+	urls     = g_new0(const char* , returned_count + 1);
 
 	to = xmlnode_get_attrib(packet, "to");
 	to_name = jabber_get_bare_jid(to);
@@ -123,16 +128,12 @@
 	if (i>0)
 		purple_notify_emails(js->gc, count, count == i, (const char**) subjects, froms, tos,
 				urls, NULL, NULL);
-	else
-		purple_notify_emails(js->gc, count, FALSE, NULL, NULL, (const char**) default_tos, NULL, NULL, NULL);
-
 
 	g_free(to_name);
 	g_free(tos);
-	g_free(default_tos[0]);
 	g_free(froms);
-	for (; i > 0; i--)
-		g_free(subjects[i - 1]);
+	for (i = 0; i < returned_count; i++)
+		g_free(subjects[i]);
 	g_free(subjects);
 	g_free(urls);
 
--- a/libpurple/protocols/jabber/jabber.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Thu May 14 21:17:11 2009 +0000
@@ -77,6 +77,10 @@
 						  "xmlns:stream='http://etherx.jabber.org/streams' "
 						  "version='1.0'>",
 						  js->user->domain);
+	if (js->reinit)
+		/* Close down the current stream to keep the XML parser happy */
+		jabber_parser_close_stream(js);
+
 	/* setup the parser fresh for each stream */
 	jabber_parser_setup(js);
 	jabber_send_raw(js, open_stream, -1);
@@ -372,6 +376,11 @@
 			char *data_start, *tag_end = strchr(tag_start, '>');
 			text = g_strdup(data);
 
+			/* Better to print out some wacky debugging than crash
+			 * due to a plugin sending bad xml */
+			if (tag_end == NULL)
+				tag_end = tag_start;
+
 			data_start = text + (tag_end - data) + 1;
 
 			last_part = strchr(data_start, '<');
@@ -636,6 +645,9 @@
 
 static void tls_init(JabberStream *js)
 {
+	/* Close down the current stream to keep the XML parser happy */
+	jabber_parser_close_stream(js);
+
 	purple_input_remove(js->gc->inpa);
 	js->gc->inpa = 0;
 	js->gsc = purple_ssl_connect_with_host_fd(js->gc->account, js->fd,
@@ -1316,6 +1328,11 @@
 	jabber_unregister_account_cb(js);
 }
 
+/* TODO: As Will pointed out in IRC, after being notified by the core to
+ * shutdown, we should async. wait for the server to send us the stream
+ * termination before destorying everything. That seems like it would require
+ * changing the semantics of prpl->close(), so it's a good idea for 3.0.0.
+ */
 void jabber_close(PurpleConnection *gc)
 {
 	JabberStream *js = gc->proto_data;
--- a/libpurple/protocols/jabber/message.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/jabber/message.c	Thu May 14 21:17:11 2009 +0000
@@ -633,24 +633,28 @@
 					purple_debug_info("jabber", "found %d smileys\n",
 						g_list_length(smiley_refs));
 					
-					if (jm->type == JABBER_MESSAGE_GROUPCHAT) {
-						JabberID *jid = jabber_id_new(jm->from);
-						JabberChat *chat = NULL;
+					if (smiley_refs) {		
+						if (jm->type == JABBER_MESSAGE_GROUPCHAT) {
+							JabberID *jid = jabber_id_new(jm->from);
+							JabberChat *chat = NULL;
 
-						if (jid) {
-							chat = jabber_chat_find(js, jid->node, jid->domain);
-							if (chat) conv = chat->conv;
-						}
+							if (jid) {
+								chat = jabber_chat_find(js, jid->node, jid->domain);
+								if (chat) conv = chat->conv;
+							}
 
-						jabber_id_free(jid);
-					} else {
-						conv =
-							purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
-								who, account);
-						if (!conv) {
-							/* we need to create the conversation here */
-							conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
-								account, who);
+							jabber_id_free(jid);
+						} else if (jm->type == JABBER_MESSAGE_NORMAL ||
+						           jm->type == JABBER_MESSAGE_CHAT) {
+							conv =
+								purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
+									who, account);
+							if (!conv) {
+								/* we need to create the conversation here */
+								conv = 
+									purple_conversation_new(PURPLE_CONV_TYPE_IM,
+									account, who);
+							}
 						}
 					}
 
@@ -682,7 +686,7 @@
 						    TRUE)) {
 						const JabberData *data =
 								jabber_data_find_remote_by_cid(cid);
-						/* if data is already known, we add write it immediatly */
+						/* if data is already known, we write it immediatly */
 						if (data) {
 							purple_debug_info("jabber", 
 								"data is already known\n"); 
--- a/libpurple/protocols/jabber/parser.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/jabber/parser.c	Thu May 14 21:17:11 2009 +0000
@@ -205,6 +205,12 @@
 	jabber_parser_free(js);
 }
 
+void
+jabber_parser_close_stream(JabberStream *js)
+{
+	xmlParseChunk(js->context, "</stream:stream>", 16 /* length */, 0);
+}
+
 void jabber_parser_free(JabberStream *js) {
 	if (js->context) {
 		xmlParseChunk(js->context, NULL,0,1);
@@ -224,8 +230,17 @@
 		xmlParseChunk(js->context, "", 0, 0);
 	} else if ((ret = xmlParseChunk(js->context, buf, len, 0)) != XML_ERR_OK) {
 		xmlError *err = xmlCtxtGetLastError(js->context);
+		/*
+		 * libxml2 uses a global setting to determine whether or not to store
+		 * warnings.  Other libraries may set this, which causes err to be
+		 * NULL. See #8136 for details.
+		 */
+		xmlErrorLevel level = XML_ERR_WARNING;
 
-		switch (err->level) {
+		if (err)
+			level = err->level;
+
+		switch (level) {
 			case XML_ERR_NONE:
 				purple_debug_info("jabber", "xmlParseChunk returned info %i\n", ret);
 				break;
--- a/libpurple/protocols/jabber/parser.h	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/jabber/parser.h	Thu May 14 21:17:11 2009 +0000
@@ -25,6 +25,7 @@
 #include "jabber.h"
 
 void jabber_parser_setup(JabberStream *js);
+void jabber_parser_close_stream(JabberStream *js);
 void jabber_parser_free(JabberStream *js);
 void jabber_parser_process(JabberStream *js, const char *buf, int len);
 
--- a/libpurple/protocols/jabber/presence.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/jabber/presence.c	Thu May 14 21:17:11 2009 +0000
@@ -601,9 +601,11 @@
 		if(type && !strcmp(type, "unavailable")) {
 			gboolean nick_change = FALSE;
 
-			/* If we haven't joined the chat yet, we don't care that someone
-			 * left, or it was us leaving after we closed the chat */
-			if (!chat->conv || chat->left) {
+			/* If the chat nick is invalid, we haven't yet joined, or we've
+			 * already left (it was probably us leaving after we closed the
+			 * chat), we don't care.
+			 */
+			if (!jid->resource || !chat->conv || chat->left) {
 				if (chat->left &&
 						jid->resource && chat->handle && !strcmp(jid->resource, chat->handle))
 					jabber_chat_destroy(chat);
@@ -664,6 +666,19 @@
 				}
 			}
 		} else {
+			/*
+			 * XEP-0045 mandates the presence to include a resource (which is
+			 * treated as the chat nick). Some non-compliant servers allow
+			 * joining without a nick.
+			 */
+			if (!jid->resource) {
+				jabber_id_free(jid);
+				g_free(avatar_hash);
+				g_free(nickname);
+				g_free(status);
+				return;
+			}
+
 			if(!chat->conv) {
 				chat->id = i++;
 				chat->muc = muc;
--- a/libpurple/protocols/msn/contact.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Thu May 14 21:17:11 2009 +0000
@@ -697,25 +697,28 @@
 				/*TODO:  need to support the Mobile type*/
 				continue;
 			}
-			for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode;
-					contactEmailNode = xmlnode_get_next_twin(contactEmailNode)) {
-				if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled")))
-					continue;
+			for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail");
+			     contactEmailNode;
+			     contactEmailNode = xmlnode_get_next_twin(contactEmailNode)) {
+				if ((messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) {
 
-				msnEnabled = xmlnode_get_data(messengerEnabledNode);
+					msnEnabled = xmlnode_get_data(messengerEnabledNode);
+
+					if (msnEnabled && !strcmp(msnEnabled, "true")) {
+						if ((emailNode = xmlnode_get_child(contactEmailNode, "email")))
+							passport = xmlnode_get_data(emailNode);
 
-				if (msnEnabled && !strcmp(msnEnabled, "true")) {
-					if ((emailNode = xmlnode_get_child(contactEmailNode, "email")))
-						passport = xmlnode_get_data(emailNode);
+						/* Messenger enabled, Get the Passport*/
+						purple_debug_info("msn", "AB Yahoo/Federated User %s\n", passport ? passport : "(null)");
+						g_free(msnEnabled);
+						break;
+					}
 
-					/*Messenger enabled, Get the Passport*/
-					purple_debug_info("msn", "AB Yahoo/Federated User %s\n", passport ? passport : "(null)");
 					g_free(msnEnabled);
-					break;
 				}
-
-				g_free(msnEnabled);
 			}
+			if (passport == NULL) /* Couldn't find anything */
+				continue;
 		} else {
 			xmlnode *messenger_user;
 			/* ignore non-messenger contacts */
@@ -1482,8 +1485,6 @@
 			  const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
-	const char *type = "PassportMember";
-	gchar *federate = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 	MsnUser *user;
 
@@ -1501,23 +1502,28 @@
 	msn_callback_state_set_who(state, passport);
 
 	user = msn_userlist_find_user(session->userlist, passport);
-	if (user && user->networkid != MSN_NETWORK_PASSPORT) {
-		type = "EmailMember";
-		federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML,
-		                           user->networkid);
-	}
 	
 	if (list == MSN_LIST_PL) {
 		partner_scenario = MSN_PS_CONTACT_API;
-		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
-		                         type, user->membership_id[MSN_LIST_PL],
-		                         federate ? federate : "");
+		if (user && user->networkid != MSN_NETWORK_PASSPORT)
+			member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
+			                         "EmailMember", "Email",
+			                         user->membership_id[MSN_LIST_PL]);
+		else
+			member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
+			                         "PassportMember", "Passport",
+			                         user->membership_id[MSN_LIST_PL]);
 	} else {
 		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
 		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
-		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
-		                         type, passport,
-		                         federate ? federate : "");
+		if (user && user->networkid != MSN_NETWORK_PASSPORT)
+			member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+			                         "EmailMember", "Email",
+			                         "Email", passport, "Email");
+		else
+			member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+			                         "PassportMember", "Passport",
+			                         "PassportName", passport, "PassportName");
 	}
 
 	body = g_strdup_printf(MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE,
@@ -1530,7 +1536,6 @@
 	state->cb = msn_del_contact_from_list_read_cb;
 	msn_contact_request(state);
 
-	g_free(federate);
 	g_free(member);
 	g_free(body);
 }
@@ -1578,8 +1583,6 @@
 			const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
-	const char *type = "PassportMember";
-	gchar *federate = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 	MsnUser *user;
 
@@ -1596,15 +1599,16 @@
 	msn_callback_state_set_who(state, passport);
 
 	user = msn_userlist_find_user(session->userlist, passport);
-	if (user && user->networkid != MSN_NETWORK_PASSPORT) {
-		type = "EmailMember";
-		federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML,
-		                           user->networkid);
-	}
 
 	partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
-	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
-	                         type, state->who, federate ? federate : "");
+	if (user && user->networkid != MSN_NETWORK_PASSPORT)
+		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+		                         "EmailMember", "Email",
+		                         "Email", state->who, "Email");
+	else
+		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+		                         "PassportMember", "Passport",
+		                         "PassportName", state->who, "PassportName");
 
 	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
 		MsnSoapPartnerScenarioText[partner_scenario],
@@ -1616,7 +1620,6 @@
 	state->cb = msn_add_contact_to_list_read_cb;
 	msn_contact_request(state);
 
-	g_free(federate);
 	g_free(member);
 	g_free(body);
 }
--- a/libpurple/protocols/msn/contact.h	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msn/contact.h	Thu May 14 21:17:11 2009 +0000
@@ -397,28 +397,18 @@
 
 #define MSN_MEMBER_PASSPORT_XML	\
 	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\
-		"<Type>Passport</Type>"\
+		"<Type>%s</Type>"\
 		"<State>Accepted</State>"\
-		"<PassportName>%s</PassportName>"\
-		"%s"\
+		"<%s>%s</%s>"\
 	"</Member>"
 
 #define MSN_MEMBER_MEMBERSHIPID_XML	\
 	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\
-		"<Type>Passport</Type>"\
+		"<Type>%s</Type>"\
 		"<MembershipId>%u</MembershipId>"\
 		"<State>Accepted</State>"\
-		"%s"\
 	"</Member>"
 
-#define MSN_MEMBER_FEDERATED_ANNOTATION_XML \
-	"<Annotations>"\
-		"<Annotation>"\
-			"<Name>MSN.IM.BuddyType</Name>"\
-			"<Value>%02d:</Value>"\
-		"</Annotation>"\
-	"</Annotations>"
-
 /* first delete contact from allow list */
 
 #define MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
--- a/libpurple/protocols/msn/notification.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Thu May 14 21:17:11 2009 +0000
@@ -612,8 +612,23 @@
 static void
 update_contact_network(MsnSession *session, const char *passport, MsnNetwork network)
 {
-	MsnUser *user = msn_userlist_find_user(session->userlist, passport);
+	MsnUser *user;
+
+	if (network == MSN_NETWORK_UNKNOWN)
+	{
+		purple_debug_warning("msn",
+		                     "Ignoring user %s about which server knows nothing.\n",
+		                     passport);
+		/* Decrement the count for unknown results so that we'll continue login.
+		   Also, need to finish the login process here as well, because ADL OK
+		   will not be called. */
+		if (--session->adl_fqy == 0)
+			msn_session_finish_login(session);
+		return;
+	}
+
 	/* TODO: Also figure out how to update membership lists */
+	user = msn_userlist_find_user(session->userlist, passport);
 	if (user) {
 		xmlnode *adl_node;
 		char *payload;
@@ -630,7 +645,7 @@
 
 	} else {
 		purple_debug_error("msn",
-		                   "Got FQY update for unkwown user %s on network %d.\n",
+		                   "Got FQY update for unknown user %s on network %d.\n",
 		                   passport, network);
 	}
 }
@@ -665,13 +680,14 @@
 		if (user->passport && !strcmp(user->passport, "messenger@microsoft.com"))
 			continue;
 
-		if ((user->list_op & MSN_LIST_OP_MASK) == (MSN_LIST_AL_OP | MSN_LIST_BL_OP)) {
+		if ((user->list_op & MSN_LIST_OP_MASK & ~MSN_LIST_FL_OP)
+		 == (MSN_LIST_AL_OP | MSN_LIST_BL_OP)) {
 			/* The server will complain if we send it a user on both the
 			   Allow and Block lists. So assume they're on the Block list
 			   and remove them from the Allow list in the membership lists to
 			   stop this from happening again. */
 			purple_debug_warning("msn",
-			                     "User %s is on both Allow and Block list,"
+			                     "User %s is on both Allow and Block list; "
 			                     "removing from Allow list.\n",
 			                     user->passport);
 			msn_userlist_rem_buddy_from_list(session->userlist, user->passport, MSN_LIST_AL);
@@ -685,6 +701,9 @@
 			if (++adl_count % 150 == 0) {
 				payload = xmlnode_to_str(adl_node, &payload_len);
 
+				/* ADL's are returned all-together */
+				session->adl_fqy++;
+
 				msn_notification_post_adl(session->notification->cmdproc,
 					payload, payload_len);
 
@@ -696,6 +715,9 @@
 				xmlnode_set_attrib(adl_node, "l", "1");
 			}
 		} else {
+			/* FQY's are returned one-at-a-time */
+			session->adl_fqy++;
+
 			msn_add_contact_xml(session, fqy_node, user->passport,
 				0, user->networkid);
 
@@ -717,6 +739,9 @@
 	if (adl_count == 0 || adl_count % 150 != 0) {
 		payload = xmlnode_to_str(adl_node, &payload_len);
 
+		/* ADL's are returned all-together */
+		session->adl_fqy++;
+
 		msn_notification_post_adl(session->notification->cmdproc, payload, payload_len);
 
 		g_free(payload);
@@ -764,13 +789,17 @@
 		purple_debug_info("msn", "Invalid XML in ADL!\n");
 		return;
 	}
-	for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) {
+	for (domain_node = xmlnode_get_child(root, "d");
+	     domain_node;
+	     domain_node = xmlnode_get_next_twin(domain_node)) {
 		const gchar * domain = NULL;
 		xmlnode *contact_node = NULL;
 
 		domain = xmlnode_get_attrib(domain_node, "n");
 
-		for (contact_node = xmlnode_get_child(domain_node, "c"); contact_node; contact_node = xmlnode_get_next_twin(contact_node)) {
+		for (contact_node = xmlnode_get_child(domain_node, "c");
+		     contact_node;
+		     contact_node = xmlnode_get_next_twin(contact_node)) {
 			const gchar *list;
 			gint list_op = 0;
 
@@ -803,7 +832,8 @@
 
 	if (!strcmp(cmd->params[1], "OK")) {
 		/* ADL ack */
-		msn_session_finish_login(session);
+		if (--session->adl_fqy == 0)
+			msn_session_finish_login(session);
 	} else {
 		cmdproc->last_cmd->payload_cb = adl_cmd_parse;
 		cmd->payload_len = atoi(cmd->params[1]);
@@ -931,10 +961,10 @@
 
 			passport = g_strdup_printf("%s@%s", local, domain);
 
-			if (type != NULL)
+			if (!g_ascii_isdigit(cmd->command[0]) && type != NULL)
 				network = (MsnNetwork)strtoul(type, NULL, 10);
 			else
-				network = MSN_NETWORK_PASSPORT;
+				network = MSN_NETWORK_UNKNOWN;
 
 			purple_debug_info("msn", "FQY response says %s is from network %d\n",
 			                  passport, network);
@@ -949,6 +979,26 @@
 }
 
 static void
+fqy_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	MsnCommand *cmd = cmdproc->last_cmd;
+
+	purple_debug_warning("msn", "FQY error %d\n", error);
+	if (cmd->param_count > 1) {
+		cmd->payload_cb = fqy_cmd_post;
+		cmd->payload_len = atoi(cmd->params[1]);
+		cmd->payload_cbdata = GINT_TO_POINTER(error);
+	}
+#if 0
+	/* If the server didn't send us a corresponding email address for this
+	   FQY error, it's probably going to disconnect us. So it isn't necessary
+	   to tell the handler about it. */
+	else if (trans->data)
+		((MsnFqyCb)trans->data)(session, NULL, MSN_NETWORK_UNKNOWN); */
+#endif
+}
+
+static void
 fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	purple_debug_info("msn", "Process FQY\n");
@@ -1841,14 +1891,11 @@
 
 		if (count > 0)
 		{
-			const char *passport;
-			const char *url;
-
-			passport = msn_user_get_passport(session->user);
-			url = session->passport_info.mail_url;
+			const char *passports[2] = { msn_user_get_passport(session->user) };
+			const char *urls[2] = { session->passport_info.mail_url };
 
 			purple_notify_emails(gc, count, FALSE, NULL, NULL,
-							   &passport, &url, NULL, NULL);
+							   passports, urls, NULL, NULL);
 		}
 	}
 
@@ -1910,14 +1957,11 @@
 
 		if (count > 0)
 		{
-			const char *passport;
-			const char *url;
-
-			passport = msn_user_get_passport(session->user);
-			url = session->passport_info.mail_url;
+			const char *passports[2] = { msn_user_get_passport(session->user) };
+			const char *urls[2] = { session->passport_info.mail_url };
 
 			purple_notify_emails(gc, count, FALSE, NULL, NULL,
-							   &passport, &url, NULL, NULL);
+							   passports, urls, NULL, NULL);
 		}
 	}
 
@@ -2142,6 +2186,7 @@
 
 	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);
--- a/libpurple/protocols/msn/oim.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msn/oim.c	Thu May 14 21:17:11 2009 +0000
@@ -770,14 +770,14 @@
 	if (iu_node != NULL && purple_account_get_check_mail(session->account))
 	{
 		char *unread = xmlnode_get_data(iu_node);
-		const char *passport = msn_user_get_passport(session->user);
-		const char *url = session->passport_info.mail_url;
+		const char *passports[2] = { msn_user_get_passport(session->user) };
+		const char *urls[2] = { session->passport_info.mail_url };
 		int count = atoi(unread);
 
 		/* XXX/khc: pretty sure this is wrong */
 		if (count > 0)
 			purple_notify_emails(session->account->gc, count, FALSE, NULL,
-				NULL, &passport, &url, NULL, NULL);
+				NULL, passports, urls, NULL, NULL);
 		g_free(unread);
 	}
 
--- a/libpurple/protocols/msn/session.h	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msn/session.h	Thu May 14 21:17:11 2009 +0000
@@ -90,6 +90,7 @@
 
 	gboolean connected;
 	gboolean logged_in; /**< A temporal flag to ignore local buddy list adds. */
+	int      adl_fqy; /**< A count of ADL/FQY so status is only changed once. */
 	gboolean destroying; /**< A flag that states if the session is being destroyed. */
 	gboolean http_method;
 
--- a/libpurple/protocols/msn/slplink.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msn/slplink.c	Thu May 14 21:17:11 2009 +0000
@@ -493,7 +493,7 @@
 {
 	MsnSlpMessage *slpmsg;
 	const char *data;
-	gsize offset;
+	guint64 offset;
 	gsize len;
 
 #ifdef MSN_DEBUG_SLP
@@ -565,6 +565,7 @@
 			if (slpmsg->buffer == NULL)
 			{
 				purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n");
+				msn_slpmsg_destroy(slpmsg);
 				return;
 			}
 		}
--- a/libpurple/protocols/msn/soap.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msn/soap.c	Thu May 14 21:17:11 2009 +0000
@@ -667,6 +667,7 @@
 			conn->handled_len = 0;
 			conn->current_request = req;
 
+			purple_input_remove(conn->event_handle);
 			conn->event_handle = purple_input_add(conn->ssl->fd,
 				PURPLE_INPUT_WRITE, msn_soap_write_cb, conn);
 			if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) {
--- a/libpurple/protocols/msnp9/httpconn.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msnp9/httpconn.c	Thu May 14 21:17:11 2009 +0000
@@ -703,7 +703,7 @@
 		httpconn->inpa = purple_input_add(httpconn->fd, PURPLE_INPUT_READ,
 			read_cb, data);
 
-		httpconn->timer = purple_timeout_add(2000, msn_httpconn_poll, httpconn);
+		httpconn->timer = purple_timeout_add_seconds(3, msn_httpconn_poll, httpconn);
 
 		msn_httpconn_process_queue(httpconn);
 	}
--- a/libpurple/protocols/msnp9/notification.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msnp9/notification.c	Thu May 14 21:17:11 2009 +0000
@@ -1280,14 +1280,11 @@
 
 		if (count > 0)
 		{
-			const char *passport;
-			const char *url;
+			const char *passports[2] = { msn_user_get_passport(session->user) };
+			const char *urls[2] = { session->passport_info.file };
 
-			passport = msn_user_get_passport(session->user);
-			url = session->passport_info.file;
-
-			purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL,
-							   &passport, &url, NULL, NULL);
+			purple_notify_emails(gc, count, FALSE, NULL, NULL,
+							   passports, urls, NULL, NULL);
 		}
 	}
 
--- a/libpurple/protocols/msnp9/slp.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msnp9/slp.c	Thu May 14 21:17:11 2009 +0000
@@ -33,8 +33,8 @@
 
 #include "smiley.h"
 
-/* ms to delay between sending buddy icon requests to the server. */
-#define BUDDY_ICON_DELAY 20000
+/* Seconds to delay between sending buddy icon requests to the server. */
+#define BUDDY_ICON_DELAY 20
 
 static void send_ok(MsnSlpCall *slpcall, const char *branch,
 					const char *type, const char *content);
@@ -1058,8 +1058,8 @@
 		purple_timeout_remove(userlist->buddy_icon_request_timer);
 	}
 
-	/* Wait BUDDY_ICON_DELAY ms before freeing our window slot and requesting the next icon. */
-	userlist->buddy_icon_request_timer = purple_timeout_add(BUDDY_ICON_DELAY, 
+	/* Wait BUDDY_ICON_DELAY_S seconds before freeing our window slot and requesting the next icon. */
+	userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, 
 														  msn_release_buddy_icon_request_timeout, userlist);
 }
 
--- a/libpurple/protocols/msnp9/slpcall.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msnp9/slpcall.c	Thu May 14 21:17:11 2009 +0000
@@ -68,7 +68,7 @@
 
 	msn_slplink_add_slpcall(slplink, slpcall);
 
-	slpcall->timer = purple_timeout_add(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpcall);
+	slpcall->timer = purple_timeout_add_seconds(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpcall);
 
 	return slpcall;
 }
--- a/libpurple/protocols/msnp9/slpcall.h	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msnp9/slpcall.h	Thu May 14 21:17:11 2009 +0000
@@ -33,7 +33,7 @@
 #include "slpsession.h"
 
 /* The official client seems to timeout slp calls after 5 minutes */
-#define MSN_SLPCALL_TIMEOUT 300000
+#define MSN_SLPCALL_TIMEOUT 300
 
 typedef enum
 {
--- a/libpurple/protocols/msnp9/transaction.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/msnp9/transaction.c	Thu May 14 21:17:11 2009 +0000
@@ -211,7 +211,7 @@
 		purple_timeout_remove(trans->timer);
 	}
 	trans->timeout_cb = cb;
-	trans->timer = purple_timeout_add(60000, transaction_timeout, trans);
+	trans->timer = purple_timeout_add_seconds(60, transaction_timeout, trans);
 }
 
 void
--- a/libpurple/protocols/myspace/myspace.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Thu May 14 21:17:11 2009 +0000
@@ -840,8 +840,6 @@
 	MsimMessage *body;
 	guint old_inbox_status;
 	guint i, n;
-	const gchar *froms[5], *tos[5], *urls[5], *subjects[5];
-
 	/* Information for each new inbox message type. */
 	static struct
 	{
@@ -856,16 +854,22 @@
 		{ "FriendRequest", MSIM_INBOX_FRIEND_REQUEST, "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests", NULL },
 		{ "PictureComment", MSIM_INBOX_PICTURE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL }
 	};
+	const gchar *froms[G_N_ELEMENTS(message_types) + 1] = { "" },
+		*tos[G_N_ELEMENTS(message_types) + 1] = { "" },
+		*urls[G_N_ELEMENTS(message_types) + 1] = { "" },
+		*subjects[G_N_ELEMENTS(message_types) + 1] = { "" };
+
+	g_return_if_fail(reply != NULL);
 
 	/* Can't write _()'d strings in array initializers. Workaround. */
+	/* khc: then use N_() in the array initializer and use _() when they are
+	   used */
 	message_types[0].text = _("New mail messages");
 	message_types[1].text = _("New blog comments");
 	message_types[2].text = _("New profile comments");
 	message_types[3].text = _("New friend requests!");
 	message_types[4].text = _("New picture comments");
 
-	g_return_if_fail(reply != NULL);
-
 	body = msim_msg_get_dictionary(reply, "body");
 
 	if (body == NULL)
@@ -875,7 +879,7 @@
 
 	n = 0;
 
-	for (i = 0; i < sizeof(message_types) / sizeof(message_types[0]); ++i) {
+	for (i = 0; i < G_N_ELEMENTS(message_types); ++i) {
 		const gchar *key;
 		guint bit;
 
@@ -1238,7 +1242,7 @@
 
 	/* Disable due to problems with timeouts. TODO: fix. */
 #ifdef MSIM_USE_KEEPALIVE
-	purple_timeout_add(MSIM_KEEPALIVE_INTERVAL_CHECK,
+	purple_timeout_add_seconds(MSIM_KEEPALIVE_INTERVAL_CHECK,
 			(GSourceFunc)msim_check_alive, session);
 #endif
 
--- a/libpurple/protocols/myspace/myspace.h	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.h	Thu May 14 21:17:11 2009 +0000
@@ -114,8 +114,8 @@
 #define MSIM_KEEPALIVE_INTERVAL     (3 * 60)
 /*#define MSIM_USE_KEEPALIVE*/
 
-/* Time to check if alive (milliseconds) */
-#define MSIM_KEEPALIVE_INTERVAL_CHECK   (30 * 1000)
+/* Time to check if alive (seconds) */
+#define MSIM_KEEPALIVE_INTERVAL_CHECK   30
 
 /* Time to check for new mail (milliseconds) */
 #define MSIM_MAIL_INTERVAL_CHECK    (60 * 1000)
--- a/libpurple/protocols/null/nullprpl.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Thu May 14 21:17:11 2009 +0000
@@ -927,7 +927,8 @@
 static void nullprpl_set_buddy_icon(PurpleConnection *gc,
                                     PurpleStoredImage *img) {
  purple_debug_info("nullprpl", "setting %s's buddy icon to %s\n",
-                   gc->account->username, purple_imgstore_get_filename(img));
+                   gc->account->username,
+                   img ? purple_imgstore_get_filename(img) : "(null)");
 }
 
 static void nullprpl_remove_group(PurpleConnection *gc, PurpleGroup *group) {
--- a/libpurple/protocols/oscar/oscar.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu May 14 21:17:11 2009 +0000
@@ -1266,7 +1266,7 @@
 	aim_ssi_reqdata(od);
 	if (od->getblisttimer > 0)
 		purple_timeout_remove(od->getblisttimer);
-	od->getblisttimer = purple_timeout_add(30000, purple_ssi_rerequestdata, od);
+	od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
 
 	aim_locate_reqrights(od);
 	aim_buddylist_reqrights(od, conn);
@@ -3560,8 +3560,10 @@
 		gchar *to = g_strdup_printf("%s%s%s", purple_account_get_username(purple_connection_get_account(gc)),
 									emailinfo->domain ? "@" : "",
 									emailinfo->domain ? emailinfo->domain : "");
+		const char *tos[2] = { to };
+		const char *urls[2] = {emailinfo->url }; 
 		if (emailinfo->unread && havenewmail)
-			purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, (const char **)&to, (const char **)&emailinfo->url, NULL, NULL);
+			purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, tos, urls, NULL, NULL);
 		g_free(to);
 	}
 
@@ -5031,7 +5033,7 @@
 						  _("The AIM servers were temporarily unable to send your buddy list.  Your buddy list is not lost, and will probably become available in a few minutes."));
 		if (od->getblisttimer > 0)
 			purple_timeout_remove(od->getblisttimer);
-		od->getblisttimer = purple_timeout_add(30000, purple_ssi_rerequestdata, od);
+		od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
 		return 1;
 	}
 
--- a/libpurple/protocols/oscar/peer.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/oscar/peer.c	Thu May 14 21:17:11 2009 +0000
@@ -812,7 +812,7 @@
 			(conn->client_connect_data != NULL))
 		{
 			/* Connecting... */
-			conn->connect_timeout_timer = purple_timeout_add(5000,
+			conn->connect_timeout_timer = purple_timeout_add_seconds(5,
 					peer_connection_tooktoolong, conn);
 			return;
 		}
--- a/libpurple/protocols/qq/qq_crypt.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/qq/qq_crypt.c	Thu May 14 21:17:11 2009 +0000
@@ -163,9 +163,11 @@
 		c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1];
 		
 		/* set next 64 bits want to crypt*/
-		crypted_ptr += 8;
-		memcpy(crypted32, crypted_ptr, sizeof(crypted32));
-		plain32[0] = crypted32[0] ^ c32_prev[0]; plain32[1] = crypted32[1] ^ c32_prev[1];
+		if (count64 > 0) {
+			crypted_ptr += 8;
+			memcpy(crypted32, crypted_ptr, sizeof(crypted32));
+			plain32[0] = crypted32[0] ^ c32_prev[0]; plain32[1] = crypted32[1] ^ c32_prev[1];
+		}
 	}
 }
 
@@ -275,7 +277,7 @@
 	}
 	
 	count64 = crypted_len / 8;
-	while (count64-- > 0){
+	while (--count64 > 0){
 		c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1];
 		crypted_ptr += 8;
 
--- a/libpurple/protocols/sametime/sametime.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Thu May 14 21:17:11 2009 +0000
@@ -809,7 +809,7 @@
 static void blist_schedule(struct mwPurplePluginData *pd) {
   if(pd->save_event) return;
 
-  pd->save_event = purple_timeout_add(BLIST_SAVE_SECONDS * 1000,
+  pd->save_event = purple_timeout_add_seconds(BLIST_SAVE_SECONDS,
 				    blist_save_cb, pd);
 }
 
--- a/libpurple/protocols/yahoo/yahoo.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Thu May 14 21:17:11 2009 +0000
@@ -1333,10 +1333,10 @@
 		g_free(dec_subj);
 		g_free(from);
 	} else if (count > 0) {
-		const char *to = purple_account_get_username(account);
-		const char *url = yahoo_mail_url;
-
-		purple_notify_emails(gc, count, FALSE, NULL, NULL, &to, &url,
+		const char *tos[2] = { purple_account_get_username(account) };
+		const char *urls[2] = { yahoo_mail_url };
+
+		purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls,
 						   NULL, NULL);
 	}
 }
@@ -3640,7 +3640,6 @@
 					" bytes, %ld characters.  Max is %d bytes, %d chars."
 					"  Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES,
 					YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2);
-			yahoo_packet_free(pkt);
 			g_free(msg);
 			g_free(msg2);
 			return -E2BIG;
@@ -3821,8 +3820,10 @@
 			status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
 		tmp = purple_status_get_attr_string(status, "message");
 		if (tmp != NULL) {
-			msg = yahoo_string_encode(gc, tmp, NULL);
+			gboolean utf8 = TRUE;
+			msg = yahoo_string_encode(gc, tmp, &utf8);
 			msg2 = purple_markup_strip_html(msg);
+			yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
 			yahoo_packet_hash_str(pkt, 19, msg2);
 		} else {
 			/* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
--- a/libpurple/protocols/zephyr/zephyr.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Thu May 14 21:17:11 2009 +0000
@@ -1857,7 +1857,7 @@
 	} else if (use_tzc(zephyr)) {
 		zephyr->nottimer = purple_timeout_add(100, check_notify_tzc, gc);
 	} 
-	zephyr->loctimer = purple_timeout_add(20000, check_loc, gc); 
+	zephyr->loctimer = purple_timeout_add_seconds(20, check_loc, gc); 
 
 }
 
--- a/libpurple/server.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/server.c	Thu May 14 21:17:11 2009 +0000
@@ -583,6 +583,11 @@
 
 	account  = purple_connection_get_account(gc);
 
+	/*
+	 * XXX: Should we be setting this here, or relying on prpls to set it?
+	 */
+	flags |= PURPLE_MESSAGE_RECV;
+
 	if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->set_permit_deny == NULL) {
 		/* protocol does not support privacy, handle it ourselves */
 		if (!purple_privacy_check(account, who)) {
@@ -626,11 +631,6 @@
 	if (conv == NULL)
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account);
 
-	/*
-	 * XXX: Should we be setting this here, or relying on prpls to set it?
-	 */
-	flags |= PURPLE_MESSAGE_RECV;
-
 	if (conv == NULL)
 		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
 
@@ -935,6 +935,15 @@
 	if (!conv)
 		return;
 
+	/* Did I send the message? */
+	if (!strcmp(purple_conv_chat_get_nick(chat),
+				purple_normalize(purple_conversation_get_account(conv), who))) {
+		flags |= PURPLE_MESSAGE_SEND;
+		flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some prpl sets it! */
+	} else {
+		flags |= PURPLE_MESSAGE_RECV;
+	}
+
 	/*
 	 * Make copies of the message and the sender in case plugins want
 	 * to free these strings and replace them with a modifed version.
--- a/libpurple/xmlnode.c	Sun May 03 23:18:28 2009 +0000
+++ b/libpurple/xmlnode.c	Thu May 14 21:17:11 2009 +0000
@@ -665,6 +665,28 @@
 	purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg);
 }
 
+static void
+xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error)
+{
+	struct _xmlnode_parser_data *xpd = user_data;
+
+	if (error && (error->level == XML_ERR_ERROR ||
+	              error->level == XML_ERR_FATAL)) {
+		xpd->error = TRUE;
+		purple_debug_error("xmlnode", "XML parser error for xmlnode %p: "
+		                   "Domain %i, code %i, level %i: %s",
+		                   user_data, error->domain, error->code, error->level,
+		                   error->message ? error->message : "(null)\n");
+	} else if (error)
+		purple_debug_warning("xmlnode", "XML parser error for xmlnode %p: "
+		                     "Domain %i, code %i, level %i: %s",
+		                     user_data, error->domain, error->code, error->level,
+		                     error->message ? error->message : "(null)\n");
+	else
+		purple_debug_warning("xmlnode", "XML parser error for xmlnode %p\n",
+		                     user_data);
+}
+
 static xmlSAXHandler xmlnode_parser_libxml = {
 	NULL, /* internalSubset */
 	NULL, /* isStandalone */
@@ -697,7 +719,7 @@
 	NULL, /* _private */
 	xmlnode_parser_element_start_libxml, /* startElementNs */
 	xmlnode_parser_element_end_libxml,   /* endElementNs   */
-	NULL, /* serror */
+	xmlnode_parser_structural_error_libxml, /* serror */
 };
 
 xmlnode *
--- a/pidgin/gtkaccount.c	Sun May 03 23:18:28 2009 +0000
+++ b/pidgin/gtkaccount.c	Thu May 14 21:17:11 2009 +0000
@@ -756,10 +756,6 @@
 		dialog->protocol_frame = NULL;
 	}
 
-	if (dialog->prpl_info == NULL ||
-			dialog->prpl_info->protocol_options == NULL)
-		return;
-
 	while (dialog->protocol_opt_entries != NULL) {
 		ProtocolOptEntry *opt_entry = dialog->protocol_opt_entries->data;
 		g_free(opt_entry->setting);
@@ -767,6 +763,10 @@
 		dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries);
 	}
 
+	if (dialog->prpl_info == NULL ||
+			dialog->prpl_info->protocol_options == NULL)
+		return;
+
 	account = dialog->account;
 
 	/* Build the protocol options frame. */
--- a/pidgin/gtkconv.c	Sun May 03 23:18:28 2009 +0000
+++ b/pidgin/gtkconv.c	Thu May 14 21:17:11 2009 +0000
@@ -44,6 +44,7 @@
 
 #include "account.h"
 #include "cmds.h"
+#include "core.h"
 #include "debug.h"
 #include "idle.h"
 #include "imgstore.h"
@@ -329,7 +330,8 @@
 	PurpleCmdStatus status;
 
 	if (!g_ascii_strcasecmp(args[0], "version")) {
-		tmp = g_strdup_printf("me is using %s v%s.", "Pidgin", DISPLAY_VERSION);
+		tmp = g_strdup_printf("me is using Pidgin v%s with libpurple v%s.",
+				DISPLAY_VERSION, purple_core_get_version());
 		markup = g_markup_escape_text(tmp, -1);
 
 		status = purple_cmd_do_command(conv, tmp, markup, error);
--- a/pidgin/gtkdialogs.c	Sun May 03 23:18:28 2009 +0000
+++ b/pidgin/gtkdialogs.c	Thu May 14 21:17:11 2009 +0000
@@ -33,6 +33,7 @@
 #include "prpl.h"
 #include "request.h"
 #include "util.h"
+#include "core.h"
 
 #include "gtkblist.h"
 #include "gtkdialogs.h"
@@ -441,7 +442,7 @@
 	str = g_string_sized_new(4096);
 
 	g_string_append_printf(str,
-		"<CENTER><FONT SIZE=\"4\"><B>%s %s</B></FONT></CENTER><BR><BR>", PIDGIN_NAME, DISPLAY_VERSION);
+		"<CENTER><FONT SIZE=\"4\"><B>%s %s</B></FONT></CENTER><BR>(libpurple %s)<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version());
 
 	g_string_append_printf(str,
 		_("%s is a graphical modular messaging client based on "
--- a/pidgin/gtkmain.c	Sun May 03 23:18:28 2009 +0000
+++ b/pidgin/gtkmain.c	Thu May 14 21:17:11 2009 +0000
@@ -665,7 +665,8 @@
 	}
 	/* show version message */
 	if (opt_version) {
-		printf("%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
+		printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION,
+		                                 purple_core_get_version());
 #ifdef HAVE_SIGNAL_H
 		g_free(segfault_message);
 #endif
@@ -780,7 +781,7 @@
 		DBusMessage *message = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE,
 				DBUS_INTERFACE_PURPLE, "PurpleBlistSetVisible");
 		gboolean tr = TRUE;
-		dbus_message_append_args(message, DBUS_TYPE_UINT32, &tr, DBUS_TYPE_INVALID);
+		dbus_message_append_args(message, DBUS_TYPE_INT32, &tr, DBUS_TYPE_INVALID);
 		dbus_connection_send_with_reply_and_block(conn, message, -1, NULL);
 		dbus_message_unref(message);
 #endif
--- a/pidgin/gtkstatusbox.c	Sun May 03 23:18:28 2009 +0000
+++ b/pidgin/gtkstatusbox.c	Thu May 14 21:17:11 2009 +0000
@@ -417,16 +417,15 @@
 		PurpleStoredImage *img = NULL;
 
 		if (filename != NULL)
-		{
-			gchar *contents;
-			gsize size;
-			if (g_file_get_contents(filename, &contents, &size, NULL))
-			{
-				img = purple_imgstore_add(contents, size, filename);
-			}
-		}
+			img = purple_imgstore_new_from_file(filename);
 
 		pidgin_status_box_set_buddy_icon(status_box, img);
+		if (img)
+			/*
+			 * purple_imgstore_new gives us a reference and
+			 * pidgin_status_box_set_buddy_icon also takes one.
+			 */
+			purple_imgstore_unref(img);
 	}
 
 	status_box->hand_cursor = gdk_cursor_new (GDK_HAND2);
@@ -1496,6 +1495,13 @@
 				if (filename)
 					data = pidgin_convert_buddy_icon(plug, filename, &len);
 				img = purple_buddy_icons_set_account_icon(box->account, data, len);
+				if (img)
+					/*
+					 * set_account_icon doesn't give us a reference, but we
+					 * unref one below (for the other code path)
+					 */
+					purple_imgstore_ref(img);
+
 				purple_account_set_buddy_icon_path(box->account, filename);
 
 				purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL));
@@ -1515,7 +1521,7 @@
 					size_t len = 0;
 					if (filename)
 						data = pidgin_convert_buddy_icon(plug, filename, &len);
-					img = purple_buddy_icons_set_account_icon(account, data, len);
+					purple_buddy_icons_set_account_icon(account, data, len);
 					purple_account_set_buddy_icon_path(account, filename);
 				}
 			}
@@ -1523,17 +1529,12 @@
 
 		/* Even if no accounts were processed, load the icon that was set. */
 		if (filename != NULL)
-		{
-			gchar *contents;
-			gsize size;
-			if (g_file_get_contents(filename, &contents, &size, NULL))
-			{
-				img = purple_imgstore_add(contents, size, filename);
-			}
-		}
+			img = purple_imgstore_new_from_file(filename);
 	}
 
 	pidgin_status_box_set_buddy_icon(box, img);
+	if (img)
+		purple_imgstore_unref(img);
 }
 
 static void
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Sun May 03 23:18:28 2009 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Thu May 14 21:17:11 2009 +0000
@@ -813,6 +813,7 @@
 
     ; Shortcuts..
     Delete "$DESKTOP\Pidgin.lnk"
+    Delete "$SMPROGRAMS\Pidgin.lnk"
 
     Goto done
 
--- a/po/de.po	Sun May 03 23:18:28 2009 +0000
+++ b/po/de.po	Thu May 14 21:17:11 2009 +0000
@@ -11,9 +11,9 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-02-25 17:28+0100\n"
-"PO-Revision-Date: 2009-02-25 17:28+0100\n"
-"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
+"POT-Creation-Date: 2009-05-01 00:50+0200\n"
+"PO-Revision-Date: 2009-05-01 00:49+0200\n"
+"Last-Translator: Björn Voigt <bjoern@cs.tu-berlin.de>\n"
 "Language-Team: German <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -7253,6 +7253,34 @@
 msgid "Could not change buddy information."
 msgstr "Konnte Buddy-Informationen nicht bearbeiten."
 
+msgid "Mobile"
+msgstr "Mobil"
+
+msgid "Note"
+msgstr "Bemerkung"
+
+#. callback
+msgid "Buddy Memo"
+msgstr "Buddy-Notiz"
+
+msgid "Change his/her memo as you like"
+msgstr "Ändern Sie seine/ihre Notiz, wenn Sie wollen"
+
+msgid "_Modify"
+msgstr "_Bearbeiten"
+
+msgid "Memo Modify"
+msgstr "Notiz bearbeiten"
+
+msgid "Server says:"
+msgstr "Server meldet:"
+
+msgid "Your request was accepted."
+msgstr "Ihre Anfrage wurde akzeptiert."
+
+msgid "Your request was rejected."
+msgstr "Ihre Anfrage wurde abgelehnt."
+
 #, c-format
 msgid "%u requires verification"
 msgstr "%u erfordert Autorisierung"
@@ -8613,9 +8641,6 @@
 msgid "Unit"
 msgstr "Abteilung"
 
-msgid "Note"
-msgstr "Bemerkung"
-
 msgid "Join Chat"
 msgstr "Chat betreten"
 
@@ -10099,9 +10124,6 @@
 msgid "Extended away"
 msgstr "Abwesend (erweitert)"
 
-msgid "Mobile"
-msgstr "Mobil"
-
 msgid "Listening to music"
 msgstr "Musik hören"
 
@@ -12517,7 +12539,7 @@
 
 #. This is a global option that affects SOCKS4 usage even with account-specific proxy settings
 msgid "Use remote DNS with SOCKS4 proxies"
-msgstr "Remote-DNS mit SOCKS4-Proxys benuten"
+msgstr "Remote-DNS mit SOCKS4-Proxys benutzen"
 
 msgid "_User:"
 msgstr "_Benutzer:"