merge of '34b818f1b8b8e5ce50a83a03052188ad479bec8e' release-2.2.1

Mon, 01 Oct 2007 17:49:01 +0000

author
Luke Schierer <lschiere@pidgin.im>
date
Mon, 01 Oct 2007 17:49:01 +0000
branch
release-2.2.1
changeset 20417
c024bed8fdd5
parent 20416
34b818f1b8b8 (diff)
parent 20042
fa4b1205a719 (current diff)
child 20795
3da1b9dc9a7b

merge of '34b818f1b8b8e5ce50a83a03052188ad479bec8e'
and 'fa4b1205a719e569e710e124ee000c27729c4614'

libpurple/dnssrv.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/.todo file | annotate | diff | comparison | revisions
libpurple/protocols/oscar/.todo file | annotate | diff | comparison | revisions
po/sr@Latn.po file | annotate | diff | comparison | revisions
--- a/COPYRIGHT	Mon Oct 01 17:02:03 2007 +0000
+++ b/COPYRIGHT	Mon Oct 01 17:49:01 2007 +0000
@@ -246,6 +246,7 @@
 Kevin Miller
 Paul Miller
 Arkadiusz Miskiewicz
+David Mohr
 Andrew Molloy
 Michael Monreal
 Benjamin Moody
@@ -290,6 +291,7 @@
 Nathan Poznick
 Jory A. Pratt
 Brent Priddy
+Justin Pryzby
 Federicco Mena Quintero
 Yosef Radchenko
 David Raeman
--- a/ChangeLog	Mon Oct 01 17:02:03 2007 +0000
+++ b/ChangeLog	Mon Oct 01 17:49:01 2007 +0000
@@ -1,5 +1,26 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+Version 2.2.1:
+    http://developer.pidgin.im/query?status=closed&milestone=2.2.1
+        NOTE: Due to the backporting that happened for the actual
+              release, it is possible bugs marked as fixed in 2.2.1
+              will not be fixed until 2.2.2.
+
+    libpurple:
+    * A few build issues on Solaris were fixed.
+    * Cancelling the password prompt for an account will no longer leave
+      it in an ambiguous state.  (It will be disabled.)
+    * Fixed an erroneous size display for MSN file transfers. (galt)
+    * Gmail notifications are better tracked
+
+    Pidgin:
+    * Fixed keyboard tab reordering to move tabs one step instead of two.
+    * You should no longer lose proxy settings when Pidgin is restarted.
+
+    Finch:
+    * Pressing 'Insert' in the buddylist will bring up the 'Add Buddy'
+      dialog.
+
 Version 2.2.0 (09/13/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.2.0
 
--- a/NEWS	Mon Oct 01 17:02:03 2007 +0000
+++ b/NEWS	Mon Oct 01 17:49:01 2007 +0000
@@ -1,5 +1,24 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+2.2.1 (9/28/2007):
+	Richard: We have some new code in the pipeline, but it's not quite
+	ready for a general release.  Instead, this is basically a bug fix
+	release.
+
+	Luke: Unfortunately the necessity of this bug fix release means
+	some of the tickets that have been closed as part of the 2.2.1
+	milestone are not actually fixed yet.  We have grabbed as many
+	of the changes as we could while avoiding those that are as
+	yet unstable though, and this should still be a marked
+	improvement over 2.2.0.  We have spent a lot of time since the
+	last release looking at the tickets that have been submitted
+	and many of them have been closed.
+
+	Stu: I haven't NEWS'd in a while. I haven't actually done much for
+	too long also, maybe I'll find some time soon. This release is
+	basically what 2.2.0 should have been - it actually compiles this
+	time.
+
 2.2.0 (9/13/2007):
 	Sean: 2.2.0 contains the results of several major Google Summer
 	of Code branches bringing some new, extraordinary features. We
--- a/autogen.sh	Mon Oct 01 17:02:03 2007 +0000
+++ b/autogen.sh	Mon Oct 01 17:49:01 2007 +0000
@@ -58,7 +58,7 @@
 done
 
 libtoolize -c -f --automake
-glib-gettextize --force --copy --previous
+glib-gettextize --force --copy
 intltoolize --force --copy
 aclocal $ACLOCAL_FLAGS || exit;
 autoheader || exit;
--- a/configure.ac	Mon Oct 01 17:02:03 2007 +0000
+++ b/configure.ac	Mon Oct 01 17:49:01 2007 +0000
@@ -46,7 +46,7 @@
 m4_define([purple_lt_current], [2])
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [2])
-m4_define([purple_micro_version], [0])
+m4_define([purple_micro_version], [1])
 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], [2])
 m4_define([gnt_major_version], [2])
 m4_define([gnt_minor_version], [2])
-m4_define([gnt_micro_version], [0])
+m4_define([gnt_micro_version], [1])
 m4_define([gnt_version_suffix], [])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -136,7 +136,7 @@
 	;;
 esac
 
-ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr gl gu he hi hu id it ja ka kn ko ku lt mk my_MM nb ne nl nn pa pl pt_BR pt ps ro ru sk sl sq sr sr@Latn sv ta te th tr uk vi xh zh_CN zh_HK zh_TW"
+ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr gl gu he hi hu id it ja ka kn ko ku lo lt mk my_MM nb ne nl nn pa pl pt_BR pt ps ro ru sk sl sq sr sr@latin sv ta te th tr uk vi xh zh_CN zh_HK zh_TW"
 AM_GLIB_GNU_GETTEXT
 
 dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
--- a/doc/finch.1.in	Mon Oct 01 17:02:03 2007 +0000
+++ b/doc/finch.1.in	Mon Oct 01 17:49:01 2007 +0000
@@ -457,6 +457,11 @@
 .B status
 for the status window.
 
+.SH FAQ
+FAQ for \fBfinch\fR is located at
+.br
+\fIhttp://developer.pidgin.im/wiki/Using%20Finch\fR
+
 .SH BUGS
 Known bugs are listed at
 .br
--- a/doc/pidgin.1.in	Mon Oct 01 17:02:03 2007 +0000
+++ b/doc/pidgin.1.in	Mon Oct 01 17:49:01 2007 +0000
@@ -140,7 +140,7 @@
 If \fIAuto-Login\fR is chosen, this account will automatically login upon
 starting Pidgin.
 
-Each protocol has it's own specific options that can be found in the
+Each protocol has its own specific options that can be found in the
 modify screen.
 
 .SH PREFERENCES
--- a/finch/finch.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/finch.c	Mon Oct 01 17:49:01 2007 +0000
@@ -297,6 +297,7 @@
 	if (opt_version) {
 		/* Translators may want to transliterate the name.
 		 It is not to be translated. */
+		gnt_quit();
 		printf("%s %s\n", _("Finch"), VERSION);
 		return 0;
 	}
@@ -419,13 +420,13 @@
 
 	gnt_init();
 
-	gnt_start(&argc, &argv);
-
-	gnt_main();
+	if (gnt_start(&argc, &argv)) {
+		gnt_main();
 
 #ifdef STANDALONE
-	purple_core_quit();
+		purple_core_quit();
 #endif
+	}
 
 	return 0;
 }
--- a/finch/finch.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/finch.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @defgroup finch Finch (GNT User Interface)
- *
- * 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
--- a/finch/gntaccount.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntaccount.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntaccount.c GNT Account API
  * @ingroup finch
- *
- * 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
--- a/finch/gntaccount.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntaccount.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntaccount.h GNT Account API
  * @ingroup finch
- *
- * 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
--- a/finch/gntblist.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntblist.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntblist.c GNT BuddyList API
  * @ingroup finch
- *
- * 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
--- a/finch/gntblist.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntblist.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntblist.h GNT BuddyList API
  * @ingroup finch
- *
- * 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
--- a/finch/gntcertmgr.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntcertmgr.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntcertmgr.c GNT Certificate Manager API
  * @ingroup finch
- *
- * 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
--- a/finch/gntcertmgr.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntcertmgr.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntcertmgr.h GNT Certificate Manager API
  * @ingroup finch
- *
- * 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
--- a/finch/gntconn.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntconn.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntconn.c GNT Connection API
  * @ingroup finch
- *
- * 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
@@ -104,10 +105,11 @@
 {
 	FinchAutoRecon *info;
 	PurpleAccount *account = purple_connection_get_account(gc);
-
-	info = g_hash_table_lookup(hash, account);
+	GList *list;
 
 	if (!gc->wants_to_die) {
+		info = g_hash_table_lookup(hash, account);
+
 		if (info == NULL) {
 			info = g_new0(FinchAutoRecon, 1);
 			g_hash_table_insert(hash, account, info);
@@ -140,6 +142,17 @@
 		g_free(secondary);
 		purple_account_set_enabled(account, FINCH_UI, FALSE);
 	}
+
+	/* If we have any open chats, we probably want to rejoin when we get back online. */
+	list = purple_get_chats();
+	while (list) {
+		PurpleConversation *conv = list->data;
+		list = list->next;
+		if (conv->account != account ||
+				purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
+			continue;
+		purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE));
+	}
 }
 
 static void
--- a/finch/gntconn.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntconn.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntconn.h GNT Connection API
  * @ingroup finch
- *
- * 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
--- a/finch/gntconv.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntconv.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntconv.c GNT Conversation API
  * @ingroup finch
- *
- * 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
@@ -310,13 +311,41 @@
 static void
 account_signed_on_off(PurpleConnection *gc, gpointer null)
 {
-	GList *ims = purple_get_ims();
-	while (ims) {
-		PurpleConversation *conv = ims->data;
+	GList *list = purple_get_ims();
+	while (list) {
+		PurpleConversation *conv = list->data;
 		PurpleConversation *cc = find_conv_with_contact(conv->account, conv->name);
 		if (cc)
 			generate_send_to_menu(cc->ui_data);
-		ims = ims->next;
+		list = list->next;
+	}
+
+	if (PURPLE_CONNECTION_IS_CONNECTED(gc)) {
+		/* We just signed on. Let's see if there's any chat that we have open,
+		 * and hadn't left before the disconnect. */
+		list = purple_get_chats();
+		while (list) {
+			PurpleConversation *conv = list->data;
+			gboolean del = FALSE;
+			PurpleChat *chat;
+
+			list = list->next;
+			if (conv->account != gc->account ||
+					!purple_conversation_get_data(conv, "want-to-rejoin"))
+				continue;
+
+			chat = purple_blist_find_chat(conv->account, conv->name);
+			if (chat == NULL) {
+				GHashTable *hash = NULL;
+				if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL)
+					hash = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, conv->name);
+				chat = purple_chat_new(gc->account, conv->name, hash);
+				del = TRUE;
+			}
+			serv_join_chat(gc, chat->components);
+			if (del)
+				purple_blist_remove_chat(chat);
+		}
 	}
 }
 
--- a/finch/gntconv.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntconv.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntconv.h GNT Conversation API
  * @ingroup finch
- *
- * 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
--- a/finch/gntdebug.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntdebug.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntdebug.c GNT Debug API
  * @ingroup finch
- *
- * 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
--- a/finch/gntdebug.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntdebug.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntdebug.h GNT Debug API
  * @ingroup finch
- *
- * 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
--- a/finch/gntft.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntft.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntft.c GNT File Transfer UI
  * @ingroup finch
- *
- * 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
--- a/finch/gntft.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntft.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntft.h GNT File Transfer UI
  * @ingroup finch
- *
- * 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
--- a/finch/gntidle.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntidle.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntidle.h GNT Idle API
  * @ingroup finch
- *
- * finch
+ */
+
+/* finch
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/finch/gntnotify.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntnotify.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntnotify.c GNT Notify API
  * @ingroup finch
- *
- * 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
@@ -351,14 +352,17 @@
 		gnt_box_add_widget(GNT_BOX(window),
 			gnt_label_new_with_format(secondary, GNT_TEXT_FLAG_NORMAL));
 
-	columns = purple_notify_searchresults_get_columns_count(results);
+	columns = g_list_length(results->columns);
 	tree = gnt_tree_new_with_columns(columns);
 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
 	gnt_box_add_widget(GNT_BOX(window), tree);
 
-	for (i = 0; i < columns; i++)
-		gnt_tree_set_column_title(GNT_TREE(tree), i, 
-				purple_notify_searchresults_column_get_title(results, i));
+	i = 0;
+	for (iter = results->columns; iter; iter = iter->next)
+	{
+		gnt_tree_set_column_title(GNT_TREE(tree), i, iter->data);
+		i++;
+	}
 
 	box = gnt_hbox_new(TRUE);
 
--- a/finch/gntnotify.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntnotify.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntnotify.h GNT Notify API
  * @ingroup finch
- *
- * 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
--- a/finch/gntplugin.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntplugin.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntplugin.c GNT Plugins API
  * @ingroup finch
- *
- * 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
--- a/finch/gntplugin.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntplugin.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntplugin.h GNT Plugins API
  * @ingroup finch
- *
- * 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
--- a/finch/gntpounce.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntpounce.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntpounce.c GNT Buddy Pounce API
  * @ingroup finch
- *
- * 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
--- a/finch/gntpounce.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntpounce.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntpounce.h GNT Buddy Pounce API
  * @ingroup finch
- *
- * 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
--- a/finch/gntprefs.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntprefs.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntprefs.c GNT Preferences API
  * @ingroup finch
- *
- * 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
--- a/finch/gntprefs.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntprefs.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntprefs.h GNT Preferences API
  * @ingroup finch
- *
- * 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
--- a/finch/gntrequest.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntrequest.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntrequest.c GNT Request API
  * @ingroup finch
- *
- * 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
--- a/finch/gntrequest.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntrequest.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntrequest.h GNT Request API
  * @ingroup finch
- *
- * 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
--- a/finch/gntsound.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntsound.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntsound.c GNT Sound API
  * @ingroup finch
- *
- * 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
--- a/finch/gntsound.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntsound.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntsound.h GNT Sound API
  * @ingroup finch
- *
- * 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
--- a/finch/gntstatus.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntstatus.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntstatus.c GNT Status API
  * @ingroup finch
- *
- * 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
--- a/finch/gntstatus.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/gntstatus.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gntstatus.h GNT Status API
  * @ingroup finch
- *
- * 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
--- a/finch/libgnt/gntentry.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/libgnt/gntentry.c	Mon Oct 01 17:49:01 2007 +0000
@@ -100,6 +100,15 @@
 	return changed;
 }
 
+static int
+max_common_prefix(const char *s, const char *t)
+{
+	const char *f = s;
+	while (*f && *t && *f == *t++)
+		f++;
+	return f - s;
+}
+
 static gboolean
 show_suggest_dropdown(GntEntry *entry)
 {
@@ -110,6 +119,7 @@
 	GList *iter;
 	const char *text = NULL;
 	const char *sgst = NULL;
+	int max = -1;
 
 	if (entry->word)
 	{
@@ -121,14 +131,13 @@
 	else
 		suggest = g_strdup(entry->start);
 	len = strlen(suggest);  /* Don't need to use the utf8-function here */
-	
+
 	if (entry->ddown == NULL)
 	{
 		GntWidget *box = gnt_vbox_new(FALSE);
 		entry->ddown = gnt_tree_new();
 		gnt_tree_set_compare_func(GNT_TREE(entry->ddown), (GCompareFunc)g_utf8_collate);
 		gnt_box_add_widget(GNT_BOX(box), entry->ddown);
-		/* XXX: Connect to the "activate" signal for the dropdown tree */
 
 		GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_TRANSIENT);
 
@@ -151,6 +160,10 @@
 					gnt_tree_create_row(GNT_TREE(entry->ddown), text),
 					NULL, NULL);
 			count++;
+			if (max == -1)
+				max = strlen(text) - len;
+			else if (max)
+				max = MIN(max, max_common_prefix(sgst + len, text + len));
 			sgst = text;
 		}
 	}
@@ -163,6 +176,17 @@
 		destroy_suggest(entry);
 		return complete_suggest(entry, sgst);
 	} else {
+		if (max > 0) {
+			GntWidget *ddown = entry->ddown;
+			char *match = g_strndup(sgst + len, max);
+			entry->ddown = NULL;
+			gnt_entry_key_pressed(GNT_WIDGET(entry), match);
+			g_free(match);
+			if (entry->ddown)
+				gnt_widget_destroy(ddown);
+			else
+				entry->ddown = ddown;
+		}
 		gnt_widget_draw(entry->ddown->parent);
 	}
 
--- a/finch/libgnt/gntmain.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/libgnt/gntmain.c	Mon Oct 01 17:49:01 2007 +0000
@@ -409,7 +409,8 @@
 	case SIGWINCH:
 		erase();
 		g_idle_add(refresh_screen, NULL);
-		org_winch_handler(sig);
+		if (org_winch_handler)
+			org_winch_handler(sig);
 		signal(SIGWINCH, sighandler);
 		break;
 #endif
--- a/finch/libgnt/gntstyle.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/libgnt/gntstyle.c	Mon Oct 01 17:49:01 2007 +0000
@@ -26,7 +26,6 @@
 
 #include <glib.h>
 #include <ctype.h>
-#include <glib/gprintf.h>
 #include <stdlib.h>
 #include <string.h>
 
--- a/finch/libgnt/gntwm.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/libgnt/gntwm.c	Mon Oct 01 17:49:01 2007 +0000
@@ -27,8 +27,8 @@
 
 #include "config.h"
 
+#include <glib.h>
 #include <ctype.h>
-#include <glib/gprintf.h>
 #include <gmodule.h>
 #include <stdlib.h>
 #include <string.h>
--- a/finch/plugins/lastlog.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/finch/plugins/lastlog.c	Mon Oct 01 17:49:01 2007 +0000
@@ -96,6 +96,7 @@
 {
 	cmd = purple_cmd_register("lastlog", "s", PURPLE_CMD_P_DEFAULT,
 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
+			/* Translator Note: The "backlog" is the conversation buffer/history. */
 			lastlog_cb, _("lastlog: Searches for a substring in the backlog."), NULL);
 	return TRUE;
 }
--- a/libpurple/account.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/account.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file account.c Account API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -916,7 +917,7 @@
 purple_account_set_register_callback(PurpleAccount *account, PurpleAccountRegistrationCb cb, void *user_data)
 {
 	g_return_if_fail(account != NULL);
-	
+
 	account->registration_cb = cb;
 	account->registration_cb_user_data = user_data;
 }
@@ -936,10 +937,10 @@
 purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data)
 {
 	g_return_if_fail(account != NULL);
-	
+
 	purple_debug_info("account", "Unregistering account %s\n",
 					  purple_account_get_username(account));
-	
+
 	purple_connection_new_unregister(account, purple_account_get_password(account), cb, user_data);
 }
 
@@ -959,13 +960,21 @@
 	}
 
 	if(remember)
-	  purple_account_set_remember_password(account, TRUE);
+		purple_account_set_remember_password(account, TRUE);
 
 	purple_account_set_password(account, entry);
 
 	purple_connection_new(account, FALSE, entry);
 }
 
+static void
+request_password_cancel_cb(PurpleAccount *account, PurpleRequestFields *fields)
+{
+	/* Disable the account as the user has canceled connecting */
+	purple_account_set_enabled(account, purple_core_get_ui(), FALSE);
+}
+
+
 void
 purple_account_request_password(PurpleAccount *account, GCallback ok_cb,
 				GCallback cancel_cb, void *user_data)
@@ -1039,7 +1048,7 @@
 	if ((password == NULL) &&
 		!(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
 		!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
-		purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), NULL, account);
+		purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), G_CALLBACK(request_password_cancel_cb), account);
 	else
 		purple_connection_new(account, FALSE, password);
 }
@@ -1110,18 +1119,18 @@
 	g_free(info);
 }
 
-void 
+void
 purple_account_request_close_with_account(PurpleAccount *account)
 {
 	GList *l, *l_next;
-	
+
 	g_return_if_fail(account != NULL);
-	
+
 	for (l = handles; l != NULL; l = l_next) {
 		PurpleAccountRequestInfo *info = l->data;
-		
+
 		l_next = l->next;
-		
+
 		if (info->account == account) {
 			handles = g_list_remove(handles, info);
 			purple_account_request_close_info(info);
@@ -1129,18 +1138,18 @@
 	}
 }
 
-void 
+void
 purple_account_request_close(void *ui_handle)
 {
 	GList *l, *l_next;
-	
+
 	g_return_if_fail(ui_handle != NULL);
-	
+
 	for (l = handles; l != NULL; l = l_next) {
 		PurpleAccountRequestInfo *info = l->data;
-		
+
 		l_next = l->next;
-		
+
 		if (info->ui_handle == ui_handle) {
 			handles = g_list_remove(handles, info);
 			purple_account_request_close_info(info);
@@ -1171,7 +1180,7 @@
 		handles = g_list_append(handles, info);
 		return info->ui_handle;
 	}
-	
+
 	return NULL;
 }
 
@@ -2442,7 +2451,7 @@
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
 							 			PURPLE_SUBTYPE_ACCOUNT),
 						 purple_value_new(PURPLE_TYPE_STRING));
-	
+
 	load_accounts();
 
 }
--- a/libpurple/account.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/account.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file account.h Account API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/accountopt.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/accountopt.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file accountopt.c Account Options API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/accountopt.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/accountopt.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file accountopt.h Account Options API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -49,8 +50,10 @@
 
 	} default_value;
 
-	gboolean masked;
-
+	gboolean masked;        /**< Whether the value entered should be
+	                         *   obscured from view (for passwords and
+	                         *   similar options)
+	                         */
 } PurpleAccountOption;
 
 /**
@@ -184,10 +187,12 @@
 											const char *value);
 
 /**
- * Sets the masking for an account option.
+ * Sets the masking for an account option. Setting this to %TRUE acts
+ * as a hint to the UI that the option's value should be obscured from
+ * view, like a password.
  *
  * @param option The account option.
- * @param masked  The masking.
+ * @param masked The masking.
  */
 void
 purple_account_option_set_masked(PurpleAccountOption *option, gboolean masked);
@@ -282,11 +287,13 @@
 	const PurpleAccountOption *option);
 
 /**
- * Returns the masking for an account option.
+ * Returns whether an option's value should be masked from view, like a
+ * password.  If so, the UI might display each character of the option
+ * as a '*' (for example).
  *
  * @param option The account option.
  *
- * @return The masking.
+ * @return %TRUE if the option's value should be obscured.
  */
 gboolean
 purple_account_option_get_masked(const PurpleAccountOption *option);
--- a/libpurple/blist.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/blist.c	Mon Oct 01 17:49:01 2007 +0000
@@ -843,6 +843,13 @@
 		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
 }
 
+static gboolean
+purple_strings_are_different(const char *one, const char *two)
+{
+	return !((one && two && g_utf8_collate(one, two) == 0) ||
+			((one == NULL || *one == '\0') && (two == NULL || *two == '\0')));
+}
+
 void purple_blist_alias_contact(PurpleContact *contact, const char *alias)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
@@ -852,6 +859,9 @@
 
 	g_return_if_fail(contact != NULL);
 
+	if (!purple_strings_are_different(contact->alias, alias))
+		return;
+
 	old_alias = contact->alias;
 
 	if ((alias != NULL) && (*alias != '\0'))
@@ -886,6 +896,9 @@
 
 	g_return_if_fail(chat != NULL);
 
+	if (!purple_strings_are_different(chat->alias, alias))
+		return;
+
 	old_alias = chat->alias;
 
 	if ((alias != NULL) && (*alias != '\0'))
@@ -911,6 +924,9 @@
 
 	g_return_if_fail(buddy != NULL);
 
+	if (!purple_strings_are_different(buddy->alias, alias))
+		return;
+
 	old_alias = buddy->alias;
 
 	if ((alias != NULL) && (*alias != '\0'))
@@ -941,6 +957,9 @@
 
 	g_return_if_fail(buddy != NULL);
 
+	if (!purple_strings_are_different(buddy->server_alias, alias))
+		return;
+
 	old_alias = buddy->server_alias;
 
 	if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
@@ -1428,6 +1447,9 @@
 
 	g_return_if_fail(contact != NULL);
 
+	if (!purple_strings_are_different(contact->alias, alias))
+		return;
+
 	old_alias = contact->alias;
 
 	if ((alias != NULL) && (*alias != '\0'))
--- a/libpurple/blist.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/blist.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file blist.h Buddy List API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/buddyicon.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/buddyicon.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file icon.c Buddy Icon API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/buddyicon.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/buddyicon.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file buddyicon.h Buddy Icon API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/certificate.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/certificate.c	Mon Oct 01 17:49:01 2007 +0000
@@ -540,7 +540,7 @@
 
 	/* Determine whether the name matches */
 	if (purple_certificate_check_subject_name(crt, vrq->subject_name)) {
-		cn_match = _("");
+		cn_match = "";
 	} else {
 		cn_match = _("(DOES NOT MATCH)");
 	}
@@ -1460,15 +1460,14 @@
 	
 	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,tls_peers_name);
 
-	/* TODO: This should probably just prompt the user instead of throwing
-	   an angry fit */
 	if (!tls_peers) {
 		purple_debug_error("certificate/x509/tls_cached",
-				   "Couldn't find local peers cache %s\nReturning INVALID to callback\n",
+				   "Couldn't find local peers cache %s\nPrompting the user\n",
 				   tls_peers_name);
 
-		purple_certificate_verify_complete(vrq,
-						   PURPLE_CERTIFICATE_INVALID);
+
+		/* vrq now becomes the problem of unknown_peer */
+		x509_tls_cached_unknown_peer(vrq);
 		return;
 	}
 	
--- a/libpurple/cipher.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/cipher.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file cipher.h Purple Cipher API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/circbuffer.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/circbuffer.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file circbuffer.h Buffer Utility Functions
  * @ingroup core
- *
- * Purple is the legal property of its developers, whose names are too numerous
+ */
+
+/* Purple 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.
  *
--- a/libpurple/circbuffer.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/circbuffer.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
-/*
+/**
  * @file circbuffer.h Buffer Utility Functions
  * @ingroup core
- *
- * Purple is the legal property of its developers, whose names are too numerous
+ */
+
+/* Purple 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.
  *
--- a/libpurple/cmds.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/cmds.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file cmds.c Commands API
  * @ingroup core
- *
- * Copyright (C) 2003-2004 Timothy Ringenbach <omarvo@hotmail.com
+ */
+
+/* Copyright (C) 2003-2004 Timothy Ringenbach <omarvo@hotmail.com
  *
  * 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
--- a/libpurple/cmds.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/cmds.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file cmds.h Commands API
  * @ingroup core
- *
- * Copyright (C) 2003 Timothy Ringenbach <omarvo@hotmail.com>
+ */
+
+/* Copyright (C) 2003 Timothy Ringenbach <omarvo@hotmail.com>
  *
  * 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
@@ -66,10 +67,20 @@
 	PURPLE_CMD_P_VERY_HIGH =  6000,
 };
 
+/** Flags used to set various properties of commands.  Every command should
+ *  have at least one of #PURPLE_CMD_FLAG_IM and #PURPLE_CMD_FLAG_CHAT set in
+ *  order to be even slighly useful.
+ *
+ *  @see purple_cmd_register
+ */
 enum _PurpleCmdFlag {
+	/** Command is usable in IMs. */
 	PURPLE_CMD_FLAG_IM               = 0x01,
+	/** Command is usable in multi-user chats. */
 	PURPLE_CMD_FLAG_CHAT             = 0x02,
+	/** Command is usable only for a particular prpl. */
 	PURPLE_CMD_FLAG_PRPL_ONLY        = 0x04,
+	/** Incorrect arguments to this command should be accepted anyway. */
 	PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08,
 };
 
@@ -91,36 +102,49 @@
  * The command will only happen if commands are enabled,
  * which is a UI pref. UIs don't have to support commands at all.
  *
- * @param cmd The command. This should be a UTF8 (or ASCII) string, with no spaces
+ * @param cmd The command. This should be a UTF-8 (or ASCII) string, with no spaces
  *            or other white space.
- * @param args This tells Purple how to parse the arguments to the command for you.
- *             If what the user types doesn't match, Purple will keep looking for another
- *             command, unless the flag @c PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed in f.
- *             This string contains no whitespace, and uses a single character for each argument.
- *             The recognized characters are:
- *               'w' Matches a single word.
- *               'W' Matches a single word, with formatting.
- *               's' Matches the rest of the arguments after this point, as a single string.
- *               'S' Same as 's' but with formatting.
+ * @param args A string of characters describing to libpurple how to parse this
+ *             command's arguments.  If what the user types doesn't match this
+ *             pattern, libpurple will keep looking for another command, unless
+ *             the flag #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed in @a f.
+ *             This string should contain no whitespace, and use a single
+ *             character for each argument.  The recognized characters are:
+ *             <ul>
+ *               <li><tt>'w'</tt>: Matches a single word.</li>
+ *               <li><tt>'W'</tt>: Matches a single word, with formatting.</li>
+ *               <li><tt>'s'</tt>: Matches the rest of the arguments after this
+ *                                 point, as a single string.</li>
+ *               <li><tt>'S'</tt>: Same as <tt>'s'</tt> but with formatting.</li>
+ *             </ul>
  *             If args is the empty string, then the command accepts no arguments.
- *             The args passed to callback func will be a @c NULL terminated array of null
- *             terminated strings, and will always match the number of arguments asked for,
- *             unless PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed.
- * @param p This is the priority. Higher priority commands will be run first, and usually the
- *          first command will stop any others from being called.
- * @param f These are the flags. You need to at least pass one of PURPLE_CMD_FLAG_IM or
- *          PURPLE_CMD_FLAG_CHAT (can may pass both) in order for the command to ever actually
- *          be called.
- * @param prpl_id This is the prpl's id string. This is only meaningful if the proper flag is set.
+ *             The args passed to the callback @a func will be a @c NULL
+ *             terminated array of @c NULL terminated strings, and will always
+ *             match the number of arguments asked for, unless
+ *             #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed.
+ * @param p This is the priority. Higher priority commands will be run first,
+ *          and usually the first command will stop any others from being
+ *          called.
+ * @param f Flags specifying various options about this command, combined with
+ *          <tt>|</tt> (bitwise OR). You need to at least pass one of
+ *          #PURPLE_CMD_FLAG_IM or #PURPLE_CMD_FLAG_CHAT (you may pass both) in
+ *          order for the command to ever actually be called.
+ * @param prpl_id If the #PURPLE_CMD_FLAG_PRPL_ONLY flag is set, this is the id
+ *                of the prpl to which the command applies (such as
+ *                <tt>"prpl-msn"</tt>). If the flag is not set, this parameter
+ *                is ignored; pass @c NULL (or a humourous string of your
+ *                choice!).
  * @param func This is the function to call when someone enters this command.
- * @param helpstr This is a whitespace sensitive, UTF-8, HTML string describing how to use the command.
- *                The preferred format of this string shall be the commands name, followed by a space
- *                and any arguments it accepts (if it takes any arguments, otherwise no space), followed
- *                by a colon, two spaces, and a description of the command in sentence form. No slash
- *                before the command name.
- * @param data User defined data to pass to the PurpleCmdFunc
- * @return A PurpleCmdId. This is only used for calling purple_cmd_unregister.
- *         Returns 0 on failure.
+ * @param helpstr a whitespace sensitive, UTF-8, HTML string describing how to
+ *                use the command.  The preferred format of this string is the
+ *                command's name, followed by a space and any arguments it
+ *                accepts (if it takes any arguments, otherwise no space),
+ *                followed by a colon, two spaces, and a description of the
+ *                command in sentence form.  Do not include a slash before the
+ *                command name.
+ * @param data User defined data to pass to the #PurpleCmdFunc @a f.
+ * @return A #PurpleCmdId, which is only used for calling
+ *         #purple_cmd_unregister, or @a 0 on failure.
  */
 PurpleCmdId purple_cmd_register(const gchar *cmd, const gchar *args, PurpleCmdPriority p, PurpleCmdFlag f,
                              const gchar *prpl_id, PurpleCmdFunc func, const gchar *helpstr, void *data);
@@ -132,7 +156,7 @@
  * or something else that might go away. Normally this is called when the plugin
  * unloads itself.
  *
- * @param id The PurpleCmdId to unregister.
+ * @param id The #PurpleCmdId to unregister, as returned by #purple_cmd_register.
  */
 void purple_cmd_unregister(PurpleCmdId id);
 
