changeset 32716:4f8626d4ef63

propagate from branch 'im.pidgin.pidgin' (head deb06ab6aa118efb674f1a8434ff529e67b28809) to branch 'im.pidgin.cpw.qulogic.gtk3' (head 34c3103eabae4a4c23439048d383f73c94dfd23e)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Thu, 23 Feb 2012 08:13:23 +0000
parents 4ae0c82b158d (current diff) 3f81e3c3064c (diff)
children 194f66d5089a
files pidgin/gtkconv.c pidgin/gtkdialogs.c pidgin/gtkprefs.c
diffstat 37 files changed, 762 insertions(+), 441 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jan 21 12:02:41 2012 +0000
+++ b/ChangeLog	Thu Feb 23 08:13:23 2012 +0000
@@ -1,14 +1,12 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 3.0.0 (??/??/????):
-	libpurple:
-	* Support new connection states and signals for NetworkManager 0.9+.
-	  (Dan Williams) (#13859)
+	Finch:
+	* Support the conversation-extended signal for extending the
+	  conversation menu. (Howard Chu) (#14818)
 
 	AIM and ICQ:
 	* Make buddy list management code more efficient. (Oliver) (#4816)
-	* Don't try to format ICQ usernames entered as email addresses.
-	  Gets rid of an "Unable to format username" error at login. (#13883)
 
 	Bonjour:
 	* Support file transfers up to ~9 EiB.
@@ -26,6 +24,8 @@
 	MSN:
 	* Fix file transfer with older Mac MSN clients.
 	* Support file transfers up to ~9 EiB.
+	* Support new protocol version MSNP18. (#14753)
+	* Fix messages to offline contacts. (#14302)
 
 	MXit:
 	* Remove all reference to Hidden Number.
@@ -45,6 +45,35 @@
 	* The Voice/Video Settings plugin supports using the sndio GStreamer
 	 backends. (Brad Smith) (#14414)
 
+version 2.10.2 (02/22/2012):
+	General:
+	* Fix compilation when using binutils 2.22 and new GDK pixbuf. (#14799)
+	* Fix compilation of the MXit protocol plugin with GLib 2.31. (#14773)
+
+	Pidgin:
+	* Add support for the GNOME3 Network dialog. (#13882)
+	* Fix rare crash. (#14392)
+	* Add support for the GNOME3 Default Application dialog for configuring
+	  the Browser.
+
+	libpurple:
+	* Support new connection states and signals for NetworkManager 0.9+.
+	  (Dan Williams) (#13859)
+
+	AIM and ICQ:
+	* Allow signing on with usernames containing periods and
+	  underscores. (#13500)
+	* Allow adding buddies containing periods and underscores. (#13500)
+	* Don't try to format ICQ usernames entered as email addresses.
+	  Gets rid of an "Unable to format username" error at login. (#13883)
+
+	MSN:
+	* Fix possible crashes caused by not validating incoming messages as
+	  UTF-8. (Thijs Alkemade) (#14884)
+
+	Windows-Specific Changes:
+	* Fix compilation of the Bonjour protocol plugin. (#14802)
+
 version 2.10.1 (12/06/2011):
 	Finch:
 	* Fix compilation on OpenBSD.
--- a/ChangeLog.API	Sat Jan 21 12:02:41 2012 +0000
+++ b/ChangeLog.API	Thu Feb 23 08:13:23 2012 +0000
@@ -201,8 +201,8 @@
 		* purple_txt_cancel
 		* purple_txt_resolve_account
 		* purple_util_fetch_url_len. Use purple_util_fetch_url, instead.
-		* purple_util_fetch_url_request_len. Use purple_util_fetch_url_request,
-		  instead.
+		* purple_util_fetch_url_request_len. Use
+		* purple_util_fetch_url_request, instead.
 		* purple_util_fetch_url_request_len_with_account.  Use
 		  purple_util_fetch_url_request, instead.
 		* PurpleConnectionUiOps.report_disconnect_reason
--- a/finch/Makefile.am	Sat Jan 21 12:02:41 2012 +0000
+++ b/finch/Makefile.am	Thu Feb 23 08:13:23 2012 +0000
@@ -27,6 +27,7 @@
 	gntidle.c \
 	gntlog.c \
 	gntmedia.c \
+	gntmenuutil.c \
 	gntnotify.c \
 	gntplugin.c \
 	gntpounce.c \
@@ -49,6 +50,7 @@
 	gntidle.h \
 	gntlog.h \
 	gntmedia.h \
+	gntmenuutil.h \
 	gntnotify.h \
 	gntplugin.h \
 	gntpounce.h \
--- a/finch/gntblist.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/finch/gntblist.c	Thu Feb 23 08:13:23 2012 +0000
@@ -50,6 +50,7 @@
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 #include "gntmenuitemcheck.h"
+#include "gntmenuutil.h"
 #include "gntpounce.h"
 #include "gntstyle.h"
 #include "gnttree.h"
@@ -1072,46 +1073,6 @@
 }
 
 static void
-context_menu_callback(GntMenuItem *item, gpointer data)
-{
-	PurpleMenuAction *action = data;
-	PurpleBlistNode *node = ggblist->cnode;
-	if (action) {
-		void (*callback)(PurpleBlistNode *, gpointer);
-		callback = (void (*)(PurpleBlistNode *, gpointer))
-			purple_menu_action_get_callback(action);
-		if (callback)
-			callback(node, purple_menu_action_get_data(action));
-		else
-			return;
-	}
-}
-
-static void
-gnt_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer parent)
-{
-	GList *list;
-	GntMenuItem *item;
-
-	if (action == NULL)
-		return;
-
-	item = gnt_menuitem_new(purple_menu_action_get_label(action));
-	if (purple_menu_action_get_callback(action))
-		gnt_menuitem_set_callback(GNT_MENU_ITEM(item), context_menu_callback, action);
-	gnt_menu_add_item(menu, GNT_MENU_ITEM(item));
-
-	list = purple_menu_action_get_children(action);
-
-	if (list) {
-		GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
-		gnt_menuitem_set_submenu(item, GNT_MENU(sub));
-		for (; list; list = list->next)
-			gnt_append_menu_action(GNT_MENU(sub), list->data, action);
-	}
-}
-
-static void
 append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node)
 {
 	GList *list;
@@ -1127,9 +1088,7 @@
 		if (!act)
 			continue;
 		purple_menu_action_set_data(act, node);
-		gnt_append_menu_action(menu, act, NULL);
-		g_signal_connect_swapped(G_OBJECT(menu), "destroy",
-			G_CALLBACK(purple_menu_action_free), act);
+		gnt_append_menu_action(menu, act, node);
 	}
 }
 
@@ -1139,8 +1098,6 @@
 {
 	PurpleMenuAction *action = purple_menu_action_new(label, callback, data, NULL);
 	gnt_append_menu_action(menu, action, NULL);
-	g_signal_connect_swapped(G_OBJECT(menu), "destroy",
-			G_CALLBACK(purple_menu_action_free), action);
 }
 
 static void
@@ -1379,9 +1336,7 @@
 	for (iter = purple_blist_node_get_extended_menu(node);
 			iter; iter = g_list_delete_link(iter, iter))
 	{
-		gnt_append_menu_action(menu, iter->data, NULL);
-		g_signal_connect_swapped(G_OBJECT(menu), "destroy",
-				G_CALLBACK(purple_menu_action_free), iter->data);
+		gnt_append_menu_action(menu, iter->data, node);
 	}
 }
 
--- a/finch/gntconv.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/finch/gntconv.c	Thu Feb 23 08:13:23 2012 +0000
@@ -52,6 +52,7 @@
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 #include "gntmenuitemcheck.h"
+#include "gntmenuutil.h"
 #include "gntstyle.h"
 #include "gnttextview.h"
 #include "gnttree.h"
@@ -404,6 +405,30 @@
 }
 
 static void
+gg_extended_menu(FinchConv *ggc)
+{
+	GntWidget *sub;
+	GList *list;
+
+	sub = gnt_menu_new(GNT_MENU_POPUP);
+	gnt_menuitem_set_submenu(ggc->plugins, GNT_MENU(sub));
+
+	for (list = purple_conversation_get_extended_menu(ggc->active_conv);
+			list; list = g_list_delete_link(list, list))
+	{
+		gnt_append_menu_action(GNT_MENU(sub), list->data, ggc->active_conv);
+	}
+}
+
+static void
+conv_updated(PurpleConversation *conv, PurpleConvUpdateType type)
+{
+	if (type == PURPLE_CONV_UPDATE_FEATURES) {
+		gg_extended_menu(purple_conversation_get_ui_data(conv));
+	}
+}
+
+static void
 clear_scrollback_cb(GntMenuItem *item, gpointer ggconv)
 {
 	FinchConv *ggc = ggconv;
@@ -595,6 +620,12 @@
 }
 
 static void
+plugin_changed_cb(PurplePlugin *p, gpointer data)
+{
+	gg_extended_menu(data);
+}
+
+static void
 gg_create_menu(FinchConv *ggc)
 {
 	GntWidget *menu, *sub;
@@ -665,6 +696,12 @@
 			!(ggc->flags & FINCH_CONV_NO_SOUND));
 	gnt_menu_add_item(GNT_MENU(sub), item);
 	gnt_menuitem_set_callback(item, toggle_sound_cb, ggc);
+
+	item = gnt_menuitem_new(_("Plugins"));
+	gnt_menu_add_item(GNT_MENU(menu), item);
+	ggc->plugins = item;
+
+	gg_extended_menu(ggc);
 }
 
 static void
@@ -876,6 +913,11 @@
 	purple_signal_connect(purple_cmds_get_handle(), "cmd-removed", ggc,
 			G_CALLBACK(cmd_removed_cb), ggc);
 
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-load", ggc,
+				PURPLE_CALLBACK(plugin_changed_cb), ggc);
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", ggc,
+				PURPLE_CALLBACK(plugin_changed_cb), ggc);
+
 	g_free(title);
 	gnt_box_give_focus_to_child(GNT_BOX(ggc->window), ggc->entry);
 	g_signal_connect(G_OBJECT(ggc->window), "gained-focus", G_CALLBACK(gained_focus_cb), ggc);
@@ -1475,6 +1517,8 @@
 					PURPLE_CALLBACK(chat_left_cb), NULL);
 	purple_signal_connect(purple_conversations_get_handle(), "cleared-message-history", finch_conv_get_handle(),
 					PURPLE_CALLBACK(cleared_message_history_cb), NULL);
+	purple_signal_connect(purple_conversations_get_handle(), "conversation-updated", finch_conv_get_handle(),
+					PURPLE_CALLBACK(conv_updated), NULL);
 	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_conv_get_handle(),
 					PURPLE_CALLBACK(buddy_signed_on_off), NULL);
 	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_conv_get_handle(),
--- a/finch/gntconv.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/finch/gntconv.h	Thu Feb 23 08:13:23 2012 +0000
@@ -59,6 +59,7 @@
 	GntWidget *tv;            /* text-view */
 	GntWidget *menu;
 	GntWidget *info;
+	GntMenuItem *plugins;
 	FinchConversationFlag flags;
 
 	union
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntmenuutil.c	Thu Feb 23 08:13:23 2012 +0000
@@ -0,0 +1,79 @@
+/**
+ * @file gntmenuutil.c GNT Menu Utility Functions
+ * @ingroup finch
+ */
+
+/* finch
+ *
+ * Finch is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include <internal.h>
+#include "finch.h"
+
+#include "gnt.h"
+#include "gntmenu.h"
+#include "gntmenuitem.h"
+#include "gntmenuutil.h"
+
+static void
+context_menu_callback(GntMenuItem *item, gpointer data)
+{
+	PurpleMenuAction *action = data;
+	if (action) {
+		void (*callback)(gpointer, gpointer);
+		callback = (void (*)(gpointer, gpointer))
+			purple_menu_action_get_callback(action);
+		if (callback) {
+			gpointer ctx = g_object_get_data(G_OBJECT(item), "menuctx");
+			callback(ctx, purple_menu_action_get_data(action));
+		}
+	}
+}
+
+void
+gnt_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx)
+{
+	GList *list;
+	GntMenuItem *item;
+
+	if (action == NULL)
+		return;
+
+	item = gnt_menuitem_new(purple_menu_action_get_label(action));
+	if (purple_menu_action_get_callback(action)) {
+		gnt_menuitem_set_callback(item, context_menu_callback, action);
+		g_object_set_data(G_OBJECT(item), "menuctx", ctx);
+	}
+	gnt_menu_add_item(menu, item);
+
+	list = purple_menu_action_get_children(action);
+
+	if (list) {
+		GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
+		gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+		for (; list; list = g_list_delete_link(list, list))
+			gnt_append_menu_action(GNT_MENU(sub), list->data, action);
+		purple_menu_action_set_children(action, NULL);
+	}
+
+	g_signal_connect_swapped(G_OBJECT(menu), "destroy",
+		G_CALLBACK(purple_menu_action_free), action);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntmenuutil.h	Thu Feb 23 08:13:23 2012 +0000
@@ -0,0 +1,49 @@
+/**
+ * @file gntmenuutil.h GNT Menu Utility Functions
+ * @ingroup finch
+ */
+
+/* finch
+ *
+ * Finch is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#ifndef _GNT_MENUUTIL_H
+#define _GNT_MENUUTIL_H
+
+#include <gnt.h>
+#include <gntmenu.h>
+
+/***************************************************************************
+ * @name GNT Menu Utility Functions
+ ***************************************************************************/
+/*@{*/
+
+/**
+ * Add a PurpleMenuAction to a GntMenu.
+ *
+ * @param menu   the GntMenu to add to
+ * @param action the PurpleMenuAction to add
+ * @param ctx    the callback context, passed as the first argument to
+ *               the PurpleMenuAction's PurpleCallback function.
+ */
+void gnt_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx);
+
+/*@}*/
+
+#endif
--- a/libpurple/blist.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/blist.c	Thu Feb 23 08:13:23 2012 +0000
@@ -2908,6 +2908,17 @@
 	return node->type;
 }
 
+gboolean
+purple_blist_node_has_setting(PurpleBlistNode* node, const char *key)
+{
+	g_return_val_if_fail(node != NULL, FALSE);
+	g_return_val_if_fail(node->settings != NULL, FALSE);
+	g_return_val_if_fail(key != NULL, FALSE);
+
+	/* Boxed type, so it won't ever be NULL, so no need for _extended */
+	return (g_hash_table_lookup(node->settings, key) != NULL);
+}
+
 void
 purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
 {
--- a/libpurple/blist.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/blist.h	Thu Feb 23 08:13:23 2012 +0000
@@ -1052,6 +1052,16 @@
 void purple_blist_request_add_group(void);
 
 /**
+ * Checks whether a named setting exists for a node in the buddy list
+ *
+ * @param node  The node to check from which to check settings
+ * @param key   The identifier of the data
+ *
+ * @return TRUE if a value exists, or FALSE if there is no setting
+ */
+gboolean purple_blist_node_has_setting(PurpleBlistNode *node, const char *key);
+
+/**
  * Associates a boolean with a node in the buddy list
  *
  * @param node  The node to associate the data with
--- a/libpurple/protocols/jabber/caps.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/jabber/caps.c	Thu Feb 23 08:13:23 2012 +0000
@@ -888,7 +888,8 @@
 					                                   field->values);
 				}
 			} else {
-				g_list_free_full(field->values, g_free);
+				g_list_foreach(field->values, (GFunc) g_free, NULL);
+				g_list_free(field->values);
 			}
 
 			g_free(field->var);
--- a/libpurple/protocols/msn/contact.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/contact.c	Thu Feb 23 08:13:23 2012 +0000
@@ -740,6 +740,20 @@
 		}
 
 		passportName = xmlnode_get_child(contactInfo, "passportName");
+		if (passportName != NULL) {
+			xmlnode *messenger_user;
+			/* ignore non-messenger contacts */
+			if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) {
+				char *is_messenger_user = xmlnode_get_data(messenger_user);
+
+				if (is_messenger_user && !strcmp(is_messenger_user, "false")) {
+					passportName = NULL;
+				}
+
+				g_free(is_messenger_user);
+			}
+		}
+
 		if (passportName == NULL) {
 			xmlnode *emailsNode, *contactEmailNode, *emailNode;
 			xmlnode *messengerEnabledNode;
@@ -773,19 +787,6 @@
 				}
 			}
 		} else {
-			xmlnode *messenger_user;
-			/* ignore non-messenger contacts */
-			if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) {
-				char *is_messenger_user = xmlnode_get_data(messenger_user);
-
-				if (is_messenger_user && !strcmp(is_messenger_user, "false")) {
-					g_free(is_messenger_user);
-					continue;
-				}
-
-				g_free(is_messenger_user);
-			}
-
 			passport = xmlnode_get_data(passportName);
 		}
 