@@ -152,7 +176,7 @@
  *               include both the default formatting and any extra manual formatting.
  * @param errormsg If the command failed errormsg is filled in with the appropriate error
  *                 message. It must be freed by the caller with g_free().
- * @return A PurpleCmdStatus indicated if the command succeeded or failed.
+ * @return A #PurpleCmdStatus indicated if the command succeeded or failed.
  */
 PurpleCmdStatus purple_cmd_do_command(PurpleConversation *conv, const gchar *cmdline,
                                   const gchar *markup, gchar **errormsg);
@@ -160,13 +184,15 @@
 /**
  * List registered commands.
  *
- * Returns a GList (which must be freed by the caller) of all commands
- * that are valid in the context of conv, or all commands, if conv is
- * @c NULL. Don't keep this list around past the main loop, or anything else
- * that might unregister a command, as the char*'s used get freed then.
+ * Returns a <tt>GList</tt> (which must be freed by the caller) of all commands
+ * that are valid in the context of @a conv, or all commands, if @a conv is @c
+ * NULL.  Don't keep this list around past the main loop, or anything else that
+ * might unregister a command, as the <tt>const char *</tt>'s used get freed
+ * then.
  *
  * @param conv The conversation, or @c NULL.
- * @return A GList of const char*, which must be freed with g_list_free().
+ * @return A @c GList of <tt>const char *</tt>, which must be freed with
+ *         <tt>g_list_free()</tt>.
  */
 GList *purple_cmd_list(PurpleConversation *conv);
 
@@ -179,7 +205,7 @@
  * @param conv The conversation, or @c NULL for no context.
  * @param cmd The command. No wildcards accepted, but returns help for all
  *            commands if @c NULL.
- * @return A GList of const char*s, which is the help string
+ * @return A <tt>GList</tt> of <tt>const char *</tt>s, which is the help string
  *         for that command.
  */
 GList *purple_cmd_help(PurpleConversation *conv, const gchar *cmd);
--- a/libpurple/connection.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/connection.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file connection.c Connection API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/connection.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/connection.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file connection.h Connection API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/conversation.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/conversation.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file conversation.h Conversation API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/core.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/core.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file core.c Purple Core API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/core.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/core.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @defgroup core libpurple
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/dbus-bindings.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/dbus-bindings.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file dbus-bindings.h Purple DBUS Bindings
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/dbus-server.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/dbus-server.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file dbus-server.h Purple DBUS Server
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/debug.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/debug.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file debug.c Debug API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/debug.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/debug.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file debug.h Debug API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/desktopitem.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/desktopitem.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file purple-desktop-item.c Functions for managing .desktop files
  * @ingroup core
- *
- * Purple is the legal property of its developers, whose names are too numerous
+ */
+
+/* Purple 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.
  *
--- a/libpurple/desktopitem.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/desktopitem.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file desktopitem.h Functions for managing .desktop files
  * @ingroup core
- *
- * Purple is the legal property of its developers, whose names are too numerous
+ */
+
+/* Purple 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.
  *
--- a/libpurple/dnsquery.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/dnsquery.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file dnsquery.c DNS query API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/dnsquery.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/dnsquery.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file dnsquery.h DNS query API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/dnssrv.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/dnssrv.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file dnssrv.c
- *
- * purple
+ */
+
+/* purple
  *
  * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
  *
--- a/libpurple/dnssrv.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/dnssrv.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,10 +1,11 @@
 /**
  * @file dnssrv.h
- *
- * purple
+ */
+
+/* purple
  *
  * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
- *i
+ *
  * 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
--- a/libpurple/eventloop.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/eventloop.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file eventloop.c Purple Event Loop API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/eventloop.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/eventloop.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file eventloop.h Purple Event Loop API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/ft.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/ft.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file ft.c File Transfer API
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/ft.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/ft.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file ft.h File Transfer API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/gaim-compat.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/gaim-compat.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gaim-compat.h Gaim Compat macros
  * @ingroup core
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/idle.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/idle.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file idle.h Idle API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/imgstore.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/imgstore.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file imgstore.h IM Image Store API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/imgstore.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/imgstore.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file imgstore.h IM Image Store API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/internal.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/internal.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file internal.h Internal definitions and includes
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/log.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/log.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file log.c Logging API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/log.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/log.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file log.h Logging API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/nat-pmp.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/nat-pmp.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file nat-pmp.c NAT-PMP Implementation
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/nat-pmp.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/nat-pmp.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file nat-pmp.h NAT-PMP Implementation
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/network.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/network.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file network.c Network Implementation
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/network.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/network.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file network.h Network API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/notify.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/notify.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file notify.c Notification API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/notify.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/notify.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file notify.h Notification API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -292,7 +293,17 @@
 
 /**
  * Returns a number of the rows in the search results object.
- * 
+ *
+ * @deprecated This function will be removed in Pidgin 3.0.0 unless
+ *             there is sufficient demand to keep it.  Using this
+ *             function encourages looping through the results
+ *             inefficiently.  Instead of using this function you
+ *             should iterate through the results using a loop
+ *             similar to this:
+ *                for (l = results->rows; l != NULL; l = l->next)
+ *             If you really need to get the number of rows you
+ *             can use g_list_length(results->rows).
+ *
  * @param results The search results object.
  *
  * @return Number of the result rows.
@@ -302,6 +313,16 @@
 /**
  * Returns a number of the columns in the search results object.
  *
+ * @deprecated This function will be removed in Pidgin 3.0.0 unless
+ *             there is sufficient demand to keep it.  Using this
+ *             function encourages looping through the columns
+ *             inefficiently.  Instead of using this function you
+ *             should iterate through the columns using a loop
+ *             similar to this:
+ *                for (l = results->columns; l != NULL; l = l->next)
+ *             If you really need to get the number of columns you
+ *             can use g_list_length(results->columns).
+ *
  * @param results The search results object.
  *
  * @return Number of the columns.
@@ -311,6 +332,16 @@
 /**
  * Returns a row of the results from the search results object.
  *
+ * @deprecated This function will be removed in Pidgin 3.0.0 unless
+ *             there is sufficient demand to keep it.  Using this
+ *             function encourages looping through the results
+ *             inefficiently.  Instead of using this function you
+ *             should iterate through the results using a loop
+ *             similar to this:
+ *                for (l = results->rows; l != NULL; l = l->next)
+ *             If you really need to get the data for a particular
+ *             row you can use g_list_nth_data(results->rows, row_id).
+ *
  * @param results The search results object.
  * @param row_id  Index of the row to be returned.
  *
@@ -321,7 +352,15 @@
 
 /**
  * Returns a title of the search results object's column.
- * 
+ *
+ * @deprecated This function will be removed in Pidgin 3.0.0 unless
+ *             there is sufficient demand to keep it.  Using this
+ *             function encourages looping through the columns
+ *             inefficiently.  Instead of using this function you
+ *             should iterate through the name of a particular
+ *             column you can use
+ *             g_list_nth_data(results->columns, row_id).
+ *
  * @param results   The search results object.
  * @param column_id Index of the column.
  *
--- a/libpurple/ntlm.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/ntlm.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file ntlm.c
- *
- * purple
+ */
+
+/* purple
  *
  * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
  *
--- a/libpurple/ntlm.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/ntlm.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file ntlm.h
- *
- * purple
+ */
+
+/* purple
  *
  * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
  *
--- a/libpurple/plugin.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/plugin.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file plugin.h Plugin API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/pluginpref.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/pluginpref.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file pluginpref.h Plugin Preferences API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/plugins/debug_example.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/plugins/debug_example.c	Mon Oct 01 17:49:01 2007 +0000
@@ -27,6 +27,16 @@
 /* We're including glib.h again for the gboolean type. */
 #include <glib.h>
 
+/* This will prevent compiler errors in some instances and is better explained in the
+ * how-to documents on the wiki */
+#ifndef G_GNUC_NULL_TERMINATED
+# if __GNUC__ >= 4
+#  define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+#  define G_GNUC_NULL_TERMINATED
+# endif
+#endif
+
 /* This is the required definition of PURPLE_PLUGINS as required for a plugin,
  * but we protect it with an #ifndef because config.h may define it for us
  * already and this would cause an unneeded compiler warning. */
--- a/libpurple/plugins/helloworld.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/plugins/helloworld.c	Mon Oct 01 17:49:01 2007 +0000
@@ -33,6 +33,16 @@
 
 #include <glib.h>
 
+/* This will prevent compiler errors in some instances and is better explained in the
+ * how-to documents on the wiki */
+#ifndef G_GNUC_NULL_TERMINATED
+# if __GNUC__ >= 4
+#  define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+#  define G_GNUC_NULL_TERMINATED
+# endif
+#endif
+
 #include <notify.h>
 #include <plugin.h>
 #include <version.h>
--- a/libpurple/plugins/notify_example.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/plugins/notify_example.c	Mon Oct 01 17:49:01 2007 +0000
@@ -26,6 +26,16 @@
 
 #include <glib.h>
 
+/* This will prevent compiler errors in some instances and is better explained in the
+ * how-to documents on the wiki */
+#ifndef G_GNUC_NULL_TERMINATED
+# if __GNUC__ >= 4
+#  define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+#  define G_GNUC_NULL_TERMINATED
+# endif
+#endif
+
 /* This is the required definition of PURPLE_PLUGINS as required for a plugin,
  * but we protect it with an #ifndef because config.h may define it for us
  * already and this would cause an unneeded compiler warning. */
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Mon Oct 01 17:49:01 2007 +0000
@@ -111,7 +111,7 @@
 	PurpleSslGnutlsData *gnutls_data = PURPLE_SSL_GNUTLS_DATA(gsc);
 	ssize_t ret;
 
-	purple_debug_info("gnutls", "Handshaking with %s\n", gsc->host);
+	/*purple_debug_info("gnutls", "Handshaking with %s\n", gsc->host);*/
 	ret = gnutls_handshake(gnutls_data->session);
 
 	if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
@@ -163,9 +163,9 @@
 		g_list_free(peers);
 
 		{
-			const gnutls_datum_t *cert_list;
+			const gnutls_datum *cert_list;
 			unsigned int cert_list_size = 0;
-			gnutls_session_t session=gnutls_data->session;
+			gnutls_session session=gnutls_data->session;
 			int i;
 
 			cert_list =
@@ -182,7 +182,7 @@
 				gchar tbuf[256];
 				gsize tsz=sizeof(tbuf);
 				gchar * tasc = NULL;
-				gnutls_x509_crt_t cert;
+				gnutls_x509_crt cert;
 
 				gnutls_x509_crt_init(&cert);
 				gnutls_x509_crt_import (cert, &cert_list[i],
@@ -271,6 +271,8 @@
 	gnutls_data->handshake_handler = purple_input_add(gsc->fd,
 		PURPLE_INPUT_READ, ssl_gnutls_handshake_cb, gsc);
 
+	purple_debug_info("gnutls", "Starting handshake with %s\n", gsc->host);
+
 	/* Orborde asks: Why are we configuring a callback, then
 	   immediately calling it?
 
@@ -363,7 +365,7 @@
 
 /* Forward declarations are fun! */
 static PurpleCertificate *
-x509_import_from_datum(const gnutls_datum_t dt, gnutls_x509_crt_fmt_t mode);
+x509_import_from_datum(const gnutls_datum dt, gnutls_x509_crt_fmt mode);
 
 static GList *
 ssl_gnutls_get_peer_certificates(PurpleSslConnection * gsc)
@@ -374,7 +376,7 @@
 	GList * peer_certs = NULL;
 
 	/* List of raw certificates as given by GnuTLS */
-	const gnutls_datum_t *cert_list;
+	const gnutls_datum *cert_list;
 	unsigned int cert_list_size = 0;
 
 	unsigned int i;
@@ -412,7 +414,7 @@
 /** Refcounted GnuTLS certificate data instance */
 typedef struct {
 	gint refcount;
-	gnutls_x509_crt_t crt;
+	gnutls_x509_crt crt;
 } x509_crtdata_t;
 
 /** Helper functions for reference counting */
@@ -434,9 +436,6 @@
 
 	/* If the refcount reaches zero, kill the structure */
 	if (cd->refcount <= 0) {
-		purple_debug_info("gnutls/x509",
-				  "Freeing unused cert data at %p\n",
-				  cd);
 		/* Kill the internal data */
 		gnutls_x509_crt_deinit( cd->crt );
 		/* And kill the struct */
@@ -447,7 +446,7 @@
 /** Helper macro to retrieve the GnuTLS crt_t from a PurpleCertificate */
 #define X509_GET_GNUTLS_DATA(pcrt) ( ((x509_crtdata_t *) (pcrt->data))->crt)
 
-/** Transforms a gnutls_datum_t containing an X.509 certificate into a Certificate instance under the x509_gnutls scheme
+/** Transforms a gnutls_datum containing an X.509 certificate into a Certificate instance under the x509_gnutls scheme
  *
  * @param dt   Datum to transform
  * @param mode GnuTLS certificate format specifier (GNUTLS_X509_FMT_PEM for
@@ -457,7 +456,7 @@
  * @return A newly allocated Certificate structure of the x509_gnutls scheme
  */
 static PurpleCertificate *
-x509_import_from_datum(const gnutls_datum_t dt, gnutls_x509_crt_fmt_t mode)
+x509_import_from_datum(const gnutls_datum dt, gnutls_x509_crt_fmt mode)
 {
 	/* Internal certificate data structure */
 	x509_crtdata_t *certdat;
@@ -492,7 +491,7 @@
 	PurpleCertificate *crt;  /* Certificate being constructed */
 	gchar *buf;        /* Used to load the raw file data */
 	gsize buf_sz;      /* Size of the above */
-	gnutls_datum_t dt; /* Struct to pass down to GnuTLS */
+	gnutls_datum dt; /* Struct to pass down to GnuTLS */
 
 	purple_debug_info("gnutls",
 			  "Attempting to load X.509 certificate from %s\n",
@@ -533,7 +532,7 @@
 static gboolean
 x509_export_certificate(const gchar *filename, PurpleCertificate *crt)
 {
-	gnutls_x509_crt_t crt_dat; /* GnuTLS cert struct */
+	gnutls_x509_crt crt_dat; /* GnuTLS cert struct */
 	int ret;
 	gchar * out_buf; /* Data to output */
 	size_t out_size; /* Output size */
@@ -640,8 +639,8 @@
 x509_certificate_signed_by(PurpleCertificate * crt,
 			   PurpleCertificate * issuer)
 {
-	gnutls_x509_crt_t crt_dat;
-	gnutls_x509_crt_t issuer_dat;
+	gnutls_x509_crt crt_dat;
+	gnutls_x509_crt issuer_dat;
 	unsigned int verify; /* used to store result from GnuTLS verifier */
 	int ret;
 
@@ -674,10 +673,10 @@
 			purple_debug_info("gnutls/x509",
 					  "Certificate for %s claims to be "
 					  "issued by %s, but the certificate "
-					  "for %s does not match. A strcmp "
-					  "says %d\n",
-					  crt_id, crt_issuer_id, issuer_id,
-					  strcmp(crt_issuer_id, issuer_id));
+					  "for %s does not match.\n",
+					  crt_id ? crt_id : "(null)",
+					  crt_issuer_id ? crt_issuer_id : "(null)",
+					  issuer_id ? issuer_id : "(null)");
 			g_free(crt_id);
 			g_free(issuer_id);
 			g_free(crt_issuer_id);
@@ -727,7 +726,7 @@
 {
 	size_t hashlen = 20; /* SHA1 hashes are 20 bytes */
 	size_t tmpsz = hashlen; /* Throw-away variable for GnuTLS to stomp on*/
-	gnutls_x509_crt_t crt_dat;
+	gnutls_x509_crt crt_dat;
 	GByteArray *hash; /**< Final hash container */
 	guchar hashbuf[hashlen]; /**< Temporary buffer to contain hash */
 
@@ -754,7 +753,7 @@
 static gchar *
 x509_cert_dn (PurpleCertificate *crt)
 {
-	gnutls_x509_crt_t cert_dat;
+	gnutls_x509_crt cert_dat;
 	gchar *dn = NULL;
 	size_t dn_size;
 
@@ -770,7 +769,10 @@
 	gnutls_x509_crt_get_dn(cert_dat, dn, &dn_size);
 
 	/* Now allocate and get the Distinguished Name */
-	dn = g_new0(gchar, dn_size);
+	/* Old versions of GnuTLS have an off-by-one error in reporting
+	   the size of the needed buffer in some functions, so allocate
+	   an extra byte */
+	dn = g_new0(gchar, ++dn_size);
 	if (0 != gnutls_x509_crt_get_dn(cert_dat, dn, &dn_size)) {
 		purple_debug_error("gnutls/x509",
 				   "Failed to get Distinguished Name\n");
@@ -784,7 +786,7 @@
 static gchar *
 x509_issuer_dn (PurpleCertificate *crt)
 {
-	gnutls_x509_crt_t cert_dat;
+	gnutls_x509_crt cert_dat;
 	gchar *dn = NULL;
 	size_t dn_size;
 
@@ -800,7 +802,10 @@
 	gnutls_x509_crt_get_issuer_dn(cert_dat, dn, &dn_size);
 
 	/* Now allocate and get the Distinguished Name */
-	dn = g_new0(gchar, dn_size);
+	/* Old versions of GnuTLS have an off-by-one error in reporting
+	   the size of the needed buffer in some functions, so allocate
+	   an extra byte */
+	dn = g_new0(gchar, ++dn_size);
 	if (0 != gnutls_x509_crt_get_issuer_dn(cert_dat, dn, &dn_size)) {
 		purple_debug_error("gnutls/x509",
 				   "Failed to get issuer's Distinguished "
@@ -815,7 +820,7 @@
 static gchar *
 x509_common_name (PurpleCertificate *crt)
 {
-	gnutls_x509_crt_t cert_dat;
+	gnutls_x509_crt cert_dat;
 	gchar *cn = NULL;
 	size_t cn_size;
 	int ret;
@@ -836,7 +841,10 @@
 				      cn, &cn_size);
 
 	/* Now allocate and get the Common Name */
-	cn = g_new0(gchar, cn_size);
+	/* Old versions of GnuTLS have an off-by-one error in reporting
+	   the size of the needed buffer in some functions, so allocate
+	   an extra byte */
+	cn = g_new0(gchar, ++cn_size);
 	ret = gnutls_x509_crt_get_dn_by_oid(cert_dat,
 					    GNUTLS_OID_X520_COMMON_NAME,
 					    0, /* First CN found, please */
@@ -855,7 +863,7 @@
 static gboolean
 x509_check_name (PurpleCertificate *crt, const gchar *name)
 {
-	gnutls_x509_crt_t crt_dat;
+	gnutls_x509_crt crt_dat;
 
 	g_return_val_if_fail(crt, FALSE);
 	g_return_val_if_fail(crt->scheme == &x509_gnutls, FALSE);
@@ -873,7 +881,7 @@
 static gboolean
 x509_times (PurpleCertificate *crt, time_t *activation, time_t *expiration)
 {
-	gnutls_x509_crt_t crt_dat;
+	gnutls_x509_crt crt_dat;
 	/* GnuTLS time functions return this on error */
 	const time_t errval = (time_t) (-1);
 
--- a/libpurple/pounce.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/pounce.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file pounce.c Buddy Pounce API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/pounce.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/pounce.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file pounce.h Buddy Pounce API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/prefs.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/prefs.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file prefs.h Prefs API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/privacy.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/privacy.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file privacy.h Privacy API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/protocols/jabber/.todo	Mon Oct 01 17:02:03 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-<todo version="0.1.19">
-    <note priority="medium" time="1036044198">
-        Browsing
-    </note>
-    <note priority="medium" time="1036044416">
-        Server Admin operations (messages, etc.)
-    </note>
-    <note priority="medium" time="1036044448">
-        Add option for user info to be published or not in JUD.
-    </note>
-    <note priority="medium" time="1036044583">
-        Delete server account.
-    </note>
-    <note priority="medium" time="1036045649">
-        Permit/Deny buddy support.
-    </note>
-    <note priority="medium" time="1036046413">
-        a populate roster from local blist. most useful if you want to migrate a blist from one account to another, also useful if something freaky happens and the server blist is lost.
-    </note>
-    <note priority="medium" time="1037892911">
-        info
-        <note priority="medium" time="1037893000">
-            formatted. enhancement-request so that the birthday field in the setinfo form would split up into relevant fields allowing for a strict syntax (like year--month--day or so, perhaps even dropdown menus)
-        </note>
-    </note>
-    <note priority="verylow" time="1036044192">
-        Jabber Transports (having them show up on the buddy list should be fairly easy; having an appropriate right-click menu for them should also be somewhat easy. Providing a UI for adding transports should be rather difficult.)
-    </note>
-</todo>
--- a/libpurple/protocols/jabber/adhoccommands.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.c	Mon Oct 01 17:49:01 2007 +0000
@@ -151,8 +151,11 @@
 		/* display result */
 		xmlnode *note = xmlnode_get_child(command,"note");
 		
-		if(note)
-			purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), xmlnode_get_data(note), NULL);
+		if(note) {
+			char *data = xmlnode_get_data(note);
+			purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), data, NULL);
+			g_free(data);
+		}
 		
 		if(xdata)
 			jabber_x_data_request(js, xdata, (jabber_x_data_cb)do_adhoc_ignoreme, NULL);
--- a/libpurple/protocols/jabber/auth.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/auth.c	Mon Oct 01 17:49:01 2007 +0000
@@ -23,6 +23,7 @@
 #include "account.h"
 #include "debug.h"
 #include "cipher.h"
+#include "core.h"
 #include "conversation.h"
 #include "request.h"
 #include "sslconn.h"
@@ -66,10 +67,10 @@
 
 		auth = xmlnode_new("auth");
 		xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl");
-		
+
 		xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
 		xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
-		
+
 		response = g_string_new("");
 		response = g_string_append_len(response, "\0", 1);
 		response = g_string_append(response, js->user->node);
@@ -202,7 +203,7 @@
 
 	return TRUE;
 }
-	
+
 static void auth_pass_cb(PurpleConnection *conn, PurpleRequestFields *fields)
 {
 	JabberStream *js;
@@ -236,7 +237,7 @@
 
 	if (!auth_pass_generic(js, fields))
 		return;
-	
+
 	/* Restart our connection */
 	jabber_auth_start_old(js);
 }
@@ -253,7 +254,8 @@
 
 	js = conn->proto_data;
 
-	purple_connection_error(conn, _("Password is required to sign on."));
+	/* Disable the account as the user has canceled connecting */
+	purple_account_set_enabled(conn->account, purple_core_get_ui(), FALSE);
 }
 
 static void jabber_auth_start_cyrus(JabberStream *js)
@@ -621,7 +623,7 @@
 	 * to OPTIONAL for this protocol. So, we need to do our own
 	 * password prompting here
 	 */
-	
+
 	if (!purple_account_get_password(js->gc->account)) {
 		purple_account_request_password(js->gc->account, G_CALLBACK(auth_old_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc);
 		return;
--- a/libpurple/protocols/jabber/buddy.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1455,10 +1455,13 @@
 		return;
 	
 	img = purple_base64_decode(b64data, &size);
-	if(!img)
+	if(!img) {
+		g_free(b64data);
 		return;
+	}
 	
 	purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum);
+	g_free(b64data);
 }
 
 void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) {
@@ -1653,8 +1656,10 @@
 
 	if(!strcmp(ns, "jabber:iq:last")) {
 		if(!strcmp(jbr->client.name, "Trillian")) {
-			if(!strcmp(jbr->client.version, "3.1.0.121")) {
-				/* verified by nwalp 2007/05/09 */
+			/* verified by nwalp 2007/05/09 */
+			if(!strcmp(jbr->client.version, "3.1.0.121") ||
+					/* verified by nwalp 2007/09/19 */
+					!strcmp(jbr->client.version, "3.1.7.0")) {
 				return TRUE;
 			}
 		}
--- a/libpurple/protocols/jabber/google.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/google.c	Mon Oct 01 17:49:01 2007 +0000
@@ -39,6 +39,8 @@
 	char *subject;
 	const char *in_str;
 	char *to_name;
+	char *default_tos[1];
+
 	int i, count = 1, returned_count;
 
 	const char **tos, **froms, **urls;
@@ -55,15 +57,21 @@
 	if (in_str && *in_str)
 		count = atoi(in_str);
 
-	if (count == 0)
-		return;
+	/* 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]);
+		return;
+	}
+
 	/* Loop once to see how many messages were returned so we can allocate arrays
 	 * accordingly */
-	if (!message)
-		return;
 	for (returned_count = 0; message; returned_count++, message=xmlnode_get_next_twin(message));
 
 	froms    = g_new0(const char* , returned_count);
@@ -115,9 +123,13 @@
 	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]);
@@ -241,7 +253,8 @@
 
 	if (grt && (*grt == 'H' || *grt == 'h')) {
 		PurpleBuddy *buddy = purple_find_buddy(account, jid_norm);
-		purple_blist_remove_buddy(buddy);
+		if (buddy)
+			purple_blist_remove_buddy(buddy);
 		g_free(jid_norm);
 		return FALSE;
 	}
--- a/libpurple/protocols/jabber/iq.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/iq.c	Mon Oct 01 17:49:01 2007 +0000
@@ -255,7 +255,7 @@
 	if(type && !strcmp(type, "get")) {
 		GHashTable *ui_info;
 		const char *ui_name = NULL, *ui_version = NULL;
-
+#if 0
 		if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) {
 			struct utsname osinfo;
 
@@ -263,7 +263,7 @@
 			os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release,
 					osinfo.machine);
 		}
-
+#endif
 		from = xmlnode_get_attrib(packet, "from");
 		id = xmlnode_get_attrib(packet, "id");
 
--- a/libpurple/protocols/jabber/jabber.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Oct 01 17:49:01 2007 +0000
@@ -141,6 +141,7 @@
 		if(jabber_process_starttls(js, packet))
 			return;
 	} else if(purple_account_get_bool(js->gc->account, "require_tls", FALSE) && !js->gsc) {
+		js->gc->wants_to_die = TRUE;
 		purple_connection_error(js->gc, _("You require encryption, but it is not available on this server."));
 		return;
 	}
@@ -813,7 +814,7 @@
 		if(account->registration_cb)
 			(account->registration_cb)(account, FALSE, account->registration_cb_user_data);
 		jabber_connection_schedule_close(cbdata->js);
-}
+	}
 	g_free(cbdata->who);
 	g_free(cbdata);
 }
@@ -882,12 +883,12 @@
 				if((href = xmlnode_get_data(url))) {
 					purple_notify_uri(NULL, href);
 					g_free(href);
-				if(js->registration) {
-					js->gc->wants_to_die = TRUE;
-					if(account->registration_cb) /* succeeded, but we have no login info */
-						(account->registration_cb)(account, TRUE, account->registration_cb_user_data);
-					jabber_connection_schedule_close(js);
-				}
+					if(js->registration) {
+						js->gc->wants_to_die = TRUE;
+						if(account->registration_cb) /* succeeded, but we have no login info */
+							(account->registration_cb)(account, TRUE, account->registration_cb_user_data);
+						jabber_connection_schedule_close(js);
+					}
 					return;
 				}
 			}
@@ -987,14 +988,14 @@
 		purple_request_field_group_add_field(group, field);
 	}
 
-		if((y = xmlnode_get_child(query, "instructions")))
-			instructions = xmlnode_get_data(y);
+	if((y = xmlnode_get_child(query, "instructions")))
+		instructions = xmlnode_get_data(y);
 	else if(registered)
 		instructions = g_strdup(_("Please fill out the information below "
 					"to change your account registration."));
-		else
-			instructions = g_strdup(_("Please fill out the information below "
-						"to register your new account."));
+	else
+		instructions = g_strdup(_("Please fill out the information below "
+					"to register your new account."));
 
 	cbdata = g_new0(JabberRegisterCBData, 1);
 	cbdata->js = js;
@@ -1019,8 +1020,8 @@
 		g_free(title);
 	}
 
-		g_free(instructions);
-	}
+	g_free(instructions);
+}
 
 void jabber_register_start(JabberStream *js)
 {
@@ -1531,15 +1532,15 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1554,15 +1555,15 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1577,15 +1578,15 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1600,15 +1601,15 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -1623,15 +1624,15 @@
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
-			"tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
-			"tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
+			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
@@ -2239,6 +2240,44 @@
 	return PURPLE_CMD_RET_FAILED;
 }
 