@@ -864,6 +865,21 @@
 	g_free(alias);
 }
 
+static void
+msn_parse_addressbook_circles(MsnSession *session, xmlnode *node)
+{
+	xmlnode *ticket;
+
+	/* TODO: Parse groups */
+
+	ticket = xmlnode_get_child(node, "CircleTicket");
+	if (ticket) {
+		char *data = xmlnode_get_data(ticket);
+		msn_notification_send_circle_auth(session, data);
+		g_free(data);
+	}
+}
+
 static gboolean
 msn_parse_addressbook(MsnSession *session, xmlnode *node)
 {
@@ -871,6 +887,7 @@
 	xmlnode *groups;
 	xmlnode *contacts;
 	xmlnode *abNode;
+	xmlnode *circleNode;
 	xmlnode *fault;
 
 	if ((fault = xmlnode_get_child(node, "Body/Fault"))) {
@@ -897,7 +914,7 @@
 		return FALSE;
 	}
 
-	result = xmlnode_get_child(node, "Body/ABFindAllResponse/ABFindAllResult");
+	result = xmlnode_get_child(node, "Body/ABFindContactsPagedResponse/ABFindContactsPagedResult");
 	if (result == NULL) {
 		purple_debug_misc("msn", "Received no address book update\n");
 		return TRUE;
@@ -906,7 +923,7 @@
 	/* I don't see this "groups" tag documented on msnpiki, need to find out
 	   if they are really there, and update msnpiki */
 	/*Process Group List*/
-	groups = xmlnode_get_child(result, "groups");
+	groups = xmlnode_get_child(result, "Groups");
 	if (groups != NULL) {
 		msn_parse_addressbook_groups(session, groups);
 	}
@@ -931,12 +948,12 @@
 
 	/*Process contact List*/
 	purple_debug_info("msn", "Process contact list...\n");
-	contacts = xmlnode_get_child(result, "contacts");
+	contacts = xmlnode_get_child(result, "Contacts");
 	if (contacts != NULL) {
 		msn_parse_addressbook_contacts(session, contacts);
 	}
 
-	abNode = xmlnode_get_child(result, "ab");
+	abNode = xmlnode_get_child(result, "Ab");
 	if (abNode != NULL) {
 		xmlnode *node2;
 		char *tmp = NULL;
@@ -954,6 +971,11 @@
 		g_free(tmp);
 	}
 
+	circleNode = xmlnode_get_child(result, "CircleResult");
+	if (circleNode != NULL) {
+		msn_parse_addressbook_circles(session, circleNode);
+	}
+
 	return TRUE;
 }
 
--- a/libpurple/protocols/msn/contact.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/contact.h	Thu Feb 23 08:13:23 2012 +0000
@@ -62,7 +62,7 @@
 
 #define MSN_APPLICATION_ID "CFE80F9D-180F-4399-82AB-413F33A1FA11"
 
-#define MSN_CONTACT_SERVER	"omega.contacts.msn.com"
+#define MSN_CONTACT_SERVER	"local-bay.contacts.msn.com"
 
 /* Get Contact List */
 
@@ -142,11 +142,13 @@
 "</soap:Envelope>"
 
 /* Get AddressBook */
-#define MSN_GET_ADDRESS_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABFindAll"
+#define MSN_GET_ADDRESS_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABFindContactsPaged"
 #define MSN_GET_ADDRESS_FULL_TIME	"0001-01-01T00:00:00.0000000-08:00"
 #define MSN_GET_ADDRESS_UPDATE_XML \
-	"<deltasOnly>true</deltasOnly>"\
-	"<lastChange>%s</lastChange>"
+	"<filterOptions>"\
+		"<deltasOnly>true</deltasOnly>"\
+		"<lastChange>%s</lastChange>"\
+	"</filterOptions>"
 
 #define MSN_GET_GLEAM_UPDATE_XML \
 	"%s"\
@@ -171,11 +173,11 @@
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
-		"<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+		"<ABFindContactsPaged xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<abView>Full</abView>"\
+			"<extendedContent>AB AllGroups CircleResult</extendedContent>"\
 			"%s"\
-		"</ABFindAll>"\
+		"</ABFindContactsPaged>"\
 	"</soap:Body>"\
 "</soap:Envelope>"
 
--- a/libpurple/protocols/msn/msn.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/msn.c	Thu Feb 23 08:13:23 2012 +0000
@@ -1223,7 +1223,7 @@
 	m = g_list_append(m, act);
 	m = g_list_append(m, NULL);
 
-	if (session->enable_mpop && session->protocol_ver >= 16)
+	if (session->enable_mpop)
 	{
 		act = purple_plugin_action_new(_("View Locations..."),
 		                               msn_show_locations);
@@ -1571,89 +1571,68 @@
 		return 0;
 	}
 
-	if (msn_user_is_online(account, who) ||
-		msn_user_is_yahoo(account, who) ||
-		swboard != NULL) {
-		/*User online or have a swboard open because it's invisible
-		 * and sent us a message,then send Online Instant Message*/
-
-		if (msglen + strlen(msgformat) + strlen(VERSION) > 1564)
-		{
-			g_free(msgformat);
-			g_free(msgtext);
-
-			return -E2BIG;
-		}
-
-		msg = msn_message_new_plain(msgtext);
-		msg->remote_user = g_strdup(who);
-		msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
-
+	if (msglen + strlen(msgformat) + strlen(VERSION) > 1564)
+	{
 		g_free(msgformat);
 		g_free(msgtext);
 
-		purple_debug_info("msn", "prepare to send online Message\n");
-		if (g_ascii_strcasecmp(who, username))
-		{
-			if (flags & PURPLE_MESSAGE_AUTO_RESP) {
-				msn_message_set_flag(msg, 'U');
-			}
-			if (msn_user_is_yahoo(account, who)) {
-				/*we send the online and offline Message to Yahoo User via UBM*/
-				purple_debug_info("msn", "send to Yahoo User\n");
-				uum_send_msg(session, msg);
-			} else {
-				purple_debug_info("msn", "send via switchboard\n");
-				msn_send_im_message(session, msg);
-			}
+		return -E2BIG;
+	}
+
+	msg = msn_message_new_plain(msgtext);
+	msg->remote_user = g_strdup(who);
+	msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
+
+	g_free(msgformat);
+	g_free(msgtext);
+
+	purple_debug_info("msn", "prepare to send online Message\n");
+	if (g_ascii_strcasecmp(who, username))
+	{
+		if (flags & PURPLE_MESSAGE_AUTO_RESP) {
+			msn_message_set_flag(msg, 'U');
+		}
+
+		if (msn_user_is_yahoo(account, who) || !(msn_user_is_online(account, who) || swboard != NULL)) {
+			/*we send the online and offline Message to Yahoo User via UBM*/
+			purple_debug_info("msn", "send to Yahoo User\n");
+			msn_notification_send_uum(session, msg);
+		} else {
+			purple_debug_info("msn", "send via switchboard\n");
+			msn_send_im_message(session, msg);
 		}
-		else
-		{
-			char *body_str, *body_enc, *pre, *post;
-			const char *format;
-			MsnIMData *imdata = g_new0(MsnIMData, 1);
-			/*
-			 * In MSN, you can't send messages to yourself, so
-			 * we'll fake like we received it ;)
-			 */
-			body_str = msn_message_to_string(msg);
-			body_enc = g_markup_escape_text(body_str, -1);
-			g_free(body_str);
-
-			format = msn_message_get_header_value(msg, "X-MMS-IM-Format");
-			msn_parse_format(format, &pre, &post);
-			body_str = g_strdup_printf("%s%s%s", pre ? pre :  "",
-									   body_enc ? body_enc : "", post ? post : "");
-			g_free(body_enc);
-			g_free(pre);
-			g_free(post);
-
-			serv_got_typing_stopped(gc, who);
-			imdata->gc = gc;
-			imdata->who = who;
-			imdata->msg = body_str;
-			imdata->flags = flags & ~PURPLE_MESSAGE_SEND;
-			imdata->when = time(NULL);
-			purple_timeout_add(0, msn_send_me_im, imdata);
-		}
-
-		msn_message_unref(msg);
-	} else {
-		/*send Offline Instant Message,only to MSN Passport User*/
-		char *friendname;
-
-		purple_debug_info("msn", "prepare to send offline Message\n");
-
-		friendname = msn_encode_mime(purple_account_get_username(account));
-		msn_oim_prep_send_msg_info(session->oim,
-			purple_account_get_username(account),
-			friendname, who, msgtext);
-		msn_oim_send_msg(session->oim);
-
-		g_free(msgformat);
-		g_free(msgtext);
-		g_free(friendname);
 	}
+	else
+	{
+		char *body_str, *body_enc, *pre, *post;
+		const char *format;
+		MsnIMData *imdata = g_new0(MsnIMData, 1);
+		/*
+		 * In MSN, you can't send messages to yourself, so
+		 * we'll fake like we received it ;)
+		 */
+		body_str = msn_message_to_string(msg);
+		body_enc = g_markup_escape_text(body_str, -1);
+		g_free(body_str);
+
+		format = msn_message_get_header_value(msg, "X-MMS-IM-Format");
+		msn_parse_format(format, &pre, &post);
+		body_str = g_strdup_printf("%s%s%s", pre ? pre :  "",
+								   body_enc ? body_enc : "", post ? post : "");
+		g_free(body_enc);
+		g_free(pre);
+		g_free(post);
+
+		serv_got_typing_stopped(gc, who);
+		imdata->gc = gc;
+		imdata->who = who;
+		imdata->msg = body_str;
+		imdata->flags = flags & ~PURPLE_MESSAGE_SEND;
+		imdata->when = time(NULL);
+		purple_timeout_add(0, msn_send_me_im, imdata);
+	}
+
+	msn_message_unref(msg);
 
 	return 1;
 }
--- a/libpurple/protocols/msn/msn.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/msn.h	Thu Feb 23 08:13:23 2012 +0000
@@ -115,10 +115,10 @@
 #define MSN_SERVER "messenger.hotmail.com"
 #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com"
 #define MSN_PORT 1863
-#define WLM_PROT_VER		16
+#define WLM_PROT_VER		18
 
-#define WLM_MAX_PROTOCOL	16
-#define WLM_MIN_PROTOCOL	15
+#define WLM_MAX_PROTOCOL	18
+#define WLM_MIN_PROTOCOL	18
 
 #define MSN_TYPING_RECV_TIMEOUT 6
 #define MSN_TYPING_SEND_TIMEOUT	4
--- a/libpurple/protocols/msn/msnutils.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Thu Feb 23 08:13:23 2012 +0000
@@ -490,6 +490,21 @@
 	*ret_port = port;
 }
 
+void
+msn_parse_user(const char *str, char **ret_user, int *ret_network)
+{
+	char **tokens;
+
+	tokens = g_strsplit(str, ":", 2);
+
+	*ret_network = atoi(tokens[0]);
+	*ret_user = tokens[1];
+
+	g_free(tokens[0]);
+	/* tokens[1] is returned */
+	g_free(tokens);
+}
+
 gboolean
 msn_email_is_valid(const char *passport)
 {
--- a/libpurple/protocols/msn/msnutils.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/msnutils.h	Thu Feb 23 08:13:23 2012 +0000
@@ -76,6 +76,15 @@
 void msn_parse_socket(const char *str, char **ret_host, int *ret_port);
 
 /**
+ * Parses a user name
+ *
+ * @param str         A network:username string.
+ * @param ret_user    Return of the user's passport.
+ * @param ret_network Return of the user's network.
+ */
+void msn_parse_user(const char *str, char **ret_user, int *ret_network);
+
+/**
  * Verify if the email is a vaild passport.
  *
  * @param passport 	The email
--- a/libpurple/protocols/msn/nexus.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/nexus.c	Thu Feb 23 08:13:23 2012 +0000
@@ -44,9 +44,8 @@
 	{"messenger.msn.com", "?id=507"},        /* Authentication for receiving OIMs. */
 	{"contacts.msn.com", "MBI"},             /* Authentication for the Contact server. */
 	{"messengersecure.live.com", "MBI_SSL"}, /* Authentication for sending OIMs. */
-	{"spaces.live.com", "MBI"},              /* Authentication for the Windows Live Spaces */
-	{"livecontacts.live.com", "MBI"},        /* Live Contacts API, a simplified version of the Contacts SOAP service */
 	{"storage.live.com", "MBI"},             /* Storage REST API */
+	{"sup.live.com", "MBI"},                 /* What's New service */
 };
 
 /**************************************************************************
--- a/libpurple/protocols/msn/nexus.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/nexus.h	Thu Feb 23 08:13:23 2012 +0000
@@ -36,9 +36,8 @@
 	MSN_AUTH_MESSENGER_WEB = 1,
 	MSN_AUTH_CONTACTS      = 2,
 	MSN_AUTH_LIVE_SECURE   = 3,
-	MSN_AUTH_SPACES        = 4,
-	MSN_AUTH_LIVE_CONTACTS = 5,
-	MSN_AUTH_STORAGE       = 6
+	MSN_AUTH_STORAGE       = 4,
+	MSN_AUTH_WHATSNEW      = 5
 } MsnAuthDomains;
 
 #define MSN_SSO_SERVER	"login.live.com"
--- a/libpurple/protocols/msn/notification.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/notification.c	Thu Feb 23 08:13:23 2012 +0000
@@ -164,10 +164,7 @@
 
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
 
-	if (session->protocol_ver >= 16)
-		trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s %s", ticket, response, session->guid);
-	else
-		trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s", ticket, response);
+	trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s %s", ticket, response, session->guid);
 
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
@@ -359,23 +356,34 @@
 
 /*send Message to Yahoo Messenger*/
 void
-uum_send_msg(MsnSession *session,MsnMessage *msg)
+msn_notification_send_uum(MsnSession *session, MsnMessage *msg)
 {
 	MsnCmdProc *cmdproc;
 	MsnTransaction *trans;
 	char *payload;
 	gsize payload_len;
 	int type;
+	MsnUser *user;
+	int network;
+
+	g_return_if_fail(msg != NULL);
 
 	cmdproc = session->notification->cmdproc;
-	g_return_if_fail(msg     != NULL);
+
 	payload = msn_message_gen_payload(msg, &payload_len);
+	type = msg->type;
+	user = msn_userlist_find_user(session->userlist, msg->remote_user);
+	if (user)
+		network = msn_user_get_network(user);
+	else
+		network = MSN_NETWORK_PASSPORT;
+
 	purple_debug_info("msn",
 		"send UUM, payload{%s}, strlen:%" G_GSIZE_FORMAT ", len:%" G_GSIZE_FORMAT "\n",
 		payload, strlen(payload), payload_len);
-	type = msg->type;
-	trans = msn_transaction_new(cmdproc, "UUM", "%s 32 %d %" G_GSIZE_FORMAT,
-		msg->remote_user, type, payload_len);
+
+	trans = msn_transaction_new(cmdproc, "UUM", "%s %d %d %" G_GSIZE_FORMAT,
+		msg->remote_user, network, type, payload_len);
 	msn_transaction_set_payload(trans, payload, strlen(payload));
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
@@ -390,10 +398,7 @@
 	 * command and we are processing it */
 	if (cmd->payload == NULL) {
 		cmdproc->last_cmd->payload_cb = msg_cmd_post;
-		if (cmdproc->session->protocol_ver >= 16)
-			cmd->payload_len = atoi(cmd->params[5]);
-		else
-			cmd->payload_len = atoi(cmd->params[3]);
+		cmd->payload_len = atoi(cmd->params[5]);
 	} else {
 		g_return_if_fail(cmd->payload_cb != NULL);
 
@@ -1042,7 +1047,7 @@
 		networkid = atoi(cmd->params[3]);
 		friendly = g_strdup(purple_url_decode(cmd->params[4]));
 		clientid = strtoul(cmd->params[5], &extcap_str, 10);
-		if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+		if (extcap_str && *extcap_str)
 			extcaps = strtoul(extcap_str+1, NULL, 10);
 		else
 			extcaps = 0;
@@ -1056,7 +1061,7 @@
 		networkid = atoi(cmd->params[3]);
 		friendly = g_strdup(purple_url_decode(cmd->params[4]));
 		clientid = strtoul(cmd->params[5], &extcap_str, 10);
-		if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+		if (extcap_str && *extcap_str)
 			extcaps = strtoul(extcap_str+1, NULL, 10);
 		else
 			extcaps = 0;
@@ -1069,7 +1074,7 @@
 			networkid = atoi(cmd->params[3]);
 			friendly = g_strdup(purple_url_decode(cmd->params[4]));
 			clientid = strtoul(cmd->params[5], &extcap_str, 10);
-			if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+			if (extcap_str && *extcap_str)
 				extcaps = strtoul(extcap_str+1, NULL, 10);
 			else
 				extcaps = 0;
@@ -1077,7 +1082,7 @@
 			/* MSNP8+ with Display Picture object */
 			friendly = g_strdup(purple_url_decode(cmd->params[3]));
 			clientid = strtoul(cmd->params[4], &extcap_str, 10);
-			if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+			if (extcap_str && *extcap_str)
 				extcaps = strtoul(extcap_str+1, NULL, 10);
 			else
 				extcaps = 0;
@@ -1087,7 +1092,7 @@
 		/* MSNP8+ without Display Picture object */
 		friendly = g_strdup(purple_url_decode(cmd->params[3]));
 		clientid = strtoul(cmd->params[4], &extcap_str, 10);
-		if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+		if (extcap_str && *extcap_str)
 			extcaps = strtoul(extcap_str+1, NULL, 10);
 		else
 			extcaps = 0;
@@ -1250,15 +1255,15 @@
 	MsnObject *msnobj;
 	unsigned long clientid, extcaps;
 	char *extcap_str;
+	char *passport;
 	int networkid;
-	const char *state, *passport, *friendly;
+	const char *state, *friendly;
 
 	session = cmdproc->session;
 
-	state    = cmd->params[0];
-	passport = cmd->params[1];
-	networkid = atoi(cmd->params[2]);
-	friendly = purple_url_decode(cmd->params[3]);
+	state = cmd->params[0];
+	msn_parse_user(cmd->params[1], &passport, &networkid);
+	friendly = purple_url_decode(cmd->params[2]);
 
 	user = msn_userlist_find_user(session->userlist, passport);
 	if (user == NULL) return;
@@ -1268,9 +1273,9 @@
 		msn_update_contact(session, passport, MSN_UPDATE_DISPLAY, friendly);
 	}
 
-	if (cmd->param_count == 6)
+	if (cmd->param_count == 5)
 	{
-		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
+		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[4]));
 		msn_user_set_object(user, msnobj);
 	}
 	else