+GList *jabber_attention_types(PurpleAccount *account)
+{
+	static GList *types = NULL;
+	PurpleAttentionType *attn;
+
+	if (!types) {
+		attn = g_new0(PurpleAttentionType, 1);
+		attn->name = _("Buzz");
+		attn->incoming_description = _("%s has buzzed you!");
+		attn->outgoing_description = _("Buzzing %s...");
+		types = g_list_append(types, attn);
+	}
+
+	return types;
+}
+
+gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code)
+{
+	PurpleConversation *conv;
+	char *error;
+	char *args[1];
+	PurpleCmdRet ret;
+
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, username, gc->account);
+
+	args[0] = (char *)username;
+
+	ret = jabber_cmd_buzz(conv, "buzz", args, &error, NULL);
+
+	if (ret == PURPLE_CMD_RET_FAILED) {
+		purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error ? error : "(NULL)");
+		return FALSE;
+	} else {
+		return TRUE;
+	}
+}
+
+
 gboolean jabber_offline_message(const PurpleBuddy *buddy)
 {
 	return TRUE;
--- a/libpurple/protocols/jabber/jabber.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Mon Oct 01 17:49:01 2007 +0000
@@ -66,6 +66,9 @@
 
 #define CAPS0115_NODE "http://pidgin.im/caps"
 
+/* Index into attention_types list */
+#define JABBER_BUZZ 0
+
 typedef enum {
 	JABBER_STREAM_OFFLINE,
 	JABBER_STREAM_CONNECTING,
@@ -231,6 +234,8 @@
 void jabber_register_gateway(JabberStream *js, const char *gateway);
 void jabber_register_account(PurpleAccount *account);
 void jabber_unregister_account(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data);
+gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code);
+GList *jabber_attention_types(PurpleAccount *account);
 void jabber_convo_closed(PurpleConnection *gc, const char *who);
 PurpleChat *jabber_find_blist_chat(PurpleAccount *account, const char *name);
 gboolean jabber_offline_message(const PurpleBuddy *buddy);
--- a/libpurple/protocols/jabber/jutil.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/jutil.h	Mon Oct 01 17:49:01 2007 +0000
@@ -22,8 +22,6 @@
 #ifndef _PURPLE_JABBER_JUTIL_H_
 #define _PURPLE_JABBER_JUTIL_H_
 
-#include "account.h"
-
 typedef struct _JabberID {
 	char *node;
 	char *domain;
--- a/libpurple/protocols/jabber/libxmpp.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Mon Oct 01 17:49:01 2007 +0000
@@ -113,10 +113,10 @@
 	jabber_prpl_send_raw,			/* send_raw */
 	jabber_roomlist_room_serialize, /* roomlist_room_serialize */
 	jabber_unregister_account,		/* unregister_user */
+	jabber_attention_types,                 /* attention_types */
+	jabber_send_attention,                  /* send_attention */
 
 	/* padding */
-	NULL,
-	NULL,
 	NULL
 };
 
--- a/libpurple/protocols/jabber/message.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/message.c	Mon Oct 01 17:49:01 2007 +0000
@@ -324,7 +324,7 @@
 	if(type) {
 		if(!strcmp(type, "normal"))
 			jm->type = JABBER_MESSAGE_NORMAL;
-	else if(!strcmp(type, "chat"))
+		else if(!strcmp(type, "chat"))
 			jm->type = JABBER_MESSAGE_CHAT;
 		else if(!strcmp(type, "groupchat"))
 			jm->type = JABBER_MESSAGE_GROUPCHAT;
--- a/libpurple/protocols/jabber/presence.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/presence.c	Mon Oct 01 17:49:01 2007 +0000
@@ -192,16 +192,11 @@
 		jabber_tune_set(js->gc, &tuneinfo);
 		
 		/* update old values */
-		if(js->old_artist)
-			g_free(js->old_artist);
-		if(js->old_title)
-			g_free(js->old_title);
-		if(js->old_source)
-			g_free(js->old_source);
-		if(js->old_uri)
-			g_free(js->old_uri);
-		if(js->old_track)
-			g_free(js->old_track);
+		g_free(js->old_artist);
+		g_free(js->old_title);
+		g_free(js->old_source);
+		g_free(js->old_uri);
+		g_free(js->old_track);
 		js->old_artist = g_strdup(artist);
 		js->old_title = g_strdup(title);
 		js->old_source = g_strdup(source);
--- a/libpurple/protocols/jabber/usernick.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/usernick.c	Mon Oct 01 17:49:01 2007 +0000
@@ -33,7 +33,7 @@
 	xmlnode *item = xmlnode_get_child(items, "item");
 	JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
 	xmlnode *nick;
-	const char *nickname = NULL;
+	char *nickname = NULL;
 	
 	/* ignore the tune of people not on our buddy list */
 	if (!buddy || !item)
@@ -43,8 +43,8 @@
 	if (!nick)
 		return;
 	nickname = xmlnode_get_data(nick);
-
 	serv_got_alias(js->gc, from, nickname);
+	g_free(nickname);
 }
 
 static void do_nick_set(JabberStream *js, const char *nick) {
@@ -64,7 +64,7 @@
 }
 
 static void do_nick_got_own_nick_cb(JabberStream *js, const char *from, xmlnode *items) {
-	const char *oldnickname = NULL;
+	char *oldnickname = NULL;
 	xmlnode *item = xmlnode_get_child(items,"item");
 	
 	if(item) {
@@ -77,6 +77,7 @@
 		_("This information is visible to all contacts on your contact list, so choose something appropriate."),
 		oldnickname, FALSE, FALSE, NULL, _("Set"), PURPLE_CALLBACK(do_nick_set), _("Cancel"), NULL,
 		purple_connection_get_account(js->gc), NULL, NULL, js);
+	g_free(oldnickname);
 }
 
 static void do_nick_set_nick(PurplePluginAction *action) {
--- a/libpurple/protocols/jabber/usertune.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/jabber/usertune.c	Mon Oct 01 17:49:01 2007 +0000
@@ -41,48 +41,61 @@
 	if (!buddy || !item)
 		return;
 	
-	tuneinfodata.artist = "";
-	tuneinfodata.title = "";
-	tuneinfodata.album = "";
-	tuneinfodata.track = "";
+	tuneinfodata.artist = NULL;
+	tuneinfodata.title = NULL;
+	tuneinfodata.album = NULL;
+	tuneinfodata.track = NULL;
 	tuneinfodata.time = -1;
-	tuneinfodata.url = "";
-	
+	tuneinfodata.url = NULL;
+
 	tune = xmlnode_get_child_with_namespace(item, "tune", "http://jabber.org/protocol/tune");
 	if (!tune)
 		return;
+	resource = jabber_buddy_find_resource(buddy, NULL);
+	if(!resource)
+		return; /* huh? */
 	for (tuneinfo = tune->child; tuneinfo; tuneinfo = tuneinfo->next) {
 		if (tuneinfo->type == XMLNODE_TYPE_TAG) {
 			if (!strcmp(tuneinfo->name, "artist")) {
-				if (tuneinfodata.artist[0] == '\0') /* only pick the first one */
+				if (tuneinfodata.artist == NULL) /* only pick the first one */
 					tuneinfodata.artist = xmlnode_get_data(tuneinfo);
 			} else if (!strcmp(tuneinfo->name, "length")) {
 				if (tuneinfodata.time == -1) {
 					char *length = xmlnode_get_data(tuneinfo);
 					if (length)
 						tuneinfodata.time = strtol(length, NULL, 10);
+					g_free(length);
 				}
 			} else if (!strcmp(tuneinfo->name, "source")) {
-				if (tuneinfodata.album[0] == '\0') /* only pick the first one */
+				if (tuneinfodata.album == NULL) /* only pick the first one */
 					tuneinfodata.album = xmlnode_get_data(tuneinfo);
 			} else if (!strcmp(tuneinfo->name, "title")) {
-				if (tuneinfodata.title[0] == '\0') /* only pick the first one */
+				if (tuneinfodata.title == NULL) /* only pick the first one */
 					tuneinfodata.title = xmlnode_get_data(tuneinfo);
 			} else if (!strcmp(tuneinfo->name, "track")) {
-				if (tuneinfodata.track[0] == '\0') /* only pick the first one */
+				if (tuneinfodata.track == NULL) /* only pick the first one */
 					tuneinfodata.track = xmlnode_get_data(tuneinfo);
 			} else if (!strcmp(tuneinfo->name, "uri")) {
-				if (tuneinfodata.url[0] == '\0') /* only pick the first one */
+				if (tuneinfodata.url == NULL) /* only pick the first one */
 					tuneinfodata.url = xmlnode_get_data(tuneinfo);
 			}
 		}
 	}
-	resource = jabber_buddy_find_resource(buddy, NULL);
-	if(!resource)
-		return; /* huh? */
 	status_id = jabber_buddy_state_get_status_id(resource->state);
 
-	purple_prpl_got_user_status(js->gc->account, from, status_id, PURPLE_TUNE_ARTIST, tuneinfodata.artist, PURPLE_TUNE_TITLE, tuneinfodata.title, PURPLE_TUNE_ALBUM, tuneinfodata.album, PURPLE_TUNE_TRACK, tuneinfodata.track, PURPLE_TUNE_TIME, tuneinfodata.time, PURPLE_TUNE_URL, tuneinfodata.url, NULL);
+	purple_prpl_got_user_status(js->gc->account, from, status_id,
+			PURPLE_TUNE_ARTIST, tuneinfodata.artist,
+			PURPLE_TUNE_TITLE, tuneinfodata.title,
+			PURPLE_TUNE_ALBUM, tuneinfodata.album,
+			PURPLE_TUNE_TRACK, tuneinfodata.track,
+			PURPLE_TUNE_TIME, tuneinfodata.time,
+			PURPLE_TUNE_URL, tuneinfodata.url, NULL);
+
+	g_free(tuneinfodata.artist);
+	g_free(tuneinfodata.title);
+	g_free(tuneinfodata.album);
+	g_free(tuneinfodata.track);
+	g_free(tuneinfodata.url);
 }
 
 void jabber_tune_init(void) {
--- a/libpurple/protocols/msn/directconn.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Mon Oct 01 17:49:01 2007 +0000
@@ -405,6 +405,15 @@
 	}
 }
 
+static void
+directconn_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+	if (error_message)
+		purple_debug_error("msn", "Error making direct connection: %s\n", error_message);
+
+	connect_cb(data, source, PURPLE_INPUT_READ);
+}
+
 gboolean
 msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port)
 {
@@ -424,14 +433,9 @@
 #endif
 
 	directconn->connect_data = purple_proxy_connect(NULL, session->account,
-			host, port, connect_cb, directconn);
+			host, port, directconn_connect_cb, directconn);
 
-	if (directconn->connect_data != NULL)
-	{
-		return TRUE;
-	}
-	else
-		return FALSE;
+	return (directconn->connect_data != NULL);
 }
 
 #if 0
--- a/libpurple/protocols/msn/httpconn.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/msn/httpconn.c	Mon Oct 01 17:49:01 2007 +0000
@@ -663,6 +663,8 @@
 	httpconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN);
 	httpconn->tx_handler = 0;
 
+	httpconn->fd = -1;
+
 	return httpconn;
 }
 
--- a/libpurple/protocols/msn/msn.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Mon Oct 01 17:49:01 2007 +0000
@@ -2196,8 +2196,7 @@
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
 
-	/* TODO: Mark translatable as soon as 2.2.0 is released and we're not string frozen */
-	option = purple_account_option_string_new("HTTP Method Server",
+	option = purple_account_option_string_new(_("HTTP Method Server"),
 										  "http_method_server", MSN_HTTPCONN_SERVER);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
--- a/libpurple/protocols/msn/servconn.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Mon Oct 01 17:49:01 2007 +0000
@@ -53,6 +53,8 @@
 	servconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN);
 	servconn->tx_handler = 0;
 
+	servconn->fd = -1;
+
 	return servconn;
 }
 
--- a/libpurple/protocols/msn/slp.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Mon Oct 01 17:49:01 2007 +0000
@@ -343,7 +343,7 @@
 		if (xfer)
 		{
 			bin = (char *)purple_base64_decode(context, &bin_len);
-			file_size = GUINT32_FROM_LE(*(gsize *)(bin + 2));
+			file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8));
 
 			uni_name = (gunichar2 *)(bin + 20);
 			while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) {
--- a/libpurple/protocols/msn/switchboard.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Mon Oct 01 17:49:01 2007 +0000
@@ -947,25 +947,14 @@
 nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
 	MsnSwitchBoard *swboard;
-	char *username, *str;
 	PurpleAccount *account;
-	PurpleBuddy *buddy;
 	const char *user;
 
-	str = NULL;
-
 	swboard = cmdproc->data;
 	account = cmdproc->session->account;
 	user = msg->remote_user;
 
-	if ((buddy = purple_find_buddy(account, user)) != NULL)
-		username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
-	else
-		username = g_markup_escape_text(user, -1);
-
-	serv_got_attention(account->gc, buddy->name, MSN_NUDGE);
-	g_free(username);
-	g_free(str);
+	serv_got_attention(account->gc, user, MSN_NUDGE);
 }
 
 /**************************************************************************
--- a/libpurple/protocols/myspace/markup.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/markup.c	Mon Oct 01 17:49:01 2007 +0000
@@ -258,8 +258,8 @@
 	}
 
 
-	*begin = gs_begin->str;
-	*end = gs_end->str;
+	*begin = g_string_free(gs_begin,FALSE);
+	*end = g_string_free(gs_end, FALSE);
 }
 
 /** Convert a msim markup color to a color suitable for libpurple.
@@ -426,13 +426,14 @@
 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, 
 		gchar **end)
 {
-	/* TODO: Coalesce nested tags into one <f> tag!
-	 * Currently, the 's' value will be overwritten when b/i/u is nested
-	 * within another one, and only the inner-most formatting will be 
-	 * applied to the text. */
-	if (!purple_utf8_strcasecmp(root->name, "root")) {
-		*begin = g_strdup("");
-		*end = g_strdup("");
+    if (!purple_utf8_strcasecmp(root->name, "root") ||
+        !purple_utf8_strcasecmp(root->name, "html")) {
+        *begin = g_strdup("");
+        *end = g_strdup("");
+    /* TODO: Coalesce nested tags into one <f> tag!
+     * Currently, the 's' value will be overwritten when b/i/u is nested
+     * within another one, and only the inner-most formatting will be 
+     * applied to the text. */
 	} else if (!purple_utf8_strcasecmp(root->name, "b")) {
 		*begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD);
 		*end = g_strdup("</f>");
@@ -503,8 +504,20 @@
 
 		/* TODO: color (bg uses <body>), emoticons */
 	} else {
-		*begin = g_strdup_printf("[%s]", root->name);
-		*end = g_strdup_printf("[/%s]", root->name);
+        gchar *err;
+
+#ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS
+        *begin = g_strdup_printf("[%s]", root->name);
+        *end = g_strdup_printf("[/%s]", root->name);
+#else
+        *begin = g_strdup("");
+        *end = g_strdup("");
+#endif
+
+        err = g_strdup_printf("html_tag_to_msim_markup: unrecognized "
+                "HTML tag %s was sent by the IM client; ignoring", (root->name ? root->name : "(NULL)"));
+        msim_unrecognized(NULL, NULL, err);
+        g_free(err);
 	}
 }
 
@@ -582,7 +595,7 @@
 	purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n",
 			(final && final->str) ? final->str : "(NULL)");
 
-	return final->str;
+	return g_string_free(final, FALSE); 
 }
 
 /** Convert XML to something based on MSIM_XMLNODE_CONVERT. */
--- a/libpurple/protocols/myspace/markup.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/markup.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,27 +1,27 @@
-/* MySpaceIM Protocol Plugin - markup
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * 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 _MYSPACE_MARKUP_H
-#define _MYSPACE_MARKUP_H
-
-/* High-level msim markup <=> Purple html conversion functions. */
-gchar *msim_markup_to_html(MsimSession *, const gchar *raw);
-gchar *html_to_msim_markup(MsimSession *, const gchar *raw);
-
-#endif /* !_MYSPACE_MARKUP_H */
+/* MySpaceIM Protocol Plugin - markup
+ *
+ * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ *
+ * 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 _MYSPACE_MARKUP_H
+#define _MYSPACE_MARKUP_H
+
+/* High-level msim markup <=> Purple html conversion functions. */
+gchar *msim_markup_to_html(MsimSession *, const gchar *raw);
+gchar *html_to_msim_markup(MsimSession *, const gchar *raw);
+
+#endif /* !_MYSPACE_MARKUP_H */
--- a/libpurple/protocols/myspace/message.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/message.c	Mon Oct 01 17:49:01 2007 +0000
@@ -80,7 +80,7 @@
 	purple_debug_info("msim", "msim_escape: msg=%s, ret=%s\n", msg, gs->str);
 #endif
 
-	return gs->str;
+	return g_string_free(gs, FALSE);
 }
 
 /**
@@ -120,7 +120,7 @@
 	purple_debug_info("msim", "msim_unescape: msg=%s, ret=%s\n", msg, gs->str);
 #endif
 
-	return gs->str;
+	return g_string_free(gs, FALSE);
 }
 
 /** Create a new MsimMessage. 
@@ -691,7 +691,7 @@
 				++i;
 			}
 			
-			string = gs->str;
+			string = g_string_free(gs, FALSE);
 			break;
 
 		default:
@@ -798,7 +798,7 @@
 					g_string_append(gs, "|");
 			}
 			
-			return gs->str;
+			return g_string_free(gs, FALSE);
 
 		default:
 			purple_debug_info("msim", "field %s, unknown type %d\n", 
@@ -1337,9 +1337,7 @@
 			gs = (GString *)elem->data;
 
 			/* Duplicate data, so caller can g_free() it. */
-			*binary_data = g_new0(char, gs->len);
-			memcpy(*binary_data, gs->str, gs->len);
-
+			*binary_data = g_memdup(gs->str, gs->len);
 			*binary_length = gs->len;
 
 			return TRUE;
--- a/libpurple/protocols/myspace/myspace.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Mon Oct 01 17:49:01 2007 +0000
@@ -43,7 +43,7 @@
 
 static int msim_send_really_raw(PurpleConnection *gc, const char *buf, int total_bytes);
 static gboolean msim_login_challenge(MsimSession *session, MsimMessage *msg);
-static const gchar *msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE], const gchar *email, const gchar *password, guint *response_len);
+static gchar *msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE], const gchar *email, const gchar *password, guint *response_len);
 
 static gboolean msim_incoming_bm_record_cv(MsimSession *session, MsimMessage *msg);
 static gboolean msim_incoming_bm(MsimSession *session, MsimMessage *msg);
@@ -331,10 +331,11 @@
 msim_login_challenge(MsimSession *session, MsimMessage *msg) 
 {
 	PurpleAccount *account;
-	const gchar *response;
+	gchar *response;
 	guint response_len;
 	gchar *nc;
 	gsize nc_len;
+	gboolean ret;
 
 	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
@@ -362,11 +363,11 @@
 
 	g_free(nc);
 
-	return msim_send(session, 
+	ret = msim_send(session,
 			"login2", MSIM_TYPE_INTEGER, MSIM_AUTH_ALGORITHM,
 			/* This is actually user's email address. */
 			"username", MSIM_TYPE_STRING, g_strdup(account->username),
-			/* GString and gchar * response will be freed in msim_msg_free() in msim_send(). */
+			/* GString will be freed in msim_msg_free() in msim_send(). */
 			"response", MSIM_TYPE_BINARY, g_string_new_len(response, response_len),
 			"clientver", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION,
 			"langid", MSIM_TYPE_INTEGER, MSIM_LANGUAGE_ID_ENGLISH,
@@ -375,6 +376,10 @@
 			"status", MSIM_TYPE_INTEGER, 100,
 			"id", MSIM_TYPE_INTEGER, 1,
 			NULL);
+
+	g_free(response);
+
+	return ret;
 }
 
 /**
@@ -388,7 +393,7 @@
  * @return Binary login challenge response, ready to send to the server. 
  * Must be g_free()'d when finished. NULL if error.
  */
-static const gchar *
+static gchar *
 msim_compute_login_response(const gchar nonce[2 * NONCE_SIZE], 
 		const gchar *email, const gchar *password, guint *response_len)
 {
@@ -453,6 +458,7 @@
 	purple_cipher_context_append(key_context, hash_pw, HASH_SIZE);
 	purple_cipher_context_append(key_context, (guchar *)(nonce + NONCE_SIZE), NONCE_SIZE);
 	purple_cipher_context_digest(key_context, sizeof(key), key, NULL);
+	purple_cipher_context_destroy(key_context);
 
 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
 	purple_debug_info("msim", "key = ");
@@ -485,8 +491,13 @@
 	purple_cipher_context_encrypt(rc4, (const guchar *)data, 
 			data_len, data_out, &data_out_len);
 	purple_cipher_context_destroy(rc4);
-
-	g_assert(data_out_len == data_len);
+	g_free(data);
+
+	if (data_out_len != data_len) {
+		purple_debug_info("msim", "msim_compute_login_response: "
+				"data length mismatch: %d != %d\n",
+				data_out_len, data_len);
+	}
 
 #ifdef MSIM_DEBUG_LOGIN_CHALLENGE
 	purple_debug_info("msim", "response=<%s>\n", data_out);
@@ -494,7 +505,7 @@
 
 	*response_len = data_out_len;
 
-	return (const gchar *)data_out;
+	return (gchar *)data_out;
 }
 
 /**
@@ -1029,7 +1040,7 @@
 	PurpleStatusType *type;
 	MsimSession *session;
 	guint status_code;
-	const gchar *statstring;
+	gchar *statstring;
 
 	session = (MsimSession *)account->gc->proto_data;
 
@@ -1063,7 +1074,7 @@
 			break;
 	}
 
-	statstring = purple_status_get_attr_string(status, "message");
+	statstring = (gchar *)purple_status_get_attr_string(status, "message");
 
 	if (!statstring) {
 		statstring = "";
@@ -1072,7 +1083,7 @@
 	/* Status strings are plain text. */
 	statstring = purple_markup_strip_html(statstring);
 
-	msim_set_status_code(session, status_code, g_strdup(statstring));
+	msim_set_status_code(session, status_code, statstring);
 }
 
 /** Go idle. */
@@ -1203,7 +1214,7 @@
 		if (uid == wanted_uid)
 		{
 			ret = g_strdup(name);
-            break;
+			break;
 		}
 	}
 
@@ -1295,7 +1306,6 @@
 msim_check_inbox_cb(MsimSession *session, MsimMessage *reply, gpointer data)
 {
 	MsimMessage *body;
-	GString *notification;
 	guint old_inbox_status;
 	guint i, n;
 	const gchar *froms[5], *tos[5], *urls[5], *subjects[5];
@@ -1329,8 +1339,6 @@
 	body = msim_msg_get_dictionary(reply, "body");
 	g_return_if_fail(body != NULL);
 
-	notification = g_string_new("");
-
 	old_inbox_status = session->inbox_status;
 
 	n = 0;
@@ -1860,6 +1868,7 @@
 		purple_blist_add_buddy(buddy, NULL, NULL, NULL);
 
 		user = msim_get_user_from_buddy(buddy);
+		/* TODO: free user. memory leak? */
 
 		/* All buddies on list should have 'uid' integer associated with them. */
 		purple_blist_node_set_int(&buddy->node, "UserID", msim_msg_get_integer(msg, "f"));
@@ -2856,7 +2865,7 @@
 }
 
 /** Callbacks called by Purple, to access this plugin. */
-PurplePluginProtocolInfo prpl_info = {
+static PurplePluginProtocolInfo prpl_info = {
 	/* options */
 	  OPT_PROTO_USE_POINTSIZE        /* specify font size in sane point size */
 	| OPT_PROTO_MAIL_CHECK,
@@ -3004,7 +3013,7 @@
 	msg = msim_msg_new(NULL);      /* Create a new, empty message. */
 
 	/* Append some new elements. */
-	msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len(g_strdup("XXX"), 3));
+	msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len("XXX", 3));
 	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v1"));
 	msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(42));
 	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v43"));
--- a/libpurple/protocols/myspace/myspace.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.h	Mon Oct 01 17:49:01 2007 +0000
@@ -67,6 +67,10 @@
 /*#define MSIM_DEBUG_LOGIN_CHALLENGE*/
 /*#define MSIM_DEBUG_RXBUF            */
 