@@ -1278,8 +1283,8 @@
 		msn_user_set_object(user, NULL);
 	}
 
-	clientid = strtoul(cmd->params[4], &extcap_str, 10);
-	if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+	clientid = strtoul(cmd->params[3], &extcap_str, 10);
+	if (extcap_str && *extcap_str)
 		extcaps = strtoul(extcap_str+1, NULL, 10);
 	else
 		extcaps = 0;
@@ -1292,6 +1297,8 @@
 
 	msn_user_set_state(user, state);
 	msn_user_update(user);
+
+	g_free(passport);
 }
 
 #if 0
@@ -1705,22 +1712,27 @@
 {
 	MsnSession *session;
 	MsnUser *user;
-	const char *passport;
+	char *passport;
+	int network;
 	xmlnode *payloadNode;
 	char *psm_str, *str;
 
 	session = cmdproc->session;
 
-	passport = cmd->params[0];
+	msn_parse_user(cmd->params[0], &passport, &network);
 	user = msn_userlist_find_user(session->userlist, passport);
+
 	if (user == NULL) {
-		char *str = g_strndup(payload, len);
+		str = g_strndup(payload, len);
 		purple_debug_info("msn", "unknown user %s, payload is %s\n",
 			passport, str);
+		g_free(passport);
 		g_free(str);
 		return;
 	}
 
+	g_free(passport);
+
 	/* Free any existing media info for this user */
 	if (user->extinfo) {
 		g_free(user->extinfo->media_album);
@@ -1767,7 +1779,7 @@
 {
 	purple_debug_misc("msn", "UBX received.\n");
 	cmdproc->last_cmd->payload_cb  = ubx_cmd_post;
-	cmd->payload_len = atoi(cmd->params[2]);
+	cmd->payload_len = atoi(cmd->params[1]);
 }
 
 static void
@@ -1812,10 +1824,7 @@
 	epDataNode = xmlnode_new("EndpointData");
 
 	capNode = xmlnode_new_child(epDataNode, "Capabilities");
-	if (session->protocol_ver >= 16)
-		caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS);
-	else
-		caps = g_strdup_printf("%d", MSN_CLIENT_ID_CAPABILITIES);
+	caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS);
 	xmlnode_insert_data(capNode, caps, -1);
 	g_free(caps);
 
@@ -1942,6 +1951,22 @@
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
+void
+msn_notification_send_circle_auth(MsnSession *session, const char *ticket)
+{
+	MsnTransaction *trans;
+	MsnCmdProc *cmdproc;
+	char *encoded;
+
+	cmdproc = session->notification->cmdproc;
+
+	encoded = purple_base64_encode((guchar *)ticket, strlen(ticket));
+	trans = msn_transaction_new(cmdproc, "USR", "SHA A %s", encoded);
+	msn_cmdproc_send_trans(cmdproc, trans);
+
+	g_free(encoded);
+}
+
 /**************************************************************************
  * Message Types
  **************************************************************************/
--- a/libpurple/protocols/msn/notification.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/notification.h	Thu Feb 23 08:13:23 2012 +0000
@@ -76,8 +76,6 @@
 
 } MsnUnifiedNotificationType;
 