+/* Encode unknown HTML tags from IM clients in messages as [tag], instead of 
+ * ignoring. Useful for debugging */
+/*#define MSIM_MARKUP_SHOW_UNKNOWN_TAGS  */
+
 /* Define to cause init_plugin() to run some tests and print
  * the results to the Purple debug log, then exit. Useful to 
  * run with 'pidgin -d' to see the output. Don't define if
--- a/libpurple/protocols/myspace/session.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/session.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,95 +1,95 @@
-/* MySpaceIM Protocol Plugin, session
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * 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 "myspace.h"
-
-/* Session methods */
-
-/**
- * Create a new MSIM session.
- *
- * @param acct The account to create the session from.
- *
- * @return Pointer to a new session. Free with msim_session_destroy.
- */
-MsimSession *
-msim_session_new(PurpleAccount *acct)
-{
-	MsimSession *session;
-
-	g_return_val_if_fail(acct != NULL, NULL);
-
-	session = g_new0(MsimSession, 1);
-
-	session->magic = MSIM_SESSION_STRUCT_MAGIC;
-	session->account = acct;
-	session->gc = purple_account_get_connection(acct);
-	session->sesskey = 0;
-	session->userid = 0;
-	session->username = NULL;
-	session->fd = -1;
-
-	/* TODO: Remove. */
-	session->user_lookup_cb = g_hash_table_new_full(g_direct_hash, 
-			g_direct_equal, NULL, NULL);  /* do NOT free function pointers! (values) */
-	session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash, 
-			g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are,
-											 they could be integers inside gpointers
-											 or strings, so I don't freed them.
-											 Figure this out, once free cache. */
-
-	/* Created in msim_process_server_info() */
-	session->server_info = NULL;
-
-	session->rxoff = 0;
-	session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE);
-	session->next_rid = 1;
-	session->last_comm = time(NULL);
-	session->inbox_status = 0;
-	
-	return session;
-}
-
-/**
- * Free a session.
- *
- * @param session The session to destroy.
- */
-void 
-msim_session_destroy(MsimSession *session)
-{
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-	
-	session->magic = -1;
-
-	g_free(session->rxbuf);
-	g_free(session->username);
-
-	/* TODO: Remove. */
-	g_hash_table_destroy(session->user_lookup_cb);
-	g_hash_table_destroy(session->user_lookup_cb_data);
-
-	if (session->server_info) {
-		msim_msg_free(session->server_info);
-	}
-	
-	g_free(session);
-}
-
+/* MySpaceIM Protocol Plugin, session
+ *
+ * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ *
+ * 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 "myspace.h"
+
+/* Session methods */
+
+/**
+ * Create a new MSIM session.
+ *
+ * @param acct The account to create the session from.
+ *
+ * @return Pointer to a new session. Free with msim_session_destroy.
+ */
+MsimSession *
+msim_session_new(PurpleAccount *acct)
+{
+	MsimSession *session;
+
+	g_return_val_if_fail(acct != NULL, NULL);
+
+	session = g_new0(MsimSession, 1);
+
+	session->magic = MSIM_SESSION_STRUCT_MAGIC;
+	session->account = acct;
+	session->gc = purple_account_get_connection(acct);
+	session->sesskey = 0;
+	session->userid = 0;
+	session->username = NULL;
+	session->fd = -1;
+
+	/* TODO: Remove. */
+	session->user_lookup_cb = g_hash_table_new_full(g_direct_hash, 
+			g_direct_equal, NULL, NULL);  /* do NOT free function pointers! (values) */
+	session->user_lookup_cb_data = g_hash_table_new_full(g_direct_hash, 
+			g_direct_equal, NULL, NULL);/* TODO: we don't know what the values are,
+											 they could be integers inside gpointers
+											 or strings, so I don't freed them.
+											 Figure this out, once free cache. */
+
+	/* Created in msim_process_server_info() */
+	session->server_info = NULL;
+
+	session->rxoff = 0;
+	session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE);
+	session->next_rid = 1;
+	session->last_comm = time(NULL);
+	session->inbox_status = 0;
+	
+	return session;
+}
+
+/**
+ * Free a session.
+ *
+ * @param session The session to destroy.
+ */
+void 
+msim_session_destroy(MsimSession *session)
+{
+	g_return_if_fail(MSIM_SESSION_VALID(session));
+	
+	session->magic = -1;
+
+	g_free(session->rxbuf);
+	g_free(session->username);
+
+	/* TODO: Remove. */
+	g_hash_table_destroy(session->user_lookup_cb);
+	g_hash_table_destroy(session->user_lookup_cb_data);
+
+	if (session->server_info) {
+		msim_msg_free(session->server_info);
+	}
+	
+	g_free(session);
+}
+
--- a/libpurple/protocols/myspace/session.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/session.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,57 +1,57 @@
-/* MySpaceIM Protocol Plugin, session
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * 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 _MYSPACE_SESSION_H
-#define _MYSPACE_SESSION_H
-
-/* Random number in every MsimSession, to ensure it is valid. */
-#define MSIM_SESSION_STRUCT_MAGIC       0xe4a6752b
-
-/* Everything needed to keep track of a session (proto_data field in PurpleConnection) */
-typedef struct _MsimSession
-{
-	guint magic;                        /**< MSIM_SESSION_STRUCT_MAGIC */
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	guint sesskey;                      /**< Session key from server */
-	guint userid;                       /**< This user's numeric user ID */
-	gchar *username;                    /**< This user's unique username */
-	gint fd;                            /**< File descriptor to/from server */
-
-	/* TODO: Remove. */
-	GHashTable *user_lookup_cb;         /**< Username -> userid lookup callback */
-	GHashTable *user_lookup_cb_data;    /**< Username -> userid lookup callback data */
-
-	MsimMessage *server_info;           /**< Parameters from server */
-
-	gchar *rxbuf;                       /**< Receive buffer */
-	guint rxoff;                        /**< Receive buffer offset */
-	guint next_rid;                     /**< Next request/response ID */
-	time_t last_comm;                   /**< Time received last communication */
-	guint inbox_status;                 /**< Bit field of inbox notifications */
-} MsimSession;
-
-/* Check if an MsimSession is valid */
-#define MSIM_SESSION_VALID(s) (session != NULL && session->magic == MSIM_SESSION_STRUCT_MAGIC)
-
-
-MsimSession *msim_session_new(PurpleAccount *acct);
-void msim_session_destroy(MsimSession *session);
-
-#endif /* !_MYSPACE_SESSION_H */
+/* MySpaceIM Protocol Plugin, session
+ *
+ * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ *
+ * 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 _MYSPACE_SESSION_H
+#define _MYSPACE_SESSION_H
+
+/* Random number in every MsimSession, to ensure it is valid. */
+#define MSIM_SESSION_STRUCT_MAGIC       0xe4a6752b
+
+/* Everything needed to keep track of a session (proto_data field in PurpleConnection) */
+typedef struct _MsimSession
+{
+	guint magic;                        /**< MSIM_SESSION_STRUCT_MAGIC */
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	guint sesskey;                      /**< Session key from server */
+	guint userid;                       /**< This user's numeric user ID */
+	gchar *username;                    /**< This user's unique username */
+	gint fd;                            /**< File descriptor to/from server */
+
+	/* TODO: Remove. */
+	GHashTable *user_lookup_cb;         /**< Username -> userid lookup callback */
+	GHashTable *user_lookup_cb_data;    /**< Username -> userid lookup callback data */
+
+	MsimMessage *server_info;           /**< Parameters from server */
+
+	gchar *rxbuf;                       /**< Receive buffer */
+	guint rxoff;                        /**< Receive buffer offset */
+	guint next_rid;                     /**< Next request/response ID */
+	time_t last_comm;                   /**< Time received last communication */
+	guint inbox_status;                 /**< Bit field of inbox notifications */
+} MsimSession;
+
+/* Check if an MsimSession is valid */
+#define MSIM_SESSION_VALID(s) (session != NULL && session->magic == MSIM_SESSION_STRUCT_MAGIC)
+
+
+MsimSession *msim_session_new(PurpleAccount *acct);
+void msim_session_destroy(MsimSession *session);
+
+#endif /* !_MYSPACE_SESSION_H */
--- a/libpurple/protocols/myspace/user.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/user.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,437 +1,454 @@
-/* MySpaceIM Protocol Plugin, header file
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * 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 "myspace.h"
-
-static void msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user);
-static gchar *msim_format_now_playing(gchar *band, gchar *song);
-static void msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text,
-		gsize len, const gchar *error_message);
-
-/** Format the "now playing" indicator, showing the artist and song.
- * @return Return a new string (must be g_free()'d), or NULL.
- */
-static gchar *
-msim_format_now_playing(gchar *band, gchar *song)
-{
-	if ((band && strlen(band)) || (song && strlen(song))) {
-		return g_strdup_printf("%s - %s",
-			(band && strlen(band)) ? band : "Unknown Artist",
-			(song && strlen(song)) ? song : "Unknown Song");
-	} else {
-		return NULL;
-	}
-}
-/** Get the MsimUser from a PurpleBuddy, creating it if needed. */
-MsimUser *
-msim_get_user_from_buddy(PurpleBuddy *buddy)
-{
-	MsimUser *user;
-
-	if (!buddy) {
-		return NULL;
-	}
-
-	if (!buddy->proto_data) {
-		/* No MsimUser for this buddy; make one. */
-
-		/* TODO: where is this freed? */
-		user = g_new0(MsimUser, 1);
-		user->buddy = buddy;
-		buddy->proto_data = (gpointer)user;
-	} 
-
-	user = (MsimUser *)(buddy->proto_data);
-
-	return user;
-}
-
-/** Find and return an MsimUser * representing a user on the buddy list, or NULL. */
-MsimUser *
-msim_find_user(MsimSession *session, const gchar *username)
-{
-	PurpleBuddy *buddy;
-	MsimUser *user;
-
-	buddy = purple_find_buddy(session->account, username);
-	if (!buddy) {
-		return NULL;
-	}
-
-	user = msim_get_user_from_buddy(buddy);
-
-	return user;
-}
-
-/** Append user information to a PurpleNotifyUserInfo, given an MsimUser. 
- * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile.
- */
-void
-msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full)
-{
-	gchar *str;
-	guint uid;
-	guint cv;
-
-	/* Useful to identify the account the tooltip refers to. 
-	 *  Other prpls show this. */
-	if (user->username) {
-		purple_notify_user_info_add_pair(user_info, _("User"), user->username);
-	}
-
-	uid = purple_blist_node_get_int(&user->buddy->node, "UserID");
-
-	if (full) {
-		/* TODO: link to username, if available */
-		purple_notify_user_info_add_pair(user_info, _("Profile"),
-				g_strdup_printf("<a href=\"http://myspace.com/%d\">http://myspace.com/%d</a>",
-					uid, uid));
-	}
-
-
-	/* a/s/l...the vitals */
-	if (user->age) {
-		purple_notify_user_info_add_pair(user_info, _("Age"),
-				g_strdup_printf("%d", user->age));
-	}
-
-	if (user->gender && strlen(user->gender)) {
-		purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender);
-	}
-
-	if (user->location && strlen(user->location)) {
-		purple_notify_user_info_add_pair(user_info, _("Location"), user->location);
-	}
-
-	/* Other information */
-	if (user->headline && strlen(user->headline)) {
-		purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline);
-	}
-
-	str = msim_format_now_playing(user->band_name, user->song_name);
-	if (str && strlen(str)) {
-		purple_notify_user_info_add_pair(user_info, _("Song"), str);
-	}
-
-	/* Note: total friends only available if looked up by uid, not username. */
-	if (user->total_friends) {
-		purple_notify_user_info_add_pair(user_info, _("Total Friends"),
-			g_strdup_printf("%d", user->total_friends));
-	}
-
-	if (full) {
-		/* Client information */
-
-		str = user->client_info;
-		cv = user->client_cv;
-
-		if (str && cv != 0) {
-			purple_notify_user_info_add_pair(user_info, _("Client Version"),
-					g_strdup_printf("%s (build %d)", str, cv));
-		} else if (str) {
-			purple_notify_user_info_add_pair(user_info, _("Client Version"),
-					g_strdup(str));
-		} else if (cv) {
-			purple_notify_user_info_add_pair(user_info, _("Client Version"),
-					g_strdup_printf("Build %d", cv));
-		}
-	}
-}
-
-/** Store a field of information about a buddy. */
-void 
-msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user)
-{
-	if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) {
-		/* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */
-		if (user->buddy)
-		{
-			purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name);
-			purple_blist_node_set_int(&user->buddy->node, "UserID", atol(value_str));
-		}
-		/* Need to store in MsimUser, too? What if not on blist? */
-	} else if (g_str_equal(key_str, "Age")) {
-		user->age = atol(value_str);
-	} else if (g_str_equal(key_str, "Gender")) {
-		user->gender = g_strdup(value_str);
-	} else if (g_str_equal(key_str, "Location")) {
-		user->location = g_strdup(value_str);
-	} else if (g_str_equal(key_str, "TotalFriends")) {
-		user->total_friends = atol(value_str);
-	} else if (g_str_equal(key_str, "DisplayName")) {
-		user->display_name = g_strdup(value_str);
-	} else if (g_str_equal(key_str, "BandName")) {
-		user->band_name = g_strdup(value_str);
-	} else if (g_str_equal(key_str, "SongName")) {
-		user->song_name = g_strdup(value_str);
-	} else if (g_str_equal(key_str, "UserName") || g_str_equal(key_str, "IMName") || g_str_equal(key_str, "NickName")) {
-		/* Ignore because PurpleBuddy knows this already */
-		;
-	} else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) {
-		const gchar *previous_url;
-
-		user->image_url = g_strdup(value_str);
-
-		/* Instead of showing 'no photo' picture, show nothing. */
-		if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif"))
-		{
-			purple_buddy_icons_set_for_user(user->buddy->account,
-				user->buddy->name,
-				NULL, 0, NULL);
-			return;
-		}
-	
-		/* TODO: use ETag for checksum */
-		previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy);
-
-		/* Only download if URL changed */
-		if (!previous_url || !g_str_equal(previous_url, user->image_url)) {
-			purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
-		}
-	} else if (g_str_equal(key_str, "LastImageUpdated")) {
-		/* TODO: use somewhere */
-		user->last_image_updated = atol(value_str);
-	} else if (g_str_equal(key_str, "Headline")) {
-		user->headline = g_strdup(value_str);
-	} else {
-		/* TODO: other fields in MsimUser */
-		gchar *msg;
-
-		msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s",
-				key_str, value_str);
-
-		msim_unrecognized(NULL, NULL, msg);
-
-		g_free(msg);
-	}
-}
-
-/** Save buddy information to the buddy list from a user info reply message.
- *
- * @param session
- * @param msg The user information reply, with any amount of information.
- * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data.
- *
- * Variable information is saved to the passed MsimUser structure. Permanent
- * information (UserID) is stored in the blist node of the buddy list (and
- * ends up in blist.xml, persisted to disk) if it exists.
- *
- * If the function has no buddy information, this function
- * is a no-op (and returns FALSE).
- *
- */
-gboolean 
-msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user)
-{
-	gchar *username;
-	MsimMessage *body, *body_node;
-
-	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
-	g_return_val_if_fail(msg != NULL, FALSE);
-
-	body = msim_msg_get_dictionary(msg, "body");
-	if (!body) {
-		return FALSE;
-	}
-
-	username = msim_msg_get_string(body, "UserName");
-
-	if (!username) {
-		purple_debug_info("msim", 
-			"msim_process_reply: not caching body, no UserName\n");
-		msim_msg_free(body);
-		g_free(username);
-		return FALSE;
-	}
-	
-	/* Null user = find and store in PurpleBuddy's proto_data */
-	if (!user) {
-		user = msim_find_user(session, username);
-		if (!user) {
-			msim_msg_free(body);
-			g_free(username);
-			return FALSE;
-		}
-	}
-
-	/* TODO: make looping over MsimMessage's easier. */
-	for (body_node = body; 
-		body_node != NULL; 
-		body_node = msim_msg_get_next_element_node(body_node))
-	{
-		const gchar *key_str;
-		gchar *value_str;
-		MsimMessageElement *elem;
-
-		elem = (MsimMessageElement *)body_node->data;
-		key_str = elem->name;
-
-		value_str = msim_msg_get_string_from_element(elem);
-		msim_store_user_info_each(key_str, value_str, user);
-		g_free(value_str);
-	}
-
-	if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN &&
-		msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) {
-		/* TODO: do something with our own IM info, if we need it for some
-		 * specific purpose. Otherwise it is available on the buddy list,
-		 * if the user has themselves as their own buddy. 
-		 *
-		 * However, much of the info is already available in MsimSession,
-		 * stored in msim_we_are_logged_on(). */
-	} else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN &&
-			msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) {
-		/* TODO: same as above, but for MySpace info. */
-	}
-
-	msim_msg_free(body);
-
-	return TRUE;
-}
-
-/**
- * Asynchronously lookup user information, calling callback when receive result.
- *
- * @param session
- * @param user The user id, email address, or username. Not freed.
- * @param cb Callback, called with user information when available.
- * @param data An arbitray data pointer passed to the callback.
- */
-/* TODO: change to not use callbacks */
-void 
-msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data)
-{
-	MsimMessage *body;
-	gchar *field_name;
-	guint rid, cmd, dsn, lid;
-
-	g_return_if_fail(MSIM_SESSION_VALID(session));
-	g_return_if_fail(user != NULL);
-	/* Callback can be null to not call anything, just lookup & store information. */
-	/*g_return_if_fail(cb != NULL);*/
-
-	purple_debug_info("msim", "msim_lookup_userid: "
-			"asynchronously looking up <%s>\n", user);
-
-	msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data);
-
-	/* Setup callback. Response will be associated with request using 'rid'. */
-	rid = msim_new_reply_callback(session, cb, data);
-
-	/* Send request */
-
-	cmd = MSIM_CMD_GET;
-
-	if (msim_is_userid(user)) {
-		field_name = "UserID";
-		dsn = MG_MYSPACE_INFO_BY_ID_DSN; 
-		lid = MG_MYSPACE_INFO_BY_ID_LID; 
-	} else if (msim_is_email(user)) {
-		field_name = "Email";
-		dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
-		lid = MG_MYSPACE_INFO_BY_STRING_LID;
-	} else {
-		field_name = "UserName";
-		dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
-		lid = MG_MYSPACE_INFO_BY_STRING_LID;
-	}
-
-	body = msim_msg_new(
-			field_name, MSIM_TYPE_STRING, g_strdup(user),
-			NULL);
-
-	g_return_if_fail(msim_send(session,
-			"persist", MSIM_TYPE_INTEGER, 1,
-			"sesskey", MSIM_TYPE_INTEGER, session->sesskey,
-			"cmd", MSIM_TYPE_INTEGER, 1,
-			"dsn", MSIM_TYPE_INTEGER, dsn,
-			"uid", MSIM_TYPE_INTEGER, session->userid,
-			"lid", MSIM_TYPE_INTEGER, lid,
-			"rid", MSIM_TYPE_INTEGER, rid,
-			"body", MSIM_TYPE_DICTIONARY, body,
-			NULL));
-} 
-
-
-/**
- * Check if a string is a userid (all numeric).
- *
- * @param user The user id, email, or name.
- *
- * @return TRUE if is userid, FALSE if not.
- */
-gboolean 
-msim_is_userid(const gchar *user)
-{
-	g_return_val_if_fail(user != NULL, FALSE);
-
-	return strspn(user, "0123456789") == strlen(user);
-}
-
-/**
- * Check if a string is an email address (contains an @).
- *
- * @param user The user id, email, or name.
- *
- * @return TRUE if is an email, FALSE if not.
- *
- * This function is not intended to be used as a generic
- * means of validating email addresses, but to distinguish
- * between a user represented by an email address from
- * other forms of identification.
- */ 
-gboolean 
-msim_is_email(const gchar *user)
-{
-	g_return_val_if_fail(user != NULL, FALSE);
-
-	return strchr(user, '@') != NULL;
-}
-
-
-/** Callback for when a buddy icon finished being downloaded. */
-static void
-msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data,
-		gpointer user_data,
-		const gchar *url_text,
-		gsize len,
-		const gchar *error_message)
-{
-	MsimUser *user;
-
-	user = (MsimUser *)user_data;
-
-	purple_debug_info("msim_downloaded_buddy_icon",
-			"Downloaded %d bytes\n", len);
-
-	if (!url_text) {
-		purple_debug_info("msim_downloaded_buddy_icon",
-				"failed to download icon for %s",
-				user->buddy->name);
-		return;
-	}
-
-	purple_buddy_icons_set_for_user(user->buddy->account,
-			user->buddy->name,
-			g_memdup((gchar *)url_text, len), len, 
-			/* Use URL itself as buddy icon "checksum" (TODO: ETag) */
-			user->image_url);		/* checksum */
-}
-
-
+/* MySpaceIM Protocol Plugin, header file
+ *
+ * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ *
+ * 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 "myspace.h"
+
+static void msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user);
+static gchar *msim_format_now_playing(gchar *band, gchar *song);
+static void msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text,
+		gsize len, const gchar *error_message);
+
+/** Format the "now playing" indicator, showing the artist and song.
+ * @return Return a new string (must be g_free()'d), or NULL.
+ */
+static gchar *
+msim_format_now_playing(gchar *band, gchar *song)
+{
+	if ((band && strlen(band)) || (song && strlen(song))) {
+		return g_strdup_printf("%s - %s",
+			(band && strlen(band)) ? band : "Unknown Artist",
+			(song && strlen(song)) ? song : "Unknown Song");
+	} else {
+		return NULL;
+	}
+}
+/** Get the MsimUser from a PurpleBuddy, creating it if needed. */
+MsimUser *
+msim_get_user_from_buddy(PurpleBuddy *buddy)
+{
+	MsimUser *user;
+
+	if (!buddy) {
+		return NULL;
+	}
+
+	if (!buddy->proto_data) {
+		/* No MsimUser for this buddy; make one. */
+
+		/* TODO: where is this freed? */
+		user = g_new0(MsimUser, 1);
+		user->buddy = buddy;
+		buddy->proto_data = (gpointer)user;
+	} 
+
+	user = (MsimUser *)(buddy->proto_data);
+
+	return user;
+}
+
+/** Find and return an MsimUser * representing a user on the buddy list, or NULL. */
+MsimUser *
+msim_find_user(MsimSession *session, const gchar *username)
+{
+	PurpleBuddy *buddy;
+	MsimUser *user;
+
+	buddy = purple_find_buddy(session->account, username);
+	if (!buddy) {
+		return NULL;
+	}
+
+	user = msim_get_user_from_buddy(buddy);
+
+	return user;
+}
+
+/** Append user information to a PurpleNotifyUserInfo, given an MsimUser. 
+ * Used by msim_tooltip_text() and msim_get_info_cb() to show a user's profile.
+ */
+void
+msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full)
+{
+	gchar *str;
+	guint uid;
+	guint cv;
+
+	/* Useful to identify the account the tooltip refers to. 
+	 *  Other prpls show this. */
+	if (user->username) {
+		purple_notify_user_info_add_pair(user_info, _("User"), user->username);
+	}
+
+	uid = purple_blist_node_get_int(&user->buddy->node, "UserID");
+
+	if (full) {
+		/* TODO: link to username, if available */
+		purple_notify_user_info_add_pair(user_info, _("Profile"),
+				g_strdup_printf("<a href=\"http://myspace.com/%d\">http://myspace.com/%d</a>",
+					uid, uid));
+	}
+
+
+	/* a/s/l...the vitals */
+	if (user->age) {
+		purple_notify_user_info_add_pair(user_info, _("Age"),
+				g_strdup_printf("%d", user->age));
+	}
+
+	if (user->gender && strlen(user->gender)) {
+		purple_notify_user_info_add_pair(user_info, _("Gender"), user->gender);
+	}
+
+	if (user->location && strlen(user->location)) {
+		purple_notify_user_info_add_pair(user_info, _("Location"), user->location);
+	}
+
+	/* Other information */
+	if (user->headline && strlen(user->headline)) {
+		purple_notify_user_info_add_pair(user_info, _("Headline"), user->headline);
+	}
+
+	str = msim_format_now_playing(user->band_name, user->song_name);
+	if (str && strlen(str)) {
+		purple_notify_user_info_add_pair(user_info, _("Song"), str);
+	}
+
+	/* Note: total friends only available if looked up by uid, not username. */
+	if (user->total_friends) {
+		purple_notify_user_info_add_pair(user_info, _("Total Friends"),
+			g_strdup_printf("%d", user->total_friends));
+	}
+
+	if (full) {
+		/* Client information */
+
+		str = user->client_info;
+		cv = user->client_cv;
+
+		if (str && cv != 0) {
+			purple_notify_user_info_add_pair(user_info, _("Client Version"),
+					g_strdup_printf("%s (build %d)", str, cv));
+		} else if (str) {
+			purple_notify_user_info_add_pair(user_info, _("Client Version"),
+					g_strdup(str));
+		} else if (cv) {
+			purple_notify_user_info_add_pair(user_info, _("Client Version"),
+					g_strdup_printf("Build %d", cv));
+		}
+	}
+}
+
+/** Store a field of information about a buddy. 
+ *
+ * @param key_str Key to store.
+ * @param value_str Value string, either user takes ownership of this string
+ *                  or it is freed if MsimUser doesn't store the string.
+ * @param user User to store data in. Existing data will be replaced.
+ * */
+void 
+msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user)
+{
+	if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) {
+		/* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */
+		if (user->buddy)
+		{
+			purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name);
+			purple_blist_node_set_int(&user->buddy->node, "UserID", atol(value_str));
+		}
+		/* Need to store in MsimUser, too? What if not on blist? */
+	} else if (g_str_equal(key_str, "Age")) {
+		user->age = atol(value_str);
+		g_free(value_str);
+	} else if (g_str_equal(key_str, "Gender")) {
+		g_free(user->gender);
+		user->gender = value_str;
+	} else if (g_str_equal(key_str, "Location")) {
+		g_free(user->location);
+		user->location = value_str;
+	} else if (g_str_equal(key_str, "TotalFriends")) {
+		user->total_friends = atol(value_str);
+	} else if (g_str_equal(key_str, "DisplayName")) {
+		g_free(user->display_name);
+		user->display_name = value_str;
+	} else if (g_str_equal(key_str, "BandName")) {
+		g_free(user->band_name);
+		user->band_name = value_str;
+	} else if (g_str_equal(key_str, "SongName")) {
+		g_free(user->song_name);
+		user->song_name = value_str;
+	} else if (g_str_equal(key_str, "UserName") || g_str_equal(key_str, "IMName") || g_str_equal(key_str, "NickName")) {
+		/* Ignore because PurpleBuddy knows this already */
+		g_free(value_str);
+	} else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) {
+		const gchar *previous_url;
+
+		g_free(user->image_url);
+
+		user->image_url = value_str;
+
+		/* Instead of showing 'no photo' picture, show nothing. */
+		if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif"))
+		{
+			purple_buddy_icons_set_for_user(user->buddy->account,
+				user->buddy->name,
+				NULL, 0, NULL);
+			return;
+		}
+	
+		/* TODO: use ETag for checksum */
+		previous_url = purple_buddy_icons_get_checksum_for_user(user->buddy);
+
+		/* Only download if URL changed */
+		if (!previous_url || !g_str_equal(previous_url, user->image_url)) {
+			purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
+		}
+	} else if (g_str_equal(key_str, "LastImageUpdated")) {
+		/* TODO: use somewhere */
+		user->last_image_updated = atol(value_str);
+		g_free(value_str);
+	} else if (g_str_equal(key_str, "Headline")) {
+		g_free(user->headline);
+		user->headline = value_str;
+	} else {
+		/* TODO: other fields in MsimUser */
+		gchar *msg;
+
+		msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s",
+				key_str, value_str);
+		g_free(value_str);
+
+		msim_unrecognized(NULL, NULL, msg);
+
+		g_free(msg);
+	}
+}
+
+/** Save buddy information to the buddy list from a user info reply message.
+ *
+ * @param session
+ * @param msg The user information reply, with any amount of information.
+ * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data.
+ *
+ * Variable information is saved to the passed MsimUser structure. Permanent
+ * information (UserID) is stored in the blist node of the buddy list (and
+ * ends up in blist.xml, persisted to disk) if it exists.
+ *
+ * If the function has no buddy information, this function
+ * is a no-op (and returns FALSE).
+ *
+ */
+gboolean 
+msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user)
+{
+	gchar *username;
+	MsimMessage *body, *body_node;
+
+	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
+	g_return_val_if_fail(msg != NULL, FALSE);
+
+	body = msim_msg_get_dictionary(msg, "body");
+	if (!body) {
+		return FALSE;
+	}
+
+	username = msim_msg_get_string(body, "UserName");
+
+	if (!username) {
+		purple_debug_info("msim", 
+			"msim_process_reply: not caching body, no UserName\n");
+		msim_msg_free(body);
+		g_free(username);
+		return FALSE;
+	}
+	
+	/* Null user = find and store in PurpleBuddy's proto_data */
+	if (!user) {
+		user = msim_find_user(session, username);
+		if (!user) {
+			msim_msg_free(body);
+			g_free(username);
+			return FALSE;
+		}
+	}
+
+	/* TODO: make looping over MsimMessage's easier. */
+	for (body_node = body; 
+		body_node != NULL; 
+		body_node = msim_msg_get_next_element_node(body_node))
+	{
+		const gchar *key_str;
+		gchar *value_str;
+		MsimMessageElement *elem;
+
+		elem = (MsimMessageElement *)body_node->data;
+		key_str = elem->name;
+
+		value_str = msim_msg_get_string_from_element(elem);
+		msim_store_user_info_each(key_str, value_str, user);
+	}
+
+	if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN &&
+		msim_msg_get_integer(msg, "lid") == MG_OWN_IM_INFO_LID) {
+		/* TODO: do something with our own IM info, if we need it for some
+		 * specific purpose. Otherwise it is available on the buddy list,
+		 * if the user has themselves as their own buddy. 
+		 *
+		 * However, much of the info is already available in MsimSession,
+		 * stored in msim_we_are_logged_on(). */
+	} else if (msim_msg_get_integer(msg, "dsn") == MG_OWN_MYSPACE_INFO_DSN &&
+			msim_msg_get_integer(msg, "lid") == MG_OWN_MYSPACE_INFO_LID) {
+		/* TODO: same as above, but for MySpace info. */
+	}
+
+	msim_msg_free(body);
+	g_free(username);
+
+	return TRUE;
+}
+
+/**
+ * Asynchronously lookup user information, calling callback when receive result.
+ *
+ * @param session
+ * @param user The user id, email address, or username. Not freed.
+ * @param cb Callback, called with user information when available.
+ * @param data An arbitray data pointer passed to the callback.
+ */
+/* TODO: change to not use callbacks */
+void 
+msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data)
+{
+	MsimMessage *body;
+	gchar *field_name;
+	guint rid, cmd, dsn, lid;
+
+	g_return_if_fail(MSIM_SESSION_VALID(session));
+	g_return_if_fail(user != NULL);
+	/* Callback can be null to not call anything, just lookup & store information. */
+	/*g_return_if_fail(cb != NULL);*/
+
+	purple_debug_info("msim", "msim_lookup_userid: "
+			"asynchronously looking up <%s>\n", user);
+
+	msim_msg_dump("msim_lookup_user: data=%s\n", (MsimMessage *)data);
+
+	/* Setup callback. Response will be associated with request using 'rid'. */
+	rid = msim_new_reply_callback(session, cb, data);
+
+	/* Send request */
+
+	cmd = MSIM_CMD_GET;
+
+	if (msim_is_userid(user)) {
+		field_name = "UserID";
+		dsn = MG_MYSPACE_INFO_BY_ID_DSN; 
+		lid = MG_MYSPACE_INFO_BY_ID_LID; 
+	} else if (msim_is_email(user)) {
+		field_name = "Email";
+		dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
+		lid = MG_MYSPACE_INFO_BY_STRING_LID;
+	} else {
+		field_name = "UserName";
+		dsn = MG_MYSPACE_INFO_BY_STRING_DSN;
+		lid = MG_MYSPACE_INFO_BY_STRING_LID;
+	}
+
+	body = msim_msg_new(
+			field_name, MSIM_TYPE_STRING, g_strdup(user),
+			NULL);
+
+	g_return_if_fail(msim_send(session,
+			"persist", MSIM_TYPE_INTEGER, 1,
+			"sesskey", MSIM_TYPE_INTEGER, session->sesskey,
+			"cmd", MSIM_TYPE_INTEGER, 1,
+			"dsn", MSIM_TYPE_INTEGER, dsn,
+			"uid", MSIM_TYPE_INTEGER, session->userid,
+			"lid", MSIM_TYPE_INTEGER, lid,
+			"rid", MSIM_TYPE_INTEGER, rid,
+			"body", MSIM_TYPE_DICTIONARY, body,
+			NULL));
+} 
+
+
+/**
+ * Check if a string is a userid (all numeric).
+ *
+ * @param user The user id, email, or name.
+ *
+ * @return TRUE if is userid, FALSE if not.
+ */
+gboolean 
+msim_is_userid(const gchar *user)
+{
+	g_return_val_if_fail(user != NULL, FALSE);
+
+	return strspn(user, "0123456789") == strlen(user);
+}
+
+/**
+ * Check if a string is an email address (contains an @).
+ *
+ * @param user The user id, email, or name.
+ *
+ * @return TRUE if is an email, FALSE if not.
+ *
+ * This function is not intended to be used as a generic
+ * means of validating email addresses, but to distinguish
+ * between a user represented by an email address from
+ * other forms of identification.
+ */ 
+gboolean 
+msim_is_email(const gchar *user)
+{
+	g_return_val_if_fail(user != NULL, FALSE);
+
+	return strchr(user, '@') != NULL;
+}
+
+
+/** Callback for when a buddy icon finished being downloaded. */
+static void
+msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data,
+		gpointer user_data,
+		const gchar *url_text,
+		gsize len,
+		const gchar *error_message)
+{
+	MsimUser *user;
+
+	user = (MsimUser *)user_data;
+
+	purple_debug_info("msim_downloaded_buddy_icon",
+			"Downloaded %d bytes\n", len);
+
+	if (!url_text) {
+		purple_debug_info("msim_downloaded_buddy_icon",
+				"failed to download icon for %s",
+				user->buddy->name);
+		return;
+	}
+
+	purple_buddy_icons_set_for_user(user->buddy->account,
+			user->buddy->name,
+			g_memdup((gchar *)url_text, len), len, 
+			/* Use URL itself as buddy icon "checksum" (TODO: ETag) */
+			user->image_url);		/* checksum */
+}
+
+
--- a/libpurple/protocols/myspace/user.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/user.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,55 +1,55 @@
-/* MySpaceIM Protocol Plugin, header file
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * 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 _MYSPACE_USER_H
-#define _MYSPACE_USER_H
-
-/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */
-/* GHashTable? */
-typedef struct _MsimUser
-{
-	PurpleBuddy *buddy;
-	guint client_cv;
-	gchar *client_info;
-	guint age;
-	gchar *gender;
-	gchar *location;
-	guint total_friends;
-	gchar *headline;
-	gchar *display_name;
-	/* Note: uid is in &buddy->node (set_blist_node_int), since it never changes */
-	gchar *username;
-	gchar *band_name, *song_name;
-	gchar *image_url;
-	guint last_image_updated;
-} MsimUser;
-
-/* Callback function pointer type for when a user's information is received, 
- * initiated from a user lookup. */
-typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, MsimMessage *userinfo, gpointer data);
-
-MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy);
-MsimUser *msim_find_user(MsimSession *session, const gchar *username);
-void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full);
-gboolean msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user);
-gboolean msim_is_userid(const gchar *user);
-gboolean msim_is_email(const gchar *user);
-void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data);
-
-#endif /* !_MYSPACE_USER_H */
+/* MySpaceIM Protocol Plugin, header file
+ *
+ * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ *
+ * 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 _MYSPACE_USER_H
+#define _MYSPACE_USER_H
+
+/* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */
+/* GHashTable? */
+typedef struct _MsimUser
+{
+	PurpleBuddy *buddy;
+	guint client_cv;
+	gchar *client_info;
+	guint age;
+	gchar *gender;
+	gchar *location;
+	guint total_friends;
+	gchar *headline;
+	gchar *display_name;
+	/* Note: uid is in &buddy->node (set_blist_node_int), since it never changes */
+	gchar *username;
+	gchar *band_name, *song_name;
+	gchar *image_url;
+	guint last_image_updated;
+} MsimUser;
+
+/* Callback function pointer type for when a user's information is received, 
+ * initiated from a user lookup. */
+typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, MsimMessage *userinfo, gpointer data);
+
+MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy);
+MsimUser *msim_find_user(MsimSession *session, const gchar *username);
+void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full);
+gboolean msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user);
+gboolean msim_is_userid(const gchar *user);
+gboolean msim_is_email(const gchar *user);
+void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data);
+
+#endif /* !_MYSPACE_USER_H */
--- a/libpurple/protocols/myspace/zap.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/zap.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,208 +1,208 @@
-/* MySpaceIM Protocol Plugin - zap support
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * 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 "myspace.h"
-#include "zap.h"
-
-static gboolean msim_send_zap(MsimSession *session, const gchar *username, guint code);
-static void msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr);
-
-
-/** Get zap types. */
-GList *
-msim_attention_types(PurpleAccount *acct)
-{
-	static GList *types = NULL;
-	MsimAttentionType* attn;
-
-	if (!types) {
-#define _MSIM_ADD_NEW_ATTENTION(icn, nme, incoming, outgoing)              \
-		attn = g_new0(MsimAttentionType, 1);                       \
-		attn->icon_name = icn;                                     \
-		attn->name = nme;                                          \
-		attn->incoming_description = incoming;                     \
-		attn->outgoing_description = outgoing;                     \
-		types = g_list_append(types, attn);
-
-		/* TODO: icons for each zap */
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Zap"), _("%s has zapped you!"), _("Zapping %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Whack"), _("%s has whacked you!"), _("Whacking %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Torch"), _("%s has torched you!"), _("Torching %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Smooch"), _("%s has smooched you!"), _("Smooching %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Hug"), _("%s has hugged you!"), _("Hugging %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Slap"), _("%s has slapped you!"), _("Slapping %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Goose"), _("%s has goosed you!"), _("Goosing %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("High-five"), _("%s has high-fived you!"), _("High-fiving %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Punk"), _("%s has punk'd you!"), _("Punking %s..."));
-		_MSIM_ADD_NEW_ATTENTION(NULL, _("Raspberry"), _("%s has raspberried you!"), _("Raspberrying %s..."));
-	}
-
-	return types;
-}
-
-/** Send a zap */
-gboolean
-msim_send_attention(PurpleConnection *gc, const gchar *username, guint code)
-{
-	GList *types;
-	MsimSession *session;
-	MsimAttentionType *attn;
-	PurpleBuddy *buddy;
-
-	session = (MsimSession *)gc->proto_data;
-
-	/* Look for this attention type, by the code index given. */
-	types = msim_attention_types(gc->account);
-	attn = (MsimAttentionType *)g_list_nth_data(types, code);
-
-	if (!attn) {
-		purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code);
-		return FALSE;
-	}
-
-	buddy = purple_find_buddy(session->account, username);
-	if (!buddy) {
-		return FALSE;
-	}
-
-	msim_send_zap(session, username, code);
-
-	return TRUE;
-}
-
-/** Send a zap to a user. */
-static gboolean
-msim_send_zap(MsimSession *session, const gchar *username, guint code)
-{
-	gchar *zap_string;
-	gboolean rc;
-
-	g_return_val_if_fail(session != NULL, FALSE);
-	g_return_val_if_fail(username != NULL, FALSE);
-
-	/* Construct and send the actual zap command. */
-	zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", code);
-
-	if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION)) {
-		purple_debug_info("msim_send_zap_from_menu", "msim_send_bm failed: zapping %s with %s\n",
-				username, zap_string);
-		rc = FALSE;
-	} else {
-		rc = TRUE;
-	}
-	
-	g_free(zap_string);
-
-	return rc;
-
-}
-
-/** Zap someone. Callback from msim_blist_node_menu zap menu. */
-static void
-msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr)
-{
-	PurpleBuddy *buddy;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	MsimSession *session;
-	guint zap;
-
-	if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-		/* Only know about buddies for now. */
-		return;
-	}
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *)node;
-
-	/* Find the session */
-	account = buddy->account;
-	gc = purple_account_get_connection(account);
-	session = (MsimSession *)gc->proto_data;
-
-	zap = GPOINTER_TO_INT(zap_num_ptr);
-
-	serv_send_attention(session->gc, buddy->name, zap);
-}
-
-/** Return menu, if any, for a buddy list node. */
-GList *
-msim_blist_node_menu(PurpleBlistNode *node)
-{
-	GList *menu, *zap_menu;
-	GList *types;
-	PurpleMenuAction *act;
-	guint i;
-
-	if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-		/* Only know about buddies for now. */
-		return NULL;
-	}
-
-	zap_menu = NULL;
-
-	/* TODO: get rid of once is accessible directly in GUI */
-	types = msim_attention_types(NULL);
-	i = 0;
-	do
-	{
-		MsimAttentionType *attn;
-
-		attn = (MsimAttentionType *)types->data;
-
-		act = purple_menu_action_new(attn->name, PURPLE_CALLBACK(msim_send_zap_from_menu),
-				GUINT_TO_POINTER(i), NULL);
-		zap_menu = g_list_append(zap_menu, act);
-
-		++i;
-	} while ((types = g_list_next(types)));
-
-	act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu);
-	menu = g_list_append(NULL, act);
-
-	return menu;
-}
-
-/** Process an incoming zap. */
-gboolean
-msim_incoming_zap(MsimSession *session, MsimMessage *msg)
-{
-	gchar *msg_text, *username;
-	gint zap;
-
-	msg_text = msim_msg_get_string(msg, "msg");
-	username = msim_msg_get_string(msg, "_username");
-
-	g_return_val_if_fail(msg_text != NULL, FALSE);
-	g_return_val_if_fail(username != NULL, FALSE);
-
-	g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE);
-
-	zap = CLAMP(zap, 0, 9);
-
-	serv_got_attention(session->gc, username, zap);
-
-	g_free(msg_text);
-	g_free(username);
-
-	return TRUE;
-}
-
-
+/* MySpaceIM Protocol Plugin - zap support
+ *
+ * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ *
+ * 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 "myspace.h"
+#include "zap.h"
+
+static gboolean msim_send_zap(MsimSession *session, const gchar *username, guint code);
+static void msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr);
+
+
+/** Get zap types. */
+GList *
+msim_attention_types(PurpleAccount *acct)
+{
+	static GList *types = NULL;
+	MsimAttentionType* attn;
+
+	if (!types) {
+#define _MSIM_ADD_NEW_ATTENTION(icn, nme, incoming, outgoing)              \
+		attn = g_new0(MsimAttentionType, 1);                       \
+		attn->icon_name = icn;                                     \
+		attn->name = nme;                                          \
+		attn->incoming_description = incoming;                     \
+		attn->outgoing_description = outgoing;                     \
+		types = g_list_append(types, attn);
+
+		/* TODO: icons for each zap */
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Zap"), _("%s has zapped you!"), _("Zapping %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Whack"), _("%s has whacked you!"), _("Whacking %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Torch"), _("%s has torched you!"), _("Torching %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Smooch"), _("%s has smooched you!"), _("Smooching %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Hug"), _("%s has hugged you!"), _("Hugging %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Slap"), _("%s has slapped you!"), _("Slapping %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Goose"), _("%s has goosed you!"), _("Goosing %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("High-five"), _("%s has high-fived you!"), _("High-fiving %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Punk"), _("%s has punk'd you!"), _("Punking %s..."));
+		_MSIM_ADD_NEW_ATTENTION(NULL, _("Raspberry"), _("%s has raspberried you!"), _("Raspberrying %s..."));
+	}
+
+	return types;
+}
+
+/** Send a zap */
+gboolean
+msim_send_attention(PurpleConnection *gc, const gchar *username, guint code)
+{
+	GList *types;
+	MsimSession *session;
+	MsimAttentionType *attn;
+	PurpleBuddy *buddy;
+
+	session = (MsimSession *)gc->proto_data;
+
+	/* Look for this attention type, by the code index given. */
+	types = msim_attention_types(gc->account);
+	attn = (MsimAttentionType *)g_list_nth_data(types, code);
+
+	if (!attn) {
+		purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code);
+		return FALSE;
+	}
+
+	buddy = purple_find_buddy(session->account, username);
+	if (!buddy) {
+		return FALSE;
+	}
+
+	msim_send_zap(session, username, code);
+
+	return TRUE;
+}
+
+/** Send a zap to a user. */
+static gboolean
+msim_send_zap(MsimSession *session, const gchar *username, guint code)
+{
+	gchar *zap_string;
+	gboolean rc;
+
+	g_return_val_if_fail(session != NULL, FALSE);
+	g_return_val_if_fail(username != NULL, FALSE);
+
+	/* Construct and send the actual zap command. */
+	zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", code);
+
+	if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION)) {
+		purple_debug_info("msim_send_zap_from_menu", "msim_send_bm failed: zapping %s with %s\n",
+				username, zap_string);
+		rc = FALSE;
+	} else {
+		rc = TRUE;
+	}
+	
+	g_free(zap_string);
+
+	return rc;
+
+}
+
+/** Zap someone. Callback from msim_blist_node_menu zap menu. */
+static void
+msim_send_zap_from_menu(PurpleBlistNode *node, gpointer zap_num_ptr)
+{
+	PurpleBuddy *buddy;
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	MsimSession *session;
+	guint zap;
+
+	if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		/* Only know about buddies for now. */
+		return;
+	}
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *)node;
+
+	/* Find the session */
+	account = buddy->account;
+	gc = purple_account_get_connection(account);
+	session = (MsimSession *)gc->proto_data;
+
+	zap = GPOINTER_TO_INT(zap_num_ptr);
+
+	serv_send_attention(session->gc, buddy->name, zap);
+}
+
+/** Return menu, if any, for a buddy list node. */
+GList *
+msim_blist_node_menu(PurpleBlistNode *node)
+{
+	GList *menu, *zap_menu;
+	GList *types;
+	PurpleMenuAction *act;
+	guint i;
+
+	if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		/* Only know about buddies for now. */
+		return NULL;
+	}
+
+	zap_menu = NULL;
+
+	/* TODO: get rid of once is accessible directly in GUI */
+	types = msim_attention_types(NULL);
+	i = 0;
+	do
+	{
+		MsimAttentionType *attn;
+
+		attn = (MsimAttentionType *)types->data;
+
+		act = purple_menu_action_new(attn->name, PURPLE_CALLBACK(msim_send_zap_from_menu),
+				GUINT_TO_POINTER(i), NULL);
+		zap_menu = g_list_append(zap_menu, act);
+
+		++i;
+	} while ((types = g_list_next(types)));
+
+	act = purple_menu_action_new(_("Zap"), NULL, NULL, zap_menu);
+	menu = g_list_append(NULL, act);
+
+	return menu;
+}
+
+/** Process an incoming zap. */
+gboolean
+msim_incoming_zap(MsimSession *session, MsimMessage *msg)
+{
+	gchar *msg_text, *username;
+	gint zap;
+
+	msg_text = msim_msg_get_string(msg, "msg");
+	username = msim_msg_get_string(msg, "_username");
+
+	g_return_val_if_fail(msg_text != NULL, FALSE);
+	g_return_val_if_fail(username != NULL, FALSE);
+
+	g_return_val_if_fail(sscanf(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", &zap) == 1, FALSE);
+
+	zap = CLAMP(zap, 0, 9);
+
+	serv_got_attention(session->gc, username, zap);
+
+	g_free(msg_text);
+	g_free(username);
+
+	return TRUE;
+}
+
+
--- a/libpurple/protocols/myspace/zap.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/myspace/zap.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,28 +1,28 @@
-/* MySpaceIM Protocol Plugin - zap support
- *
- * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
- *
- * 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 _MYSPACE_ZAP_H
-#define _MYSPACE_ZAP_H
-
-GList *msim_attention_types(PurpleAccount *acct);
-gboolean msim_send_attention(PurpleConnection *gc, const gchar *username, guint code);
-GList *msim_blist_node_menu(PurpleBlistNode *node);
-gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg);
-
-#endif /* !_MYSPACE_ZAP_H */
+/* MySpaceIM Protocol Plugin - zap support
+ *
+ * Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
+ *
+ * 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 _MYSPACE_ZAP_H
+#define _MYSPACE_ZAP_H
+
+GList *msim_attention_types(PurpleAccount *acct);
+gboolean msim_send_attention(PurpleConnection *gc, const gchar *username, guint code);
+GList *msim_blist_node_menu(PurpleBlistNode *node);
+gboolean msim_incoming_zap(MsimSession *session, MsimMessage *msg);
+
+#endif /* !_MYSPACE_ZAP_H */
--- a/libpurple/protocols/oscar/.todo	Mon Oct 01 17:02:03 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-<todo version="0.1.19">
-    <note priority="high" time="1036040788">
-        watch for aol's upcoming increased aim/icq merge
-    </note>
-    <note priority="medium" time="1036040405">
-        AIM
-        <note priority="high" time="1036040899">
-            option to ignore chat room invitations. (this is something you could vary conceivably want to be done per account, so perhaps a protocol action would be best)
-        </note>
-        <note priority="medium" time="1092939731">
-            buddies in Recent Buddies don't seem to delete right
-        </note>
-        <note priority="low" time="1036040980">
-            direct im
-            <note priority="veryhigh" time="1036040919">
-                some way to close direct connect w/out closing convo.
-            </note>
-            <note priority="low" time="1036041084">
-                failed direct im attempt should allow new attempt some way to cancel an attempt that isn't happening
-            </note>
-        </note>
-        <note priority="verylow" time="1036041121">
-            Voice Chat
-        </note>
-    </note>
-    <note priority="medium" time="1036040416">
-        ICQ
-        <note priority="veryhigh" time="1036041206">
-            more privacy options
-        </note>
-        <note priority="veryhigh" time="1036041223">
-            increased authorization support
-            <note priority="medium" time="1038877503">
-                add info to dialog requesting autorization to allow you to check the info of the person who is requesting
-            </note>
-        </note>
-        <note priority="high" time="1036041199">
-            color support
-        </note>
-        <note priority="high" time="1036041251">
-            set status message and of course when libpurple can set them, it needs to be able to get the ones it sets. (yes this is redundant. its a reflection of my current mood)
-        </note>
-        <note priority="medium" time="1036041165">
-            Chat (this is different from aim chat)
-        </note>
-        <note priority="medium" time="1036041190">
-            New User Registration
-        </note>
-        <note priority="medium" time="1036041215">
-            more info support
-        </note>
-        <note priority="medium" time="1036041260">
-            search for users
-        </note>
-        <note priority="low" time="1036041152">
-            set the Nickname to be the self-alias
-        </note>
-        <note priority="low" time="1092939688">
-            icq i18n without breaking aim
-        </note>
-    </note>
-    <note priority="medium" time="1036040870">
-        The order of groups and buddies in the server list is not updated when groups and buddies are re-arranged locally in libpurple.
-    </note>
-</todo>
--- a/libpurple/protocols/oscar/AUTHORS	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/oscar/AUTHORS	Mon Oct 01 17:49:01 2007 +0000
@@ -1,3 +1,7 @@
+
+N: ComBOTS Product GmbH (htfv)
+T: 2007
+E: foss@combots.com
 
 N: Jonathan Clark
 T: 2005-2006
--- a/libpurple/protocols/oscar/family_auth.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Mon Oct 01 17:49:01 2007 +0000
@@ -438,10 +438,6 @@
  * login request instead of the normal SNAC one.
  *
  * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/.
- *
- * XXX This may cause problems if the client relies on callbacks only
- * being called from the context of aim_rxdispatch()...
- *
  */
 static int
 goddamnicq(OscarData *od, FlapConnection *conn, const char *sn)
--- a/libpurple/protocols/oscar/family_feedbag.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/oscar/family_feedbag.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1424,7 +1424,7 @@
 		aim_tlvlist_free(data);
 
 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-			ret = userfunc(od, conn, frame, type, name);
+			ret = userfunc(od, conn, frame, snac->subtype, type, name);
 
 		g_free(name);
 	}
@@ -1434,8 +1434,6 @@
 
 /*
  * Subtype 0x0009 - Incoming SSI mod.
- *
- * XXX - It would probably be good for the client to actually do something when it gets this.
  */
 static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
@@ -1485,7 +1483,7 @@
 		}
 
 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-			ret = userfunc(od, conn, frame);
+			ret = userfunc(od, conn, frame, snac->subtype, type, name);
 
 		g_free(name);
 		aim_tlvlist_free(data);
--- a/libpurple/protocols/oscar/oscar.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Oct 01 17:49:01 2007 +0000
@@ -4,8 +4,9 @@
  * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
  * Some code copyright (C) 1999-2001, Eric Warmenhoven
  * Some code copyright (C) 2001-2003, Sean Egan
- * Some code copyright (C) 2001-2005, Mark Doliner <thekingant@users.sourceforge.net>
+ * Some code copyright (C) 2001-2007, Mark Doliner <thekingant@users.sourceforge.net>
  * Some code copyright (C) 2005, Jonathan Clark <ardentlygnarly@users.sourceforge.net>
+ * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com>
  *
  * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
  * Some libfaim code copyright (C) 2001-2004 Mark Doliner <thekingant@users.sourceforge.net>
@@ -188,7 +189,7 @@
 static int purple_ssi_parserights  (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_ssi_parselist    (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_ssi_parseack     (OscarData *, FlapConnection *, FlapFrame *, ...);
-static int purple_ssi_parseadd     (OscarData *, FlapConnection *, FlapFrame *, ...);
+static int purple_ssi_parseaddmod  (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_ssi_authgiven    (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_ssi_authrequest  (OscarData *, FlapConnection *, FlapFrame *, ...);
 static int purple_ssi_authreply    (OscarData *, FlapConnection *, FlapFrame *, ...);
@@ -1219,7 +1220,8 @@
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO, purple_ssi_parserights, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_LIST, purple_ssi_parselist, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SRVACK, purple_ssi_parseack, 0);
-	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADD, purple_ssi_parseadd, 0);
+	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADD, purple_ssi_parseaddmod, 0);
+	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_MOD, purple_ssi_parseaddmod, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTH, purple_ssi_authgiven, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0);
 	oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0);
@@ -1632,7 +1634,8 @@
 {
 	PurpleConnection *gc;
 	PurpleAccount *account;
-	ClientInfo info = CLIENTINFO_PURPLE;
+	ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
+	ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
 	va_list ap;
 	char *key;
 	gboolean truncate_pass;
@@ -1647,7 +1650,7 @@
 
 	aim_send_login(od, conn, purple_account_get_username(account),
 			purple_connection_get_password(gc), truncate_pass,
-			&info, key);
+			od->icq ? &icqinfo : &aiminfo, key);
 
 	purple_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
 	ck[2] = 0x6c;
@@ -1870,6 +1873,10 @@
 			saved_b16 = purple_buddy_icons_get_checksum_for_user(b);
 
 		if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) {
+			/* Invalidate the old icon for this user */
+			purple_buddy_icons_set_for_user(account, info->sn, NULL, 0, NULL);
+
+			/* Fetch the new icon (if we're not already doing so) */
 			if (g_slist_find_custom(od->requesticon, info->sn,
 					(GCompareFunc)aim_sncmp) == NULL)
 			{
@@ -4932,10 +4939,7 @@
 					b = purple_find_buddy_in_group(gc->account, curitem->name, g);
 					if (b) {
 						/* Get server stored alias */
-						if (alias_utf8) {
-							g_free(b->alias);
-							b->alias = g_strdup(alias_utf8);
-						}
+						purple_blist_alias_buddy(b, alias_utf8);
 					} else {
 						b = purple_buddy_new(gc->account, curitem->name, alias_utf8);
 
@@ -5079,16 +5083,23 @@
 	return 1;
 }
 
-static int purple_ssi_parseadd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
-	PurpleConnection *gc = od->gc;
+static int
+purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
+{
+	PurpleConnection *gc;
+	PurpleAccount *account;
 	char *gname, *gname_utf8, *alias, *alias_utf8;
 	PurpleBuddy *b;
 	PurpleGroup *g;
 	va_list ap;
-	guint16 type;
+	guint16 snac_subtype, type;
 	const char *name;
 
+	gc = od->gc;
+	account = purple_connection_get_account(gc);
+
 	va_start(ap, fr);
+	snac_subtype = (guint16)va_arg(ap, int);
 	type = (guint16)va_arg(ap, int);
 	name = va_arg(ap, char *);
 	va_end(ap);
@@ -5097,7 +5108,7 @@
 		return 1;
 
 	gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
-	gname_utf8 = gname ? oscar_utf8_try_convert(gc->account, gname) : NULL;
+	gname_utf8 = gname ? oscar_utf8_try_convert(account, gname) : NULL;
 
 	alias = aim_ssi_getalias(od->ssi.local, gname, name);
 	if (alias != NULL)
@@ -5105,22 +5116,26 @@
 		if (g_utf8_validate(alias, -1, NULL))
 			alias_utf8 = g_strdup(alias);
 		else
-			alias_utf8 = oscar_utf8_try_convert(purple_connection_get_account(gc), alias);
+			alias_utf8 = oscar_utf8_try_convert(account, alias);
 	}
 	else
 		alias_utf8 = NULL;
-
-	b = purple_find_buddy(gc->account, name);
 	g_free(alias);
 
+	b = purple_find_buddy(account, name);
 	if (b) {
-		/* Get server stored alias */
-		if (alias_utf8) {
-			g_free(b->alias);
-			b->alias = g_strdup(alias_utf8);
-		}
-	} else {
-		b = purple_buddy_new(gc->account, name, alias_utf8);
+		/*
+		 * You're logged in somewhere else and you aliased one
+		 * of your buddies, so update our local buddy list with
+		 * the person's new alias.
+		 */
+		purple_blist_alias_buddy(b, alias_utf8);
+	} else if (snac_subtype == 0x0008) {
+		/*
+		 * You're logged in somewhere else and you added a buddy to
+		 * your server list, so add them to your local buddy list.
+		 */
+		b = purple_buddy_new(account, name, alias_utf8);
 
 		if (!(g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
 			g = purple_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
@@ -5131,6 +5146,7 @@
 				   "ssi: adding buddy %s to group %s to local list\n", name, gname_utf8 ? gname_utf8 : _("Orphans"));
 		purple_blist_add_buddy(b, NULL, g, NULL);
 	}
+
 	g_free(gname_utf8);
 	g_free(alias_utf8);
 
--- a/libpurple/protocols/oscar/oscar.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Mon Oct 01 17:49:01 2007 +0000
@@ -3,6 +3,8 @@
  * This file is the legal property of its developers.
  * Please see the AUTHORS file distributed alongside this file.
  *
+ * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
@@ -270,6 +272,15 @@
 	"us", "en", \
 }
 
+#define CLIENTINFO_ICQBASIC_14_34_3000 { \
+	"ICQBasic", \
+	0x010a, \
+	0x0014, 0x0034, \
+	0x0000, 0x0bb8, \
+	0x0000043d, \
+	"us", "en", \
+}
+
 #define CLIENTINFO_NETSCAPE_7_0_1 { \
 	"Netscape 2000 an approved user of AOL Instant Messenger (SM)", \
 	0x1d0d, \
@@ -280,10 +291,11 @@
 }
 
 /*
- * TODO: Use PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, and
- *       PURPLE_MICRO_VERSION?  Or did that break things?
+ * We need to use the major-minor-micro versions from the official
+ * AIM and ICQ programs here or AOL won't let us use certain features.
  */
-#define CLIENTINFO_PURPLE { \
+
+#define CLIENTINFO_PURPLE_AIM { \
 	"Purple/" VERSION, \
 	0x0109, \
 	0x0005, 0x0001, \
@@ -292,6 +304,15 @@
 	"us", "en", \
 }
 
+#define CLIENTINFO_PURPLE_ICQ { \
+	"Purple/" VERSION, \
+	0x010a, \
+	0x0014, 0x0034, \
+	0x0000, 0x0bb8, \
+	0x0000043d, \
+	"us", "en", \
+}
+
 #define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
 #define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777
 
--- a/libpurple/protocols/yahoo/yahoo.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Mon Oct 01 17:49:01 2007 +0000
@@ -860,10 +860,13 @@
 			/* If a Doodle session doesn't exist between this user */
 			if(wb == NULL)
 			{
+				doodle_session *ds;
 				wb = purple_whiteboard_create(gc->account, im->from, DOODLE_STATE_REQUESTED);
-
-				yahoo_doodle_command_send_request(gc, im->from);
-				yahoo_doodle_command_send_ready(gc, im->from);
+				ds = wb->proto_data;
+				ds->imv_key = g_strdup(imv);
+
+				yahoo_doodle_command_send_request(gc, im->from, imv);
+				yahoo_doodle_command_send_ready(gc, im->from, imv);
 			}
 		}
 	}
--- a/libpurple/protocols/yahoo/yahoo_aliases.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Mon Oct 01 17:49:01 2007 +0000
@@ -45,7 +45,7 @@
  */
 struct callback_data {
 	PurpleConnection *gc;
-	const char *id;
+	char *id;
 };
 
 
@@ -56,11 +56,16 @@
 static void
 yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,const gchar *url_text, size_t len, const gchar *error_message)
 {
+	struct callback_data *cb = user_data;
+	PurpleConnection *gc = cb->gc;
+	struct yahoo_data *yd = gc->proto_data;
+
+	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+
 	if (len == 0) {
 		purple_debug_info("yahoo","No Aliases to process\n");
 	} else {
 		const char *yid, *full_name, *nick_name, *alias, *id, *fn, *ln, *nn;
-		struct callback_data *cb = user_data;
 		PurpleBuddy *b = NULL;
 		xmlnode *item, *contacts;
 
@@ -123,8 +128,9 @@
 			}
 		}
 		xmlnode_free(contacts);
-		g_free(cb);
 	}
+	g_free(cb->id);
+	g_free(cb);
 }
 
 void
@@ -134,6 +140,7 @@
 	struct callback_data *cb;
 	char *url, *request, *webpage, *webaddress, *strtmp;
 	int inttmp;
+	PurpleUtilFetchUrlData *url_data;
 
 	/* Using callback_data so I have access to gc in the callback function */
 	cb = g_new0(struct callback_data, 1);
@@ -150,7 +157,12 @@
 				 webpage, yd->cookie_t,yd->cookie_y, webaddress);
 
 	/* We have a URL and some header information, let's connect and get some aliases  */
-	purple_util_fetch_url_request(url, FALSE, NULL, TRUE, request, FALSE, yahoo_fetch_aliases_cb, cb);
+	url_data = purple_util_fetch_url_request(url, FALSE, NULL, TRUE, request, FALSE, yahoo_fetch_aliases_cb, cb);
+	if (url_data != NULL) {
+		yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+	} else {
+		g_free(cb);
+	}
 
 	g_free(url);
 	g_free(request);
@@ -165,6 +177,11 @@
 {
 	xmlnode *node, *result;
 	struct callback_data *cb = user_data;
+	PurpleConnection *gc = cb->gc;
+	struct yahoo_data *yd;
+
+	yd = gc->proto_data;
+	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
 
 	result = xmlnode_from_str(url_text, -1);
 
@@ -184,6 +201,7 @@
 		purple_debug_info("yahoo", "Alias update failed (No contact record returned)\n");
 	}
 
+	g_free(cb->id);
 	g_free(cb);
 	xmlnode_free(result);
 }
@@ -197,6 +215,7 @@
 	int inttmp;
 	struct callback_data *cb;
 	PurpleBuddy *buddy;
+	PurpleUtilFetchUrlData *url_data;
    
 	g_return_if_fail(alias!= NULL);
 	g_return_if_fail(who!=NULL);
@@ -216,6 +235,7 @@
 	/* Using callback_data so I have access to gc in the callback function */
 	cb = g_new0(struct callback_data, 1);
 	cb->id = g_strdup(yu->id);
+	cb->gc = gc;
 
 	/*  Build all the info to make the web request */
 	url = g_strdup(YAHOO_ALIAS_UPDATE_URL);
@@ -236,7 +256,13 @@
 			 	  strlen(content), content);
 
 	/* We have a URL and some header information, let's connect and update the alias  */
-	purple_util_fetch_url_request(url, FALSE, NULL, TRUE, request, FALSE, yahoo_update_alias_cb, cb);
+	url_data = purple_util_fetch_url_request(url, FALSE, NULL, TRUE, request, FALSE, yahoo_update_alias_cb, cb);
+	if (url_data != NULL) {
+		yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+	} else {
+		g_free(cb->id);
+		g_free(cb);
+	}
 
 	g_free(content);
 	g_free(url);
--- a/libpurple/protocols/yahoo/yahoo_doodle.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_doodle.c	Mon Oct 01 17:49:01 2007 +0000
@@ -118,19 +118,19 @@
 		/* Insert this 'session' in the list.  At this point, it's only a
 		 * requested session.
 		 */
-		purple_whiteboard_create(account, to, DOODLE_STATE_REQUESTING);
+		wb = purple_whiteboard_create(account, to, DOODLE_STATE_REQUESTING);
 	}
 
 	/* NOTE Perhaps some careful handling of remote assumed established
 	 * sessions
 	 */
 
-	yahoo_doodle_command_send_ready(gc, to);
-	yahoo_doodle_command_send_request(gc, to);
+	yahoo_doodle_command_send_ready(gc, to, DOODLE_IMV_KEY);
+	yahoo_doodle_command_send_request(gc, to, DOODLE_IMV_KEY);
 
 }
 
-static void yahoo_doodle_command_got_request(PurpleConnection *gc, const char *from)
+static void yahoo_doodle_command_got_request(PurpleConnection *gc, const char *from, const char *imv_key)
 {
 	PurpleAccount *account;
 	PurpleWhiteboard *wb;
@@ -147,6 +147,7 @@
 	/* If a session with the remote user doesn't exist */
 	if(wb == NULL)
 	{
+		doodle_session *ds;
 		/* Ask user if they wish to accept the request for a doodle session */
 		/* TODO Ask local user to start Doodle session with remote user */
 		/* NOTE This if/else statement won't work right--must use dialog
@@ -160,9 +161,11 @@
 		dialog_message, NULL, NULL, NULL);
 		*/
 
-		purple_whiteboard_create(account, from, DOODLE_STATE_REQUESTED);
+		wb = purple_whiteboard_create(account, from, DOODLE_STATE_REQUESTED);
+		ds = wb->proto_data;
+		ds->imv_key = g_strdup(imv_key);
 
-		yahoo_doodle_command_send_ready(gc, from);
+		yahoo_doodle_command_send_ready(gc, from, imv_key);
 	}
 
 	/* TODO Might be required to clear the canvas of an existing doodle
@@ -170,7 +173,7 @@
 	 */
 }
 
-static void yahoo_doodle_command_got_ready(PurpleConnection *gc, const char *from)
+static void yahoo_doodle_command_got_ready(PurpleConnection *gc, const char *from, const char *imv_key)
 {
 	PurpleAccount *account;
 	PurpleWhiteboard *wb;
@@ -189,11 +192,15 @@
 
 	if(wb->state == DOODLE_STATE_REQUESTING)
 	{
+		doodle_session *ds = wb->proto_data;
 		purple_whiteboard_start(wb);
 
 		wb->state = DOODLE_STATE_ESTABLISHED;
 
-		yahoo_doodle_command_send_confirm(gc, from);
+		yahoo_doodle_command_send_confirm(gc, from, imv_key);
+		/* Let's steal the imv_key and reuse it */
+		g_free(ds->imv_key);
+		ds->imv_key = g_strdup(imv_key);
 	}
 	else if(wb->state == DOODLE_STATE_ESTABLISHED)
 	{
@@ -208,7 +215,7 @@
 	else if(wb->state == DOODLE_STATE_REQUESTED)
 	{
 		/* purple_whiteboard_start(wb); */
-		yahoo_doodle_command_send_ready(gc, from);
+		yahoo_doodle_command_send_ready(gc, from, imv_key);
 	}
 }
 
@@ -296,14 +303,14 @@
 
 
 static void
-yahoo_doodle_command_got_extra(PurpleConnection *gc, const char *from, const char *message)
+yahoo_doodle_command_got_extra(PurpleConnection *gc, const char *from, const char *message, const char *imv_key)
 {
 	purple_debug_info("yahoo", "doodle: Got Extra (%s)\n", from);
 
 	/* I do not like these 'extra' features, so I'll only handle them in one
 	 * way, which is returning them with the command/packet to turn them off
 	 */
-	yahoo_doodle_command_send_extra(gc, from, DOODLE_EXTRA_NONE);
+	yahoo_doodle_command_send_extra(gc, from, DOODLE_EXTRA_NONE, imv_key);
 }
 
 static void yahoo_doodle_command_got_confirm(PurpleConnection *gc, const char *from)
@@ -399,34 +406,34 @@
 	yahoo_packet_send_and_free(pkt, yd);
 }
 
-void yahoo_doodle_command_send_ready(PurpleConnection *gc, const char *to)
+void yahoo_doodle_command_send_ready(PurpleConnection *gc, const char *to, const char *imv_key)
 {
-	yahoo_doodle_command_send_generic("Ready", gc, to, "1", DOODLE_CMD_READY, NULL, "1");
+	yahoo_doodle_command_send_generic("Ready", gc, to, "1", DOODLE_CMD_READY, imv_key, "1");
 }
 
-void yahoo_doodle_command_send_request(PurpleConnection *gc, const char *to)
+void yahoo_doodle_command_send_request(PurpleConnection *gc, const char *to, const char *imv_key)
 {
-	yahoo_doodle_command_send_generic("Request", gc, to, "", DOODLE_CMD_REQUEST, NULL, "0");
+	yahoo_doodle_command_send_generic("Request", gc, to, "", DOODLE_CMD_REQUEST, imv_key, "0");
 }
 
-void yahoo_doodle_command_send_draw(PurpleConnection *gc, const char *to, const char *message)
+void yahoo_doodle_command_send_draw(PurpleConnection *gc, const char *to, const char *message, const char *imv_key)
 {
-	yahoo_doodle_command_send_generic("Draw", gc, to, message, DOODLE_CMD_DRAW, NULL, "1");
+	yahoo_doodle_command_send_generic("Draw", gc, to, message, DOODLE_CMD_DRAW, imv_key, "1");
 }
 
-void yahoo_doodle_command_send_clear(PurpleConnection *gc, const char *to)
+void yahoo_doodle_command_send_clear(PurpleConnection *gc, const char *to, const char *imv_key)
 {
-	yahoo_doodle_command_send_generic("Clear", gc, to, " ", DOODLE_CMD_CLEAR, NULL, "1");
+	yahoo_doodle_command_send_generic("Clear", gc, to, " ", DOODLE_CMD_CLEAR, imv_key, "1");
 }
 
-void yahoo_doodle_command_send_extra(PurpleConnection *gc, const char *to, const char *message)
+void yahoo_doodle_command_send_extra(PurpleConnection *gc, const char *to, const char *message, const char *imv_key)
 {
-	yahoo_doodle_command_send_generic("Extra", gc, to, message, DOODLE_CMD_EXTRA, NULL, "1");
+	yahoo_doodle_command_send_generic("Extra", gc, to, message, DOODLE_CMD_EXTRA, imv_key, "1");
 }
 
-void yahoo_doodle_command_send_confirm(PurpleConnection *gc, const char *to)
+void yahoo_doodle_command_send_confirm(PurpleConnection *gc, const char *to, const char *imv_key)
 {
-	yahoo_doodle_command_send_generic("Confirm", gc, to, "1", DOODLE_CMD_CONFIRM, NULL, "1");
+	yahoo_doodle_command_send_generic("Confirm", gc, to, "1", DOODLE_CMD_CONFIRM, imv_key, "1");
 }
 
 void yahoo_doodle_command_send_shutdown(PurpleConnection *gc, const char *to)
@@ -450,12 +457,14 @@
 void yahoo_doodle_end(PurpleWhiteboard *wb)
 {
 	PurpleConnection *gc = purple_account_get_connection(wb->account);
+	doodle_session *ds = wb->proto_data;
 
 	/* g_debug_debug("yahoo", "doodle: yahoo_doodle_end()\n"); */
 
 	if (gc && wb->state != DOODLE_STATE_CANCELED)
 		yahoo_doodle_command_send_shutdown(gc, wb->who);
 
+	g_free(ds->imv_key);
 	g_free(wb->proto_data);
 }
 
@@ -492,13 +501,14 @@
 	g_return_if_fail(draw_list != NULL);
 
 	message = yahoo_doodle_build_draw_string(ds, draw_list);
-	yahoo_doodle_command_send_draw(wb->account->gc, wb->who, message);
+	yahoo_doodle_command_send_draw(wb->account->gc, wb->who, message, ds->imv_key);
 	g_free(message);
 }
 
 void yahoo_doodle_clear(PurpleWhiteboard *wb)
 {
-	yahoo_doodle_command_send_clear(wb->account->gc, wb->who);
+	doodle_session *ds = wb->proto_data;
+	yahoo_doodle_command_send_clear(wb->account->gc, wb->who, ds->imv_key);
 }
 
 
@@ -551,14 +561,14 @@
 
 void yahoo_doodle_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
 {
-	doodle_session *ds = (doodle_session *)wb->proto_data;
+	doodle_session *ds = wb->proto_data;
 	*size = ds->brush_size;
 	*color = ds->brush_color;
 }
 
 void yahoo_doodle_set_brush(PurpleWhiteboard *wb, int size, int color)
 {
-	doodle_session *ds = (doodle_session *)wb->proto_data;
+	doodle_session *ds = wb->proto_data;
 	ds->brush_size = size;
 	ds->brush_color = color;
 
@@ -567,7 +577,7 @@
 }
 
 void yahoo_doodle_process(PurpleConnection *gc, const char *me, const char *from,
-						  const char *command, const char *message)
+						  const char *command, const char *message, const char *imv_key)
 {
 	if(!command)
 		return;
@@ -576,11 +586,11 @@
 	switch(atoi(command))
 	{
 		case DOODLE_CMD_REQUEST:
-			yahoo_doodle_command_got_request(gc, from);
+			yahoo_doodle_command_got_request(gc, from, imv_key);
 			break;
 
 		case DOODLE_CMD_READY:
-			yahoo_doodle_command_got_ready(gc, from);
+			yahoo_doodle_command_got_ready(gc, from, imv_key);
 			break;
 
 		case DOODLE_CMD_CLEAR:
@@ -592,7 +602,7 @@
 			break;
 
 		case DOODLE_CMD_EXTRA:
-			yahoo_doodle_command_got_extra(gc, from, message);
+			yahoo_doodle_command_got_extra(gc, from, message, imv_key);
 			break;
 
 		case DOODLE_CMD_CONFIRM:
--- a/libpurple/protocols/yahoo/yahoo_doodle.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_doodle.h	Mon Oct 01 17:49:01 2007 +0000
@@ -31,7 +31,7 @@
 #include "whiteboard.h"
 #include "cmds.h"
 
-#define DOODLE_IMV_KEY "doodle;103"
+#define DOODLE_IMV_KEY "doodle;106"
 
 /******************************************************************************
  * Defines
@@ -94,6 +94,7 @@
 {
 	int brush_size;  /* Size of drawing brush */
 	int brush_color; /* Color of drawing brush */
+	gchar *imv_key;
 } doodle_session;
 
 /******************************************************************************
@@ -104,17 +105,17 @@
 									   char **error, void *data);
 
 void yahoo_doodle_process(PurpleConnection *gc, const char *me, const char *from,
-						  const char *command, const char *message);
+						  const char *command, const char *message, const char *imv_key);
 void yahoo_doodle_initiate(PurpleConnection *gc, const char *to);
 
 void yahoo_doodle_command_got_shutdown(PurpleConnection *gc, const char *from);
 
-void yahoo_doodle_command_send_request(PurpleConnection *gc, const char *to);
-void yahoo_doodle_command_send_ready(PurpleConnection *gc, const char *to);
-void yahoo_doodle_command_send_draw(PurpleConnection *gc, const char *to, const char *message);
-void yahoo_doodle_command_send_clear(PurpleConnection *gc, const char *to);
-void yahoo_doodle_command_send_extra(PurpleConnection *gc, const char *to, const char *message);
-void yahoo_doodle_command_send_confirm(PurpleConnection *gc, const char *to);
+void yahoo_doodle_command_send_request(PurpleConnection *gc, const char *to, const char *imv_key);
+void yahoo_doodle_command_send_ready(PurpleConnection *gc, const char *to, const char *imv_key);
+void yahoo_doodle_command_send_draw(PurpleConnection *gc, const char *to, const char *message, const char *imv_key);
+void yahoo_doodle_command_send_clear(PurpleConnection *gc, const char *to, const char *imv_key);
+void yahoo_doodle_command_send_extra(PurpleConnection *gc, const char *to, const char *message, const char *imv_key);
+void yahoo_doodle_command_send_confirm(PurpleConnection *gc, const char *to, const char *imv_key);
 void yahoo_doodle_command_send_shutdown(PurpleConnection *gc, const char *to);
 
 void yahoo_doodle_start(PurpleWhiteboard *wb);
--- a/libpurple/proxy.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/proxy.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file proxy.c Proxy API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/proxy.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/proxy.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file proxy.h Proxy API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/prpl.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/prpl.c	Mon Oct 01 17:49:01 2007 +0000
@@ -226,6 +226,9 @@
 	{
 		if (!purple_account_is_disconnected(account))
 			purple_account_disconnect(account);
+		/* Clear out the unsaved password if we're already disconnected and we switch to offline status */
+		else if (!purple_account_get_remember_password(account))
+			purple_account_set_password(account, NULL);
 		return;
 	}
 