-void uum_send_msg(MsnSession *session, MsnMessage *msg);
-
 void msn_notification_end(void);
 void msn_notification_init(void);
 
@@ -97,6 +95,8 @@
 void msn_notification_disconnect(MsnNotification *notification);
 void msn_notification_dump_contact(MsnSession *session);
 
+void msn_notification_send_uum(MsnSession *session, MsnMessage *msg);
+
 void msn_notification_send_uux(MsnSession *session, const char *payload);
 
 void msn_notification_send_uux_endpointdata(MsnSession *session);
@@ -108,6 +108,8 @@
                                MsnUnifiedNotificationType type,
                                const char *payload);
 
+void msn_notification_send_circle_auth(MsnSession *session, const char *ticket);
+
 /**
  * Closes a notification.
  *
--- a/libpurple/protocols/msn/oim.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/oim.c	Thu Feb 23 08:13:23 2012 +0000
@@ -606,11 +606,12 @@
 	const char *date;
 	const char *from;
 	const char *boundary;
-	char *decode_msg = NULL;
+	char *decode_msg = NULL, *clean_msg = NULL;
 	gsize body_len;
 	char **tokens;
 	char *passport = NULL;
 	time_t stamp;
+	const char *charset = NULL;
 
 	message = msn_message_new(MSN_MSG_UNKNOWN);
 
@@ -638,6 +639,8 @@
 			type = msn_message_get_content_type(multipart);
 			if (type && !strcmp(type, "text/plain")) {
 				decode_msg = (char *)purple_base64_decode(multipart->body, &body_len);
+				charset = msn_message_get_charset(multipart);
+
 				msn_message_unref(multipart);
 				break;
 			}
@@ -654,6 +657,46 @@
 		}
 	} else {
 		decode_msg = (char *)purple_base64_decode(message->body, &body_len);
+		charset = msn_message_get_charset(message);
+	}
+
+	if (charset && !((strncasecmp(charset, "UTF-8", 5) == 0) || (strncasecmp(charset, "UTF8", 4) == 0))) {
+		clean_msg = g_convert(decode_msg, body_len, "UTF-8", charset, NULL, NULL, NULL);
+
+		if (!clean_msg) {
+			char *clean = purple_utf8_salvage(decode_msg);
+
+			purple_debug_error("msn", "Failed to convert charset from %s to UTF-8 for OIM message: %s\n", charset, clean);
+
+			clean_msg = g_strdup_printf(_("%s (There was an error receiving this message. "
+			                              "Converting the encoding from %s to UTF-8 failed.)"),
+			                            clean, charset);
+			g_free(clean);
+		}
+
+		g_free(decode_msg);
+
+	} else if (!g_utf8_validate(decode_msg, body_len, NULL)) {
+		char *clean = purple_utf8_salvage(decode_msg);
+
+		purple_debug_error("msn", "Received an OIM message that is not UTF-8,"
+		                          " and no encoding specified: %s\n", clean);
+
+		if (charset) {
+			clean_msg = g_strdup_printf(_("%s (There was an error receiving this message."
+			                              " The charset was %s, but it was not valid UTF-8.)"),
+			                            clean, charset);
+		} else {
+			clean_msg = g_strdup_printf(_("%s (There was an error receiving this message."
+			                              " The charset was missing, but it was not valid UTF-8.)"),
+			                            clean);
+		}
+
+		g_free(clean);
+		g_free(decode_msg);
+
+	} else {
+		clean_msg = decode_msg;
 	}
 
 	from = msn_message_get_header_value(message, "X-OIM-originatingSource");
@@ -695,7 +738,7 @@
 	purple_debug_info("msn", "oim Date:{%s},passport{%s}\n",
 	                  date, passport);
 
-	serv_got_im(purple_account_get_connection(rdata->oim->session->account), passport, decode_msg, 0,
+	serv_got_im(purple_account_get_connection(rdata->oim->session->account), passport, clean_msg, 0,
 	            stamp);
 
 	/*Now get the oim message ID from the oim_list.
@@ -704,7 +747,7 @@
 	msn_oim_post_delete_msg(rdata);
 
 	g_free(passport);
-	g_free(decode_msg);
+	g_free(clean_msg);
 	msn_message_unref(message);
 }
 
--- a/libpurple/protocols/msn/session.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/session.c	Thu Feb 23 08:13:23 2012 +0000
@@ -491,11 +491,10 @@
 		msn_session_sync_users(session);
 	}
 
-	if (session->protocol_ver >= 16) {
-		/* TODO: Send this when updating status instead? */
-		msn_notification_send_uux_endpointdata(session);
-		msn_notification_send_uux_private_endpointdata(session);
-	}
+	/* TODO: Send this when updating status instead? */
+	msn_notification_send_uux_endpointdata(session);
+	msn_notification_send_uux_private_endpointdata(session);
+
 	msn_change_status(session);
 }
 
--- a/libpurple/protocols/msn/state.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/state.c	Thu Feb 23 08:13:23 2012 +0000
@@ -186,7 +186,7 @@
 	statusline_stripped = purple_markup_strip_html(statusline);
 	media = create_media_string(presence);
 	g_free(session->psm);
-	session->psm = msn_build_psm(statusline_stripped, media, session->protocol_ver >= 16 ? session->guid : NULL, session->protocol_ver);
+	session->psm = msn_build_psm(statusline_stripped, media, session->guid, session->protocol_ver);
 
 	payload = session->psm;
 
@@ -245,10 +245,8 @@
 
 	if (msnobj == NULL)
 	{
-		if (session->protocol_ver >= 16)
-			trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u 0", state_text, caps, MSN_CLIENT_ID_EXT_CAPS);
-		else
-			trans = msn_transaction_new(cmdproc, "CHG", "%s %u", state_text, caps);
+		trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u 0", state_text,
+		                            caps, MSN_CLIENT_ID_EXT_CAPS);
 	}
 	else
 	{
@@ -256,12 +254,9 @@
 
 		msnobj_str = msn_object_to_string(msnobj);
 
-		if (session->protocol_ver >= 16)
-			trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u %s", state_text,
-							 caps, MSN_CLIENT_ID_EXT_CAPS, purple_url_encode(msnobj_str));
-		else
-			trans = msn_transaction_new(cmdproc, "CHG", "%s %u %s", state_text,
-							 caps, purple_url_encode(msnobj_str));
+		trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u %s", state_text,
+		                            caps, MSN_CLIENT_ID_EXT_CAPS,
+		                            purple_url_encode(msnobj_str));
 
 		g_free(msnobj_str);
 	}
--- a/libpurple/protocols/msn/switchboard.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Thu Feb 23 08:13:23 2012 +0000
@@ -707,10 +707,7 @@
 ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	purple_debug_misc("msn", "get UBM...\n");
-	if (cmdproc->session->protocol_ver >= 16)
-		cmd->payload_len = atoi(cmd->params[5]);
-	else
-		cmd->payload_len = atoi(cmd->params[3]);
+	cmd->payload_len = atoi(cmd->params[5]);
 	cmdproc->last_cmd->payload_cb = msg_cmd_post;
 }
 
@@ -867,12 +864,9 @@
 	swboard = cmdproc->data;
 	g_return_if_fail(swboard != NULL);
 
-	if (servconn->session->protocol_ver >= 16)
-		username = g_strdup_printf("%s;{%s}",
-		                           purple_account_get_username(account),
-		                           servconn->session->guid);
-	else
-		username = g_strdup(purple_account_get_username(account));
+	username = g_strdup_printf("%s;{%s}",
+	                           purple_account_get_username(account),
+	                           servconn->session->guid);
 
 	if (msn_switchboard_is_invited(swboard))
 	{
--- a/libpurple/protocols/msn/user.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/msn/user.c	Thu Feb 23 08:13:23 2012 +0000
@@ -699,6 +699,14 @@
 	return NULL;
 }
 
+MsnNetwork
+msn_user_get_network(const MsnUser *user)
+{
+	g_return_val_if_fail(user != NULL, MSN_NETWORK_UNKNOWN);
+
+	return user->networkid;
+}
+
 MsnObject *
 msn_user_get_object(const MsnUser *user)
 {
--- a/libpurple/protocols/oscar/family_feedbag.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/oscar/family_feedbag.c	Thu Feb 23 08:13:23 2012 +0000
@@ -769,7 +769,7 @@
  * @param od The oscar odion.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-int aim_ssi_cleanlist(OscarData *od)
+static int aim_ssi_cleanlist(OscarData *od)
 {
 	struct aim_ssi_item *cur, *next;
 
@@ -971,7 +971,7 @@
 	aim_ssi_itemlist_del(&od->ssi.local, del);
 
 	/* Modify the parent group */
-	aim_ssi_itemlist_rebuildgroup(&od->ssi.local, group);
+	aim_ssi_itemlist_rebuildgroup(&od->ssi.local, NULL);
 
 	/* Sync our local list with the server list */
 	return aim_ssi_sync(od);
@@ -1336,6 +1336,9 @@
 		for (cur=od->ssi.official.data; cur; cur=cur->next)
 			aim_ssi_itemlist_add(&od->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
 
+		/* Clean the buddy list */
+		aim_ssi_cleanlist(od);
+
 		od->ssi.received_data = TRUE;
 
 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
--- a/libpurple/protocols/oscar/family_icbm.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Thu Feb 23 08:13:23 2012 +0000
@@ -650,6 +650,9 @@
 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
+	g_return_if_fail(bn != NULL);
+	g_return_if_fail(ip != NULL);
+
 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 	if (conn == NULL)
 		return;
--- a/libpurple/protocols/oscar/oscar.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Feb 23 08:13:23 2012 +0000
@@ -3924,9 +3924,6 @@
 
 	purple_debug_info("oscar", "ssi: syncing local list and server list\n");
 
-	/* Clean the buddy list */
-	aim_ssi_cleanlist(od);
-
 	/*** Begin code for pruning buddies from local list if they're not in server list ***/
 
 	/* Buddies */
--- a/libpurple/protocols/oscar/oscar.h	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Thu Feb 23 08:13:23 2012 +0000
@@ -940,7 +940,6 @@
 int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias);
 int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *alias);
 int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn);
-int aim_ssi_cleanlist(OscarData *od);
 int aim_ssi_deletelist(OscarData *od);
 int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny);
 int aim_ssi_setpresence(OscarData *od, guint32 presence);
--- a/libpurple/protocols/oscar/peer.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/oscar/peer.c	Thu Feb 23 08:13:23 2012 +0000
@@ -656,6 +656,7 @@
 	char *tmp;
 	FlapConnection *bos_conn;
 	const char *listener_ip;
+	const guchar *ip_atoi;
 	unsigned short listener_port;
 
 	conn = data;
@@ -690,11 +691,28 @@
 		listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
 	else
 		listener_ip = purple_network_get_my_ip(bos_conn->fd);
+
+	ip_atoi = purple_network_ip_atoi(listener_ip);
+	if (ip_atoi == NULL) {
+		/* Could not convert IP to 4 byte array--weird, but this does
+		   happen for some users (#4829, Adium #15839).  Maybe they're
+		   connecting with IPv6...?  Maybe through a proxy? */
+		purple_debug_error("oscar", "Can't ask peer to connect to us "
+				"because purple_network_ip_atoi(%s) returned NULL. "
+				"fd=%d. is_ssl=%d\n",
+				listener_ip ? listener_ip : "(null)",
+				bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
+				bos_conn->gsc ? 1 : 0);
+		peer_connection_trynext(conn);
+		return;
+	}
+
 	listener_port = purple_network_get_port_from_fd(conn->listenerfd);