--- a/libpurple/prpl.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/prpl.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file prpl.h Protocol Plugin functions
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/request.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/request.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file request.c Request API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/request.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/request.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file request.h Request API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/roomlist.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/roomlist.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file roomlist.c Room List API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/roomlist.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/roomlist.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file roomlist.h Room List API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/savedstatuses.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/savedstatuses.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file savedstatuses.c Saved Status API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/savedstatuses.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/savedstatuses.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file savedstatuses.h Saved Status API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/server.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/server.c	Mon Oct 01 17:49:01 2007 +0000
@@ -143,6 +143,7 @@
 	 */
 	auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
 	if ((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
+			flags & PURPLE_MESSAGE_AUTO_RESP &&
 			!purple_presence_is_available(presence) &&
 			strcmp(auto_reply_pref, "never")) {
 
--- a/libpurple/server.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/server.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file server.h Server API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/signals.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/signals.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file signals.c Signal API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/signals.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/signals.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file signals.h Signal API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/sound.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/sound.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file sound.h Sound API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/sslconn.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/sslconn.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file sslconn.c SSL API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/sslconn.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/sslconn.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file sslconn.h SSL API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/status.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/status.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file status.c Status API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -600,7 +601,7 @@
 	{
 		time_t current_time = time(NULL);
 		const char *buddy_alias = purple_buddy_get_alias(buddy);
-		char *tmp;
+		char *tmp, *logtmp;
 		PurpleLog *log;
 
 		if (old_status != NULL)
@@ -608,6 +609,10 @@
 			tmp = g_strdup_printf(_("%s changed status from %s to %s"), buddy_alias,
 			                      purple_status_get_name(old_status),
 			                      purple_status_get_name(new_status));
+			logtmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias, buddy->name,
+			                      purple_status_get_name(old_status),
+			                      purple_status_get_name(new_status));
+
 		}
 		else
 		{
@@ -617,11 +622,16 @@
 			{
 				tmp = g_strdup_printf(_("%s is now %s"), buddy_alias,
 				                      purple_status_get_name(new_status));
+				logtmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name,
+				                      purple_status_get_name(new_status));
+
 			}
 			else
 			{
 				tmp = g_strdup_printf(_("%s is no longer %s"), buddy_alias,
 				                      purple_status_get_name(new_status));
+				logtmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name,
+				                      purple_status_get_name(new_status));
 			}
 		}
 
@@ -629,10 +639,11 @@
 		if (log != NULL)
 		{
 			purple_log_write(log, PURPLE_MESSAGE_SYSTEM, buddy_alias,
-			               current_time, tmp);
+			               current_time, logtmp);
 		}
 
 		g_free(tmp);
+		g_free(logtmp);
 	}
 }
 
--- a/libpurple/stringref.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/stringref.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file stringref.c Reference-counted immutable strings
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/stringref.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/stringref.h	Mon Oct 01 17:49:01 2007 +0000
@@ -3,8 +3,9 @@
 /**
  * @file stringref.h Reference-counted immutable strings
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/stun.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/stun.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file stun.c STUN (RFC3489) Implementation
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * STUN implementation inspired by jstun [http://jstun.javawi.de/]
  *
--- a/libpurple/stun.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/stun.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file stun.h STUN API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/upnp.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/upnp.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file upnp.c UPnP Implementation
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/upnp.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/upnp.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file upnp.h Universal Plug N Play API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/util.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/util.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file util.h Utility Functions
  * @ingroup core
- *
- * Purple is the legal property of its developers, whose names are too numerous
+ */
+
+/* Purple 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.
  *
@@ -67,7 +68,7 @@
 };
 
 static char *custom_user_dir = NULL;
-static char *home_dir = NULL;
+static char *user_dir = NULL;
 
 PurpleMenuAction *
 purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data,
@@ -1542,7 +1543,8 @@
 					while(*p && *p != '>') {
 						if(!g_ascii_strncasecmp(p, "href=", strlen("href="))) {
 							const char *q = p + strlen("href=");
-							g_string_free(url, TRUE);
+							if (url)
+								g_string_free(url, TRUE);
 							url = g_string_new("");
 							cdata = g_string_new("");
 							if(*q == '\'' || *q == '\"')
@@ -2463,10 +2465,10 @@
 {
 	if (custom_user_dir != NULL)
 		return custom_user_dir;
-	else if (!home_dir)
-		home_dir = g_build_filename(purple_home_dir(), ".purple", NULL);
-
-	return home_dir;
+	else if (!user_dir)
+		user_dir = g_build_filename(purple_home_dir(), ".purple", NULL);
+
+	return user_dir;
 }
 
 void purple_util_set_user_dir(const char *dir)
--- a/libpurple/util.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/util.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file util.h Utility Functions
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/value.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/value.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file value.c Value wrapper API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/value.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/value.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file value.h Value wrapper API
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/version.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/version.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file version.c Version Functions
  * @ingroup core
- *
- * Purple is the legal property of its developers, whose names are too numerous
+ */
+
+/* Purple 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.
  *
--- a/libpurple/whiteboard.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/whiteboard.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file whiteboard.h The PurpleWhiteboard core object
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/win32/win32dep.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/win32/win32dep.c	Mon Oct 01 17:49:01 2007 +0000
@@ -43,7 +43,6 @@
 /*
  *  DEFINES & MACROS
  */
-#define WIN32_PROXY_REGKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
 
 /* For shfolder.dll */
 typedef HRESULT (CALLBACK* LPFNSHGETFOLDERPATHA)(HWND, int, HANDLE, DWORD, LPSTR);
@@ -57,9 +56,6 @@
 
 static HINSTANCE libpurpledll_hInstance = 0;
 
-static HANDLE proxy_change_event = NULL;
-static HKEY proxy_regkey = NULL;
-
 /*
  *  PUBLIC CODE
  */
@@ -431,86 +427,93 @@
 	return result;
 }
 
-static void wpurple_refresh_proxy(void) {
-	gboolean set_proxy = FALSE;
-	DWORD enabled = 0;
+/* the winapi headers don't yet have winhttp.h, so we use the struct from msdn directly */
+typedef struct {
+  BOOL fAutoDetect;
+  LPWSTR lpszAutoConfigUrl;
+  LPWSTR lpszProxy;
+  LPWSTR lpszProxyBypass;
+} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
 
-	wpurple_read_reg_dword(HKEY_CURRENT_USER, WIN32_PROXY_REGKEY,
-				"ProxyEnable", &enabled);
-
-	if (enabled & 1) {
-		char *c = NULL;
-		char *tmp = wpurple_read_reg_string(HKEY_CURRENT_USER, WIN32_PROXY_REGKEY,
-				"ProxyServer");
+typedef BOOL (CALLBACK* LPFNWINHTTPGETIEPROXYCONFIG)(/*IN OUT*/ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* pProxyConfig);
 
-		/* There are proxy settings for several protocols */
-		if (tmp && (c = g_strstr_len(tmp, strlen(tmp), "http="))) {
-			char *d;
-			c += strlen("http=");
-			d = strchr(c, ';');
-			if (d)
-				*d = '\0';
-			/* c now points the proxy server (and port) */
+gboolean wpurple_check_for_proxy_changes(void) {
+	static gboolean loaded = FALSE;
+	static LPFNWINHTTPGETIEPROXYCONFIG MyWinHttpGetIEProxyConfig = NULL;
 
-		/* There is only a global proxy */
-		} else if (tmp && strlen(tmp) > 0 && !strchr(tmp, ';')) {
-			c = tmp;
+	WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config;
+	char *tmp = NULL, *c = NULL;
+	gboolean changed = FALSE;
+
+	if (!loaded) {
+		loaded = TRUE;
+
+		if (getenv("HTTP_PROXY") || getenv("http_proxy") || getenv("HTTPPROXY")) {
+			purple_debug_info("wpurple", "HTTP_PROXY env. var already set.  Ignoring win32 Internet Settings.\n");
+			return FALSE;
 		}
 
-		if (c) {
+		MyWinHttpGetIEProxyConfig = (LPFNWINHTTPGETIEPROXYCONFIG)
+			wpurple_find_and_loadproc("winhttp.dll", "WinHttpGetIEProxyConfigForCurrentUser");
+		if (!MyWinHttpGetIEProxyConfig)
+			purple_debug_info("wpurple", "Unable to read Windows Proxy Settings.\n");
+	}
+
+	if (!MyWinHttpGetIEProxyConfig)
+		return FALSE;
+
+	ZeroMemory(&ie_proxy_config, sizeof(ie_proxy_config));
+	if (!MyWinHttpGetIEProxyConfig(&ie_proxy_config)) {
+		purple_debug_error("wpurple", "Error reading Windows Proxy Settings(%u).\n", GetLastError());
+		return FALSE;
+	}
+
+	/* We can't do much if it is autodetect*/
+	if (ie_proxy_config.fAutoDetect)
+		purple_debug_error("wpurple", "Windows Proxy Settings set to autodetect (not supported).\n");
+	else if (ie_proxy_config.lpszProxy) {
+		tmp = g_utf16_to_utf8(ie_proxy_config.lpszProxy, -1,
+						NULL, NULL, NULL);
+		/* We can't do anything about the bypass list, as we don't have the url */
+	} else
+		purple_debug_info("wpurple", "No Windows Proxy Set.\n");
+
+	if (ie_proxy_config.lpszAutoConfigUrl)
+		GlobalFree(ie_proxy_config.lpszAutoConfigUrl);
+	if (ie_proxy_config.lpszProxy)
+		GlobalFree(ie_proxy_config.lpszProxy);
+	if (ie_proxy_config.lpszProxyBypass)
+		GlobalFree(ie_proxy_config.lpszProxyBypass);
+
+	/* There are proxy settings for several protocols */
+	if (tmp && (c = g_strstr_len(tmp, strlen(tmp), "http="))) {
+		char *d;
+		c += strlen("http=");
+		d = strchr(c, ';');
+		if (d)
+			*d = '\0';
+		/* c now points the proxy server (and port) */
+	/* There is only a global proxy */
+	} else if (tmp && strlen(tmp) > 0 && !strchr(tmp, ';')) {
+		c = tmp;
+	}
+
+	if (c && *c) {
+		const char *current = g_getenv("HTTP_PROXY");
+		if (!current || strcmp(current, c)) {
 			purple_debug_info("wpurple", "Setting HTTP Proxy: 'http://%s'\n", c);
 			g_setenv("HTTP_PROXY", c, TRUE);
-			set_proxy = TRUE;
+			changed = TRUE;
 		}
-		g_free(tmp);
 	}
-
 	/* If there previously was a proxy set and there isn't one now, clear it */
-	if (getenv("HTTP_PROXY") && !set_proxy) {
+	else if (getenv("HTTP_PROXY")) {
 		purple_debug_info("wpurple", "Clearing HTTP Proxy\n");
 		g_unsetenv("HTTP_PROXY");
-	}
-}
-
-static void watch_for_proxy_changes(void) {
-	LONG rv;
-	DWORD filter = REG_NOTIFY_CHANGE_NAME |
-			REG_NOTIFY_CHANGE_LAST_SET;
-
-	if (!proxy_regkey &&
-			!(proxy_regkey = _reg_open_key(HKEY_CURRENT_USER,
-				WIN32_PROXY_REGKEY, KEY_NOTIFY))) {
-		return;
-	}
-
-	if (!(proxy_change_event = CreateEvent(NULL, TRUE, FALSE, NULL))) {
-		char *errmsg = g_win32_error_message(GetLastError());
-		purple_debug_error("wpurple", "Unable to watch for proxy changes: %s\n", errmsg);
-		g_free(errmsg);
-		return;
+		changed = TRUE;
 	}
 
-	rv = RegNotifyChangeKeyValue(proxy_regkey, TRUE, filter, proxy_change_event, TRUE);
-	if (rv != ERROR_SUCCESS) {
-		char *errmsg = g_win32_error_message(rv);
-		purple_debug_error("wpurple", "Unable to watch for proxy changes: %s\n", errmsg);
-		g_free(errmsg);
-		CloseHandle(proxy_change_event);
-		proxy_change_event = NULL;
-	}
-
-}
-
-gboolean wpurple_check_for_proxy_changes(void) {
-	gboolean changed = FALSE;
-
-	if (proxy_change_event && WaitForSingleObject(proxy_change_event, 0) == WAIT_OBJECT_0) {
-		CloseHandle(proxy_change_event);
-		proxy_change_event = NULL;
-		changed = TRUE;
-		wpurple_refresh_proxy();
-		watch_for_proxy_changes();
-	}
+	g_free(tmp);
 
 	return changed;
 }
@@ -555,15 +558,6 @@
 	if (!g_thread_supported())
 		g_thread_init(NULL);
 
-	/* If the proxy server environment variables are already set,
-	 * we shouldn't override them */
-	if (!getenv("HTTP_PROXY") && !getenv("http_proxy") && !getenv("HTTPPROXY")) {
-		wpurple_refresh_proxy();
-		watch_for_proxy_changes();
-	} else {
-		purple_debug_info("wpurple", "HTTP_PROXY env. var already set.  Ignoring win32 Internet Settings.\n");
-	}
-
 	purple_debug_info("wpurple", "wpurple_init end\n");
 }
 
@@ -578,11 +572,6 @@
 	g_free(app_data_dir);
 	app_data_dir = NULL;
 
-	if (proxy_regkey) {
-		RegCloseKey(proxy_regkey);
-		proxy_regkey = NULL;
-	}
-
 	libpurpledll_hInstance = NULL;
 }
 
--- a/libpurple/xmlnode.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/xmlnode.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file xmlnode.c XML DOM functions
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/libpurple/xmlnode.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/libpurple/xmlnode.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file xmlnode.h XML DOM functions
  * @ingroup core
- *
- * purple
+ */
+
+/* purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/Makefile.am	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/Makefile.am	Mon Oct 01 17:49:01 2007 +0000
@@ -29,6 +29,7 @@
 		win32/nsis/langmacros.nsh \
 		win32/nsis/translations/afrikaans.nsh \
 		win32/nsis/translations/albanian.nsh \
+		win32/nsis/translations/arabic.nsh \
 		win32/nsis/translations/basque.nsh \
 		win32/nsis/translations/bulgarian.nsh \
 		win32/nsis/translations/catalan.nsh \
--- a/pidgin/gtkaccount.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkaccount.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkaccount.c GTK+ Account Editor UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -249,9 +250,6 @@
 
 	new_plugin = purple_find_prpl(id);
 
-	if (new_plugin == dialog->plugin)
-		return;
-
 	dialog->plugin = new_plugin;
 
 	if (dialog->plugin != NULL)
@@ -269,6 +267,8 @@
 	add_user_options(dialog,     dialog->top_vbox);
 	add_protocol_options(dialog, dialog->bottom_vbox);
 
+	gtk_widget_grab_focus(dialog->protocol_menu);
+
 	if (!dialog->prpl_info || !dialog->prpl_info->register_user || 
 	    g_object_get_data(G_OBJECT(item), "fake")) {
 		gtk_widget_hide(dialog->register_button);
--- a/pidgin/gtkaccount.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkaccount.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkaccount.h GTK+ Account Editor UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkblist.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkblist.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkblist.c GTK+ BuddyList API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -87,6 +88,8 @@
 	GtkWidget *group_combo;
 	GtkWidget *entries_box;
 	GtkSizeGroup *sg;
+	GtkWidget *autojoin;
+	GtkWidget *persistent;
 
 	GList *entries;
 
@@ -137,13 +140,23 @@
 static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender);
 static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node);
 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded);
-
-struct _pidgin_blist_node {
+static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node);
+
+typedef enum {
+	PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE    =  1 << 0,  /* Whether there's pending message in a conversation */
+} PidginBlistNodeFlags;
+
+typedef struct _pidgin_blist_node {
 	GtkTreeRowReference *row;
 	gboolean contact_expanded;
 	gboolean recent_signonoff;
 	gint recent_signonoff_timer;
-};
+	struct {
+		PurpleConversation *conv;
+		time_t last_message;          /* timestamp for last displayed message */
+		PidginBlistNodeFlags flags;
+	} conv;
+} PidginBlistNode;
 
 static char dim_grey_string[8] = "";
 static char *dim_grey()
@@ -305,12 +318,36 @@
 	serv_send_file(b->account->gc, b->name, NULL);
 }
 
+static void gtk_blist_menu_move_to_cb(GtkWidget *w, PurpleBlistNode *node)
+{
+	PurpleGroup *group = g_object_get_data(G_OBJECT(w), "groupnode");
+	purple_blist_add_contact((PurpleContact *)node, group, NULL);
+
+}
+
 static void gtk_blist_menu_autojoin_cb(GtkWidget *w, PurpleChat *chat)
 {
 	purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin",
 			gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
 }
 
+static void gtk_blist_menu_persistent_cb(GtkWidget *w, PurpleChat *chat)
+{
+	purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent",
+			gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
+}
+
+static PurpleConversation *
+find_conversation_with_buddy(PurpleBuddy *buddy)
+{
+	PidginBlistNode *ui = buddy->node.ui_data;
+	if (ui)
+		return ui->conv.conv;
+	return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+									     purple_buddy_get_name(buddy),
+									     purple_buddy_get_account(buddy));
+}
+
 static void gtk_blist_join_chat(PurpleChat *chat)
 {
 	PurpleConversation *conv;
@@ -394,6 +431,105 @@
 }
 #endif
 
+static void
+gtk_blist_do_personize(GList *merges)
+{
+	PurpleBlistNode *contact = NULL;
+	int max = 0;
+	GList *tmp;
+
+	/* First, we find the contact to merge the rest of the buddies into.
+ 	 * This will be the contact with the most buddies in it; ties are broken
+ 	 * by which contact is higher in the list
+ 	 */
+	for (tmp = merges; tmp; tmp = tmp->next) {
+		PurpleBlistNode *node = tmp->data;
+		PurpleBlistNode *b;
+		int i = 0;
+
+		if (node->type == PURPLE_BLIST_BUDDY_NODE)
+			node = node->parent;
+
+		if (node->type != PURPLE_BLIST_CONTACT_NODE)
+			continue;
+		
+
+		for (b = node->child; b; b = b->next)
+			i++;
+		if (i > max) {
+			contact = node;
+			max = i;
+		}
+	}
+
+	if (contact == NULL)
+		return;
+
+	/* Merge all those buddies into this contact */
+	for (tmp = merges; tmp; tmp = tmp->next) {
+		PurpleBlistNode *node = tmp->data;
+		if (node->type == PURPLE_BLIST_BUDDY_NODE)
+			node = node->parent;
+
+		if (node == contact)
+			continue;
+
+		purple_blist_merge_contact((PurpleContact *)node, contact);
+	}
+
+	/* And show the expanded contact, so the people know what's going on */
+	pidgin_blist_expand_contact_cb(NULL, contact);
+	g_list_free(merges);
+}
+
+static void
+gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias)
+{
+	PurpleBlistNode *contact;
+	PurpleBlistNode *buddy;
+	GList *merges = NULL;
+	int i = 0;
+	char *a = g_utf8_casefold(alias, -1);
+
+	for (contact = group->child; contact; contact = contact->next) {
+		char *node_alias;
+		if (contact->type != PURPLE_BLIST_CONTACT_NODE)
+			continue;
+
+		node_alias = g_utf8_casefold(purple_contact_get_alias((PurpleContact *)contact), -1);
+		if (node_alias && !g_utf8_collate(node_alias, a)) {
+			merges = g_list_append(merges, contact);
+			i++;
+			g_free(node_alias);
+			continue;
+		}
+		g_free(node_alias);
+
+		for (buddy = contact->child; buddy; buddy = buddy->next) {
+			if (buddy->type != PURPLE_BLIST_BUDDY_NODE)
+				continue;
+
+			node_alias = g_utf8_casefold(purple_buddy_get_alias((PurpleBuddy *)buddy), -1);
+			if (node_alias && !g_utf8_collate(node_alias, a)) {
+				merges = g_list_append(merges, buddy);
+				i++;
+			}
+			g_free(node_alias);
+		}
+	}
+	g_free(a);
+	
+	if (i > 1)
+	{
+		char *msg = g_strdup_printf(ngettext("You currently have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias);
+		purple_request_action(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. "
+							 "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL, NULL, NULL,
+				      merges, 2, _("_Merge"), PURPLE_CALLBACK(gtk_blist_do_personize), _("_Cancel"), PURPLE_CALLBACK(g_list_free));
+		g_free(msg);
+	} else
+		g_list_free(merges);
+}
+
 static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *arg1,
 					 char *arg2, PurpleBuddyList *list)
 {
@@ -420,13 +556,14 @@
 				PurpleContact *contact = (PurpleContact *)node;
 				struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data;
 
-				if (contact->alias || gtknode->contact_expanded)
+				if (contact->alias || gtknode->contact_expanded) {
 					purple_blist_alias_contact(contact, arg2);
-				else
-				{
+					gtk_blist_auto_personize(node->parent, arg2);
+				} else {
 					PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact);
 					purple_blist_alias_buddy(buddy, arg2);
 					serv_alias_buddy(buddy);
+					gtk_blist_auto_personize(node->parent, arg2);
 				}
 			}
 			break;
@@ -434,6 +571,7 @@
 		case PURPLE_BLIST_BUDDY_NODE:
 			purple_blist_alias_buddy((PurpleBuddy*)node, arg2);
 			serv_alias_buddy((PurpleBuddy *)node);
+			gtk_blist_auto_personize(node->parent->parent, arg2);
 			break;
 		case PURPLE_BLIST_GROUP_NODE:
 			dest = purple_find_group(arg2);
@@ -1118,6 +1256,34 @@
 	g_list_free(ll);
 }
 
+
+
+static void
+pidgin_append_blist_node_move_to_menu(GtkWidget *menu, PurpleBlistNode *node)
+{
+	GtkWidget *submenu;
+	GtkWidget *menuitem;
+	PurpleBlistNode *group;
+
+	menuitem = gtk_menu_item_new_with_label(_("Move to"));
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+	gtk_widget_show(menuitem);
+
+	submenu = gtk_menu_new();
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+
+	for (group = purple_blist_get_root(); group; group = group->next) {
+		if (group->type != PURPLE_BLIST_GROUP_NODE)
+			continue;
+		if (group == node->parent)
+			continue;
+		menuitem = pidgin_new_item_from_stock(submenu, purple_group_get_name((PurpleGroup *)group), NULL,
+						      G_CALLBACK(gtk_blist_menu_move_to_cb), node, 0, 0, NULL);
+		g_object_set_data(G_OBJECT(menuitem), "groupnode", group);
+	}
+	gtk_widget_show_all(submenu);
+}
+
 void
 pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub) {
 	PurplePluginProtocolInfo *prpl_info;
@@ -1169,6 +1335,9 @@
 										  (PurpleBlistNode *)buddy);
 	pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy);
 
+	if (!contact_expanded)
+		pidgin_append_blist_node_move_to_menu(menu, (PurpleBlistNode *)contact);
+
 	if (((PurpleBlistNode*)buddy)->parent && ((PurpleBlistNode*)buddy)->parent->child->next && 
               !sub && !contact_expanded) {
 		pidgin_separator(menu);
@@ -1255,16 +1424,19 @@
 create_chat_menu(PurpleBlistNode *node, PurpleChat *c)
 {
 	GtkWidget *menu;
-	gboolean autojoin;
+	gboolean autojoin, persistent;
 
 	menu = gtk_menu_new();
 	autojoin = (purple_blist_node_get_bool(node, "gtk-autojoin") ||
 			(purple_blist_node_get_string(node, "gtk-autojoin") != NULL));
+	persistent = purple_blist_node_get_bool(node, "gtk-persistent");
 
 	pidgin_new_item_from_stock(menu, _("_Join"), PIDGIN_STOCK_CHAT,
 			G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL);
 	pidgin_new_check_item(menu, _("Auto-Join"),
 			G_CALLBACK(gtk_blist_menu_autojoin_cb), node, autojoin);
+	pidgin_new_check_item(menu, _("Persistent"),
+			G_CALLBACK(gtk_blist_menu_persistent_cb), node, persistent);
 	pidgin_new_item_from_stock(menu, _("View _Log"), NULL,
 			G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL);
 
@@ -1306,7 +1478,6 @@
 				 node, 0, 0, NULL);
 
 	pidgin_append_blist_node_extended_menu(menu, node);
-
 	return menu;
 }
 
@@ -2264,18 +2435,19 @@
 		if (prpl_info && prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_DISPLAY)
 			purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &scale_width, &scale_height);
 
-		if (scaled) {
+		if (scaled || scale_height > 200 || scale_width > 200) {
+			float scale_size = scaled ? 32.0 : 200.0;
 			if(scale_height > scale_width) {
-				scale_width = 32.0 * (double)scale_width / (double)scale_height;
-				scale_height = 32;
+				scale_width = scale_size * (double)scale_width / (double)scale_height;
+				scale_height = scale_size;
 			} else {
-				scale_height = 32.0 * (double)scale_height / (double)scale_width;
-				scale_width = 32;
+				scale_height = scale_size * (double)scale_height / (double)scale_width;
+				scale_width = scale_size;
 			}
 
-			ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
+			ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_size, scale_size);
 			gdk_pixbuf_fill(ret, 0x00000000);
-			gdk_pixbuf_scale(buf, ret, (32-scale_width)/2, (32-scale_height)/2, scale_width, scale_height, (32-scale_width)/2, (32-scale_height)/2, (double)scale_width/(double)orig_width, (double)scale_height/(double)orig_height, GDK_INTERP_BILINEAR);
+			gdk_pixbuf_scale(buf, ret, (scale_size-scale_width)/2, (scale_size-scale_height)/2, scale_width, scale_height, (scale_size-scale_width)/2, (scale_size-scale_height)/2, (double)scale_width/(double)orig_width, (double)scale_height/(double)orig_height, GDK_INTERP_BILINEAR);
 			if (pidgin_gdk_pixbuf_is_opaque(ret))
 				pidgin_gdk_pixbuf_make_round(ret);
 		} else {
@@ -2905,7 +3077,7 @@
 	{ N_("/_Help"), NULL, NULL, 0, "<Branch>", NULL },
 	{ N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP },
 	{ N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "<Item>", NULL },
-#if GTK_CHECK_VERSION(2,6,0)	
+#if GTK_CHECK_VERSION(2,6,0)
 	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 0,  "<StockItem>", GTK_STOCK_ABOUT },
 #else
 	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 0,  "<Item>", NULL },
@@ -3143,8 +3315,6 @@
 	GdkPixbuf *ret;
 	PurplePresence *p;
 
-
-
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		if(!gtknode->contact_expanded) {
 			buddy = purple_contact_get_priority_buddy((PurpleContact*)node);
@@ -3186,6 +3356,13 @@
 		return ret;
 	}
 
+	if (purple_status_get_attr_string(purple_presence_get_active_status(p), PURPLE_TUNE_TITLE)) {
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
+		ret = gdk_pixbuf_new_from_file(path, NULL);
+		g_free(path);
+		return ret;
+	}
+
 	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
 	if (!prpl)
 		return NULL;
@@ -3259,22 +3436,22 @@
 	}
 
 	if(buddy) {
-	  	PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-									     purple_buddy_get_name(buddy),
-									     purple_buddy_get_account(buddy));
+	  	PurpleConversation *conv = find_conversation_with_buddy(buddy);
 		PurplePresence *p;
 		gboolean trans;
 
 		if(conv != NULL) {
 			PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
-			if((gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) && size == PIDGIN_STATUS_ICON_SMALL) {
-				return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_MESSAGE,
-							       icon_size, "GtkTreeView");
+			if (gtkconv == NULL && size == PIDGIN_STATUS_ICON_SMALL) {
+				PidginBlistNode *ui = buddy->node.ui_data;
+				if (ui == NULL || (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE))
+					return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview),
+							PIDGIN_STOCK_STATUS_MESSAGE, icon_size, "GtkTreeView");
 			}
 		}
 
 		p = purple_buddy_get_presence(buddy);
-		trans = (purple_presence_is_idle(p) && size == PIDGIN_STATUS_ICON_SMALL);
+		trans = purple_presence_is_idle(p);
 
 		if (PURPLE_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff)
 			icon = PIDGIN_STOCK_STATUS_LOGIN;
@@ -3325,16 +3502,17 @@
 	struct _pidgin_blist_node *gtkcontactnode = NULL;
 	char *idletime = NULL, *statustext = NULL;
 	time_t t;
-	PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-								     purple_buddy_get_name(b),
-								     purple_buddy_get_account(b));
-	PidginConversation *gtkconv;
+	PurpleConversation *conv = find_conversation_with_buddy(b);
 	gboolean hidden_conv = FALSE;
 
-	if(conv != NULL) {
-		gtkconv = PIDGIN_CONVERSATION(conv);
-		if(gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) {
-			hidden_conv = TRUE;
+	if (conv != NULL) {
+		PidginBlistNode *ui = b->node.ui_data;
+		if (ui) {
+			if (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)
+				hidden_conv = TRUE;
+		} else {
+			if (PIDGIN_CONVERSATION(conv) == NULL)
+				hidden_conv = TRUE;
 		}
 	}
 
@@ -3669,7 +3847,7 @@
 		menu = NULL;
 	}
 
-	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
 	if (!convs)
 		/* no conversations added, don't show the menu */
 		return;
@@ -3725,7 +3903,7 @@
 		gtkblist->menutrayicon = NULL;
 	}
 
-	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0);
+	convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0);
 	if (convs) {
 		GtkWidget *img = NULL;
 		GString *tooltip_text = NULL;
@@ -3733,14 +3911,10 @@
 		tooltip_text = g_string_new("");
 		l = convs;
 		while (l != NULL) {
-			if (PIDGIN_IS_PIDGIN_CONVERSATION(l->data)) {
-				PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data);
-
-				g_string_append_printf(tooltip_text,
-						ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
-						gtkconv->unseen_count,
-						gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
-			}
+			int count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count"));
+			g_string_append_printf(tooltip_text,
+					ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count),
+					count, purple_conversation_get_name(l->data));
 			l = l->next;
 		}
 		if(tooltip_text->len > 0) {
@@ -3768,6 +3942,88 @@
 	conversation_updated_cb(conv, PURPLE_CONV_UPDATE_UNSEEN, gtkblist);
 }
 
+static void
+conversation_deleted_update_ui_cb(PurpleConversation *conv, struct _pidgin_blist_node *ui)
+{
+	if (ui->conv.conv != conv)
+		return;
+	ui->conv.conv = NULL;
+	ui->conv.flags = 0;
+	ui->conv.last_message = 0;
+}
+
+static void
+written_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message,
+		PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node)
+{
+	PidginBlistNode *ui = node->ui_data;
+	if (ui->conv.conv != conv || PIDGIN_CONVERSATION(conv) ||
+			!(flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
+		return;
+	ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+	ui->conv.last_message = time(NULL);    /* XXX: for lack of better data */
+	pidgin_blist_update(purple_get_blist(), node);
+}
+
+static void
+displayed_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message,
+		PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node)
+{
+	PidginBlistNode *ui = node->ui_data;
+	if (ui->conv.conv != conv)
+		return;
+	ui->conv.flags &= ~PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+	pidgin_blist_update(purple_get_blist(), node);
+}
+
+static void
+conversation_created_cb(PurpleConversation *conv, PidginBuddyList *gtkblist)
+{
+	switch (conv->type) {
+		case PURPLE_CONV_TYPE_IM:
+			{
+				GSList *buddies = purple_find_buddies(conv->account, conv->name);
+				while (buddies) {
+					PurpleBlistNode *buddy = buddies->data;
+					struct _pidgin_blist_node *ui = buddy->ui_data;
+					buddies = g_slist_delete_link(buddies, buddies);
+					if (!ui)
+						continue;
+					ui->conv.conv = conv;
+					ui->conv.flags = 0;
+					ui->conv.last_message = 0;
+					purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+							ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+					purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg",
+							ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy);
+					purple_signal_connect(pidgin_conversations_get_handle(), "displayed-im-msg",
+							ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy);
+				}
+			}
+		case PURPLE_CONV_TYPE_CHAT:
+			{
+				PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name);
+				struct _pidgin_blist_node *ui;
+				if (!chat)
+					break;
+				ui = chat->node.ui_data;
+				if (!ui)
+					break;
+				ui->conv.conv = conv;
+				ui->conv.flags = 0;
+				ui->conv.last_message = 0;
+				purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
+						ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui);
+				purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg",
+						ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat);
+				purple_signal_connect(pidgin_conversations_get_handle(), "displayed-chat-msg",
+						ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat);
+			}
+		default:
+			break;
+	}
+}
+
 /**********************************************************************************
  * Public API Functions                                                           *
  **********************************************************************************/
@@ -4536,7 +4792,7 @@
 							  "visible", EMBLEM_VISIBLE_COLUMN, NULL);
 
 	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);	
+	gtk_tree_view_column_pack_start(column, rend, FALSE);
 	gtk_tree_view_column_set_attributes(column, rend,
 					   "pixbuf", PROTOCOL_ICON_COLUMN,
 					   "visible", PROTOCOL_ICON_VISIBLE_COLUMN,
@@ -4681,6 +4937,9 @@
 	purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
 						gtkblist, PURPLE_CALLBACK(conversation_deleting_cb),
 						gtkblist);
+	purple_signal_connect(purple_conversations_get_handle(), "conversation-created",
+			gtkblist, PURPLE_CALLBACK(conversation_created_cb),
+			gtkblist);
 
 	gtk_widget_hide(gtkblist->headline_hbox);
 	gtk_widget_hide(gtkblist->error_buttons);
@@ -4787,6 +5046,7 @@
 		if(gtknode->recent_signonoff_timer > 0)
 			purple_timeout_remove(gtknode->recent_signonoff_timer);
 
+		purple_signals_disconnect_by_handle(node->ui_data);
 		g_free(node->ui_data);
 		node->ui_data = NULL;
 	}
@@ -4951,7 +5211,7 @@
 				   STATUS_ICON_COLUMN, NULL,
 				   NAME_COLUMN, title,
 				   NODE_COLUMN, gnode,
-				   BGCOLOR_COLUMN, &bgcolor,
+	/* 			   BGCOLOR_COLUMN, &bgcolor,     */
 				   GROUP_EXPANDER_COLUMN, TRUE,
 				   GROUP_EXPANDER_VISIBLE_COLUMN, TRUE,
 				   CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE,
@@ -4977,7 +5237,7 @@
 
 	group = (PurpleGroup*)gnode;
 	textcolor = gtkblist->treeview->style->fg[GTK_STATE_ACTIVE];
-        
+
 	if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)), NULL, &iter)) {
 		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter,
 				NODE_COLUMN, &selected_node, -1);
@@ -5211,14 +5471,17 @@
 		GdkPixbuf *emblem;
 		char *mark;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
-		const char *name = purple_chat_get_name(chat);
-		PurpleConversation *conv =
-				purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account);
-		gboolean hidden = (conv && !PIDGIN_CONVERSATION(conv));
-
-		if(!insert_node(list, node, &iter))
+		PidginBlistNode *ui;
+		PurpleConversation *conv;
+		gboolean hidden;
+
+		if (!insert_node(list, node, &iter))
 			return;
 
+		ui = node->ui_data;
+		conv = ui->conv.conv;
+		hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE));
+
 		status = pidgin_blist_get_status_icon(node,
 				 PIDGIN_STATUS_ICON_SMALL);
 		emblem = pidgin_blist_get_emblem(node);
@@ -5419,7 +5682,8 @@
 		if (*whoalias == '\0')
 			whoalias = NULL;
 
-		if ((g = purple_find_group(grp)) == NULL)
+		g = NULL;
+		if ((grp != NULL) && (*grp != '\0') && ((g = purple_find_group(grp)) == NULL))
 		{
 			g = purple_group_new(grp);
 			purple_blist_add_group(g, NULL);
@@ -5629,7 +5893,8 @@
 
 	group_name = pidgin_text_combo_box_entry_get_text(data->group_combo);
 
-	if ((group = purple_find_group(group_name)) == NULL)
+	group = NULL;
+	if ((group_name != NULL) && (*group_name != '\0') && ((group = purple_find_group(group_name)) == NULL))
 	{
 		group = purple_group_new(group_name);
 		purple_blist_add_group(group, NULL);
@@ -5640,6 +5905,12 @@
 		purple_blist_add_chat(chat, group, NULL);
 	}
 
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->autojoin)))
+		purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin", TRUE);
+
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->persistent)))
+		purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent", TRUE);
+
 	gtk_widget_destroy(data->window);
 	g_free(data->default_chat_name);
 	g_list_free(data->entries);
@@ -5934,6 +6205,11 @@
 	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_BIN(data->group_combo)->child);
 	pidgin_set_accessible_label (data->group_combo, label);
 	gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0);
+	
+	data->autojoin = gtk_check_button_new_with_mnemonic(_("Autojoin when account becomes online."));
+	data->persistent = gtk_check_button_new_with_mnemonic(_("Hide chat when the window is closed."));
+	gtk_box_pack_start(GTK_BOX(vbox), data->autojoin, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), data->persistent, FALSE, FALSE, 0);
 
 	g_signal_connect(G_OBJECT(data->window), "response",
 					 G_CALLBACK(add_chat_resp_cb), data);
@@ -6015,9 +6291,13 @@
 	gtkblist->headline_callback = callback;
 	gtkblist->headline_data = user_data;
 	gtkblist->headline_destroy = destroy;
-	if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
-		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
-	gtk_widget_show_all(gtkblist->headline_hbox);
+	if (text != NULL || pixbuf != NULL) {
+		if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
+			pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+		gtk_widget_show_all(gtkblist->headline_hbox);
+	} else {
+		gtk_widget_hide(gtkblist->headline_hbox);
+	}
 }
 
 static PurpleBlistUiOps blist_ui_ops =
--- a/pidgin/gtkblist.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkblist.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkblist.h GTK+ Buddy List API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkcellrendererexpander.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkcellrendererexpander.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkcellrendererexpander.c GTK+ Cell Renderer Expander
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -269,6 +270,9 @@
 			    cell_area->x + cell->xpad + (width / 2),
 			    cell_area->y + cell->ypad + (height / 2),
 			    cell->is_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED);
+	if (cell->is_expanded)
+		gtk_paint_hline (widget->style, window, state, NULL, widget, NULL, 0, 
+				 widget->allocation.width, cell_area->y + cell_area->height);
 }
 
 static gboolean pidgin_cell_renderer_expander_activate(GtkCellRenderer *r,
--- a/pidgin/gtkcellrendererprogress.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkcellrendererprogress.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkcellrendererprogress.c GTK+ Cell Renderer Progress
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkcertmgr.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkcertmgr.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkcertmgr.c GTK+ Certificate Manager API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkconn.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkconn.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkconn.c GTK+ Connection API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -52,7 +53,7 @@
  * The key is a pointer to the PurpleAccount and the
  * value is a pointer to a PidginAutoRecon.
  */
-static GHashTable *hash = NULL;
+static GHashTable *auto_reconns = NULL;
 
 static void
 pidgin_connection_connect_progress(PurpleConnection *gc,
@@ -79,7 +80,7 @@
 		pidgin_status_box_set_connecting(PIDGIN_STATUS_BOX(gtkblist->statusbox),
 					   (purple_connections_get_connecting() != NULL));
 
-	g_hash_table_remove(hash, account);
+	g_hash_table_remove(auto_reconns, account);
 
 	pidgin_blist_update_account_error_state(account, NULL);
 }
@@ -119,7 +120,7 @@
 
 	purple_debug_info("autorecon", "do_signon called\n");
 	g_return_val_if_fail(account != NULL, FALSE);
-	info = g_hash_table_lookup(hash, account);
+	info = g_hash_table_lookup(auto_reconns, account);
 
 	if (info)
 		info->timeout = 0;
@@ -142,13 +143,13 @@
 	PidginAutoRecon *info;
 
 	account = purple_connection_get_account(gc);
-	info = g_hash_table_lookup(hash, account);
+	info = g_hash_table_lookup(auto_reconns, account);
 
 	pidgin_blist_update_account_error_state(account, text);
 	if (!gc->wants_to_die) {
 		if (info == NULL) {
 			info = g_new0(PidginAutoRecon, 1);
-			g_hash_table_insert(hash, account, info);
+			g_hash_table_insert(auto_reconns, account, info);
 			info->delay = g_random_int_range(INITIAL_RECON_DELAY_MIN, INITIAL_RECON_DELAY_MAX);
 		} else {
 			info->delay = MIN(2 * info->delay, MAX_RECON_DELAY);
@@ -159,7 +160,7 @@
 	} else {
 		char *p, *s, *n=NULL ;
 		if (info != NULL)
-			g_hash_table_remove(hash, account);
+			g_hash_table_remove(auto_reconns, account);
 
 		if (purple_account_get_alias(account))
 		{
@@ -203,7 +204,7 @@
 
 	while (list) {
 		PurpleAccount *account = (PurpleAccount*)list->data;
-		g_hash_table_remove(hash, account);
+		g_hash_table_remove(auto_reconns, account);
 		if (purple_account_is_disconnected(account))
 			do_signon(account);
 		list = list->next;
@@ -264,7 +265,7 @@
 static void
 account_removed_cb(PurpleAccount *account, gpointer user_data)
 {
-	g_hash_table_remove(hash, account);
+	g_hash_table_remove(auto_reconns, account);
 
 	pidgin_blist_update_account_error_state(account, NULL);
 }
@@ -285,7 +286,7 @@
 void
 pidgin_connection_init(void)
 {
-	hash = g_hash_table_new_full(
+	auto_reconns = g_hash_table_new_full(
 							g_direct_hash, g_direct_equal,
 							NULL, free_auto_recon);
 
@@ -299,5 +300,5 @@
 {
 	purple_signals_disconnect_by_handle(pidgin_connection_get_handle());
 
-	g_hash_table_destroy(hash);
+	g_hash_table_destroy(auto_reconns);
 }
--- a/pidgin/gtkconn.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkconn.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file gtkconn.h GTK+ Connection API
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkconv.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkconv.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkconv.c GTK+ Conversation API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -69,6 +70,8 @@
 
 #include "gtknickcolors.h"
 
+#define CLOSE_CONV_TIMEOUT_SECS  (10 * 60)
+
 #define AUTO_RESPONSE "&lt;AUTO-REPLY&gt; : "
 
 typedef  enum
@@ -122,7 +125,6 @@
 static GtkWidget *invite_dialog = NULL;
 static GtkWidget *warn_close_dialog = NULL;
 
-static PidginWindow *hidden_convwin = NULL;
 static GList *window_list = NULL;
 
 /* Lists of status icons at all available sizes for use as window icons */
@@ -160,6 +162,7 @@
 static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv);
 static gboolean pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv);
 static void pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv);
+static void hide_conv(PidginConversation *gtkconv, gboolean closetimer);
 
 static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y,
 		int width, int height);
@@ -207,12 +210,49 @@
  **************************************************************************/
 
 static gboolean
-close_conv_cb(GtkWidget *w, GdkEventButton *event, PidginConversation *gtkconv)
-{
+close_this_sucker(gpointer data)
+{
+	PidginConversation *gtkconv = data;
 	GList *list = g_list_copy(gtkconv->convs);
-
 	g_list_foreach(list, (GFunc)purple_conversation_destroy, NULL);
 	g_list_free(list);
+	return FALSE;
+}
+
+static gboolean
+close_conv_cb(GtkWidget *w, GdkEventButton *dontuse, PidginConversation *gtkconv)
+{
+	/* We are going to destroy the conversations immediately only if the 'close immediately'
+	 * preference is selected. Otherwise, close the conversation after a reasonable timeout
+	 * (I am going to consider 10 minutes as a 'reasonable timeout' here.
+	 * For chats, close immediately if the chat is not in the buddylist, or if the chat is
+	 * not marked 'Persistent' */
+	PurpleConversation *conv = gtkconv->active_conv;
+	PurpleAccount *account = purple_conversation_get_account(conv);
+	const char *name = purple_conversation_get_name(conv);
+
+	switch (purple_conversation_get_type(conv)) {
+		case PURPLE_CONV_TYPE_IM:
+		{
+			if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately"))
+				close_this_sucker(gtkconv);
+			else
+				hide_conv(gtkconv, TRUE);
+			break;
+		}
+		case PURPLE_CONV_TYPE_CHAT:
+		{
+			PurpleChat *chat = purple_blist_find_chat(account, name);
+			if (!chat ||
+					!purple_blist_node_get_bool(&chat->node, "gtk-persistent"))
+				close_this_sucker(gtkconv);
+			else
+				hide_conv(gtkconv, FALSE);
+			break;
+		}
+		default:
+			;
+	}
 
 	return TRUE;
 }
@@ -1314,18 +1354,33 @@
 	add_remove_cb(NULL, PIDGIN_CONVERSATION(conv));
 }
 
-#if 0
-static void
-menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget)
-{
-	PidginWindow *win = data;
-	PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win);
-	PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
+static gboolean
+close_already(gpointer data)
+{
+	purple_conversation_destroy(data);
+	return FALSE;
+}
+
+static void
+hide_conv(PidginConversation *gtkconv, gboolean closetimer)
+{
+	GList *list;
+
 	purple_signal_emit(pidgin_conversations_get_handle(),
 			"conversation-hiding", gtkconv);
-	purple_conversation_set_ui_ops(conv, NULL);
-}
-#endif
+
+	for (list = g_list_copy(gtkconv->convs); list; list = g_list_delete_link(list, list)) {
+		PurpleConversation *conv = list->data;
+		if (closetimer) {
+			guint timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+			if (timer)
+				purple_timeout_remove(timer);
+			timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv);
+			purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer));
+		}
+		purple_conversation_set_ui_ops(conv, NULL);
+	}
+}
 
 static void
 menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget)
@@ -1858,6 +1913,7 @@
 				gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
 						gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
 						curconv - 1);
+				return TRUE;
 				break;
 
 			case GDK_period:
@@ -1868,6 +1924,7 @@
 #else
 						(curconv + 1) % g_list_length(GTK_NOTEBOOK(win->notebook)->children));
 #endif
+				return TRUE;
 				break;
 
 		} /* End of switch */
@@ -2340,63 +2397,69 @@
 	return get_prpl_icon_list(account);
 }
 
-GdkPixbuf *
-pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon)
-{
-        PurpleAccount *account = NULL;
-        const char *name = NULL;
-        GdkPixbuf *status = NULL;
-        PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL;
-        g_return_val_if_fail(conv != NULL, NULL);
-
-        account = purple_conversation_get_account(conv);
-        name = purple_conversation_get_name(conv);
-
-        g_return_val_if_fail(account != NULL, NULL);
-        g_return_val_if_fail(name != NULL, NULL);
-
-        /* Use the buddy icon, if possible */
-        if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
-                PurpleBuddy *b = purple_find_buddy(account, name);
-                if (b != NULL) {
+static GdkPixbuf *
+pidgin_conv_get_icon(PurpleConversation *conv, GtkWidget *parent, const char *icon_size)
+{
+	PurpleAccount *account = NULL;
+	const char *name = NULL;
+	GdkPixbuf *status = NULL;
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	g_return_val_if_fail(conv != NULL, NULL);
+
+	account = purple_conversation_get_account(conv);
+	name = purple_conversation_get_name(conv);
+
+	g_return_val_if_fail(account != NULL, NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	/* Use the buddy icon, if possible */
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+		PurpleBuddy *b = purple_find_buddy(account, name);
+		if (b != NULL) {
 			PurplePresence *p = purple_buddy_get_presence(b);
-                        /* I hate this hack.  It fixes a bug where the pending message icon
-                          * displays in the conv tab even though it shouldn't.
-                          * A better solution would be great. */
-                        if (ops && ops->update)
-                                ops->update(NULL, (PurpleBlistNode*)b);
+			/* I hate this hack.  It fixes a bug where the pending message icon
+			 * displays in the conv tab even though it shouldn't.
+			 * A better solution would be great. */
+			if (ops && ops->update)
+				ops->update(NULL, (PurpleBlistNode*)b);
 
 			/* XXX Seanegan: We really need a util function to return a pixbuf for a Presence to avoid all this switching */	
 			if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY))
-	                        status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size);
+				status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, parent, icon_size);
 			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY))
-	                        status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- 			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE))
-	                        status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- 			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE))
-	                        status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- 			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE))
-	                        status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
- 			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE))
-	                        status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size);
-                }
-        }
-
-        /* If they don't have a buddy icon, then use the PRPL icon */
-        if (status == NULL) {
+				status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, parent, icon_size);
+			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE))
+				status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, parent, icon_size);
+			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE))
+				status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, parent, icon_size);
+			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE))
+				status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, parent, icon_size);
+			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE))
+				status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, parent, icon_size);
+		}
+	}
+
+	/* If they don't have a buddy icon, then use the PRPL icon */
+	if (status == NULL) {
 		GtkIconSize size = gtk_icon_size_from_name(icon_size);
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
-        		status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_PERSON,
-                                                 size, "GtkWidget");
+			status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_PERSON,
+					size, "GtkWidget");
 		} else {
-	        		status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_CHAT,
-                                                 size, "GtkWidget");
+			status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_CHAT,
+					size, "GtkWidget");
 		}
 	}	
 	return status;
 }
 
+GdkPixbuf *
+pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon)
+{
+	const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL;
+	return pidgin_conv_get_icon(conv, PIDGIN_CONVERSATION(conv)->icon, icon_size);
+}
+
 
 static void
 update_tab_icon(PurpleConversation *conv)
@@ -2748,9 +2811,9 @@
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 	GdkModifierType state;
 
-	if(gtkconv->win==hidden_convwin) {
-		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
-		pidgin_conv_placement_place(gtkconv);
+	if (gtkconv == NULL) {
+		pidgin_conv_attach_to_conversation(conv);
+		gtkconv = PIDGIN_CONVERSATION(conv);
 	}
 
 	pidgin_conv_switch_active_conversation(conv);
@@ -2783,15 +2846,19 @@
 		PurpleConversation *conv = (PurpleConversation*)l->data;
 		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
-		if(gtkconv == NULL || gtkconv->active_conv != conv)
+		if (gtkconv != NULL && gtkconv->active_conv != conv)
 			continue;
-
-		if (gtkconv->unseen_state >= min_state
-			&& (!hidden_only ||
-				(hidden_only && gtkconv->win == hidden_convwin))) {
-
+		if (gtkconv == NULL) {
+			if (!hidden_only ||
+					!purple_conversation_get_data(conv, "unseen-count"))
+				continue;
 			r = g_list_prepend(r, conv);
 			c++;
+		} else {
+			if (gtkconv->unseen_state >= min_state && !hidden_only) {
+				r = g_list_prepend(r, conv);
+				c++;
+			}
 		}
 	}
 
@@ -2831,11 +2898,11 @@
 		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
 		GtkWidget *icon = gtk_image_new();
-		GdkPixbuf *pbuf = pidgin_conv_get_tab_icon(conv, TRUE);
+		GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC);
 		GtkWidget *item;
 		gchar *text = g_strdup_printf("%s (%d)",
-				gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)),
-				gtkconv->unseen_count);
+				gtkconv ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : purple_conversation_get_name(conv),
+				gtkconv ? gtkconv->unseen_count : GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")));
 
 		gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf);
 		g_object_unref(pbuf);
@@ -3093,7 +3160,7 @@
 	PurpleConversation *conv;
 	GtkWidget *item;
 
-	if (win->window == NULL || win == hidden_convwin)
+	if (win->window == NULL)
 		return;
 
 	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
@@ -4423,7 +4490,7 @@
 	g_signal_connect(G_OBJECT(list), "motion-notify-event",
 					 G_CALLBACK(pidgin_userlist_motion_cb), gtkconv);
 	g_signal_connect(G_OBJECT(list), "leave-notify-event",
-					 G_CALLBACK(pidgin_userlist_motion_cb), gtkconv);
+					 G_CALLBACK(pidgin_conv_leave_cb), gtkconv);
 	g_signal_connect(G_OBJECT(list), "popup-menu",
 			 G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv);
 	g_signal_connect(G_OBJECT(lbox), "size-allocate", G_CALLBACK(lbox_size_allocate_cb), gtkconv);
@@ -4551,10 +4618,8 @@
 
 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
 
-	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(gtkchat->list),
-								  tooltip.userlistx, tooltip.userlisty, &path, &column, &x, &y);
-
-	if (path == NULL)
+	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(gtkchat->list),
+								  tooltip.userlistx, tooltip.userlisty, &path, &column, &x, &y))
 		return FALSE;
 
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
@@ -4942,6 +5007,9 @@
 	GtkWidget *tab_cont;
 	PurpleBlistNode *convnode;
 
+	if (hidden)
+		return;
+
 	if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) {
 		conv->ui_data = gtkconv;
 		if (!g_list_find(gtkconv->convs, conv))
@@ -5041,10 +5109,7 @@
 	                         G_CALLBACK(gtk_widget_grab_focus),
 	                         gtkconv->entry);
 
-	if (hidden)
-		pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
-	else
-		pidgin_conv_placement_place(gtkconv);
+	pidgin_conv_placement_place(gtkconv);
 
 	if (nick_colors == NULL) {
 		nbr_nick_colors = NUM_NICK_COLORS;
@@ -5052,11 +5117,13 @@
 	}
 }
 
+#if 0
 static void
 pidgin_conv_new_hidden(PurpleConversation *conv)
 {
 	private_gtkconv_new(conv, TRUE);
 }
+#endif
 
 void
 pidgin_conv_new(PurpleConversation *conv)
@@ -5069,26 +5136,22 @@
 				   PurpleConversation *conv, PurpleMessageFlags flags)
 {
 	PurpleConversationUiOps *ui_ops = pidgin_conversations_get_conv_ui_ops();
-	if (conv != NULL)
-		return;
 
 	/* create hidden conv if hide_new pref is always */
-	if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0)
-	{
-		ui_ops->create_conversation = pidgin_conv_new_hidden;
-		purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
-		ui_ops->create_conversation = pidgin_conv_new;
-		return;
-	}
-
-	/* create hidden conv if hide_new pref is away and account is away */
-	if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 &&
-	    !purple_status_is_available(purple_account_get_active_status(account)))
-	{
-		ui_ops->create_conversation = pidgin_conv_new_hidden;
-		purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
-		ui_ops->create_conversation = pidgin_conv_new;
-		return;
+	/* or if hide_new pref is away and account is away */
+	if ((strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) ||
+		(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 &&
+		 !purple_status_is_available(purple_account_get_active_status(account)))) {
+		if (!conv) {
+			ui_ops->create_conversation = NULL;
+			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
+			purple_conversation_set_ui_ops(conv, NULL);
+			ui_ops->create_conversation = pidgin_conv_new;
+		}
+	} else {
+		/* new message for an IM */
+		if (conv && conv->type == PURPLE_CONV_TYPE_IM)
+			pidgin_conv_attach_to_conversation(conv);
 	}
 }
 
@@ -5097,6 +5160,9 @@
 {
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
+	if (!gtkconv)
+		return;
+
 	gtkconv->convs = g_list_remove(gtkconv->convs, conv);
 	/* Don't destroy ourselves until all our convos are gone */
 	if (gtkconv->convs) {
@@ -6349,8 +6415,13 @@
 		if ((purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) &&
 				(gtkconv->u.im->anim))
 		{
+			PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name);
 			window_icon =
 				gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
+		
+			if (buddy &&  !PURPLE_BUDDY_IS_ONLINE(buddy))
+				gdk_pixbuf_saturate_and_pixelate(window_icon, window_icon, 0.0, FALSE);
+			
 			g_object_ref(window_icon);
 			l = g_list_append(l, window_icon);
 		} else {
@@ -6569,6 +6640,19 @@
 	pidgin_conv_update_fields(conv, flags);
 }
 
+static void
+wrote_msg_update_unseen_cb(PurpleAccount *account, const char *who, const char *message,
+		PurpleConversation *conv, PurpleMessageFlags flag, gpointer null)
+{
+	if (conv == NULL || PIDGIN_IS_PIDGIN_CONVERSATION(conv))
+		return;
+	if (flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) {
+		purple_conversation_set_data(conv, "unseen-count",
+				GINT_TO_POINTER(GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")) + 1));
+		purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
+	}
+}
+
 static PurpleConversationUiOps conversation_ui_ops =
 {
 	pidgin_conv_new,
@@ -6752,7 +6836,9 @@
 
 	event = gtk_event_box_new();
 	gtk_container_add(GTK_CONTAINER(gtkconv->u.im->icon_container), event);
+#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
+#endif
 	gtk_widget_add_events(event,
                               GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
 	g_signal_connect(G_OBJECT(event), "button-press-event",
@@ -7066,6 +7152,7 @@
 account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus,
                           PurpleStatus *newstatus)
 {
+#if 0
 	GList *l;
 	PurpleConversation *conv = NULL;
 	PidginConversation *gtkconv;
@@ -7075,27 +7162,7 @@
 
 	if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus))
 		return;
-
-	while ((l = hidden_convwin->gtkconvs) != NULL)
-	{
-		gtkconv = l->data;
-
-		conv = gtkconv->active_conv;
-
-		while(l && !purple_status_is_available(
-					purple_account_get_active_status(
-					purple_conversation_get_account(conv))))
-			l = l->next;
-		if (!l)
-			break;
-
-		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
-		pidgin_conv_placement_place(gtkconv);
-
-		/* TODO: do we need to do anything for any other conversations that are in the same gtkconv here?
-		 * I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/
-		purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN);
-	}
+#endif
 }
 
 static void
@@ -7103,32 +7170,25 @@
 				 gconstpointer value, gpointer data)
 {
 	GList *l;
-	PurpleConversation *conv = NULL;
-	PidginConversation *gtkconv;
 	gboolean when_away = FALSE;
 
-	if(!hidden_convwin)
-		return;
-
 	if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always")==0)
 		return;
 
 	if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0)
 		when_away = TRUE;
 
-	while ((l = hidden_convwin->gtkconvs) != NULL)
+	for (l = purple_get_conversations(); l; l = l->next)
 	{
-		gtkconv = l->data;
-
-		conv = gtkconv->active_conv;
-
+		PurpleConversation *conv = l->data;
+		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+		if (gtkconv)
+			continue;
 		if(when_away && !purple_status_is_available(
 							purple_account_get_active_status(
 							purple_conversation_get_account(conv))))
 			continue;
-
-		pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv);
-		pidgin_conv_placement_place(gtkconv);
+		pidgin_conv_attach_to_conversation(conv);
 	}
 }
 
@@ -7311,9 +7371,15 @@
 	PidginConversation *gtkconv = data;
 	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"));
+
 	gtkconv->attach.timer = 0;
 	while (gtkconv->attach.current && count < 100) {  /* XXX: 100 is a random value here */
 		PurpleConvMessage *msg = gtkconv->attach.current->data;
+		if (when && when < msg->when) {
+			gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
+			g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
+		}
 		pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when);
 		gtkconv->attach.current = gtkconv->attach.current->prev;
 		count++;
@@ -7333,18 +7399,21 @@
 {
 	GList *list;
 	PidginConversation *gtkconv;
+	int timer;
 
 	if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
 		return FALSE;
 
+	purple_conversation_set_data(conv, "unseen-count", NULL);
 	purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops());
 	private_gtkconv_new(conv, FALSE);
 	gtkconv = PIDGIN_CONVERSATION(conv);
 
 	list = purple_conversation_get_message_history(conv);
 	if (list) {
-		list = g_list_last(list);
-		gtkconv->attach.current = list;
+		g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time",
+				GINT_TO_POINTER(((PurpleConvMessage*)(list->data))->when));
+		gtkconv->attach.current = g_list_last(list);
 		gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
 	} else {
 		purple_signal_emit(pidgin_conversations_get_handle(),
@@ -7356,6 +7425,10 @@
 		pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE);
 	}
 
+	timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer"));
+	if (timer)
+		purple_timeout_remove(timer);
+
 	return TRUE;
 }
 
@@ -7421,6 +7494,7 @@
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE);
 
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never");
+	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", FALSE);
 
 #ifdef _WIN32
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", FALSE);
@@ -7584,9 +7658,6 @@
 
 	purple_conversations_set_ui_ops(&conversation_ui_ops);
 
-	hidden_convwin = pidgin_conv_window_new();
-	window_list = g_list_remove(window_list, hidden_convwin);
-
 	purple_signal_connect(purple_accounts_get_handle(), "account-status-changed",
                         handle, PURPLE_CALLBACK(account_status_changed_cb), NULL);
 
@@ -7622,6 +7693,10 @@
 	purple_signal_connect_priority(purple_conversations_get_handle(), "conversation-updated", handle,
 						PURPLE_CALLBACK(pidgin_conv_updated), NULL,
 						PURPLE_SIGNAL_PRIORITY_LOWEST);
+	purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", handle,
+			PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
+	purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle,
+			PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
 }
 
 void
@@ -7630,8 +7705,6 @@
 	purple_prefs_disconnect_by_handle(pidgin_conversations_get_handle());
 	purple_signals_disconnect_by_handle(pidgin_conversations_get_handle());
 	purple_signals_unregister_by_instance(pidgin_conversations_get_handle());
-	pidgin_conv_window_destroy(hidden_convwin);
-	hidden_convwin=NULL;
 }
 
 
@@ -8763,15 +8836,10 @@
 
 	if (win->gtkconvs) {
 		while (win->gtkconvs) {
-			GList *nextgtk = win->gtkconvs->next;
-			PidginConversation *gtkconv = win->gtkconvs->data;
-			GList *nextcore = gtkconv->convs->next;
-			PurpleConversation *conv = gtkconv->convs->data;
-			purple_conversation_destroy(conv);
-			if (!nextgtk && !nextcore)
-			/* we'll end up invoking ourselves when we destroy our last child */
-			/* so don't destroy ourselves right now */
-				return;
+			gboolean last = (win->gtkconvs->next == NULL);
+			close_conv_cb(NULL, NULL, win->gtkconvs->data);
+			if (last)
+				break;
 		}
 		return;
 	}
@@ -8858,7 +8926,9 @@
 
 	/* Close button. */
 	gtkconv->close = gtk_event_box_new();
+#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(gtkconv->close), FALSE);
+#endif
 	gtk_widget_set_events(gtkconv->close, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
 	close_image = gtk_label_new("×");
 	g_signal_connect(G_OBJECT(gtkconv->close), "enter-notify-event", G_CALLBACK(close_button_entered_cb), close_image);
@@ -9048,7 +9118,7 @@
 	if (win->gtkconvs && win->gtkconvs->next == NULL)
 		pidgin_conv_tab_pack(win, win->gtkconvs->data);
 
-	if (!win->gtkconvs && win != hidden_convwin)
+	if (!win->gtkconvs)
 		pidgin_conv_window_destroy(win);
 }
 
@@ -9587,9 +9657,7 @@
 gboolean
 pidgin_conv_is_hidden(PidginConversation *gtkconv)
 {
-	g_return_val_if_fail(gtkconv != NULL, FALSE);
-
-	return (gtkconv->win == hidden_convwin);
+	return (gtkconv == NULL);
 }
 
 
@@ -9690,3 +9758,4 @@
 
 	return colors;
 }