+
 	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
 	{
 		aim_im_sendch2_odc_requestdirect(od,
-				conn->cookie, conn->bn, purple_network_ip_atoi(listener_ip),
+				conn->cookie, conn->bn, ip_atoi,
 				listener_port, ++conn->lastrequestnumber);
 
 		/* Print a message to a local conversation window */
@@ -706,15 +724,6 @@
 	}
 	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
 	{
-		const guchar *ip_atoi = purple_network_ip_atoi(listener_ip);
-		if (ip_atoi == NULL) {
-			purple_debug_error("oscar", "Cannot send file. atoi(%s) failed.\n"
-					"Other possibly useful information: fd = %d, port = %d\n",
-					listener_ip ? listener_ip : "(null!)", conn->listenerfd,
-					listener_port);
-			purple_xfer_cancel_local(conn->xfer);
-			return;
-		}
 		aim_im_sendch2_sendfile_requestdirect(od,
 				conn->cookie, conn->bn,
 				ip_atoi,
--- a/libpurple/protocols/oscar/util.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/libpurple/protocols/oscar/util.c	Thu Feb 23 08:13:23 2012 +0000
@@ -141,12 +141,12 @@
 	if (purple_email_is_valid(name))
 		return TRUE;
 
-	/* Normal AIM usernames can't start with a number */
-	if (isdigit(name[0]))
+	/* Normal AIM usernames can't start with a number, period or underscore */
+	if (isalnum(name[0]) == 0)
 		return FALSE;
 
 	for (i = 0; name[i] != '\0'; i++) {
-		if (!isalnum(name[i]) && (name[i] != ' '))
+		if (!isalnum(name[i]) && name[i] != ' ' && name[i] != '.' && name[i] != '_')
 			return FALSE;
 	}
 
--- a/pidgin/gtkconv.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/pidgin/gtkconv.c	Thu Feb 23 08:13:23 2012 +0000
@@ -5797,7 +5797,7 @@
 	if (convnode == NULL || !purple_blist_node_get_bool(convnode, "gtk-mute-sound"))
 		gtkconv->make_sound = TRUE;
 
-	if (convnode != NULL) {
+	if (convnode != NULL && purple_blist_node_has_setting(convnode, "enable-logging")) {
 		gboolean logging = purple_blist_node_get_bool(convnode, "enable-logging");
 		purple_conversation_set_logging(conv, logging);
 	}
@@ -8257,6 +8257,7 @@
 add_message_history_to_gtkconv(gpointer data)
 {
 	PidginConversation *gtkconv = data;
+	GtkWebView *webview = GTK_WEBVIEW(gtkconv->webview);
 	int count = 0;
 	int timer = gtkconv->attach.timer;
 	time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->entry), "attach-start-time"));
@@ -8266,7 +8267,8 @@
 	while (gtkconv->attach.current && count < 100) {  /* XXX: 100 is a random value here */
 		PurpleConvMessage *msg = gtkconv->attach.current->data;
 		if (!im && when && when < purple_conversation_message_get_timestamp(msg)) {
-			gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>");
+			gtk_webview_append_html(webview, "<BR><HR>");
+			gtk_webview_scroll_to_end(webview, TRUE);
 			g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
 		}
 		pidgin_conv_write_conv(
@@ -8313,7 +8315,8 @@
 					purple_conversation_message_get_flags(msg),
 					purple_conversation_message_get_timestamp(msg));
 		}
-		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>");
+		gtk_webview_append_html(webview, "<BR><HR>");
+		gtk_webview_scroll_to_end(webview, TRUE);
 		g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
 	}
 
--- a/pidgin/gtkdialogs.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/pidgin/gtkdialogs.c	Thu Feb 23 08:13:23 2012 +0000
@@ -78,8 +78,6 @@
 	{"Hylke Bons",                         N_("artist"),          "hylkebons@gmail.com"},
 	{"Sadrul Habib Chowdhury",             NULL,                  NULL},
 	{"Mark 'KingAnt' Doliner",             NULL,                  "mark@kingant.net"},
-	{"Casey Harkins",                      NULL,                  NULL},
-	{"Ivan Komarov",                       NULL,                  "ivan.komarov@pidgin.im"},
 	{"Gary 'grim' Kramlich",               NULL,                  "grim@pidgin.im"},
 	{"Richard 'rlaager' Laager",           NULL,                  "rlaager@pidgin.im"},
 	{"Marcus 'malu' Lundblad",             NULL,                  NULL},
@@ -112,12 +110,14 @@
 	{"Herman Bloggs",               N_("win32 port"),          "herman@bluedigits.com"},
 	{"Thomas Butter",               NULL,                      NULL},
 	/* feel free to not translate this */
-	{N_("Ka-Hing Cheung"),                 NULL,                  NULL},
+	{N_("Ka-Hing Cheung"),          NULL,                      NULL},
 	{"Jim Duchek",                  N_("maintainer"),          "jim@linuxpimps.com"},
 	{"Sean Egan",                   NULL,                      "sean.egan@gmail.com"},
 	{"Rob Flynn",                   N_("maintainer"),          NULL},
 	{"Adam Fritzler",               N_("libfaim maintainer"),  NULL},
 	{"Christian 'ChipX86' Hammond", N_("webmaster"),           NULL},
+	{"Casey Harkins",               NULL,                      NULL},
+	{"Ivan Komarov",                NULL,                      "ivan.komarov@pidgin.im"},
 	/* If "lazy bum" translates literally into a serious insult, use something else or omit it. */
 	{"Syd Logan",                   N_("hacker and designated driver [lazy bum]"), NULL},
 	{"Christopher 'siege' O'Brien", NULL,                      "taliesein@users.sf.net"},
@@ -142,33 +142,34 @@
 	{NULL, NULL, NULL}
 };
 