+
--- a/pidgin/gtkconv.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkconv.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkconv.h GTK+ Conversation API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkconvwin.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkconvwin.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkconvwin.h GTK+ Conversation Window API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkdebug.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkdebug.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkdebug.c GTK+ Debug API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkdebug.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkdebug.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkdebug.h GTK+ Debug API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkdialogs.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkdialogs.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkdialogs.c GTK+ Dialogs
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -130,7 +131,7 @@
 /* Order: Code, then Alphabetical by Last Name */
 static struct translator current_translators[] = {
 	{N_("Afrikaans"),           "af", "Friedel Wolff", "friedel@translate.org.za"},
-	{N_("Arabic"),              "ar", "Mohamed Magdy", "alnokta@yahoo.com"},
+	{N_("Arabic"),              "ar", "Khaled Hosny", "khaledhosny@eglug.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", "vpetkov@i-space.org"},
@@ -181,10 +182,12 @@
 	{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"},
+	{N_("Lao"),                 "lo", "Anousak Souphavah", "anousak@gmail.com"},
 	{N_("Lithuanian"),          "lt", "Laurynas Biveinis", "laurynas.biveinis@gmail.com"},
 	{N_("Macedonian"),          "mk", "Arangel Angov ", "arangel@linux.net.mk"},
+	{N_("Macedonian"),          "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"},
 	{N_("Macedonian"),          "mk", "Jovan Naumovski", "jovan@lugola.net"},
-	{N_("Bokmål Norwegian"),    "nb", "Hallvard Glad", "hallvard.glad@gmail.com"},
+	{N_("Bokmål Norwegian"),    "nb", "Espen Stefansen", "espenas@gmail.com"},
 	{N_("Nepali"),              "ne", "Shyam Krishna Bal", "shyamkrishna_bal@yahoo.com"},
 	{N_("Dutch, Flemish"),      "nl", "Vincent van Adrighem", "V.vanAdrighem@dirck.mine.nu"},
 	{N_("Norwegian Nynorsk"),   "nn", "Yngve Spjeld Landro", "nynorsk@strilen.net"},
@@ -200,8 +203,8 @@
 	{N_("Slovak"),              "sk", "loptosko", "loptosko@gmail.com"},
 	{N_("Slovenian"),           "sl", "Martin Srebotnjak", "miles@filmsi.net"},
 	{N_("Albanian"),            "sq", "Besnik Bleta", "besnik@programeshqip.org"},
-	{N_("Serbian"),             "sr", "Danilo Šegan", "dsegan@gmx.net"},
-	{N_("Serbian"),             "sr", "Aleksandar Urosevic", "urke@users.sourceforge.net"},
+	{N_("Serbian"),             "sr", "Miloš Popović", "gpopac@gmail.com"},
+	{N_("Serbian"),             "sr@Latn", "Miloš Popović", "gpopac@gmail.com"},
 	{N_("Swedish"),             "sv", "Peter Hjalmarsson", "xake@telia.com"},
 	{N_("Tamil"),               "ta", "Viveka Nathan K", "vivekanathan@users.sourceforge.net"},
 	{N_("Telugu"),              "te", "Mr. Subbaramaih", "info.gist@cdac.in"},
@@ -220,6 +223,7 @@
 
 static struct translator past_translators[] = {
 	{N_("Amharic"),             "am", "Daniel Yacob", NULL},
+	{N_("Arabic"),              "ar", "Mohamed Magdy", "alnokta@yahoo.com"},
 	{N_("Bulgarian"),           "bg", "Hristo Todorov", NULL},
 	{N_("Catalan"),             "ca", "JM Pérez Cáncer", NULL},
 	{N_("Catalan"),             "ca", "Robert Millan", NULL},
@@ -248,7 +252,8 @@
 	{N_("Lithuanian"),          "lt", "Andrius Štikonas", NULL},
 	{N_("Lithuanian"),          "lt", "Gediminas Čičinskas", NULL},
 	{N_("Macedonian"),          "mk", "Tomislav Markovski", NULL},
-	{N_("Bokmål Norwegian"),   "nb", "Petter Johan Olsen", NULL},
+	{N_("Bokmål Norwegian"),    "nb", "Hallvard Glad", "hallvard.glad@gmail.com"},
+	{N_("Bokmål Norwegian"),    "nb", "Petter Johan Olsen", NULL},
 	{N_("Polish"),              "pl", "Przemysław Sułek", NULL},
 	{N_("Russian"),             "ru", "Alexandre Prokoudine", NULL},
 	{N_("Russian"),             "ru", "Sergey Volozhanin", NULL},
@@ -256,6 +261,8 @@
 	{N_("Slovak"),              "sk", "helix84", NULL},
 	{N_("Slovak"),              "sk", "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"},
 	{N_("Swedish"),             "sv", "Tore Lundqvist", NULL},
 	{N_("Swedish"),             "sv", "Christian Rose", NULL},
 	{N_("Turkish"),             "tr", "Ahmet Alp BALKAN", NULL},
@@ -285,6 +292,7 @@
 	about = NULL;
 }
 
+#if 0
 /* This function puts the version number onto the pixmap we use in the 'about' 
  * screen in Pidgin. */
 static void
@@ -322,6 +330,7 @@
 											 width, height);
 	g_object_unref(G_OBJECT(pixmap));
 }
+#endif
 
 void pidgin_dialogs_about()
 {
@@ -385,6 +394,9 @@
 	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, VERSION);
+
+	g_string_append_printf(str,
 		_("%s is a graphical modular messaging client based on "
 		  "libpurple which is capable of connecting to "
 		  "AIM, MSN, Yahoo!, XMPP, ICQ, IRC, SILC, SIP/SIMPLE, "
--- a/pidgin/gtkdialogs.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkdialogs.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @defgroup pidgin Pidgin (GTK+ User Interface)
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkdnd-hints.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkdnd-hints.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkdnd-hints.c GTK+ Drag-and-Drop arrow hints
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkdnd-hints.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkdnd-hints.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkdnd-hints.h GTK+ Drag-and-Drop arrow hints
  * @ingroup pidgin
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
+ */
+
+/* Pidgin 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.
  *
--- a/pidgin/gtkdocklet.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkdocklet.c	Mon Oct 01 17:49:01 2007 +0000
@@ -532,7 +532,7 @@
 	g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_mute), NULL);
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
-	menuitem = gtk_check_menu_item_new_with_label(_("Blink on new message"));
+	menuitem = gtk_check_menu_item_new_with_label(_("Blink on New Message"));
 	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink"));
 	g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_blink), NULL);
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
--- a/pidgin/gtkeventloop.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkeventloop.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtk_eventloop.c Purple Event Loop API (gtk implementation)
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkeventloop.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkeventloop.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkeventloop.h Pidgin GTK+ Event Loop Implementation
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkft.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkft.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkft.c GTK+ File Transfer UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkft.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkft.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkft.h GTK+ File Transfer UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkgaim-compat.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkgaim-compat.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file gtkgaim-compat.h Gtk Gaim Compat macros
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkidle.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkidle.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkidle.h GTK+ Idle API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkimhtml.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkimhtml.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkimhtml.c GTK+ IMHtml
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkimhtml.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkimhtml.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkimhtml.h GTK+ IM/HTML rendering component
  * @ingroup pidgin
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
+ */
+
+/* Pidgin 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.
  *
--- a/pidgin/gtkimhtmltoolbar.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkimhtmltoolbar.c GTK+ IMHtml Toolbar
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -739,11 +740,11 @@
 		while (it != NULL)
 		{
 			it_tmp = it;
-			gtk_box_pack_start(GTK_BOX(line), it->button, TRUE, TRUE, 0);
+			gtk_box_pack_start(GTK_BOX(line), it->button, FALSE, FALSE, 0);
 			gtk_widget_show(it->button);
 			line_width += it->width;
 			if (line_width >= max_line_width) {
-				gtk_box_pack_start(GTK_BOX(smiley_table), line, FALSE, TRUE, 0);
+				gtk_box_pack_start(GTK_BOX(smiley_table), line, FALSE, FALSE, 0);
 				line = gtk_hbox_new(FALSE, 0);
 				line_width = 0;
 				col = 0;
@@ -776,6 +777,9 @@
 	/* show everything */
 	gtk_window_set_title(GTK_WINDOW(dialog), _("Smile!"));
 	gtk_widget_show_all(dialog);
+#ifdef _WIN32
+	winpidgin_ensure_onscreen(dialog);
+#endif
 
 	toolbar->smiley_dialog = dialog;
 
@@ -1162,6 +1166,7 @@
 	GtkWidget *label;
 	GtkWidget *insert_button;
 	GtkWidget *font_button;
+	GtkWidget *smiley_button;
 	GtkWidget *font_menu;
 	GtkWidget *insert_menu;
 	GtkWidget *menuitem;
@@ -1263,12 +1268,6 @@
 	insert_menu = gtk_menu_new();
 	g_object_set_data(G_OBJECT(toolbar), "insert_menu", insert_menu);
 
-	menuitem = gtk_menu_item_new_with_mnemonic(_("_Smiley"));
-	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
-	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
-	g_signal_connect(G_OBJECT(toolbar->smiley), "notify::sensitive",
-			G_CALLBACK(button_sensitiveness_changed), menuitem);
-
 	menuitem = gtk_menu_item_new_with_mnemonic(_("_Image"));
 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->image);
 	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
@@ -1290,6 +1289,24 @@
 	g_signal_connect(G_OBJECT(insert_button), "activate", G_CALLBACK(pidgin_menu_clicked), insert_menu);
 	g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button);
 	toolbar->sml = NULL;
+	
+	/* Sep */
+	sep = gtk_vseparator_new();
+	gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
+	gtk_widget_show_all(sep);
+
+	/* Smiley */
+	smiley_button = gtk_button_new();
+	gtk_button_set_relief(GTK_BUTTON(smiley_button), GTK_RELIEF_NONE);
+	bbox = gtk_hbox_new(FALSE, 3);
+	gtk_container_add(GTK_CONTAINER(smiley_button), bbox);
+	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
+	label = gtk_label_new_with_mnemonic(_("_Smiley"));
+	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(box), smiley_button, FALSE, FALSE, 0);
+	g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
+	gtk_widget_show_all(smiley_button);
 
 	gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0);
 	g_object_set_data(G_OBJECT(hbox), "lean-view", box);
--- a/pidgin/gtklog.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtklog.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtklog.c GTK+ Log viewer
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtklog.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtklog.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtklog.h GTK+ Log viewer
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkmain.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkmain.c	Mon Oct 01 17:49:01 2007 +0000
@@ -394,6 +394,9 @@
 		       "  -n, --nologin       don't automatically login\n"
 		       "  -l, --login[=NAME]  automatically login (optional argument NAME specifies\n"
 		       "                      account(s) to use, separated by commas)\n"
+#ifndef WIN32
+		       "  --display=DISPLAY   X display to use\n"
+#endif
 		       "  -v, --version       display the current version and exit\n"), PIDGIN_NAME, VERSION, name);
 	}
 
@@ -481,6 +484,7 @@
 		{"nologin",  no_argument,       NULL, 'n'},
 		{"session",  required_argument, NULL, 's'},
 		{"version",  no_argument,       NULL, 'v'},
+		{"display",  required_argument, NULL, 'D'},
 		{0, 0, 0, 0}
 	};
 
@@ -626,6 +630,9 @@
 		case 'm':   /* do not ensure single instance. */
 			opt_si = FALSE;
 			break;
+		case 'D':   /* --display */
+			/* handled by gtk_init_check below */
+			break;
 		case '?':	/* show terse help */
 		default:
 			show_usage(argv[0], TRUE);
--- a/pidgin/gtkmenutray.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkmenutray.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkmenutray.h GTK+ Tray menu item
  * @ingroup pidgin
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
+ */
+
+/* Pidgin 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.
  *
--- a/pidgin/gtknickcolors.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtknickcolors.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtknickcolors.h GTK+ Conversation API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  * Pidgin 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.
--- a/pidgin/gtknotify.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtknotify.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtknotify.c GTK+ Notification API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -412,32 +413,50 @@
  * count > 0 mean non-detailed.
  */
 static void *
-pidgin_notify_add_mail(GtkTreeStore *treemodel, PurpleAccount *account, char *notification, const char *url, int count)
+pidgin_notify_add_mail(GtkTreeStore *treemodel, PurpleAccount *account, char *notification, const char *url, int count, gboolean clear)
 {
 	PidginNotifyMailData *data = NULL;
 	GtkTreeIter iter;
 	GdkPixbuf *icon;
 	gboolean new_n = TRUE;
 
-	icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
-
-	if (count > 0) {
+	if (count > 0 || clear) {
 		/* Allow only one non-detailed email notification for each account */
 		if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treemodel), &iter)) {
+			gboolean advanced;
 			do {
+				advanced = FALSE;
 				gtk_tree_model_get(GTK_TREE_MODEL(treemodel), &iter,
 						PIDGIN_MAIL_DATA, &data, -1);
-				if (data->account == account && data->count > 0) {
-					new_n = FALSE;
-					g_free(data->url);
-					data->url = NULL;
-					mail_dialog->total_count -= data->count;
-					break;
+				if (data->account == account) {
+					if (clear) {
+#if GTK_CHECK_VERSION(2,2,0)
+						advanced = gtk_tree_store_remove(treemodel, &iter);
+#else
+						gtk_tree_store_remove(treemodel, &iter);
+						advanced = (iter.stamp == 0) ? FALSE : TRUE;
+#endif
+						purple_notify_close(PURPLE_NOTIFY_EMAILS, data);
+						/* We're completely done if we've processed all entries */
+						if (!advanced)
+							return NULL;
+					} else if (data->count > 0) {
+						new_n = FALSE;
+						g_free(data->url);
+						data->url = NULL;
+						mail_dialog->total_count -= data->count;
+						break;
+					}
 				}
-			} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(treemodel), &iter));
+			} while (advanced || gtk_tree_model_iter_next(GTK_TREE_MODEL(treemodel), &iter));
 		}
 	}
 
+	if (clear)
+		return NULL;
+
+	icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
+
 	if (new_n) {
 		data = g_new0(PidginNotifyMailData, 1);
 		gtk_tree_store_append(treemodel, &iter, NULL);
@@ -471,9 +490,13 @@
 	PurpleAccount *account;
 	PidginNotifyMailData *data = NULL;
 
+	/* Don't bother updating if there aren't new emails and we don't have any displayed currently */
+	if (count == 0 && mail_dialog == NULL)
+		return NULL;
+
 	account = purple_connection_get_account(gc);
 	dialog = pidgin_get_mail_dialog();  /* This creates mail_dialog if necessary */
- 
+
 	mail_dialog->total_count += count;
 	if (detailed) {
 		while (count--) {
@@ -511,19 +534,33 @@
 			g_free(from_text);
 			g_free(subject_text);
 
-			data = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, 0);
+			data = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, 0, FALSE);
 			g_free(notification);
 
 			if (urls != NULL)
 				urls++;
 		}
 	} else {
-		notification = g_strdup_printf(ngettext("%s has %d new message.",
-						   "%s has %d new messages.",
-						   (int)count),
-						   *tos, (int)count);
-		data = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, count);
-		g_free(notification);
+		if (count > 0) {
+			notification = g_strdup_printf(ngettext("%s has %d new message.",
+							   "%s has %d new messages.",
+							   (int)count),
+							   *tos, (int)count);
+			data = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, count, FALSE);
+			g_free(notification);
+		} else {
+			GtkTreeIter iter;
+
+			/* Clear out all mails for the account */
+			pidgin_notify_add_mail(mail_dialog->treemodel, account, NULL, NULL, 0, TRUE);
+
+			if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(mail_dialog->treemodel), &iter)) {
+				/* There is no API to clear the headline specifically */
+				/* This will trigger reset_mail_dialog() */
+				pidgin_blist_set_headline(NULL, NULL, NULL, NULL, NULL);
+				return NULL;
+			}
+		}
 	}
 
 	if (!GTK_WIDGET_VISIBLE(dialog)) {
@@ -535,7 +572,7 @@
 		mail_dialog->in_use = TRUE;     /* So that _set_headline doesn't accidentally
 										   remove the notifications when replacing an
 										   old notification. */
-		pidgin_blist_set_headline(label_text, 
+		pidgin_blist_set_headline(label_text,
 					    pixbuf, G_CALLBACK(gtk_widget_show_all), dialog,
 					    (GDestroyNotify)reset_mail_dialog);
 		mail_dialog->in_use = FALSE;
@@ -660,30 +697,30 @@
 	GtkTreeIter iter;
 	GdkPixbuf *pixbuf;
 	guint col_num;
-	guint i;
-	guint j;
+	GList *row, *column;
+	guint n;
 
 	gtk_list_store_clear(data->model);
 
 	pixbuf = pidgin_create_prpl_icon(purple_connection_get_account(gc), 0.5);
 
 	/* +1 is for the automagically created Status column. */
-	col_num = purple_notify_searchresults_get_columns_count(results) + 1;
+	col_num = g_list_length(results->columns) + 1;
 
-	for (i = 0; i < purple_notify_searchresults_get_rows_count(results); i++) {
-		GList *row = purple_notify_searchresults_row_get(results, i);
+	for (row = results->rows; row != NULL; row = row->next) {
 
 		gtk_list_store_append(model, &iter);
 		gtk_list_store_set(model, &iter, 0, pixbuf, -1);
 
-		for (j = 1; j < col_num; j++) {
+		n = 1;
+		for (column = row->data; column != NULL; column = column->next) {
 			GValue v;
-			char *data = g_list_nth_data(row, j - 1);
 
 			v.g_type = 0;
 			g_value_init(&v, G_TYPE_STRING);
-			g_value_set_string(&v, data);
-			gtk_list_store_set_value(model, &iter, j, &v);
+			g_value_set_string(&v, column->data);
+			gtk_list_store_set_value(model, &iter, n, &v);
+			n++;
 		}
 	}
 
@@ -703,6 +740,7 @@
 	GtkListStore *model;
 	GtkCellRenderer *renderer;
 	guint col_num;
+	GList *column;
 	guint i;
 
 	GtkWidget *vbox;
@@ -750,7 +788,7 @@
 	g_free(label_text);
 
 	/* +1 is for the automagically created Status column. */
-	col_num = purple_notify_searchresults_get_columns_count(results) + 1;
+	col_num = g_list_length(results->columns) + 1;
 
 	/* Setup the list model */
 	col_types = g_new0(GType, col_num);
@@ -785,12 +823,13 @@
 	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
 					-1, "", renderer, "pixbuf", 0, NULL);
 
-	for (i = 1; i < col_num; i++) {
+	i = 1;
+	for (column = results->columns; column != NULL; column = column->next) {
 		renderer = gtk_cell_renderer_text_new();
 
 		gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1,
-				purple_notify_searchresults_column_get_title(results, i-1),
-				renderer, "text", i, NULL);
+				column->data, renderer, "text", i, NULL);
+		i++;
 	}
 
 	for (i = 0; i < g_list_length(results->buttons); i++) {
--- a/pidgin/gtknotify.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtknotify.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtknotify.h GTK+ Notification API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkplugin.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkplugin.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkplugin.c GTK+ Plugins support
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkplugin.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkplugin.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkplugin.h GTK+ Plugin API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkpluginpref.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkpluginpref.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkpluginpref.c GTK+ Plugin preferences
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkpluginpref.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkpluginpref.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkpluginpref.h GTK+ Plugin Preferences
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkpounce.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkpounce.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkpounce.c GTK+ Buddy Pounce API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -305,6 +306,13 @@
 	if (*command == '\0') command = NULL;
 	if (*sound   == '\0') sound   = NULL;
 
+	/* If the pounce has already been triggered, let's pretend it is a new one */
+	if (dialog->pounce != NULL
+			&& g_list_find(purple_pounces_get_all(), dialog->pounce) == NULL) {
+		purple_debug_info("gtkpounce", "Saving pounce that no longer exists; creating new pounce.\n");
+		dialog->pounce = NULL;
+	}
+
 	if (dialog->pounce == NULL)
 	{
 		dialog->pounce = purple_pounce_new(PIDGIN_UI, dialog->account,
--- a/pidgin/gtkpounce.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkpounce.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkpounce.h GTK+ Buddy Pounce API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkprefs.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkprefs.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkprefs.c GTK+ Preferences
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -993,6 +994,8 @@
 
 	pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
 				PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
+	pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"),
+				PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox);
 
 	iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"),
 			PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox);
--- a/pidgin/gtkprefs.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkprefs.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkprefs.h GTK+ Preferences
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkprivacy.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkprivacy.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkprivacy.c GTK+ Privacy UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkprivacy.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkprivacy.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkprivacy.h GTK+ Privacy UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkrequest.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkrequest.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkrequest.c GTK+ Request API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -998,7 +999,6 @@
 
 	/* Create the tree view */
 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
-	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
 
 	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
--- a/pidgin/gtkrequest.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkrequest.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkrequest.h GTK+ Request API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkroomlist.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkroomlist.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkroomlist.c GTK+ Room List UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkroomlist.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkroomlist.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkroomlist.h GTK+ Room List UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtksavedstatuses.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtksavedstatus.c GTK+ Saved Status Editor UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -1410,7 +1411,7 @@
 					   -1);
 	type = purple_account_get_status_type(dialog->account, id);
 	if (purple_status_type_get_attr(type, "message") != NULL)
-		message = gtk_imhtml_get_text(GTK_IMHTML(dialog->message), NULL, NULL);
+		message = gtk_imhtml_get_markup(GTK_IMHTML(dialog->message));
 	name = purple_status_type_get_name(type);
 
 	status_editor = dialog->status_editor;
--- a/pidgin/gtksavedstatuses.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtksavedstatuses.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtksavedstatuses.h GTK+ Saved Status Editor UI
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkscrollbook.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkscrollbook.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkscrollbook.c GTK+ Scrolling notebook widget 
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkscrollbook.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkscrollbook.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkscrollbook  GTK+ Scrolling notebook Widget
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtksession.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtksession.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtksession.c X Windows session management API
  * @ingroup pidgin
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
+ */
+
+/* Pidgin 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.
  *
@@ -35,6 +36,7 @@
 #include <gdk/gdkx.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <gdk/gdk.h>
 
 #define ERROR_LENGTH 512
 
@@ -140,7 +142,7 @@
 /* my magic utility function */
 
 static gchar **session_make_command(gchar *client_id, gchar *config_dir) {
-	gint i = 2;
+	gint i = 4;
 	gint j = 0;
 	gchar **ret;
 
@@ -160,6 +162,9 @@
 		ret[j++] = g_strdup(config_dir);
 	}
 
+	ret[j++] = g_strdup("--display");
+	ret[j++] = g_strdup((gchar *)gdk_display_get_name(gdk_display_get_default()));
+
 	ret[j++] = NULL;
 
 	return ret;
--- a/pidgin/gtksession.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtksession.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtksession.h X Windows session management API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtksound.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtksound.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtksound.c GTK+ Sound
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtksound.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtksound.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtksound.h GTK+ Sound API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkstatusbox.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkstatusbox.c GTK+ Status Selection Widget
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkstatusbox.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkstatusbox.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /*
  * @file gtkstatusbox.c GTK+ Status Selection Widget
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkthemes.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkthemes.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkthemes.h GTK+ Smiley Theme API
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkutils.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkutils.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkutils.c GTK+ utility functions
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkutils.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkutils.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file gtkutils.h GTK+ utility functions
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/gtkwhiteboard.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/gtkwhiteboard.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,7 +1,8 @@
 /**
  * @file gtkwhiteboard.h The PidginWhiteboard frontend object
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/pidgin.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/pidgin.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file pidgin.h UI definitions and includes
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/pidginstock.c	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/pidginstock.c	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file pidginstock.c GTK+ Stock resources
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/pidginstock.h	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/pidginstock.h	Mon Oct 01 17:49:01 2007 +0000
@@ -1,8 +1,9 @@
 /**
  * @file pidginstock.h GTK+ Stock resources
  * @ingroup pidgin
- *
- * pidgin
+ */
+
+/* pidgin
  *
  * Pidgin is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
--- a/pidgin/pixmaps/emblems/16/Makefile.am	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/pixmaps/emblems/16/Makefile.am	Mon Oct 01 17:49:01 2007 +0000
@@ -12,6 +12,7 @@
 		hiptop.png \
 		male.png \
 		mobile.png \
+		music.png \
 		not-authorized.png \
 		operator.png \
 		qq-member.png \
Binary file pidgin/pixmaps/emblems/16/music.png has changed
--- a/pidgin/pixmaps/emblems/16/scalable/Makefile.am	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/pixmaps/emblems/16/scalable/Makefile.am	Mon Oct 01 17:49:01 2007 +0000
@@ -7,6 +7,7 @@
 		game.svg \
 		male.svg \
 		mobile.svg \
+		music.svg \
 		not-authorized.svg \
 		qq-member.svg \
 		secure.svg \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/emblems/16/scalable/music.svg	Mon Oct 01 17:49:01 2007 +0000
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45"
+   sodipodi:modified="true"
+   version="1.0">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3185">
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:1;"
+         offset="0"
+         id="stop3187" />
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:0"
+         offset="1"
+         id="stop3189" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3177">
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:1;"
+         offset="0"
+         id="stop3179" />
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:0;"
+         offset="1"
+         id="stop3181" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3153">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
+         offset="0"
+         id="stop3155" />
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:0;"
+         offset="1"
+         id="stop3157" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3153"
+       id="linearGradient3159"
+       x1="2.5409546"
+       y1="10.048674"
+       x2="10.378205"
+       y2="15.928688"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3177"
+       id="radialGradient3183"
+       cx="5.2116022"
+       cy="8.4051199"
+       fx="5.2116022"
+       fy="8.4051199"
+       r="2.9404981"
+       gradientTransform="matrix(2.6050387,0,0,2.2888674,-8.415579,-10.767812)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3185"
+       id="radialGradient3191"
+       cx="5.1978397"
+       cy="8.4135866"
+       fx="5.1978397"
+       fy="8.4135866"
+       r="3.1428281"
+       gradientTransform="matrix(2.8202152,0,0,2.4999643,-9.461187,-12.455954)"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.4"
+     inkscape:cx="19.784002"
+     inkscape:cy="11.848"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="16px"
+     height="16px"
+     showgrid="true"
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="22" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#555753;fill-opacity:1;stroke:#222728;stroke-width:1.14297926;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path2160"
+       sodipodi:cx="6.4712896"
+       sodipodi:cy="14.484771"
+       sodipodi:rx="3.5986683"
+       sodipodi:ry="2.1781414"
+       d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1  2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1  10.069958 14.484771 z"
+       transform="matrix(0.8336417,0,0,0.918214,4.1052631,-0.8001194)" />
+    <rect
+       style="opacity:1;fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3143"
+       width="1"
+       height="12"
+       x="12"
+       y="1"
+       rx="0.18940361"
+       ry="0.20662212" />
+    <path
+       style="fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 12.006464,3.0203051 L 11.981638,1 C 13.945163,1 17.837365,1.1548232 14.996311,7.9684328 C 15.596089,2.2547574 13.743811,3.0203051 12.006464,3.0203051 z "
+       id="rect3146"
+       sodipodi:nodetypes="cccc" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#555753;fill-opacity:1;stroke:url(#linearGradient3159);stroke-width:1.97969818;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3151"
+       sodipodi:cx="6.4712896"
+       sodipodi:cy="14.484771"
+       sodipodi:rx="3.5986683"
+       sodipodi:ry="2.1781414"
+       d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1  2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1  10.069958 14.484771 z"
+       transform="matrix(0.5557611,0,0,0.4591071,5.903509,5.8499391)" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3191);stroke-width:0.98568761;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3173"
+       sodipodi:cx="8.0970039"
+       sodipodi:cy="11.122857"
+       sodipodi:rx="4.3089318"
+       sodipodi:ry="3.5513175"
+       d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
+       transform="matrix(1.0443424,0,0,0.9855497,-0.4560443,3.7870959e-2)"
+       sodipodi:start="2.8475788"
+       sodipodi:end="4.9442449"
+       sodipodi:open="true" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3183);stroke-width:0.58102763;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3175"
+       sodipodi:cx="8.0970039"
+       sodipodi:cy="11.122857"
+       sodipodi:rx="4.3089318"
+       sodipodi:ry="3.5513175"
+       d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
+       transform="matrix(1.6981233,0,0,1.7443645,-5.9310642,-8.697544)"
+       sodipodi:start="2.8475788"
+       sodipodi:end="4.9442449"
+       sodipodi:open="true" />
+  </g>
+</svg>
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in	Mon Oct 01 17:02:03 2007 +0000
+++ b/pidgin/pixmaps/emotes/default/24/default.theme.in	Mon Oct 01 17:49:01 2007 +0000
@@ -23,7 +23,7 @@
 foot-in-mouth.png   :-!
 shout.png           >:o     >:O
 ! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)
+! monkey.png        :-(|)  :(|)     8-|)
 ! cyclops.png       O-) o-)
 
 # Following AIM 6.1
@@ -249,7 +249,7 @@
 sick.png            :-!
 kissed.png          *KISSED*
 stop.png            *STOP*
-kiss.png            :-*
+kiss.png            :-{} :-*
 kissing.png         *KISSING* 
 embarrassed.png     :-[
 devil.png           ]:->
@@ -262,7 +262,6 @@
 shout.png           >:o     >:O
 beer.png            *DRINK*
 smile-big.png       :-D     :D
-moneymouth.png      :-$
 glasses-cool.png    8-)
 in-love.png         *IN\ LOVE*
 ! skywalker.png     C:-)    c:-)    C:)     c:)
Binary file pidgin/pixmaps/logo.png has changed
Binary file pidgin/pixmaps/protocols/22/myspace.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/protocols/22/scalable/myspace.svg	Mon Oct 01 17:49:01 2007 +0000
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="24"
+   height="24"
+   id="svg2160"
+   sodipodi:version="0.32"
+   inkscape:version="0.45"
+   sodipodi:docname="myspace48.svg"
+   sodipodi:docbase="/home/hbons/Desktop"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   sodipodi:modified="true"
+   version="1.0">
+  <defs
+     id="defs2162">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3147">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
+         offset="0"
+         id="stop3149" />
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:0;"
+         offset="1"
+         id="stop3151" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3172">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
+         offset="0"
+         id="stop3174" />
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:0;"
+         offset="1"
+         id="stop3176" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3172"
+       id="linearGradient3178"
+       x1="10.549266"
+       y1="5.125"
+       x2="52.810349"
+       y2="49.864979"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3147"
+       id="linearGradient3153"
+       x1="6.0203052"
+       y1="1.0398448"
+       x2="26.101271"
+       y2="25.83909"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="9.8994949"
+     inkscape:cx="31.349135"
+     inkscape:cy="11.3794"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="3"
+     inkscape:window-y="25"
+     width="24px"
+     height="24px" />
+  <metadata
+     id="metadata2165">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="opacity:1;fill:#babdb6;fill-opacity:1;stroke:#888a85;stroke-width:1.00000012;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 17 1.5 C 15.068 1.5 13.5 3.0680001 13.5 5 C 13.5 6.932 15.068 8.5 17 8.5 C 18.932 8.5 20.499999 6.932 20.5 5 C 20.5 3.068 18.931999 1.4999999 17 1.5 z M 10.5 2.5 C 8.8439999 2.5 7.5000001 3.8439999 7.5 5.5 C 7.5 7.1559999 8.8439997 8.4999996 10.5 8.5 C 9.395701 8.5527352 8.4448563 9.5454035 7.875 10.4375 C 7.830859 8.7148111 7.161553 7.5791344 5.6875 7.5 C 6.7035586 7.4052513 7.5 6.5406823 7.5 5.5 C 7.5 4.396 6.604 3.5 5.5 3.5 C 4.396 3.5 3.4999999 4.396 3.5 5.5 C 3.5 6.5828941 4.3626534 7.4666427 5.4375 7.5 C 4.352857 7.6401732 3.5000005 8.6243123 3.5 9.875 L 3.5 14.53125 L 7.5625 14.53125 L 7.5625 18.53125 L 12.5 18.53125 L 12.5 22.5 L 21.5 22.5 C 21.499867 19.408873 21.5 16.303843 21.5 13.21875 C 21.5 10.634939 19.434134 8.53125 16.875 8.53125 C 15.587036 8.5312503 14.40291 9.424387 13.5625 10.28125 C 13.046577 9.1148819 11.916283 8.5 10.59375 8.5 C 12.205095 8.4489386 13.5 7.1236737 13.5 5.5 C 13.5 3.8440001 12.156 2.5 10.5 2.5 z "