-/* Order: Code, then Alphabetical by Last Name */
+/* Order: Code, then Alphabetical by Last Name
+   Use NULL language and code for secondary translators. */
 static const struct translator translators[] = {
 	{N_("Afrikaans"),           "af", "Samuel Murray", "afrikaans@gmail.com"},
-	{N_("Afrikaans"),           "af", "Friedel Wolff", "friedel@translate.org.za"},
+	{NULL,                      NULL, "Friedel Wolff", "friedel@translate.org.za"},
 	{N_("Arabic"),              "ar", "Khaled Hosny", "khaledhosny@eglug.org"},
 	{N_("Assamese"),            "as", "Amitakhya Phukan", "aphukan@fedoraproject.org"},
 	{N_("Belarusian Latin"),    "be@latin", "Ihar Hrachyshka", "ihar.hrachyshka@gmail.com"},
 	{N_("Bulgarian"),           "bg", "Vladimira Girginova", "missing@here.is"},
-	{N_("Bulgarian"),           "bg", "Vladimir (Kaladan) Petkov", "kaladan@gmail.com"},
+	{NULL,                      NULL, "Vladimir (Kaladan) Petkov", "kaladan@gmail.com"},
 	{N_("Bengali"),             "bn", "Israt Jahan", "israt@ankur.org.bd"},
-	{N_("Bengali"),             "bn", "Jamil Ahmed", "jamil@bengalinux.org"},
-	{N_("Bengali"),             "bn", "Samia Nimatullah", "mailsamia2001@yahoo.com"},
+	{NULL,                      NULL, "Jamil Ahmed", "jamil@bengalinux.org"},
+	{NULL,                      NULL, "Samia Nimatullah", "mailsamia2001@yahoo.com"},
 	{N_("Bengali-India"),       "bn_IN", "Runa Bhattacharjee", "runab@fedoraproject.org"},
 	{N_("Bosnian"),             "bs", "Lejla Hadzialic", "lejlah@gmail.com"},
 	{N_("Catalan"),             "ca", "Josep Puigdemont", "josep.puigdemont@gmail.com"},
 	{N_("Valencian-Catalan"),   "ca@valencia", "Toni Hermoso", "toniher@softcatala.org"},
-	{N_("Valencian-Catalan"),   "ca@valencia", "Josep Puigdemont", "tradgnome@softcatala.org"},
+	{NULL,                      NULL, "Josep Puigdemont", "tradgnome@softcatala.org"},
 	{N_("Czech"),               "cs", "David Vachulka", "david@konstrukce-cad.com"},
 	{N_("Danish"),              "da", "Morten Brix Pedersen", "morten@wtf.dk"},
-	{N_("Danish"),              "da", "Peter Bach", "bach.peter@gmail.com"},
+	{NULL,                      NULL, "Peter Bach", "bach.peter@gmail.com"},
 	{N_("German"),              "de", "Björn Voigt", "bjoern@cs.tu-berlin.de"},
-	{N_("German"),              "de", "Jochen Kemnade", "jochenkemnade@web.de"},
+	{NULL,                      NULL, "Jochen Kemnade", "jochenkemnade@web.de"},
 	{N_("Dzongkha"),            "dz", "Norbu", "nor_den@hotmail.com"},
-	{N_("Dzongkha"),            "dz", "Jurmey Rabgay", "jur_gay@yahoo.com"},
-	{N_("Dzongkha"),            "dz", "Wangmo Sherpa", "rinwanshe@yahoo.com"},
+	{NULL,                      NULL, "Jurmey Rabgay", "jur_gay@yahoo.com"},
+	{NULL,                      NULL, "Wangmo Sherpa", "rinwanshe@yahoo.com"},
 	{N_("Greek"),               "el", "Katsaloulis Panayotis", "panayotis@panayotis.com"},
-	{N_("Greek"),               "el", "Bouklis Panos", "panos@echidna-band.com"},
+	{NULL,                      NULL, "Bouklis Panos", "panos@echidna-band.com"},
 	{N_("Australian English"),  "en_AU", "Peter Lawler", "trans@six-by-nine.com.au"},
 	{N_("British English"),     "en_GB", "Phil Hannent", "phil@hannent.co.uk"},
 	{N_("Canadian English"),    "en_CA", "Adam Weinberger", "adamw@gnome.org"},
@@ -177,18 +178,18 @@
 	{N_("Estonian"),            "et", "Ivar Smolin", "okul@linux.ee"},
 	{N_("Basque"),              "eu", "Mikel Pascual Aldabaldetreku", "mikel.paskual@gmail.com"},
 	{N_("Persian"),             "fa", "Elnaz Sarbar", "elnaz@farsiweb.info"},
-	{N_("Persian"),             "fa", "Meelad Zakaria", "meelad@farsiweb.info"},
-	{N_("Persian"),             "fa", "Roozbeh Pournader ", "roozbeh@farsiweb.info"},
+	{NULL,                      NULL, "Meelad Zakaria", "meelad@farsiweb.info"},
+	{NULL,                      NULL, "Roozbeh Pournader ", "roozbeh@farsiweb.info"},
 	{N_("Finnish"),             "fi", "Timo Jyrinki", "timo.jyrinki@iki.fi"},
 	{N_("French"),              "fr", "Éric Boumaour", "zongo_fr@users.sourceforge.net"},
 	{N_("Irish"),               "ga", "Aaron Kearns", "ajkearns6@gmail.com"},
 	{N_("Galician"),            "gl", "Mar Castro", "mariamarcp@gmail.com"},
-	{N_("Galician"),            "gl", "Frco. Javier Rial", "fjrial@cesga.es"},
+	{NULL,                      NULL, "Frco. Javier Rial", "fjrial@cesga.es"},
 	{N_("Gujarati"),            "gu", "Ankit Patel", "ankit_patel@users.sf.net"},
-	{N_("Gujarati"),            "gu", N_("Gujarati Language Team"), "indianoss-gujarati@lists.sourceforge.net"},
+	{NULL,                      NULL, N_("Gujarati Language Team"), "indianoss-gujarati@lists.sourceforge.net"},
 	{N_("Hebrew"),              "he", "Shalom Craimer", "scraimer@gmail.com"},
 	{N_("Hindi"),               "hi", "Sangeeta Kumari", "sangeeta_0975@yahoo.com"},
-	{N_("Hindi"),               "hi", "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
+	{NULL,                      NULL, "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
 	{N_("Croatian"),            "hr", "Sabina Drempetić", "bina91991@googlemail.com"},
 	{N_("Hungarian"),           "hu", "Kelemen Gábor", "kelemeng@gnome.hu"},
 	{N_("Armenian"),            "hy", "David Avsharyan", "avsharyan@gmail.com"},
@@ -200,15 +201,15 @@
 	{N_("Kannada"),             "kn", N_("Kannada Translation team"), "translation@sampada.info"},
 	{N_("Korean"),              "ko", "Sushizang", "sushizang@empal.com"},
 	{N_("Kurdish"),             "ku", "Erdal Ronahi", "erdal.ronahi@gmail.com"},
-	{N_("Kurdish"),             "ku", "Amed Ç. Jiyan", "amedcj@hotmail.com"},
-	{N_("Kurdish"),             "ku", "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
+	{NULL,                      NULL, "Amed Ç. Jiyan", "amedcj@hotmail.com"},
+	{NULL,                      NULL, "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
 	{N_("Lao"),                 "lo", "Anousak Souphavah", "anousak@gmail.com"},
 	{N_("Maithili"),            "mai", "Sangeeta Kumari", "sangeeta_0975@yahoo.com"},
-	{N_("Maithili"),            "mai", "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
+	{NULL,                      NULL, "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
 	{N_("Meadow Mari"),         "mhr", "David Preece", "davidpreece1@gmail.com"},
 	{N_("Macedonian"),          "mk", "Arangel Angov ", "arangel@linux.net.mk"},
-	{N_("Macedonian"),          "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
-	{N_("Macedonian"),          "mk", "Jovan Naumovski", "jovan@lugola.net"},
+	{NULL,                      NULL, "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
+	{NULL,                      NULL, "Jovan Naumovski", "jovan@lugola.net"},
 	{N_("Malayalam"),           "ml", "Ani Peter", "apeter@redhat.com"},
 	{N_("Mongolian"),           "mn", "gooyo", NULL},
 	{N_("Marathi"),             "mr", "Sandeep Shedmake", "sandeep.shedmake@gmail.com"},
@@ -225,20 +226,20 @@
 	{N_("Portuguese-Brazil"),   "pt_BR", "Rodrigo Luiz Marques Flores", "rodrigomarquesflores@gmail.com"},
 	{N_("Pashto"),              "ps", "Kashif Masood", "masudmails@yahoo.com"},
 	{N_("Romanian"),            "ro", "Mișu Moldovan", "dumol@gnome.org"},
-	{N_("Romanian"),            "ro", "Andrei Popescu", "andreimpopescu@gmail.com"},
+	{NULL,                      NULL, "Andrei Popescu", "andreimpopescu@gmail.com"},
 	{N_("Russian"),             "ru", "Антон Самохвалов", "samant.ua@mail.ru"},
 	{N_("Slovak"),              "sk", "Jozef Káčer", "quickparser@gmail.com"},
-	{N_("Slovak"),              "sk", "loptosko", "loptosko@gmail.com"},
+	{NULL,                      NULL, "loptosko", "loptosko@gmail.com"},
 	{N_("Slovenian"),           "sl", "Martin Srebotnjak", "miles@filmsi.net"},
 	{N_("Albanian"),            "sq", "Besnik Bleta", "besnik@programeshqip.org"},
 	{N_("Serbian"),             "sr", "Miloš Popović", "gpopac@gmail.com"},
-	{N_("Serbian"),             "sr@Latn", "Miloš Popović", "gpopac@gmail.com"},
+	{N_("Serbian Latin"),       "sr@latin", "Miloš Popović", "gpopac@gmail.com"},
 	{N_("Sinhala"),             "si", "Danishka Navin", "snavin@redhat.com"},
-	{N_("Sinhala"),             "si", "Yajith Ajantha Dayarathna", "yajith@gmail.com"},
+	{NULL,                      NULL, "Yajith Ajantha Dayarathna", "yajith@gmail.com"},
 	{N_("Swedish"),             "sv", "Peter Hjalmarsson", "xake@telia.com"},
 	{N_("Swahili"),             "sw", "Paul Msegeya", "msegeya@gmail.com"},
 	{N_("Tamil"),               "ta", "I. Felix", "ifelix25@gmail.com"},
-	{N_("Tamil"),               "ta", "Viveka Nathan K", "vivekanathan@users.sourceforge.net"},
+	{NULL,                      NULL, "Viveka Nathan K", "vivekanathan@users.sourceforge.net"},
 	{N_("Telugu"),              "te", "Krishnababu Krottapalli", "krottapalli@ymail.com"},
 	{N_("Thai"),                "th", "Isriya Paireepairit", "markpeak@gmail.com"},
 	{N_("Turkish"),             "tr", "Serdar Soytetir", "tulliana@gmail.com"},
@@ -247,10 +248,10 @@
 	{N_("Vietnamese"),          "vi", N_("T.M.Thanh and the Gnome-Vi Team"), "gnomevi-list@lists.sf.net"},
 	{N_("Simplified Chinese"),  "zh_CN", "Aron Xu", "happyaron.xu@gmail.com"},
 	{N_("Hong Kong Chinese"),   "zh_HK", "Abel Cheung", "abelindsay@gmail.com"},
-	{N_("Hong Kong Chinese"),   "zh_HK", "Ambrose C. Li", "acli@ada.dhs.org"},
-	{N_("Hong Kong Chinese"),   "zh_HK", "Paladin R. Liu", "paladin@ms1.hinet.net"},
+	{NULL,                      NULL, "Ambrose C. Li", "acli@ada.dhs.org"},
+	{NULL,                      NULL, "Paladin R. Liu", "paladin@ms1.hinet.net"},
 	{N_("Traditional Chinese"), "zh_TW", "Ambrose C. Li", "acli@ada.dhs.org"},
-	{N_("Traditional Chinese"), "zh_TW", "Paladin R. Liu", "paladin@ms1.hinet.net"},
+	{NULL,                      NULL, "Paladin R. Liu", "paladin@ms1.hinet.net"},
 	{NULL, NULL, NULL, NULL}
 };
 
@@ -260,67 +261,71 @@
 	{N_("Arabic"),              "ar", "Mohamed Magdy", "alnokta@yahoo.com"},
 	{N_("Bulgarian"),           "bg", "Hristo Todorov", NULL},
 	{N_("Bengali"),             "bn", "INDRANIL DAS GUPTA", "indradg@l2c2.org"},
-	{N_("Bengali"),             "bn", "Tisa Nafisa", "tisa_nafisa@yahoo.com"},
+	{NULL,                      NULL, "Tisa Nafisa", "tisa_nafisa@yahoo.com"},
 	{N_("Catalan"),             "ca", "JM Pérez Cáncer", NULL},
-	{N_("Catalan"),             "ca", "Robert Millan", NULL},
+	{NULL,                      NULL, "Robert Millan", NULL},
 	{N_("Czech"),               "cs", "Honza Král", NULL},
-	{N_("Czech"),               "cs", "Miloslav Trmac", "mitr@volny.cz"},
-	{N_("German"),              "de", "Daniel Seifert, Karsten Weiss", NULL},
+	{NULL,                      NULL, "Miloslav Trmac", "mitr@volny.cz"},
+	{N_("German"),              "de", "Daniel Seifert", NULL},
+	{NULL,                      NULL, "Karsten Weiss", NULL},
 	{N_("British English"),     "en_GB", "Luke Ross", "luke@lukeross.name"},
 	{N_("Spanish"),             "es", "JM Pérez Cáncer", NULL},
-	{N_("Spanish"),             "es", "Nicolás Lichtmaier", NULL},
-	{N_("Spanish"),             "es", "Amaya Rodrigo", NULL},
-	{N_("Spanish"),             "es", "Alejandro G Villar", NULL},
+	{NULL,                      NULL, "Nicolás Lichtmaier", NULL},
+	{NULL,                      NULL, "Amaya Rodrigo", NULL},
+	{NULL,                      NULL, "Alejandro G Villar", NULL},
 	{N_("Basque"),              "eu", "Iñaki Larrañaga Murgoitio", "dooteo@zundan.com"},
-	{N_("Basque"),              "eu", "Hizkuntza Politikarako Sailburuordetza", "hizkpol@ej-gv.es"},
+	{NULL,                      NULL, "Hizkuntza Politikarako Sailburuordetza", "hizkpol@ej-gv.es"},
 	{N_("Finnish"),             "fi", "Arto Alakulju", NULL},
-	{N_("Finnish"),             "fi", "Tero Kuusela", NULL},
+	{NULL,                      NULL, "Tero Kuusela", NULL},
 	{N_("French"),              "fr", "Sébastien François", NULL},
-	{N_("French"),              "fr", "Stéphane Pontier", NULL},
-	{N_("French"),              "fr", "Stéphane Wirtel", NULL},
-	{N_("French"),              "fr", "Loïc Jeannin", NULL},
+	{NULL,                      NULL, "Stéphane Pontier", NULL},
+	{NULL,                      NULL, "Stéphane Wirtel", NULL},
+	{NULL,                      NULL, "Loïc Jeannin", NULL},
 	{N_("Galician"),            "gl", "Ignacio Casal Quinteiro", NULL},
 	{N_("Hebrew"),              "he", "Pavel Bibergal", NULL},
 	{N_("Hindi"),               "hi", "Ravishankar Shrivastava", NULL},
 	{N_("Hungarian"),           "hu", "Zoltan Sutto", NULL},
 	{N_("Italian"),             "it", "Salvatore di Maggio", NULL},
 	{N_("Japanese"),            "ja", "Takashi Aihana", NULL},
-	{N_("Japanese"),            "ja", "Ryosuke Kutsuna", NULL},
-	{N_("Japanese"),            "ja", "Taku Yasui", NULL},
-	{N_("Japanese"),            "ja", "Junichi Uekawa", NULL},
+	{NULL,                      NULL, "Ryosuke Kutsuna", NULL},
+	{NULL,                      NULL, "Taku Yasui", NULL},
+	{NULL,                      NULL, "Junichi Uekawa", NULL},
 	{N_("Georgian"),            "ka", "Temuri Doghonadze", NULL},
-	{N_("Korean"),              "ko", "Sang-hyun S, A Ho-seok Lee", NULL},
-	{N_("Korean"),              "ko", "Kyeong-uk Son", NULL},
+	{N_("Korean"),              "ko", "Sang-hyun S", NULL},
+	{NULL,                      NULL, "A Ho-seok Lee", NULL},
+	{NULL,                      NULL, "Kyeong-uk Son", NULL},
 	{N_("Lithuanian"),          "lt", "Laurynas Biveinis", "laurynas.biveinis@gmail.com"},
-	{N_("Lithuanian"),          "lt", "Gediminas Čičinskas", NULL},
-	{N_("Lithuanian"),          "lt", "Andrius Štikonas", NULL},
+	{NULL,                      NULL, "Gediminas Čičinskas", NULL},
+	{NULL,                      NULL, "Andrius Štikonas", NULL},
 	{N_("Macedonian"),          "mk", "Tomislav Markovski", NULL},
 	{N_("Bokmål Norwegian"),    "nb", "Hallvard Glad", "hallvard.glad@gmail.com"},
-	{N_("Bokmål Norwegian"),    "nb", "Petter Johan Olsen", NULL},
-	{N_("Bokmål Norwegian"),    "nb", "Espen Stefansen", "espenas@gmail.com"},
+	{NULL,                      NULL, "Petter Johan Olsen", NULL},
+	{NULL,                      NULL, "Espen Stefansen", "espenas@gmail.com"},
 	{N_("Dutch, Flemish"),      "nl", "Vincent van Adrighem", "V.vanAdrighem@dirck.mine.nu"},
 	{N_("Polish"),              "pl", "Emil Nowak", "emil5@go2.pl"},
-	{N_("Polish"),              "pl", "Paweł Godlewski", "pawel@bajk.pl"},
-	{N_("Polish"),              "pl", "Krzysztof Foltman", "krzysztof@foltman.com"},
-	{N_("Polish"),              "pl", "Piotr Makowski", NULL},
-	{N_("Polish"),              "pl", "Przemysław Sułek", NULL},
+	{NULL,                      NULL, "Paweł Godlewski", "pawel@bajk.pl"},
+	{NULL,                      NULL, "Krzysztof Foltman", "krzysztof@foltman.com"},
+	{NULL,                      NULL, "Piotr Makowski", NULL},
+	{NULL,                      NULL, "Przemysław Sułek", NULL},
 	{N_("Portuguese-Brazil"),   "pt_BR", "Maurício de Lemos Rodrigues Collares Neto", "mauricioc@gmail.com"},
 	{N_("Russian"),             "ru", "Dmitry Beloglazov", "dmaa@users.sf.net"},
-	{N_("Russian"),             "ru", "Alexandre Prokoudine", NULL},
-	{N_("Russian"),             "ru", "Sergey Volozhanin", NULL},
+	{NULL,                      NULL, "Alexandre Prokoudine", NULL},
+	{NULL,                      NULL, "Sergey Volozhanin", NULL},
 	{N_("Slovak"),              "sk", "Daniel Režný", NULL},
-	{N_("Slovak"),              "sk", "helix84", NULL},
-	{N_("Slovak"),              "sk", "Richard Golier", NULL},
+	{NULL,                      NULL, "helix84", NULL},
+	{NULL,                      NULL, "Richard Golier", NULL},
 	{N_("Slovenian"),           "sl", "Matjaz Horvat", NULL},
 	{N_("Serbian"),             "sr", "Danilo Šegan", "dsegan@gmx.net"},
-	{N_("Serbian"),             "sr", "Aleksandar Urosevic", "urke@users.sourceforge.net"},
+	{NULL,                      NULL, "Aleksandar Urosevic", "urke@users.sourceforge.net"},
 	{N_("Swedish"),             "sv", "Tore Lundqvist", NULL},
-	{N_("Swedish"),             "sv", "Christian Rose", NULL},
+	{NULL,                      NULL, "Christian Rose", NULL},
 	{N_("Telugu"),              "te", "Mr. Subbaramaih", "info.gist@cdac.in"},
 	{N_("Turkish"),             "tr", "Ahmet Alp BALKAN", NULL},
-	{N_("Simplified Chinese"),  "zh_CN", "Hashao, Rocky S. Lee", NULL},
-	{N_("Simplified Chinese"),  "zh_CN", "Funda Wang", "fundawang@linux.net.cn"},
-	{N_("Traditional Chinese"), "zh_TW", "Hashao, Rocky S. Lee", NULL},
+	{N_("Simplified Chinese"),  "zh_CN", "Hashao", NULL},
+	{NULL,                      NULL, "Rocky S. Lee", NULL},
+	{NULL,                      NULL, "Funda Wang", "fundawang@linux.net.cn"},
+	{N_("Traditional Chinese"), "zh_TW", "Hashao", NULL},
+	{NULL,                      NULL, "Rocky S. Lee", NULL},
 	{NULL, NULL, NULL, NULL}
 };
 
@@ -329,13 +334,14 @@
 {
 	for (; list->name != NULL; list++) {
 		if (list->email != NULL) {
-			g_string_append_printf(str, "  <a href=\"mailto:%s\">%s</a>%s%s%s<br/>",
-			                       list->email, _(list->name),
+			g_string_append_printf(str,
+			                       "<li><a href=\"mailto:%s\" title=\"%s\">%s</a>%s%s%s</li>",
+			                       list->email, list->email, _(list->name),
 			                       list->role ? " (" : "",
 			                       list->role ? _(list->role) : "",
 			                       list->role ? ")" : "");
 		} else {
-			g_string_append_printf(str, "  %s%s%s%s<br/>",
+			g_string_append_printf(str, "<li>%s%s%s%s</li>",
 			                       _(list->name),
 			                       list->role ? " (" : "",
 			                       list->role ? _(list->role) : "",
@@ -347,18 +353,18 @@
 static void
 add_translators(GString *str, const struct translator *list)
 {
-	for (; list->language != NULL; list++) {
+	for (; list->name != NULL; list++) {
+		if (list->language && list->abbr) {
+			g_string_append_printf(str, "<dt>%s (%s)</dt>",
+			                       _(list->language), list->abbr);
+		}
 		if (list->email != NULL) {
-			g_string_append_printf(str, "  <b>%s (%s)</b> - <a href=\"mailto:%s\">%s</a><br/>",
-			                       _(list->language),
-			                       list->abbr,
-			                       list->email,
+			g_string_append_printf(str,
+			                       "<dd><a href=\"mailto:%s\" title=\"%s\">%s</a></dd>",
+			                       list->email, list->email,
 			                       _(list->name));
 		} else {
-			g_string_append_printf(str, "  <b>%s (%s)</b> - %s<br/>",
-			                       _(list->language),
-			                       list->abbr,
-			                       _(list->name));
+			g_string_append_printf(str, "<dd>%s</dd>", _(list->name));
 		}
 	}
 }
@@ -456,8 +462,6 @@
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 
 	gtk_webview_append_html(GTK_WEBVIEW(webview), string->str);
-	/* TODO WEBKIT: This doesn't seem to stay at the top. */
-	webkit_web_view_move_cursor(WEBKIT_WEB_VIEW(webview), GTK_MOVEMENT_BUFFER_ENDS, -1);
 
 	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
 	                G_CALLBACK(destroy_win), win);
@@ -491,37 +495,45 @@
 	str = g_string_sized_new(4096);
 
 	g_string_append_printf(str,
-		"<CENTER><FONT SIZE=\"4\"><B>%s %s</B></FONT></CENTER> (libpurple %s)"
-		"<BR>%s<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION,
+		"<h2>%s %s</h2>"
+		"<strong>(libpurple %s)<br/>%s</strong>",
+		PIDGIN_NAME, DISPLAY_VERSION,
 		purple_core_get_version(), REVISION);
 
 	g_string_append_printf(str,
-		_("%s is a messaging client based on libpurple which is capable of "
+		_("<p>%s is a messaging client based on libpurple which is capable of "
 		  "connecting to multiple messaging services at once.  %s is written "
 		  "in C using GTK+.  %s is released, and may be modified and "
 		  "redistributed,  under the terms of the GPL version 2 (or later).  "
 		  "A copy of the GPL is distributed with %s.  %s is copyrighted by "
 		  "its contributors, a list of whom is also distributed with %s.  "
-		  "There is no warranty for %s.<BR><BR>"), PIDGIN_NAME, PIDGIN_NAME,
+		  "There is no warranty for %s.</p>"), PIDGIN_NAME, PIDGIN_NAME,
 		PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME);
 
 	g_string_append_printf(str,
-			_("<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A "
-			  "HREF=\"%s\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked "
-			  "Questions</A><BR>\tIRC Channel: #pidgin on irc.freenode.net<BR>"
-			  "\tXMPP MUC: devel@conference.pidgin.im<BR><BR>"), PURPLE_WEBSITE,
+			_("<h3>Helpful Resources</h3>"
+			  "<ul>"
+			  "<li><a href=\"%s\" title=\"%s\">Website</a></li>"
+			  "<li><a href=\"%s\" title=\"%s\">Frequently Asked Questions</a></li>"
+			  "<li>IRC Channel: #pidgin on irc.freenode.net</li>"
+			  "<li>XMPP MUC: devel@conference.pidgin.im</li>"
+			  "</ul>"),
+			PURPLE_WEBSITE, PURPLE_WEBSITE,
+			"http://developer.pidgin.im/wiki/FAQ",
 			"http://developer.pidgin.im/wiki/FAQ");
 
 	g_string_append_printf(str,
-			_("<font size=\"4\"><b>Help from other Pidgin users</b></font> is "
-			  "available by e-mailing <a "
-			  "href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>"
-			  "This is a <b>public</b> mailing list! "
-			  "(<a href=\"http://pidgin.im/pipermail/support/\">archive</a>)<br/>"
+			_("<p><strong>Help from other Pidgin users</strong> is available "
+			  "by e-mailing <a href=\"mailto:%s\">%s</a>.<br/>"
+			  "This is a <strong>public</strong> mailing list! "
+			  "(<a href=\"%s\" title=\"%s\">archive</a>)<br/>"
 			  "We can't help with third-party protocols or plugins!<br/>"
-			  "This list's primary language is <b>English</b>.  You are "
-			  "welcome to post in another language, but the responses may "
-			  "be less helpful.<br/>"));
+			  "This list's primary language is <strong>English</strong>.  You "
+			  "are welcome to post in another language, but the responses may "
+			  "be less helpful.</p>"),
+			"support@pidgin.im", "support@pidgin.im",
+			"http://pidgin.im/pipermail/support/",
+			"http://pidgin.im/pipermail/support/");
 
 	tmp = g_strdup_printf(_("About %s"), PIDGIN_NAME);
 	about = pidgin_build_help_dialog(tmp, "about", str);
@@ -543,175 +555,179 @@
 	str = g_string_sized_new(4096);
 
 	g_string_append_printf(str,
-		"<FONT SIZE=\"4\"><B>%s %s</B></FONT> (libpurple %s)<BR>%s<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version(), REVISION);
+		"<h2>%s %s</h2>"
+		"<strong>(libpurple %s)<br/>%s</strong>",
+		PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version(), REVISION);
 
-	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s</B></FONT><br/>", _("Build Information"));
+	g_string_append_printf(str, "<h3>%s</h3><dl>", _("Build Information"));
 
-	/* The following primarly intented for user/developer interaction and thus
-	   ought not be translated */
+	/* The following is primarily intended for user/developer interaction and
+	   thus ought not be translated */
 
 #ifdef CONFIG_ARGS /* win32 build doesn't use configure */
-	g_string_append(str, "  <b>Arguments to <i>./configure</i>:</b>  " CONFIG_ARGS "<br/>");
+	g_string_append(str, "<dt>Arguments to <em>./configure</em>:</dt><dd>" CONFIG_ARGS "</dd>");
 #endif
 
 #ifndef _WIN32
 #ifdef DEBUG
-	g_string_append(str, "  <b>Print debugging messages:</b> Yes<br/>");
+	g_string_append(str, "<dt>Print debugging messages:</dt><dd>Yes</dd>");
 #else
-	g_string_append(str, "  <b>Print debugging messages:</b> No<br/>");
+	g_string_append(str, "<dt>Print debugging messages:</dt><dd>No</dd>");
 #endif
 #endif
 
 #ifdef PURPLE_PLUGINS
-	g_string_append(str, "  <b>Plugins:</b> Enabled<br/>");
+	g_string_append(str, "<dt>Plugins:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "  <b>Plugins:</b> Disabled<br/>");
+	g_string_append(str, "<dt>Plugins:</dt><dd>Disabled</dd>");
 #endif
 
 #ifdef HAVE_SSL
-	g_string_append(str, "  <b>SSL:</b> SSL support is present.<br/>");
+	g_string_append(str, "<dt>SSL:</dt><dd>SSL support is present.</dd>");
 #else
-	g_string_append(str, "  <b>SSL:</b> SSL support was <b><i>NOT</i></b> compiled!<br/>");
+	g_string_append(str, "<dt>SSL:</dt><dd>SSL support was <strong><em>NOT</em></strong> compiled!</dd>");
 #endif
 
 /* This might be useful elsewhere too, but it is particularly useful for
- * debugging stuff known to be GTK+/Glib bugs on Windows */
+ * debugging stuff known to be GTK+/GLib bugs on Windows */
 #ifdef _WIN32
-	g_string_append_printf(str, "  <b>GTK+ Runtime:</b> %u.%u.%u<br/>"
-		"  <b>Glib Runtime:</b> %u.%u.%u<br/>",
+	g_string_append_printf(str, "<dt>GTK+ Runtime:</dt><dd>%u.%u.%u</dd>"
+		"<dt>GLib Runtime:</dt><dd>%u.%u.%u</dd>",
 		gtk_major_version, gtk_minor_version, gtk_micro_version,
 		glib_major_version, glib_minor_version, glib_micro_version);
 #endif
 
-g_string_append(str, "<br/>  <b>Library Support</b><br/>");
+	g_string_append(str, "</dl><h3>Library Support</h3><dl>");
 
 #ifdef HAVE_CYRUS_SASL
-	g_string_append_printf(str, "    <b>Cyrus SASL:</b> Enabled<br/>");
+	g_string_append_printf(str, "<dt>Cyrus SASL:</dt><dd>Enabled</dd>");
 #else
-	g_string_append_printf(str, "    <b>Cyrus SASL:</b> Disabled<br/>");
+	g_string_append_printf(str, "<dt>Cyrus SASL:</dt><dd>Disabled</dd>");
 #endif
 
 #ifndef _WIN32
 #ifdef HAVE_DBUS
-	g_string_append_printf(str, "    <b>D-Bus:</b> Enabled<br/>");
+	g_string_append_printf(str, "<dt>D-Bus:</dt><dd>Enabled</dd>");
 #else
-	g_string_append_printf(str, "    <b>D-Bus:</b> Disabled<br/>");
+	g_string_append_printf(str, "<dt>D-Bus:</dt><dd>Disabled</dd>");
 #endif
 
 #ifdef HAVE_EVOLUTION_ADDRESSBOOK
-	g_string_append_printf(str, "    <b>Evolution Addressbook:</b> Enabled<br/>");
+	g_string_append_printf(str, "<dt>Evolution Addressbook:</dt><dd>Enabled</dd>");
 #else
-	g_string_append_printf(str, "    <b>Evolution Addressbook:</b> Disabled<br/>");
+	g_string_append_printf(str, "<dt>Evolution Addressbook:</dt><dd>Disabled</dd>");
 #endif
 #endif
 
 #if defined(_WIN32) || defined(USE_INTERNAL_LIBGADU)
-	g_string_append(str, "    <b>Gadu-Gadu library (libgadu):</b> Internal<br/>");
+	g_string_append(str, "<dt>Gadu-Gadu library (libgadu):</dt><dd>Internal</dd>");
 #else
 #ifdef HAVE_LIBGADU
-	g_string_append(str, "    <b>Gadu-Gadu library (libgadu):</b> Enabled<br/>");
+	g_string_append(str, "<dt>Gadu-Gadu library (libgadu):</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>Gadu-Gadu library (libgadu):</b> Disabled<br/>");
+	g_string_append(str, "<dt>Gadu-Gadu library (libgadu):</dt><dd>Disabled</dd>");
 #endif
 #endif
 
 #ifdef USE_GTKSPELL
-	g_string_append(str, "    <b>GtkSpell:</b> Enabled<br/>");
+	g_string_append(str, "<dt>GtkSpell:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>GtkSpell:</b> Disabled<br/>");
+	g_string_append(str, "<dt>GtkSpell:</dt><dd>Disabled</dd>");
 #endif
 
 #ifdef HAVE_GNUTLS
-	g_string_append(str, "    <b>GnuTLS:</b> Enabled<br/>");
+	g_string_append(str, "<dt>GnuTLS:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>GnuTLS:</b> Disabled<br/>");
+	g_string_append(str, "<dt>GnuTLS:</dt><dd>Disabled</dd>");
 #endif
 
 #ifndef _WIN32
 #ifdef USE_GSTREAMER
-	g_string_append(str, "    <b>GStreamer:</b> Enabled<br/>");
+	g_string_append(str, "<dt>GStreamer:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>GStreamer:</b> Disabled<br/>");
+	g_string_append(str, "<dt>GStreamer:</dt><dd>Disabled</dd>");
 #endif
 #endif
 
 #ifndef _WIN32
 #ifdef ENABLE_MONO
-	g_string_append(str, "    <b>Mono:</b> Enabled<br/>");
+	g_string_append(str, "<dt>Mono:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>Mono:</b> Disabled<br/>");
+	g_string_append(str, "<dt>Mono:</dt><dd>Disabled</dd>");
 #endif
 #endif
 
 #ifndef _WIN32
 #ifdef HAVE_NETWORKMANAGER
-	g_string_append(str, "    <b>NetworkManager:</b> Enabled<br/>");
+	g_string_append(str, "<dt>NetworkManager:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>NetworkManager:</b> Disabled<br/>");
+	g_string_append(str, "<dt>NetworkManager:</dt><dd>Disabled</dd>");
 #endif
 #endif
 
 #ifdef HAVE_NSS
-	g_string_append(str, "    <b>Network Security Services (NSS):</b> Enabled<br/>");
+	g_string_append(str, "<dt>Network Security Services (NSS):</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>Network Security Services (NSS):</b> Disabled<br/>");
+	g_string_append(str, "<dt>Network Security Services (NSS):</dt><dd>Disabled</dd>");
 #endif
 
-if (purple_plugins_find_with_id("core-perl") != NULL)
-	g_string_append(str, "    <b>Perl:</b> Enabled<br/>");
-else
-	g_string_append(str, "    <b>Perl:</b> Disabled<br/>");
+	if (purple_plugins_find_with_id("core-perl") != NULL)
+		g_string_append(str, "<dt>Perl:</dt><dd>Enabled</dd>");
+	else
+		g_string_append(str, "<dt>Perl:</dt><dd>Disabled</dd>");
 
-if (purple_plugins_find_with_id("core-tcl") != NULL) {
-	g_string_append(str, "    <b>Tcl:</b> Enabled<br/>");
+	if (purple_plugins_find_with_id("core-tcl") != NULL) {
+		g_string_append(str, "<dt>Tcl:</dt><dd>Enabled</dd>");
 #ifdef HAVE_TK
-	g_string_append(str, "    <b>Tk:</b> Enabled<br/>");
+		g_string_append(str, "<dt>Tk:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>Tk:</b> Disabled<br/>");
+		g_string_append(str, "<dt>Tk:</dt><dd>Disabled</dd>");
 #endif
-} else {
-	g_string_append(str, "    <b>Tcl:</b> Disabled<br/>");
-	g_string_append(str, "    <b>Tk:</b> Disabled<br/>");
-}
+	} else {
+		g_string_append(str, "<dt>Tcl:</dt><dd>Disabled</dd>");
+		g_string_append(str, "<dt>Tk:</dt><dd>Disabled</dd>");
+	}
 
 #ifdef USE_IDN
-	g_string_append(str, "    <b>UTF-8 DNS (IDN):</b> Enabled<br/>");
+	g_string_append(str, "<dt>UTF-8 DNS (IDN):</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>UTF-8 DNS (IDN):</b> Disabled<br/>");
+	g_string_append(str, "<dt>UTF-8 DNS (IDN):</dt><dd>Disabled</dd>");
 #endif
 
 #ifdef USE_VV
-	g_string_append(str, "    <b>Voice and Video:</b> Enabled<br/>");
+	g_string_append(str, "<dt>Voice and Video:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>Voice and Video:</b> Disabled<br/>");
+	g_string_append(str, "<dt>Voice and Video:</dt><dd>Disabled</dd>");
 #endif
 
 #ifndef _WIN32
 #ifdef USE_SM
-	g_string_append(str, "    <b>X Session Management:</b> Enabled<br/>");
+	g_string_append(str, "<dt>X Session Management:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>X Session Management:</b> Disabled<br/>");
+	g_string_append(str, "<dt>X Session Management:</dt><dd>Disabled</dd>");
 #endif
 
 #ifdef USE_SCREENSAVER
-	g_string_append(str, "    <b>XScreenSaver:</b> Enabled<br/>");
+	g_string_append(str, "<dt>XScreenSaver:</dt><dd>Enabled</dd>");
 #else
-	g_string_append(str, "    <b>XScreenSaver:</b> Disabled<br/>");
+	g_string_append(str, "<dt>XScreenSaver:</dt><dd>Disabled</dd>");
 #endif
 
 #ifdef LIBZEPHYR_EXT
-	g_string_append(str, "    <b>Zephyr library (libzephyr):</b> External<br/>");
+	g_string_append(str, "<dt>Zephyr library (libzephyr):</dt><dd>External</dd>");
 #else
-	g_string_append(str, "    <b>Zephyr library (libzephyr):</b> Internal<br/>");
+	g_string_append(str, "<dt>Zephyr library (libzephyr):</dt><dd>Internal</dd>");
 #endif
 
 #ifdef ZEPHYR_USES_KERBEROS
-	g_string_append(str, "    <b>Zephyr uses Kerberos:</b> Yes<br/>");
+	g_string_append(str, "<dt>Zephyr uses Kerberos:</dt><dd>Yes</dd>");
 #else
-	g_string_append(str, "    <b>Zephyr uses Kerberos:</b> No<br/>");
+	g_string_append(str, "<dt>Zephyr uses Kerberos:</dt><dd>No</dd>");
 #endif
 #endif
 
+	g_string_append(str, "</dl>");
+
 	/* End of not to be translated section */
 
 	tmp = g_strdup_printf(_("%s Build Information"), PIDGIN_NAME);
@@ -734,27 +750,28 @@
 	str = g_string_sized_new(4096);
 
 	/* Current Developers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+	g_string_append_printf(str, "<h3>%s</h3><ul>",
 						   _("Current Developers"));
 	add_developers(str, developers);
-	g_string_append(str, "<BR/>");
+	g_string_append(str, "</ul>");
 
 	/* Crazy Patch Writers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+	g_string_append_printf(str, "<h3>%s</h3><ul>",
 						   _("Crazy Patch Writers"));
 	add_developers(str, patch_writers);
-	g_string_append(str, "<BR/>");
+	g_string_append(str, "</ul>");
 
 	/* Retired Developers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+	g_string_append_printf(str, "<h3>%s</h3><ul>",
 						   _("Retired Developers"));
 	add_developers(str, retired_developers);
-	g_string_append(str, "<BR/>");
+	g_string_append(str, "</ul>");
 
 	/* Retired Crazy Patch Writers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+	g_string_append_printf(str, "<h3>%s</h3><ul>",
 						   _("Retired Crazy Patch Writers"));
 	add_developers(str, retired_patch_writers);
+	g_string_append(str, "</ul>");
 
 	tmp = g_strdup_printf(_("%s Developer Information"), PIDGIN_NAME);
 	developer_info = pidgin_build_help_dialog(tmp, "developer_info", str);
@@ -776,15 +793,16 @@
 	str = g_string_sized_new(4096);
 
 	/* Current Translators */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
+	g_string_append_printf(str, "<h3>%s</h3><dl>",
 						   _("Current Translators"));
 	add_translators(str, translators);
-	g_string_append(str, "<BR/>");
+	g_string_append(str, "</dl>");
 
 	/* Past Translators */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
+	g_string_append_printf(str, "<h3>%s</h3><dl>",
 						   _("Past Translators"));
 	add_translators(str, past_translators);
+	g_string_append(str, "</dl>");
 
 	tmp = g_strdup_printf(_("%s Translator Information"), PIDGIN_NAME);
 	translator_info = pidgin_build_help_dialog(tmp, "translator_info", str);
@@ -805,14 +823,14 @@
 
 	str = g_string_sized_new(4096);
 
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s</FONT><BR/>",
-			_("Plugin Information"));
+	g_string_append_printf(str, "<h2>%s</h2><dl>", _("Plugin Information"));
 
 	for(l = purple_plugins_get_all(); l; l = l->next) {
 		plugin = (PurplePlugin *)l->data;
 
 		pname = g_markup_escape_text(purple_plugin_get_name(plugin), -1);
-		pauthor = g_markup_escape_text(purple_plugin_get_author(plugin), -1);
+		if ((pauthor = (char *)purple_plugin_get_author(plugin)) != NULL)
+			pauthor = g_markup_escape_text(pauthor, -1);
 		pver = purple_plugin_get_version(plugin);
 		pwebsite = purple_plugin_get_homepage(plugin);
 		pid = purple_plugin_get_id(plugin);
@@ -820,22 +838,29 @@
 		ploaded = purple_plugin_is_loaded(plugin);
 
 		g_string_append_printf(str,
-				"<FONT SIZE=\"3\"><B>%s</B></FONT><BR/><FONT SIZE=\"2\">"
-				"\t<B>Author:</B> %s<BR/>\t<B>Version:</B> %s<BR/>"
-				"\t<B>Website:</B> %s<BR/>\t<B>ID String:</B> %s<BR/>"
-				"\t<B>Loadable:</B> %s<BR/>\t<B>Loaded:</B> %s<BR/>"
-				"<BR/></FONT>", pname, pauthor ? pauthor : "(null)",
+				"<dt>%s</dt><dd>"
+				"<b>Author:</b> %s<br/>"
+				"<b>Version:</b> %s<br/>"
+				"<b>Website:</b> %s<br/>"
+				"<b>ID String:</b> %s<br/>"
+				"<b>Loadable:</b> %s<br/>"
+				"<b>Loaded:</b> %s"
+				"</dd><br/>",
+				pname, pauthor ? pauthor : "(null)",
 				pver, pwebsite, pid,
-				punloadable ? "<FONT COLOR=\"#FF0000\"><B>No</B></FONT>" : "Yes",
+				punloadable ? "<span style=\"color: #FF0000;\"><b>No</b></span>" : "Yes",
 				ploaded ? "Yes" : "No");
+
+		g_free(pname);
+		g_free(pauthor);
 	}
 
+	g_string_append(str, "</dl>");
+
 	plugins_info = pidgin_build_help_dialog(title, "plugins_info", str);
 	g_signal_connect(G_OBJECT(plugins_info), "destroy",
 			G_CALLBACK(gtk_widget_destroyed), &plugins_info);
 	g_free(title);
-	g_free(pname);
-	g_free(pauthor);
 }
 
 static void
--- a/pidgin/gtkprefs.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/pidgin/gtkprefs.c	Thu Feb 23 08:13:23 2012 +0000
@@ -1859,11 +1859,11 @@
 
 #ifndef _WIN32
 static void
-browser_button_clicked_cb(GtkWidget *button, gpointer null)
+browser_button_clicked_cb(GtkWidget *button, gchar *path)
 {
 	GError *err = NULL;
 
-	if (g_spawn_command_line_async ("gnome-default-applications-properties", &err))
+	if (g_spawn_command_line_async(path, &err))
 		return;
 
 	purple_notify_error(NULL, NULL, _("Cannot start browser configuration program."), err->message);
@@ -2134,8 +2134,8 @@
 
 	vbox = pidgin_make_frame (ret, _("Browser Selection"));
 
-	if(purple_running_gnome()) {
-		gchar *path = g_find_program_in_path("gnome-default-applications-properties");
+	if (purple_running_gnome()) {
+		gchar *path;
 
 		hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 		label = gtk_label_new(_("Browser preferences are configured in GNOME preferences"));
@@ -2145,19 +2145,28 @@
 		hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 		gtk_container_add(GTK_CONTAINER(vbox), hbox);
 
-		if(path == NULL) {
+		path = g_find_program_in_path("gnome-control-center");
+		if (path != NULL) {
+			gchar *tmp = g_strdup_printf("%s info", path);
+			g_free(path);
+			path = tmp;
+		} else {
+			path = g_find_program_in_path("gnome-default-applications-properties");
+		}
+
+		if (path == NULL) {
 			label = gtk_label_new(NULL);
 			gtk_label_set_markup(GTK_LABEL(label),
 								 _("<b>Browser configuration program was not found.</b>"));
 			gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 		} else {
 			browser_button = gtk_button_new_with_mnemonic(_("Configure _Browser"));
-			g_signal_connect(G_OBJECT(browser_button), "clicked",
-							 G_CALLBACK(browser_button_clicked_cb), NULL);
+			g_signal_connect_data(G_OBJECT(browser_button), "clicked",
+			                      G_CALLBACK(browser_button_clicked_cb), path,
+			                      (GClosureNotify)g_free, 0);
 			gtk_box_pack_start(GTK_BOX(hbox), browser_button, FALSE, FALSE, 0);
 		}
 
-		g_free(path);
 		gtk_widget_show_all(ret);
 	} else {
 		sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
--- a/pidgin/gtkwebview.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/pidgin/gtkwebview.c	Thu Feb 23 08:13:23 2012 +0000
@@ -579,7 +579,6 @@
 	char *script = g_strdup_printf("document.write(%s)", escaped);
 	webkit_web_view_execute_script(WEBKIT_WEB_VIEW(webview), script);
 	priv->empty = FALSE;
-	gtk_webview_scroll_to_end(webview, TRUE);
 	g_free(script);
 	g_free(escaped);
 }
--- a/pidgin/plugins/disco/gtkdisco.c	Sat Jan 21 12:02:41 2012 +0000
+++ b/pidgin/plugins/disco/gtkdisco.c	Thu Feb 23 08:13:23 2012 +0000
@@ -462,6 +462,8 @@
 	gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN,
 	                         &val);
 	service = g_value_get_pointer(&val);
+	if (!service)
+		return FALSE;
 
 	switch (service->type) {
 		case XMPP_DISCO_SERVICE_TYPE_UNSET: