changeset 27755:9a60b9fab0ea

propagate from branch 'im.pidgin.pidgin' (head d50f362e369f486aaf9a95a0d79ec0632743f07d) to branch 'im.pidgin.pidgin.yaz' (head 85b37cd20a00067af5a2da2760336b0c1016ef47)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 07 Jan 2008 03:40:27 +0000
parents 754a82f1371b (current diff) 6f452e8d13a2 (diff)
children 9983576f3aa4
files autogen.sh configure.ac libpurple/protocols/irc/parse.c libpurple/protocols/jabber/jabber.c libpurple/protocols/msn/msn.c libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/oscar.c libpurple/protocols/yahoo/util.c libpurple/util.c libpurple/util.h pidgin/gtkconv.c pidgin/gtkimhtml.c pidgin/gtkimhtmltoolbar.c pidgin/gtkmain.c pidgin/gtkprefs.c pidgin/gtkutils.c pidgin/gtkutils.h
diffstat 177 files changed, 5989 insertions(+), 2700 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Mon Dec 17 08:20:42 2007 +0000
+++ b/AUTHORS	Mon Jan 07 03:40:27 2008 +0000
@@ -35,6 +35,7 @@
 Megan 'Cae' Schneider - support/QA
 Evan Schoenberg - Developer
 Kevin 'SimGuy' Stange - Developer & Webmaster
+Will 'resiak' Thompson - Developer
 Stu 'nosnilmot' Tomlinson - Developer
 Nathan 'faceprint' Walp - Developer
 
@@ -42,8 +43,8 @@
 -------------------
 Dennis 'EvilDennisR' Ristuccia
 Peter 'Fmoo' Ruibal
+Elliott 'QuLogic' Sales de Andrade
 Gabriel 'Nix' Schulhof
-Will 'resiak' Thompson
 
 Retired Developers:
 ------------------
--- a/COPYRIGHT	Mon Dec 17 08:20:42 2007 +0000
+++ b/COPYRIGHT	Mon Jan 07 03:40:27 2008 +0000
@@ -151,6 +151,7 @@
 Michael Golden
 Charlie Gordon
 Ryan C. Gordon
+Konrad Gräfe
 Miah Gregory
 David Grohmann
 Christian Hammond
@@ -167,6 +168,7 @@
 Nick Hebner
 Mike Heffner
 Justin Heiner
+Moos Heintzen
 Benjamin Herrenschmidt
 Fernando Herrera
 hjheins
@@ -298,6 +300,7 @@
 Jory A. Pratt
 Brent Priddy
 Justin Pryzby
+Ignacio Casal Quinteiro
 Federicco Mena Quintero
 Yosef Radchenko
 David Raeman
@@ -327,6 +330,7 @@
 Andrew Sayman
 Alceste Scalas
 Carsten Schaar
+Jonathan Schleifer <js-pidgin@webkeks.org>
 Matteo Settenvini
 Colin Seymour
 Luke Schierer
@@ -413,6 +417,7 @@
 Zsombor Welker
 Andrew Wellington
 Adam Wendt
+Simon Wenner
 Dave West
 Zac West
 Daniel Westermann-Clark
--- a/ChangeLog	Mon Dec 17 08:20:42 2007 +0000
+++ b/ChangeLog	Mon Jan 07 03:40:27 2008 +0000
@@ -1,12 +1,23 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.2 (??/??/????):
+version 2.4.0 (??/??/????):
 	libpurple:
 	* Fixed various problems with loss of status messages when going
 	  or returning from idle on MySpaceIM.
 	* Eliminated unmaintained Howl backend implementation for the
 	  Bonjour protocol.  Avahi (or Apple's Bonjour runtime on win32) is
 	  now required to use Bonjour.
+	* Partial support for viewing ICQ status notes (Collin from
+	  ComBOTS GmbH).
+
+	Pidgin:
+	* Added the ability to theme conversation name colors (red and blue)
+	  through your GTK+ theme, and exposed those theme settings to the 
+	  Pidgin GTK+ Theme Control plugin (Dustin Howett)
+	* Fixed having multiple alias edit areas in the infopane (Elliott Sales
+	  de Andrade)
+	* Save the conversation "Enable Logging" option per-contact (Moos
+	  Heintzen)
 
 	Finch:
 	* Color is used in the buddylist to indicate status, and the conversation
@@ -15,6 +26,32 @@
 	* The default keybinding for dump-screen is now M-D and uses a file
 	  request dialog. M-d will properly delete-forward-word, and M-f has been
 	  fixed to imitate readline's behavior.
+	* New bindings alt+tab and alt+shift+tab to help navigating between the
+	  higlighted windows (details on the man-page).
+	* Recently signed on (or off) buddies blink in the buddy list.
+	* New action 'Room List' in the action-list can be used to get the list of
+	  available chat rooms for an online account.
+
+version 2.3.1 (12/7/2007):
+	http://developer.pidgin.im/query?status=closed&milestone=2.3.1
+		NOTE: Due to the way this release was made, it is possible that
+		      bugs marked as fixed in 2.3.1 will not be fixed until the
+		      next release.
+	
+	* Fixed a number of MSN bugs introduced in 2.3.0, resolving problems
+	  connecting to MSN and random local display name changes
+	* Going idle on MySpaceIM will no longer clear your status and message.
+	* Idle MySpaceIM buddies should now appear online at login.
+	* Fixed crashes in XMPP when discovering a client's capabilities
+	* Don't set the current tune title if it's NULL (XMPP/Google Talk)
+	* Don't allow buddies to be manually added to Bonjour
+	* Don't advertise IPv6 on Bonjour because we don't support it
+	* Compile fixes for FreeBSD and Solaris
+	* Update QQ client version so some accounts can connect again
+	* Do not allow ISON requests to stack in IRC, preventing flooding IRC
+	  servers when temporary network outages are restored
+	* Plug several leaks in the perl plugin loader
+	* Prevent autoaccept plugin overwriting existing files
 
 version 2.3.0 (11/24/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.3.0
--- a/ChangeLog.API	Mon Dec 17 08:20:42 2007 +0000
+++ b/ChangeLog.API	Mon Jan 07 03:40:27 2008 +0000
@@ -1,7 +1,50 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.3.2 (??/??/????):
+version 2.4.0 (??/??/????):
+	libpurple:
+		Added:
+		* purple_certificate_add_ca_search_path. (Florian Quèze)
+		* purple_gai_strerror.
+		* purple_major_version, purple_minor_version,
+		  purple_micro_version variables are exported by version.h,
+		  giving the version of libpurple in use at runtime.
+		* purple_util_set_current_song, purple_util_format_song_info
+		* Some accessor functions to the Roomlist API:
+			* purple_roomlist_get_fields
+			* purple_roomlist_room_get_type
+			* purple_roomlist_room_get_name
+			* purple_roomlist_room_get_parent
+			* purple_roomlist_room_get_fields
+			* purple_roomlist_field_get_type
+			* purple_roomlist_field_get_label
+			* purple_roomlist_field_get_hidden
+
+	Pidgin:
+		Added:
+		* pidgin_create_dialog to create a window that closes on escape. Also
+		  added utility functions pidgin_dialog_get_vbox_with_properties,
+		  pidgin_dialog_get_vbox, pidgin_dialog_get_action_area to access the
+		  contents in the created dialog. (Peter 'fmoo' Ruibal)
+		* pidgin_dialog_add_button to add buttons to a dialog created by
+		  pidgin_create_dialog.
+		* GTK_IMHTML_NO_SMILEY for GtkIMHtmlOptions means not to look for
+		  smileys in the text. (Florian 'goutnet' Delizy)
+		* pidgin_auto_parent_window to make a window transient for a suitable
+		  parent window.
+		* pidgin_tooltip_setup_for_treeview, pidgin_tooltip_destroy,
+		  pidgin_tooltip_show and pidgin_tooltip_setup_for_widget to simplify
+		  the process of drawing tooltips.
+		* pidgin_add_widget_to_vbox to simplify adding a labeled widget to a
+		  window.
+
+		Deprecated:
+		* PIDGIN_DIALOG
+
 	Finch:
+		* finch_roomlist_get_ui_ops and finch_roomlist_show_all
+		* finch_request_field_get_widget to get the widget for a request
+		  field.
+
 		libgnt:
 		* Added gnt_tree_set_row_color to set the color for a row in a tree.
 		* Added gnt_style_get_string_list
--- a/ChangeLog.win32	Mon Dec 17 08:20:42 2007 +0000
+++ b/ChangeLog.win32	Mon Jan 07 03:40:27 2008 +0000
@@ -1,3 +1,6 @@
+version 2.3.1 (12/7/2007):
+	* No changes
+
 version 2.3.0 (11/24/2007):
 	* Updated GTK+ to 2.12.1 (This was actually included in 2.2.2, but 
 	  didn't get into the Changelog.)
@@ -135,7 +138,7 @@
 
 version 0.82 (08/26/2004):
 	* Selecting away messages using the system tray icon works
-	  (Thanks Fran?ois Gagn?)
+	  (Thanks François Gagné)
 	* Transparency plugin will save your settings again (Kevin Stange)
 	* Updated gtk-wimp to 0.6.2
 	* Updated libpng to 1.2.6 (major security update)
--- a/Makefile.am	Mon Dec 17 08:20:42 2007 +0000
+++ b/Makefile.am	Mon Jan 07 03:40:27 2008 +0000
@@ -30,10 +30,12 @@
 distcheck-hook: libpurple/plugins/perl/common/Purple.pm pidgin/plugins/perl/common/Pidgin.pm
 #	cp libpurple/plugins/perl/common/Gaim.pm $(distdir)/libpurple/plugins/perl/common
 
+if ENABLE_GTK
 appsdir = $(datadir)/applications
 apps_in_files = pidgin.desktop.in
 apps_DATA = $(apps_in_files:.desktop.in=.desktop)
 @INTLTOOL_DESKTOP_RULE@
+endif
 
 if ENABLE_GTK
 GTK_DIR=pidgin
--- a/NEWS	Mon Dec 17 08:20:42 2007 +0000
+++ b/NEWS	Mon Jan 07 03:40:27 2008 +0000
@@ -1,5 +1,19 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+2.3.1 (12/7/2007):
+	Stu: I'm sorry for the MSN problems and the plugin crashes in 2.3.0.
+	Hopefully this will redeem us. This fixes a number of bugs. I'm a
+	bit late but I'd like to welcome John to the team. Enjoy!
+
+	Luke: I've done absolutely nothing in the last 2 weeks, except watch
+	others commit bug and, more, leak fixes.  People should be noticing
+	remarkably fewer memory leaks now than 2 or more releases ago.
+
+	Kevin: I'm not quite sure what happened to our MySpaceIM Summer of
+	Code student, but I fixed a few MySpace bugs with idle and status.
+	I will try to fix some of the other more significant bugs, after I
+	figure out the protocol, especially including grouping issues.
+
 2.3.0 (11/20/2007):
 	Luke: While this does not have the new MSN code, rest assured that
 	we are working on it and that it is nearing release.  This contains
--- a/config.h.mingw	Mon Dec 17 08:20:42 2007 +0000
+++ b/config.h.mingw	Mon Jan 07 03:40:27 2008 +0000
@@ -355,7 +355,7 @@
 
 /* Loads static protocol plugin module initialization functions. */
 #ifndef STATIC_PROTO_INIT
-#define STATIC_PROTO_INIT static void static_proto_init() {  }
+#define STATIC_PROTO_INIT static void static_proto_init(void) {  }
 #endif
 
 /* Define to 1 if you have the ANSI C header files. */
--- a/configure.ac	Mon Dec 17 08:20:42 2007 +0000
+++ b/configure.ac	Mon Jan 07 03:40:27 2008 +0000
@@ -43,19 +43,19 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [3])
+m4_define([purple_lt_current], [4])
 m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [3])
-m4_define([purple_micro_version], [2])
+m4_define([purple_minor_version], [4])
+m4_define([purple_micro_version], [0])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
 m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
 
-m4_define([gnt_lt_current], [3])
+m4_define([gnt_lt_current], [4])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [3])
-m4_define([gnt_micro_version], [2])
+m4_define([gnt_minor_version], [4])
+m4_define([gnt_micro_version], [0])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -142,7 +142,7 @@
 dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
 dnl AM_GLIB_GNU_GETTEXT found it.
 
-if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT = x
+if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x
 then
 	AC_ERROR([
 
@@ -678,7 +678,6 @@
 PKG_CHECK_MODULES(MEANWHILE, [meanwhile >= 1.0.0 meanwhile < 2.0.0], [
 	have_meanwhile="yes"
 ], [
-	AC_MSG_RESULT(no)
 	have_meanwhile="no"
 ])
 AC_SUBST(MEANWHILE_CFLAGS)
@@ -697,7 +696,6 @@
 	avahiincludes="yes"
 	avahilibs="yes"
 ], [
-	AC_MSG_RESULT(no)
 	avahiincludes="no"
 	avahilibs="no"
 ])
@@ -742,7 +740,6 @@
 		silcincludes="yes"
 		silcclient="yes"
 	], [
-		AC_MSG_RESULT(no)
 		have_silc="no"
 	])
 	if test "x$have_silc" = "xno"; then
@@ -751,7 +748,6 @@
 			silc10includes="yes"
 			silc10client="yes"
 		], [
-			AC_MSG_RESULT(no)
 			have_silc="no"
 		])
 		dnl If silcclient.pc wasn't found, check for just silc.pc
@@ -761,7 +757,6 @@
 				silc10includes="yes"
 				silc10client="yes"
 			], [
-				AC_MSG_RESULT(no)
 				have_silc="no"
 			])
 		fi
@@ -982,7 +977,7 @@
 AM_CONDITIONAL(STATIC_YAHOO, test "x$static_yahoo" = "xyes")
 AM_CONDITIONAL(STATIC_ZEPHYR, test "x$static_zephyr" = "xyes")
 AC_SUBST(STATIC_LINK_LIBS)
-AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init() { $load_proto },
+AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init(void) { $load_proto },
 	[Loads static protocol plugin module initialization functions.])
 
 AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`])
@@ -1153,7 +1148,6 @@
 		AC_SUBST(DBUS_LIBS)
 		enable_dbus=yes
 	], [
-		AC_MSG_RESULT(no)
 		enable_dbus=no
 	])
 
--- a/doc/finch.1.in	Mon Dec 17 08:20:42 2007 +0000
+++ b/doc/finch.1.in	Mon Jan 07 03:40:27 2008 +0000
@@ -105,6 +105,12 @@
 .B Alt \+ 1 2 ... 0
 Jump to the 1st, 2nd ... 10th window.
 .TP
+.B Alt \+ Tab
+Jump to the next URGENT (highlighted) window.
+.TP
+.B Alt \+ Shift \+ Tab
+Jump to the previous URGENT (highlighted) window.
+.TP
 .B Ctrl \+ o
 Bring up the menu (if there is one) for a window.
 .TP
@@ -460,6 +466,7 @@
 .br
 # switch-window-n
 .br
+# Other actions: window-next-urgent, window-prev-urgent
 
 # For the sample custom window manager
 .br
--- a/doc/pidgin.1.in	Mon Dec 17 08:20:42 2007 +0000
+++ b/doc/pidgin.1.in	Mon Jan 07 03:40:27 2008 +0000
@@ -590,6 +590,8 @@
 .br
   Kevin 'SimGuy' Stange (developer and webmaster)
 .br
+  Will 'resiak' Thompson (developer)
+.br
   Stu 'nosnilmot' Tomlinson (developer)
 .br
   Nathan 'faceprint' Walp (developer)
@@ -602,9 +604,9 @@
 .br
   Peter 'fmoo' Ruibal
 .br
-  Gabriel 'Nix' Schulhof
+  Elliott 'QuLogic' Sales de Andrade
 .br
-  Will 'resiak' Thompson
+  Gabriel 'Nix' Schulhof
 .br
 
 
--- a/finch/Makefile.am	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/Makefile.am	Mon Jan 07 03:40:27 2008 +0000
@@ -30,6 +30,7 @@
 	gntpounce.c \
 	gntprefs.c \
 	gntrequest.c \
+	gntroomlist.c \
 	gntsound.c \
 	gntstatus.c \
 	gntui.c
@@ -49,6 +50,7 @@
 	gntpounce.h \
 	gntprefs.h \
 	gntrequest.h \
+	gntroomlist.h \
 	gntsound.h \
 	gntstatus.h \
 	gntui.h
--- a/finch/finch.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/finch.c	Mon Jan 07 03:40:27 2008 +0000
@@ -49,14 +49,14 @@
 #include "config.h"
 
 static void
-debug_init()
+debug_init(void)
 {
 	finch_debug_init();
 	purple_debug_set_ui_ops(finch_debug_get_ui_ops());
 }
 
 static GHashTable *ui_info = NULL;
-static GHashTable *finch_ui_get_info()
+static GHashTable *finch_ui_get_info(void)
 {
 	if (ui_info == NULL) {
 		ui_info = g_hash_table_new(g_str_hash, g_str_equal);
@@ -91,7 +91,7 @@
 };
 
 static PurpleCoreUiOps *
-gnt_core_get_ui_ops()
+gnt_core_get_ui_ops(void)
 {
 	return &core_ops;
 }
--- a/finch/gntaccount.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntaccount.c	Mon Jan 07 03:40:27 2008 +0000
@@ -722,7 +722,7 @@
 }
 
 static gpointer
-finch_accounts_get_handle()
+finch_accounts_get_handle(void)
 {
 	static int handle;
 
--- a/finch/gntblist.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntblist.c	Mon Jan 07 03:40:27 2008 +0000
@@ -83,6 +83,12 @@
 	GntMenuItem *plugins;
 } FinchBlist;
 
+typedef struct
+{
+	gpointer row;                /* the row in the GntTree */
+	guint signed_timer;          /* used when 'recently' signed on/off */
+} FinchBlistNode;
+
 typedef enum
 {
 	STATUS_PRIMITIVE = 0,
@@ -131,6 +137,31 @@
 static int color_offline;
 static int color_idle;
 
+static FinchBlistNode *
+create_finch_blist_node(PurpleBlistNode *node, gpointer row)
+{
+	FinchBlistNode *fnode = node->ui_data;
+	if (!fnode) {
+		fnode = g_new0(FinchBlistNode, 1);
+		fnode->signed_timer = 0;
+		node->ui_data = fnode;
+	}
+	fnode->row = row;
+	return fnode;
+}
+
+static void
+reset_blist_node_ui_data(PurpleBlistNode *node)
+{
+	FinchBlistNode *fnode = node->ui_data;
+	if (fnode == NULL)
+		return;
+	if (fnode->signed_timer)
+		purple_timeout_remove(fnode->signed_timer);
+	g_free(fnode);
+	node->ui_data = NULL;
+}
+
 static int
 get_display_color(PurpleBlistNode  *node)
 {
@@ -157,6 +188,34 @@
 	return color;
 }
 
+static GntTextFormatFlags
+get_blist_node_flag(PurpleBlistNode *node)
+{
+	GntTextFormatFlags flag = 0;
+	FinchBlistNode *fnode = node->ui_data;
+
+	if (ggblist->tagged && g_list_find(ggblist->tagged, node))
+		flag |= GNT_TEXT_FLAG_BOLD;
+
+	if (fnode && fnode->signed_timer)
+		flag |= GNT_TEXT_FLAG_BLINK;
+	else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact *)node);
+		fnode = node->ui_data;
+		if (fnode && fnode->signed_timer)
+			flag |= GNT_TEXT_FLAG_BLINK;
+	}
+
+	return flag;
+}
+
+static void
+blist_update_row_flags(PurpleBlistNode *node)
+{
+	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, get_blist_node_flag(node));
+	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
+}
+
 static gboolean
 is_contact_online(PurpleContact *contact)
 {
@@ -217,7 +276,7 @@
 		return;
 
 	gnt_tree_remove(GNT_TREE(ggblist->tree), node);
-	node->ui_data = NULL;
+	reset_blist_node_ui_data(node);
 	if (ggblist->tagged)
 		ggblist->tagged = g_list_remove(ggblist->tagged, node);
 
@@ -234,7 +293,7 @@
 				(!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)))
 			node_remove(list, node->parent);
 		for (node = node->child; node; node = node->next)
-			node->ui_data = NULL;
+			reset_blist_node_ui_data(node);
 	} else {
 		for (node = node->child; node; node = node->next)
 			node_remove(list, node);
@@ -261,7 +320,7 @@
 		gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
 				0, get_display_name(node));
 		gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
-		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
+		blist_update_row_flags(node);
 	}
 
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
@@ -269,8 +328,6 @@
 		if (purple_account_is_connected(buddy->account) &&
 				(PURPLE_BUDDY_IS_ONLINE(buddy) || purple_prefs_get_bool(PREF_ROOT "/showoffline")))
 			add_node((PurpleBlistNode*)buddy, list->ui_data);
-		else
-			node_remove(purple_get_blist(), node);
 
 		node_update(list, node->parent);
 	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
@@ -279,7 +336,7 @@
 		PurpleContact *contact = (PurpleContact*)node;
 		if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) ||
 				contact->currentsize < 1)
-			node_remove(purple_get_blist(), node);
+			/* nothing */;
 		else {
 			if (node->ui_data == NULL) {
 				/* The core seems to expect the UI to add the buddies. */
@@ -483,7 +540,7 @@
 }
 
 static void
-finch_request_add_group()
+finch_request_add_group(void)
 {
 	purple_request_input(NULL, _("Add Group"), NULL, _("Enter the name of the group"),
 			NULL, FALSE, FALSE, NULL,
@@ -511,7 +568,7 @@
 };
 
 static gpointer
-finch_blist_get_handle()
+finch_blist_get_handle(void)
 {
 	static int handle;
 
@@ -524,8 +581,8 @@
 	PurpleBlistNode *node = (PurpleBlistNode *)group;
 	if (node->ui_data)
 		return;
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
-			gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL);
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
+			gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL));
 	gnt_tree_set_expanded(GNT_TREE(ggblist->tree), node,
 		!purple_blist_node_get_bool(node, "collapsed"));
 }
@@ -539,7 +596,7 @@
 
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node))
 		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);  /* XXX: this can return NULL?! */
-	
+
 	if (node == NULL)
 		return NULL;
 
@@ -550,7 +607,7 @@
 		PurplePresence *presence;
 		PurpleStatus *now;
 		gboolean ascii = gnt_ascii_only();
-		
+
 		presence = purple_buddy_get_presence(buddy);
 		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
 			strncpy(status, ascii ? ":" : "☎", sizeof(status) - 1);
@@ -601,9 +658,9 @@
 	group = purple_chat_get_group(chat);
 	add_node((PurpleBlistNode*)group, ggblist);
 
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat,
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat,
 				gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
-				group, NULL);
+				group, NULL));
 }
 
 static void
@@ -623,9 +680,9 @@
 	group = (PurpleGroup*)node->parent;
 	add_node((PurpleBlistNode*)group, ggblist);
 
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact,
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact,
 				gnt_tree_create_row(GNT_TREE(ggblist->tree), name),
-				group, NULL);
+				group, NULL));
 
 	gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE);
 }
@@ -635,7 +692,7 @@
 {
 	PurpleContact *contact;
 	PurpleBlistNode *node = (PurpleBlistNode *)buddy;
-	int color = 0;
+
 	if (node->ui_data)
 		return;
 
@@ -647,14 +704,13 @@
 		return;
 	add_node((PurpleBlistNode*)contact, ggblist);
 
-	node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
+	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy,
 				gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
-				contact, NULL);
-
-	color = get_display_color((PurpleBlistNode*)buddy);
-	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color);
+				contact, NULL));
+
+	blist_update_row_flags((PurpleBlistNode*)buddy);
 	if (buddy == purple_contact_get_priority_buddy(contact))
-		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color);
+		blist_update_row_flags((PurpleBlistNode*)contact);
 }
 
 #if 0
@@ -1564,9 +1620,7 @@
 static void
 update_node_display(PurpleBlistNode *node, FinchBlist *ggblist)
 {
-	GntTextFormatFlags flag = 0;
-	if (ggblist->tagged && g_list_find(ggblist->tagged, node))
-		flag |= GNT_TEXT_FLAG_BOLD;
+	GntTextFormatFlags flag = get_blist_node_flag(node);
 	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, flag);
 }
 
@@ -1574,33 +1628,18 @@
 update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist)
 {
 	PurpleContact *contact;
-	GntTextFormatFlags bflag = 0, cflag = 0;
-	int color = 0;
 
 	contact = purple_buddy_get_contact(buddy);
 
 	gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy));
 	gnt_tree_change_text(GNT_TREE(ggblist->tree), contact, 0, get_display_name((PurpleBlistNode*)contact));
 
-	if (ggblist->tagged && g_list_find(ggblist->tagged, buddy))
-		bflag |= GNT_TEXT_FLAG_BOLD;
-	if (ggblist->tagged && g_list_find(ggblist->tagged, contact))
-		cflag |= GNT_TEXT_FLAG_BOLD;
+	blist_update_row_flags((PurpleBlistNode *)buddy);
+	if (buddy == purple_contact_get_priority_buddy(contact))
+		blist_update_row_flags((PurpleBlistNode *)contact);
 
 	if (ggblist->tnode == (PurpleBlistNode*)buddy)
 		draw_tooltip(ggblist);
-
-	color = get_display_color((PurpleBlistNode*)buddy);
-	gnt_tree_set_row_color(GNT_TREE(ggblist->tree), buddy, color);
-	if (buddy == purple_contact_get_priority_buddy(contact))
-		gnt_tree_set_row_color(GNT_TREE(ggblist->tree), contact, color);
-
-	gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag);
-	if (buddy == purple_contact_get_priority_buddy(contact))
-		gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag);
-
-	if (buddy != purple_contact_get_priority_buddy(contact))
-			update_buddy_display(purple_contact_get_priority_buddy(contact), ggblist);
 }
 
 static void
@@ -1649,7 +1688,7 @@
 
 	node = purple_blist_get_root();
 	while (node) {
-		node->ui_data = NULL;
+		reset_blist_node_ui_data(node);
 		node = purple_blist_node_next(node, TRUE);
 	}
 
@@ -1663,7 +1702,7 @@
 }
 
 static void
-populate_buddylist()
+populate_buddylist(void)
 {
 	PurpleBlistNode *node;
 	PurpleBuddyList *list;
@@ -1696,7 +1735,7 @@
 }
 
 static void
-populate_status_dropdown()
+populate_status_dropdown(void)
 {
 	int i;
 	GList *iter;
@@ -1762,7 +1801,7 @@
 	gnt_tree_remove_all(GNT_TREE(ggblist->tree));
 	node = purple_blist_get_root();
 	for (; node; node = purple_blist_node_next(node, TRUE))
-		node->ui_data = NULL;
+		reset_blist_node_ui_data(node);
 	populate_buddylist();
 	gnt_tree_set_selected(GNT_TREE(ggblist->tree), sel);
 	draw_tooltip(ggblist);
@@ -2122,8 +2161,53 @@
 	}
 }
 
+static gboolean
+buddy_recent_signed_on_off(gpointer data)
+{
+	PurpleBlistNode *node = data;
+	FinchBlistNode *fnode = node->ui_data;
+	PurpleBuddy *buddy = (PurpleBuddy*)node;
+
+	purple_timeout_remove(fnode->signed_timer);
+	fnode->signed_timer = 0;
+
+	if (!purple_account_is_connected(buddy->account) ||
+			(!PURPLE_BUDDY_IS_ONLINE(buddy) && !purple_prefs_get_bool(PREF_ROOT "/showoffline"))) {
+		node_remove(purple_get_blist(), node);
+	} else {
+		update_node_display(node, ggblist);
+		if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent))
+			update_node_display(node->parent, ggblist);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+buddy_signed_on_off_cb(gpointer data)
+{
+	PurpleBlistNode *node = data;
+	FinchBlistNode *fnode = node->ui_data;
+	if (!ggblist || !fnode)
+		return FALSE;
+
+	if (fnode->signed_timer)
+		purple_timeout_remove(fnode->signed_timer);
+	fnode->signed_timer = purple_timeout_add_seconds(6, (GSourceFunc)buddy_recent_signed_on_off, data);
+	update_node_display(node, ggblist);
+	if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent))
+		update_node_display(node->parent, ggblist);
+	return FALSE;
+}
+
 static void
-reconstruct_plugins_menu()
+buddy_signed_on_off(PurpleBuddy* buddy, gpointer null)
+{
+	g_idle_add(buddy_signed_on_off_cb, buddy);
+}
+
+static void
+reconstruct_plugins_menu(void)
 {
 	GntWidget *sub;
 	GntMenuItem *plg;
@@ -2155,7 +2239,7 @@
 }
 
 static void
-reconstruct_accounts_menu()
+reconstruct_accounts_menu(void)
 {
 	GntWidget *sub;
 	GntMenuItem *acc, *item;
@@ -2355,7 +2439,7 @@
 }
 
 static void
-create_menu()
+create_menu(void)
 {
 	GntWidget *menu, *sub, *subsub;
 	GntMenuItem *item;
@@ -2513,12 +2597,12 @@
 	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", finch_blist_get_handle(),
 				PURPLE_CALLBACK(reconstruct_plugins_menu), NULL);
 
+	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
+				PURPLE_CALLBACK(buddy_signed_on_off), ggblist);
+	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
+				PURPLE_CALLBACK(buddy_signed_on_off), ggblist);
+
 #if 0
-	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
-				PURPLE_CALLBACK(buddy_signed_on), ggblist);
-	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
-				PURPLE_CALLBACK(buddy_signed_off), ggblist);
-
 	/* These I plan to use to indicate unread-messages etc. */
 	purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", finch_blist_get_handle(),
 				PURPLE_CALLBACK(received_im_msg), list);
--- a/finch/gntcertmgr.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntcertmgr.c	Mon Jan 07 03:40:27 2008 +0000
@@ -246,7 +246,7 @@
 
 /* populate the list */
 static void
-populate_cert_list()
+populate_cert_list(void)
 {
 	GList *idlist, *l;
 
--- a/finch/gntconv.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntconv.c	Mon Jan 07 03:40:27 2008 +0000
@@ -350,7 +350,7 @@
 }
 
 static gpointer
-finch_conv_get_handle()
+finch_conv_get_handle(void)
 {
 	static int handle;
 	return &handle;
@@ -1119,7 +1119,7 @@
 static PurpleCmdRet
 cmd_show_window(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
 {
-	void (*callback)() = data;
+	void (*callback)(void) = data;
 	callback();
 	return PURPLE_CMD_STATUS_OK;
 }
--- a/finch/gntft.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntft.c	Mon Jan 07 03:40:27 2008 +0000
@@ -85,7 +85,7 @@
  **************************************************************************/
 
 static void
-update_title_progress()
+update_title_progress(void)
 {
 	GList *list;
 	int num_active_xfers = 0;
--- a/finch/gntft.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntft.h	Mon Jan 07 03:40:27 2008 +0000
@@ -55,7 +55,7 @@
 /**
  * Hides the file transfer dialog.
  */
-void finch_xfer_dialog_hide();
+void finch_xfer_dialog_hide(void);
 
 /**
  * Adds a file transfer to the dialog.
--- a/finch/gntidle.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntidle.c	Mon Jan 07 03:40:27 2008 +0000
@@ -28,7 +28,7 @@
 #include "idle.h"
 
 static time_t
-finch_get_idle_time()
+finch_get_idle_time(void)
 {
 	return gnt_wm_get_idle_time();
 }
--- a/finch/gntnotify.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntnotify.c	Mon Jan 07 03:40:27 2008 +0000
@@ -147,14 +147,14 @@
 }
 
 static void
-reset_email_dialog()
+reset_email_dialog(void)
 {
 	emaildialog.window = NULL;
 	emaildialog.tree = NULL;
 }
 
 static void
-setup_email_dialog()
+setup_email_dialog(void)
 {
 	GntWidget *box, *tree, *button;
 	if (emaildialog.window)
--- a/finch/gntplugin.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntplugin.c	Mon Jan 07 03:40:27 2008 +0000
@@ -171,7 +171,7 @@
 }
 
 static void
-confwin_init()
+confwin_init(void)
 {
 	confwins = g_hash_table_new(g_direct_hash, g_direct_equal);
 }
--- a/finch/gntplugin.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntplugin.h	Mon Jan 07 03:40:27 2008 +0000
@@ -40,7 +40,7 @@
  **********************************************************************/
 /*@{*/
 
-typedef GntWidget* (*FinchPluginFrame) ();
+typedef GntWidget* (*FinchPluginFrame) (void);
 
 /* Guess where these came from */
 #define FINCH_PLUGIN_TYPE FINCH_UI
--- a/finch/gntprefs.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntprefs.c	Mon Jan 07 03:40:27 2008 +0000
@@ -76,17 +76,17 @@
 	PurplePrefType type;
 	const char *pref;
 	const char *label;
-	GList *(*lv)();   /* If the value is to be selected from a number of choices */
+	GList *(*lv)(void);   /* If the value is to be selected from a number of choices */
 } Prefs;
 
 static GList *
-get_log_options()
+get_log_options(void)
 {
 	return purple_log_logger_get_options();
 }
 
 static GList *
-get_idle_options()
+get_idle_options(void)
 {
 	GList *list = NULL;
 	list = g_list_append(list, (char *)_("Based on keyboard use"));
@@ -99,7 +99,7 @@
 }
 
 static GList *
-get_status_titles()
+get_status_titles(void)
 {
 	GList *list = NULL;
 	GList *iter;
@@ -212,7 +212,7 @@
 };
 
 static void
-free_strings()
+free_strings(void)
 {
 	g_list_foreach(pref_request.freestrings, (GFunc)g_free, NULL);
 	g_list_free(pref_request.freestrings);
--- a/finch/gntrequest.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntrequest.c	Mon Jan 07 03:40:27 2008 +0000
@@ -36,6 +36,7 @@
 
 #include "finch.h"
 #include "gntrequest.h"
+#include "debug.h"
 #include "util.h"
 
 typedef struct
@@ -44,7 +45,7 @@
 	GntWidget *dialog;
 	GCallback *cbs;
 	gboolean save;
-} PurpleGntFileRequest;
+} FinchFileRequest;
 
 static GntWidget *
 setup_request_window(const char *title, const char *primary,
@@ -387,6 +388,156 @@
 	}
 }
 
+static GntWidget*
+create_boolean_field(PurpleRequestField *field)
+{
+	const char *label = purple_request_field_get_label(field);
+	GntWidget *check = gnt_check_box_new(label);
+	gnt_check_box_set_checked(GNT_CHECK_BOX(check),
+			purple_request_field_bool_get_default_value(field));
+	return check;
+}
+
+static GntWidget*
+create_string_field(PurpleRequestField *field, GntWidget **screenname)
+{
+	const char *hint = purple_request_field_get_type_hint(field);
+	GntWidget *entry = gnt_entry_new(
+			purple_request_field_string_get_default_value(field));
+	gnt_entry_set_masked(GNT_ENTRY(entry),
+			purple_request_field_string_is_masked(field));
+	if (hint && purple_str_has_prefix(hint, "screenname")) {
+		PurpleBlistNode *node = purple_blist_get_root();
+		gboolean offline = purple_str_has_suffix(hint, "all");
+		for (; node; node = purple_blist_node_next(node, offline)) {
+			if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
+				continue;
+			gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node));
+		}
+		gnt_entry_set_always_suggest(GNT_ENTRY(entry), TRUE);
+		if (screenname)
+			*screenname = entry;
+	} else if (hint && !strcmp(hint, "group")) {
+		PurpleBlistNode *node;
+		for (node = purple_blist_get_root(); node; node = node->next) {
+			if (PURPLE_BLIST_NODE_IS_GROUP(node))
+				gnt_entry_add_suggest(GNT_ENTRY(entry), ((PurpleGroup *)node)->name);
+		}
+	}
+	return entry;
+}
+
+static GntWidget*
+create_integer_field(PurpleRequestField *field)
+{
+	char str[256];
+	int val = purple_request_field_int_get_default_value(field);
+	GntWidget *entry;
+
+	snprintf(str, sizeof(str), "%d", val);
+	entry = gnt_entry_new(str);
+	gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT);
+	return entry;
+}
+
+static GntWidget*
+create_choice_field(PurpleRequestField *field)
+{
+	int id;
+	GList *list;
+	GntWidget *combo = gnt_combo_box_new();
+
+	list = purple_request_field_choice_get_labels(field);
+	for (id = 1; list; list = list->next, id++)
+	{
+		gnt_combo_box_add_data(GNT_COMBO_BOX(combo),
+				GINT_TO_POINTER(id), list->data);
+	}
+	gnt_combo_box_set_selected(GNT_COMBO_BOX(combo),
+			GINT_TO_POINTER(purple_request_field_choice_get_default_value(field)));
+	return combo;
+}
+
+static GntWidget*
+create_list_field(PurpleRequestField *field)
+{
+	GntWidget *ret = NULL;
+	GList *list;
+	gboolean multi = purple_request_field_list_get_multi_select(field);
+	if (multi)
+	{
+		GntWidget *tree = gnt_tree_new();
+
+		list = purple_request_field_list_get_items(field);
+		for (; list; list = list->next)
+		{
+			const char *text = list->data;
+			gpointer key = purple_request_field_list_get_data(field, text);
+			gnt_tree_add_choice(GNT_TREE(tree), key,
+					gnt_tree_create_row(GNT_TREE(tree), text), NULL, NULL);
+			if (purple_request_field_list_is_selected(field, text))
+				gnt_tree_set_choice(GNT_TREE(tree), key, TRUE);
+		}
+		ret = tree;
+	}
+	else
+	{
+		GntWidget *combo = gnt_combo_box_new();
+
+		list = purple_request_field_list_get_items(field);
+		for (; list; list = list->next)
+		{
+			const char *text = list->data;
+			gpointer key = purple_request_field_list_get_data(field, text);
+			gnt_combo_box_add_data(GNT_COMBO_BOX(combo), key, text);
+			if (purple_request_field_list_is_selected(field, text))
+				gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), key);
+		}
+		ret = combo;
+	}
+	return ret;
+}
+
+static GntWidget*
+create_account_field(PurpleRequestField *field)
+{
+	gboolean all;
+	PurpleAccount *def;
+	GList *list;
+	GntWidget *combo = gnt_combo_box_new();
+
+	all = purple_request_field_account_get_show_all(field);
+	def = purple_request_field_account_get_value(field);
+	if (!def)
+		def = purple_request_field_account_get_default_value(field);
+
+	if (all)
+		list = purple_accounts_get_all();
+	else
+		list = purple_connections_get_all();
+
+	for (; list; list = list->next)
+	{
+		PurpleAccount *account;
+		char *text;
+
+		if (all)
+			account = list->data;
+		else
+			account = purple_connection_get_account(list->data);
+
+		text = g_strdup_printf("%s (%s)",
+				purple_account_get_username(account),
+				purple_account_get_protocol_name(account));
+		gnt_combo_box_add_data(GNT_COMBO_BOX(combo), account, text);
+		g_free(text);
+		if (account == def)
+			gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account);
+	}
+	gnt_widget_set_size(combo, 20, 3); /* ew */
+	return combo;
+}
+
 static void *
 finch_request_fields(const char *title, const char *primary,
 		const char *secondary, PurpleRequestFields *allfields,
@@ -424,10 +575,10 @@
 			PurpleRequestField *field = fields->data;
 			PurpleRequestFieldType type = purple_request_field_get_type(field);
 			const char *label = purple_request_field_get_label(field);
-				
+
 			hbox = gnt_hbox_new(TRUE);   /* hrm */
 			gnt_box_add_widget(GNT_BOX(box), hbox);
-			
+
 			if (type != PURPLE_REQUEST_FIELD_BOOLEAN && label)
 			{
 				GntWidget *l = gnt_label_new(label);
@@ -437,154 +588,35 @@
 
 			if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
 			{
-				GntWidget *check = gnt_check_box_new(label);
-				gnt_check_box_set_checked(GNT_CHECK_BOX(check),
-						purple_request_field_bool_get_default_value(field));
-				gnt_box_add_widget(GNT_BOX(hbox), check);
-				field->ui_data = check;
+				field->ui_data = create_boolean_field(field);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_STRING)
 			{
-				const char *hint = purple_request_field_get_type_hint(field);
-				GntWidget *entry = gnt_entry_new(
-							purple_request_field_string_get_default_value(field));
-				gnt_entry_set_masked(GNT_ENTRY(entry),
-						purple_request_field_string_is_masked(field));
-				if (hint && purple_str_has_prefix(hint, "screenname")) {
-					PurpleBlistNode *node = purple_blist_get_root();
-					gboolean offline = purple_str_has_suffix(hint, "all");
-					for (; node; node = purple_blist_node_next(node, offline)) {
-						if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
-							continue;
-						gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node));
-					}
-					gnt_entry_set_always_suggest(GNT_ENTRY(entry), TRUE);
-					screenname = entry;
-				} else if (hint && !strcmp(hint, "group")) {
-					PurpleBlistNode *node;
-					for (node = purple_blist_get_root(); node; node = node->next) {
-						if (PURPLE_BLIST_NODE_IS_GROUP(node))
-							gnt_entry_add_suggest(GNT_ENTRY(entry), ((PurpleGroup *)node)->name);
-					}
-				}
-				gnt_box_add_widget(GNT_BOX(hbox), entry);
-				field->ui_data = entry;
+				field->ui_data = create_string_field(field, &screenname);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_INTEGER)
 			{
-				char str[256];
-				int val = purple_request_field_int_get_default_value(field);
-				GntWidget *entry;
-				
-				snprintf(str, sizeof(str), "%d", val);
-				entry = gnt_entry_new(str);
-				gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT);
-				gnt_box_add_widget(GNT_BOX(hbox), entry);
-				field->ui_data = entry;
+				field->ui_data = create_integer_field(field);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_CHOICE)
 			{
-				int id;
-				GList *list;
-				GntWidget *combo = gnt_combo_box_new();
-				gnt_box_add_widget(GNT_BOX(hbox), combo);
-				field->ui_data = combo;
-
-				list = purple_request_field_choice_get_labels(field);
-				for (id = 1; list; list = list->next, id++)
-				{
-					gnt_combo_box_add_data(GNT_COMBO_BOX(combo),
-							GINT_TO_POINTER(id), list->data);
-				}
-				gnt_combo_box_set_selected(GNT_COMBO_BOX(combo),
-						GINT_TO_POINTER(purple_request_field_choice_get_default_value(field)));
+				field->ui_data = create_choice_field(field);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_LIST)
 			{
-				GList *list;
-				gboolean multi = purple_request_field_list_get_multi_select(field);
-				if (multi)
-				{
-					GntWidget *tree = gnt_tree_new();
-					gnt_box_add_widget(GNT_BOX(hbox), tree);
-					field->ui_data = tree;
-
-					list = purple_request_field_list_get_items(field);
-					for (; list; list = list->next)
-					{
-						const char *text = list->data;
-						gpointer key = purple_request_field_list_get_data(field, text);
-						gnt_tree_add_choice(GNT_TREE(tree), key,
-								gnt_tree_create_row(GNT_TREE(tree), text), NULL, NULL);
-						if (purple_request_field_list_is_selected(field, text))
-							gnt_tree_set_choice(GNT_TREE(tree), key, TRUE);
-					}
-				}
-				else
-				{
-					GntWidget *combo = gnt_combo_box_new();
-					gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
-					gnt_box_add_widget(GNT_BOX(hbox), combo);
-					field->ui_data = combo;
-
-					list = purple_request_field_list_get_items(field);
-					for (; list; list = list->next)
-					{
-						const char *text = list->data;
-						gpointer key = purple_request_field_list_get_data(field, text);
-						gnt_combo_box_add_data(GNT_COMBO_BOX(combo), key, text);
-						if (purple_request_field_list_is_selected(field, text))
-							gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), key);
-					}
-				}
+				field->ui_data = create_list_field(field);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
 			{
-				gboolean all;
-				PurpleAccount *def;
-				GList *list;
-				GntWidget *combo = gnt_combo_box_new();
-				gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
-				gnt_box_add_widget(GNT_BOX(hbox), combo);
-				field->ui_data = combo;
-
-				all = purple_request_field_account_get_show_all(field);
-				def = purple_request_field_account_get_value(field);
-				if (!def)
-					def = purple_request_field_account_get_default_value(field);
-
-				if (all)
-					list = purple_accounts_get_all();
-				else
-					list = purple_connections_get_all();
-
-				for (; list; list = list->next)
-				{
-					PurpleAccount *account;
-					char *text;
-
-					if (all)
-						account = list->data;
-					else
-						account = purple_connection_get_account(list->data);
-
-					text = g_strdup_printf("%s (%s)",
-							purple_account_get_username(account),
-							purple_account_get_protocol_name(account));
-					gnt_combo_box_add_data(GNT_COMBO_BOX(combo), account, text);
-					g_free(text);
-					if (account == def)
-						gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account);
-				}
-				gnt_widget_set_size(combo, 20, 3); /* ew */
-				accountlist = combo;
+				accountlist = field->ui_data = create_account_field(field);
 			}
 			else
 			{
-				gnt_box_add_widget(GNT_BOX(hbox),
-						gnt_label_new_with_format(_("Not implemented yet."),
-							GNT_TEXT_FLAG_BOLD));
+				field->ui_data = gnt_label_new_with_format(_("Not implemented yet."),
+						GNT_TEXT_FLAG_BOLD);
 			}
+			gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
+			gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(field->ui_data));
 		}
 		if (grlist->next)
 			gnt_box_add_widget(GNT_BOX(box), gnt_hline_new());
@@ -610,7 +642,7 @@
 static void
 file_cancel_cb(gpointer fq, GntWidget *wid)
 {
-	PurpleGntFileRequest *data = fq;
+	FinchFileRequest *data = fq;
 	if (data->cbs[1] != NULL)
 		((PurpleRequestFileCb)data->cbs[1])(data->user_data, NULL);
 
@@ -620,7 +652,7 @@
 static void
 file_ok_cb(gpointer fq, GntWidget *widget)
 {
-	PurpleGntFileRequest *data = fq;
+	FinchFileRequest *data = fq;
 	char *file = gnt_file_sel_get_selected_file(GNT_FILE_SEL(data->dialog));
 	char *dir = g_path_get_dirname(file);
 	if (data->cbs[0] != NULL)
@@ -634,38 +666,30 @@
 }
 
 static void
-file_request_destroy(PurpleGntFileRequest *data)
+file_request_destroy(FinchFileRequest *data)
 {
 	g_free(data->cbs);
 	g_free(data);
 }
 
-static void *
-finch_request_file(const char *title, const char *filename,
-				gboolean savedialog,
+static FinchFileRequest *
+finch_file_request_window(const char *title, const char *path,
 				GCallback ok_cb, GCallback cancel_cb,
-				PurpleAccount *account, const char *who, PurpleConversation *conv,
 				void *user_data)
 {
 	GntWidget *window = gnt_file_sel_new();
 	GntFileSel *sel = GNT_FILE_SEL(window);
-	PurpleGntFileRequest *data = g_new0(PurpleGntFileRequest, 1);
-	const char *path;
+	FinchFileRequest *data = g_new0(FinchFileRequest, 1);
 
 	data->user_data = user_data;
 	data->cbs = g_new0(GCallback, 2);
 	data->cbs[0] = ok_cb;
 	data->cbs[1] = cancel_cb;
 	data->dialog = window;
-	data->save = savedialog;
-	gnt_box_set_title(GNT_BOX(window), title ? title : (savedialog ? _("Save File...") : _("Open File...")));
+	gnt_box_set_title(GNT_BOX(window), title);
 
-	path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder");
 	gnt_file_sel_set_current_location(sel, (path && *path) ? path : purple_home_dir());
 
-	if (savedialog)
-		gnt_file_sel_set_suggested_filename(sel, filename);
-
 	g_signal_connect(G_OBJECT(sel->cancel), "activate",
 			G_CALLBACK(action_performed), window);
 	g_signal_connect(G_OBJECT(sel->select), "activate",
@@ -678,9 +702,45 @@
 	setup_default_callback(window, file_cancel_cb, data);
 	g_object_set_data_full(G_OBJECT(window), "filerequestdata", data,
 			(GDestroyNotify)file_request_destroy);
-	gnt_widget_show(window);
+
+	return data;
+}
+
+static void *
+finch_request_file(const char *title, const char *filename,
+				gboolean savedialog,
+				GCallback ok_cb, GCallback cancel_cb,
+				PurpleAccount *account, const char *who, PurpleConversation *conv,
+				void *user_data)
+{
+	FinchFileRequest *data;
+	const char *path;
 
-	return window;
+	path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder");
+	data = finch_file_request_window(title ? title : (savedialog ? _("Save File...") : _("Open File...")), path,
+			ok_cb, cancel_cb, user_data);
+	data->save = savedialog;
+	if (savedialog)
+		gnt_file_sel_set_suggested_filename(GNT_FILE_SEL(data->dialog), filename);
+
+	gnt_widget_show(data->dialog);
+	return data->dialog;
+}
+
+static void *
+finch_request_folder(const char *title, const char *dirname, GCallback ok_cb,
+		GCallback cancel_cb, PurpleAccount *account, const char *who, PurpleConversation *conv,
+		void *user_data)
+{
+	FinchFileRequest *data;
+
+	data = finch_file_request_window(title ? title : _("Choose Location..."), dirname,
+			ok_cb, cancel_cb, user_data);
+	data->save = TRUE;
+	gnt_file_sel_set_dirs_only(GNT_FILE_SEL(data->dialog), TRUE);
+
+	gnt_widget_show(data->dialog);
+	return data->dialog;
 }
 
 static PurpleRequestUiOps uiops =
@@ -691,7 +751,7 @@
 	finch_request_fields,
 	finch_request_file,
 	finch_close_request,
-	NULL,                       /* No plans for request_folder */
+	finch_request_folder,
 	NULL,
 	NULL,
 	NULL,
@@ -765,3 +825,32 @@
 	}
 }
 
+GntWidget *finch_request_field_get_widget(PurpleRequestField *field)
+{
+	GntWidget *ret = NULL;
+	switch (purple_request_field_get_type(field)) {
+		case PURPLE_REQUEST_FIELD_BOOLEAN:
+			ret = create_boolean_field(field);
+			break;
+		case PURPLE_REQUEST_FIELD_STRING:
+			ret = create_string_field(field, NULL);
+			break;
+		case PURPLE_REQUEST_FIELD_INTEGER:
+			ret = create_integer_field(field);
+			break;
+		case PURPLE_REQUEST_FIELD_CHOICE:
+			ret = create_choice_field(field);
+			break;
+		case PURPLE_REQUEST_FIELD_LIST:
+			ret = create_list_field(field);
+			break;
+		case PURPLE_REQUEST_FIELD_ACCOUNT:
+			ret = create_account_field(field);
+			break;
+		default:
+			purple_debug_error("GntRequest", "Unimplemented request-field %d\n", purple_request_field_get_type(field));
+			break;
+	}
+	return ret;
+}
+
--- a/finch/gntrequest.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntrequest.h	Mon Jan 07 03:40:27 2008 +0000
@@ -27,6 +27,7 @@
 #define _GNT_REQUEST_H
 
 #include "request.h"
+#include "gnt.h"
 
 /**********************************************************************
  * @name GNT Request API
@@ -56,6 +57,15 @@
  */
 void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *fields);
 
+/**
+ * Create a widget field for a request-field.
+ *
+ * @param field   The request field.
+ *
+ * @return A GntWidget for the request field.
+ * @since 2.4.0
+ */
+GntWidget *finch_request_field_get_widget(PurpleRequestField *field);
 /*@}*/
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntroomlist.c	Mon Jan 07 03:40:27 2008 +0000
@@ -0,0 +1,375 @@
+/**
+ * @file gntroomlist.c GNT Room List API
+ * @ingroup finch
+ */
+
+/* finch
+ *
+ * Finch is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include"internal.h"
+
+#include "gntrequest.h"
+#include "gntroomlist.h"
+
+#include "gntbox.h"
+#include "gntbutton.h"
+#include "gntcombobox.h"
+#include "gnttextview.h"
+#include "gnttree.h"
+#include "gntwindow.h"
+
+#include "debug.h"
+
+/* Yes, just one roomlist at a time. Let's not get greedy. Aight? */
+struct _FinchRoomlist
+{
+	GntWidget *window;
+
+	GntWidget *accounts;
+	GntWidget *tree;
+	GntWidget *details;
+
+	GntWidget *getlist;
+	GntWidget *add;
+	GntWidget *join;
+	GntWidget *stop;
+	GntWidget *close;
+
+	PurpleAccount *account;
+	PurpleRoomlist *roomlist;
+} froomlist;
+
+typedef struct _FinchRoomlist FinchRoomlist;
+
+static void
+unset_roomlist(gpointer null)
+{
+	froomlist.window = NULL;
+	if (froomlist.roomlist)
+		purple_roomlist_unref(froomlist.roomlist);
+}
+
+static void
+update_roomlist(PurpleRoomlist *list)
+{
+	if (froomlist.roomlist == list)
+		return;
+
+	if (froomlist.roomlist)
+		purple_roomlist_unref(froomlist.roomlist);
+
+	if ((froomlist.roomlist = list) != NULL)
+		purple_roomlist_ref(list);
+}
+
+static void fl_stop(GntWidget *button, gpointer null)
+{
+	if (froomlist.roomlist &&
+			purple_roomlist_get_in_progress(froomlist.roomlist))
+		purple_roomlist_cancel_get_list(froomlist.roomlist);
+}
+
+static void fl_get_list(GntWidget *button, gpointer null)
+{
+	PurpleAccount *account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(froomlist.accounts));
+	PurpleConnection *gc = purple_account_get_connection(account);
+
+	if (!gc)
+		return;
+
+	froomlist.roomlist = purple_roomlist_get_list(gc);
+	gnt_box_give_focus_to_child(GNT_BOX(froomlist.window), froomlist.tree);
+}
+
+static void fl_add_chat(GntWidget *button, gpointer null)
+{
+	char *name;
+	PurpleRoomlistRoom *room = gnt_tree_get_selection_data(GNT_TREE(froomlist.tree));
+	PurpleConnection *gc = purple_account_get_connection(froomlist.account);
+	PurplePluginProtocolInfo *prpl_info = NULL;
+
+	if (gc == NULL || room == NULL)
+		return;
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if(prpl_info != NULL && prpl_info->roomlist_room_serialize)
+		name = prpl_info->roomlist_room_serialize(room);
+	else
+		name = g_strdup(purple_roomlist_room_get_name(room));
+
+	purple_blist_request_add_chat(froomlist.account, NULL, NULL, name);
+
+	g_free(name);
+}
+
+static void fl_close(GntWidget *button, gpointer null)
+{
+	gnt_widget_destroy(froomlist.window);
+}
+
+static void
+roomlist_activated(GntWidget *widget)
+{
+	PurpleRoomlistRoom *room = gnt_tree_get_selection_data(GNT_TREE(widget));
+	if (!room)
+		return;
+
+	switch (purple_roomlist_room_get_type(room)) {
+		case PURPLE_ROOMLIST_ROOMTYPE_ROOM:
+			purple_roomlist_room_join(froomlist.roomlist, room);
+			break;
+		case PURPLE_ROOMLIST_ROOMTYPE_CATEGORY:
+			purple_roomlist_expand_category(froomlist.roomlist, room);
+			break;
+	}
+}
+
+static void
+roomlist_selection_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null)
+{
+	GList *iter, *field;
+	PurpleRoomlistRoom *room = current;
+	GntTextView *tv = GNT_TEXT_VIEW(froomlist.details);
+	gboolean first = TRUE;
+
+	gnt_text_view_clear(tv);
+
+	if (!room)
+		return;
+
+	for (iter = purple_roomlist_room_get_fields(room),
+			field = purple_roomlist_get_fields(froomlist.roomlist);
+			iter && field;
+			iter = iter->next, field = field->next) {
+		PurpleRoomlistField *f = field->data;
+		char *label = NULL;
+
+		if (purple_roomlist_field_get_hidden(f)) {
+			continue;
+		}
+
+		if (!first)
+			gnt_text_view_append_text_with_flags(tv, "\n", GNT_TEXT_FLAG_NORMAL);
+
+		gnt_text_view_append_text_with_flags(tv,
+				purple_roomlist_field_get_label(f), GNT_TEXT_FLAG_BOLD);
+		gnt_text_view_append_text_with_flags(tv, ": ", GNT_TEXT_FLAG_BOLD);
+
+		switch (purple_roomlist_field_get_type(f)) {
+			case PURPLE_ROOMLIST_FIELD_BOOL:
+				label = g_strdup(iter->data ? "True" : "False");
+				break;
+			case PURPLE_ROOMLIST_FIELD_INT:
+				label = g_strdup_printf("%d", (int)iter->data);
+				break;
+			case PURPLE_ROOMLIST_FIELD_STRING:
+				label = g_strdup(iter->data);
+				break;
+		}
+		gnt_text_view_append_text_with_flags(tv, label, GNT_TEXT_FLAG_NORMAL);
+		g_free(label);
+		first = FALSE;
+	}
+
+	if (purple_roomlist_room_get_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY) {
+		gnt_text_view_append_text_with_flags(tv,
+				_("\nHit 'Enter' to find more rooms of this category."),
+				GNT_TEXT_FLAG_NORMAL);
+	}
+}
+
+static void
+roomlist_account_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null)
+{
+	if (froomlist.account == current) {
+		return;
+	}
+
+	froomlist.account = current;
+	if (froomlist.roomlist) {
+		if (purple_roomlist_get_in_progress(froomlist.roomlist))
+			purple_roomlist_cancel_get_list(froomlist.roomlist);
+		update_roomlist(NULL);
+	}
+
+	gnt_tree_remove_all(GNT_TREE(froomlist.tree));
+	gnt_widget_draw(froomlist.tree);
+}
+
+static void
+reset_account_list(PurpleAccount *account)
+{
+	GList *list;
+	GntComboBox *accounts = GNT_COMBO_BOX(froomlist.accounts);
+	gnt_combo_box_remove_all(accounts);
+	for (list = purple_connections_get_all(); list; list = list->next) {
+		PurplePluginProtocolInfo *prpl_info = NULL;
+		PurpleConnection *gc = list->data;
+
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+		if (prpl_info->roomlist_get_list != NULL) {
+			PurpleAccount *account = purple_connection_get_account(gc);
+			char *text = g_strdup_printf("%s (%s)",
+					purple_account_get_username(account),
+					purple_account_get_protocol_name(account));
+			gnt_combo_box_add_data(accounts, account, text);
+			g_free(text);
+		}
+	}
+}
+
+static void
+setup_roomlist(PurpleAccount *account)
+{
+	GntWidget *window, *tree, *hbox, *accounts;
+	int iter;
+	struct {
+		const char *label;
+		GCallback callback;
+		GntWidget **widget;
+	} buttons[] = {
+		{_("Stop"), G_CALLBACK(fl_stop), &froomlist.stop},
+		{_("Get"), G_CALLBACK(fl_get_list), &froomlist.getlist},
+		{_("Add"), G_CALLBACK(fl_add_chat), &froomlist.add},
+		{_("Close"), G_CALLBACK(fl_close), &froomlist.close},
+		{NULL, NULL, NULL}
+	};
+
+	if (froomlist.window)
+		return;
+
+	froomlist.window = window = gnt_window_new();
+	g_object_set(G_OBJECT(window), "vertical", TRUE, NULL);
+	gnt_box_set_pad(GNT_BOX(window), 0);
+	gnt_box_set_title(GNT_BOX(window), _("Room List"));
+	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
+
+	froomlist.accounts = accounts = gnt_combo_box_new();
+	reset_account_list(account);
+	gnt_box_add_widget(GNT_BOX(window), froomlist.accounts);
+	g_signal_connect(G_OBJECT(froomlist.accounts), "selection-changed",
+			G_CALLBACK(roomlist_account_changed), NULL);
+	froomlist.account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(froomlist.accounts));
+
+	froomlist.tree = tree = gnt_tree_new_with_columns(2);
+	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
+	g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(roomlist_activated), NULL);
+	gnt_tree_set_column_titles(GNT_TREE(tree), _("Name"), "");
+	gnt_tree_set_show_separator(GNT_TREE(tree), FALSE);
+	gnt_tree_set_col_width(GNT_TREE(tree), 1, 1);
+	gnt_tree_set_column_resizable(GNT_TREE(tree), 1, FALSE);
+	gnt_tree_set_search_column(GNT_TREE(tree), 0);
+
+	gnt_box_add_widget(GNT_BOX(window), tree);
+
+	froomlist.details = gnt_text_view_new();
+	gnt_text_view_set_flag(GNT_TEXT_VIEW(froomlist.details), GNT_TEXT_VIEW_TOP_ALIGN);
+	gnt_box_add_widget(GNT_BOX(window), froomlist.details);
+	gnt_widget_set_size(froomlist.details, -1, 10);
+
+	hbox = gnt_hbox_new(FALSE);
+	gnt_box_add_widget(GNT_BOX(window), hbox);
+
+	for (iter = 0; buttons[iter].label; iter++) {
+		GntWidget *button = gnt_button_new(buttons[iter].label);
+		gnt_box_add_widget(GNT_BOX(hbox), button);
+		g_signal_connect(G_OBJECT(button), "activate", buttons[iter].callback, NULL);
+		*buttons[iter].widget = button;
+		gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(froomlist.details), button);
+	}
+
+	g_signal_connect(G_OBJECT(tree), "selection-changed", G_CALLBACK(roomlist_selection_changed), NULL);
+
+	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(unset_roomlist), NULL);
+}
+
+static void
+fl_show_with_account(PurpleAccount *account)
+{
+	setup_roomlist(account);
+	gnt_window_present(froomlist.window);
+}
+
+static void
+fl_create(PurpleRoomlist *list)
+{
+	list->ui_data = &froomlist;
+	setup_roomlist(NULL);
+	update_roomlist(list);
+}
+
+static void
+fl_set_fields(PurpleRoomlist *list, GList *fields)
+{
+}
+
+static void
+fl_add_room(PurpleRoomlist *roomlist, PurpleRoomlistRoom *room)
+{
+	if (froomlist.roomlist != roomlist)
+		return;
+
+	gnt_tree_remove(GNT_TREE(froomlist.tree), room);
+	gnt_tree_add_row_after(GNT_TREE(froomlist.tree), room,
+			gnt_tree_create_row(GNT_TREE(froomlist.tree),
+				purple_roomlist_room_get_name(room),
+				purple_roomlist_room_get_type(room) == PURPLE_ROOMLIST_ROOMTYPE_CATEGORY ? "<" : ""),
+			purple_roomlist_room_get_parent(room), NULL);
+}
+
+static void
+fl_destroy(PurpleRoomlist *list)
+{
+	if (!froomlist.window)
+		return;
+
+	if (froomlist.roomlist == list) {
+		froomlist.roomlist = NULL;
+		gnt_tree_remove_all(GNT_TREE(froomlist.tree));
+		gnt_widget_draw(froomlist.tree);
+	}
+}
+
+static PurpleRoomlistUiOps ui_ops =
+{
+	fl_show_with_account, /* void (*show_with_account)(PurpleAccount *account); **< Force the ui to pop up a dialog and get the list */
+	fl_create, /* void (*create)(PurpleRoomlist *list); **< A new list was created. */
+	fl_set_fields, /* void (*set_fields)(PurpleRoomlist *list, GList *fields); **< Sets the columns. */
+	fl_add_room, /* void (*add_room)(PurpleRoomlist *list, PurpleRoomlistRoom *room); **< Add a room to the list. */
+	NULL, /* void (*in_progress)(PurpleRoomlist *list, gboolean flag); **< Are we fetching stuff still? */
+	fl_destroy, /* void (*destroy)(PurpleRoomlist *list); **< We're destroying list. */
+
+	NULL, /* void (*_purple_reserved1)(void); */
+	NULL, /* void (*_purple_reserved2)(void); */
+	NULL, /* void (*_purple_reserved3)(void); */
+	NULL /* void (*_purple_reserved4)(void); */
+};
+PurpleRoomlistUiOps *finch_roomlist_get_ui_ops(void)
+{
+	return &ui_ops;
+}
+
+void finch_roomlist_show_all(void)
+{
+	purple_roomlist_show_with_account(NULL);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntroomlist.h	Mon Jan 07 03:40:27 2008 +0000
@@ -0,0 +1,51 @@
+/**
+ * @file gntroomlist.h GNT Room List API
+ * @ingroup finch
+ */
+
+/* finch
+ *
+ * Finch is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#ifndef _GNT_ROOMLIST_H
+#define _GNT_ROOMLIST_H
+
+#include "roomlist.h"
+
+/**********************************************************************
+ * @name GNT Room List API
+ **********************************************************************/
+/*@{*/
+
+/**
+ * Get the ui-functions.
+ *
+ * @return The PurpleRoomlistUiOps structure populated with the appropriate functions.
+ */
+PurpleRoomlistUiOps *finch_roomlist_get_ui_ops(void);
+
+/**
+ * Show the roomlist dialog.
+ */
+void finch_roomlist_show_all(void);
+
+/*@}*/
+
+#endif
+
--- a/finch/gntsound.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntsound.c	Mon Jan 07 03:40:27 2008 +0000
@@ -286,7 +286,7 @@
 }
 
 static void *
-finch_sound_get_handle()
+finch_sound_get_handle(void)
 {
 	static int handle;
 
--- a/finch/gntui.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/gntui.c	Mon Jan 07 03:40:27 2008 +0000
@@ -35,6 +35,7 @@
 #include "gntpounce.h"
 #include "gntprefs.h"
 #include "gntrequest.h"
+#include "gntroomlist.h"
 #include "gntstatus.h"
 #include "gntsound.h"
 
@@ -71,14 +72,20 @@
 	finch_notify_init();
 	purple_notify_set_ui_ops(finch_notify_get_ui_ops());
 
+	/* Request */
 	finch_request_init();
 	purple_request_set_ui_ops(finch_request_get_ui_ops());
 
+	/* Pounce */
 	finch_pounces_init();
 
+	/* File transfer */
 	finch_xfers_init();
 	purple_xfers_set_ui_ops(finch_xfers_get_ui_ops());
 
+	/* Roomlist */
+	purple_roomlist_set_ui_ops(finch_roomlist_get_ui_ops());
+
 	gnt_register_action(_("Accounts"), finch_accounts_show_all);
 	gnt_register_action(_("Buddy List"), finch_blist_show);
 	gnt_register_action(_("Buddy Pounces"), finch_pounces_manager_show);
@@ -86,6 +93,7 @@
 	gnt_register_action(_("Debug Window"), finch_debug_window_show);
 	gnt_register_action(_("File Transfers"), finch_xfer_dialog_show);
 	gnt_register_action(_("Plugins"), finch_plugins_show_all);
+	gnt_register_action(_("Room List"), finch_roomlist_show_all);
 	gnt_register_action(_("Sounds"), finch_sounds_show_all);
 	gnt_register_action(_("Preferences"), finch_prefs_show_all);
 	gnt_register_action(_("Statuses"), finch_savedstatus_show_all);
--- a/finch/libgnt/configure.ac	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/configure.ac	Mon Jan 07 03:40:27 2008 +0000
@@ -24,10 +24,10 @@
 # Make sure to update ../../configure.ac with libgnt version changes.
 #
 
-m4_define([gnt_lt_current], [3])
+m4_define([gnt_lt_current], [4])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [3])
-m4_define([gnt_micro_version], [2])
+m4_define([gnt_minor_version], [4])
+m4_define([gnt_micro_version], [0])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/finch/libgnt/gnt.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gnt.h	Mon Jan 07 03:40:27 2008 +0000
@@ -141,7 +141,7 @@
  * @param label      The user-visible label for the action.
  * @param callback   The callback function for the action.
  */
-void gnt_register_action(const char *label, void (*callback)());
+void gnt_register_action(const char *label, void (*callback)(void));
 
 /**
  * Show a menu.
--- a/finch/libgnt/gntbindable.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntbindable.c	Mon Jan 07 03:40:27 2008 +0000
@@ -45,7 +45,7 @@
 } rebind_info;
 
 static void 
-gnt_bindable_free_rebind_info()
+gnt_bindable_free_rebind_info(void)
 {
 	g_free(rebind_info.name);
 	g_free(rebind_info.keys);
--- a/finch/libgnt/gntcolors.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntcolors.c	Mon Jan 07 03:40:27 2008 +0000
@@ -40,7 +40,7 @@
 } colors[GNT_TOTAL_COLORS];
 
 static void
-backup_colors()
+backup_colors(void)
 {
 	short i;
 	for (i = 0; i < GNT_TOTAL_COLORS; i++)
@@ -51,13 +51,13 @@
 }
 
 static gboolean
-can_use_custom_color()
+can_use_custom_color(void)
 {
 	return (gnt_style_get_bool(GNT_STYLE_COLOR, FALSE) && can_change_color());
 }
 
 static void
-restore_colors()
+restore_colors(void)
 {
 	short i;
 	for (i = 0; i < GNT_TOTAL_COLORS; i++)
--- a/finch/libgnt/gntcolors.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntcolors.h	Mon Jan 07 03:40:27 2008 +0000
@@ -93,7 +93,7 @@
  *
  * @return A color
  *
- * @since 2.3.1 (gnt), 2.3.1 (pidgin)
+ * @since 2.4.0
  */
 int gnt_colors_get_color(char *key);
 #endif
@@ -119,7 +119,7 @@
  *
  * @return  A color pair
  *
- * @since 2.3.1
+ * @since 2.4.0
  */
 int gnt_color_add_pair(int fg, int bg);
 #endif
--- a/finch/libgnt/gntentry.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntentry.c	Mon Jan 07 03:40:27 2008 +0000
@@ -916,7 +916,7 @@
 }
 
 static GntEntryKillRing *
-new_killring()
+new_killring(void)
 {
 	GntEntryKillRing *kr = g_new0(GntEntryKillRing, 1);
 	kr->buffer = g_string_new(NULL);
--- a/finch/libgnt/gntmain.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntmain.c	Mon Jan 07 03:40:27 2008 +0000
@@ -72,7 +72,7 @@
 
 static void setup_io(void);
 
-static gboolean refresh_screen();
+static gboolean refresh_screen(void);
 
 static GntWM *wm;
 static GntClipboard *clipboard;
@@ -326,7 +326,7 @@
 }
 
 static gboolean
-refresh_screen()
+refresh_screen(void)
 {
 	gnt_bindable_perform_action_named(GNT_BINDABLE(wm), "refresh-screen", NULL);
 	return FALSE;
@@ -363,7 +363,7 @@
 }
 
 static void
-ask_before_exit()
+ask_before_exit(void)
 {
 	static GntWidget *win = NULL;
 	GntWidget *bbox, *button;
@@ -412,7 +412,7 @@
 #ifdef SIGWINCH
 	case SIGWINCH:
 		erase();
-		g_idle_add(refresh_screen, NULL);
+		g_idle_add((GSourceFunc)refresh_screen, NULL);
 		if (org_winch_handler)
 			org_winch_handler(sig);
 		signal(SIGWINCH, sighandler);
@@ -430,7 +430,7 @@
 }
 
 static void
-init_wm()
+init_wm(void)
 {
 	const char *name = gnt_style_get(GNT_STYLE_WM);
 	gpointer handle;
@@ -614,7 +614,7 @@
 	gnt_wm_update_window(wm, widget);
 }
 
-void gnt_register_action(const char *label, void (*callback)())
+void gnt_register_action(const char *label, void (*callback)(void))
 {
 	GntAction *action = g_new0(GntAction, 1);
 	action->label = g_strdup(label);
--- a/finch/libgnt/gntstyle.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntstyle.h	Mon Jan 07 03:40:27 2008 +0000
@@ -74,7 +74,7 @@
  *
  * @return        NULL terminated string array. The array should be freed with g_strfreev().
  *
- * @since 2.3.2
+ * @since 2.4.0
  */
 char **gnt_style_get_string_list(const char *group, const char *key, gsize *length);
 
@@ -87,7 +87,7 @@
  *
  * @return  The value of the color as an int, or 0 on error.
  *
- * @since 2.3.2
+ * @since 2.4.0
  */
 int gnt_style_get_color(char *group, char *key);
 
--- a/finch/libgnt/gnttextview.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Mon Jan 07 03:40:27 2008 +0000
@@ -830,7 +830,7 @@
 
 
 static void
-cleanup_pageditor()
+cleanup_pageditor(void)
 {
 	unlink(pageditor.file);
 	g_free(pageditor.file);
--- a/finch/libgnt/gnttree.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gnttree.h	Mon Jan 07 03:40:27 2008 +0000
@@ -330,6 +330,7 @@
  * @param tree   The tree
  * @param key    The key for the row
  * @param color  The color
+ * @since 2.4.0
  */
 void gnt_tree_set_row_color(GntTree *, void *, int);
 
--- a/finch/libgnt/gntwm.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntwm.c	Mon Jan 07 03:40:27 2008 +0000
@@ -135,7 +135,7 @@
  * to expose the entire character, it is not always redrawn.
  */
 static void
-work_around_for_ncurses_bug()
+work_around_for_ncurses_bug(void)
 {
 #ifndef NO_WIDECHAR
 	PANEL *panel = NULL;
@@ -183,7 +183,7 @@
 }
 
 static void
-update_act_msg()
+update_act_msg(void)
 {
 	GntWidget *label;
 	GList *iter;
@@ -388,10 +388,10 @@
 }
 
 static void
-switch_window(GntWM *wm, int direction)
+switch_window(GntWM *wm, int direction, gboolean urgent)
 {
 	GntWidget *w = NULL, *wid = NULL;
-	int pos;
+	int pos, orgpos;
 
 	if (wm->_list.window || wm->menu)
 		return;
@@ -404,15 +404,20 @@
 	}
 
 	w = wm->cws->ordered->data;
-	pos = g_list_index(wm->cws->list, w);
-	pos += direction;
+	orgpos = pos = g_list_index(wm->cws->list, w);
+
+	do {
+		pos += direction;
 
-	if (pos < 0)
-		wid = g_list_last(wm->cws->list)->data;
-	else if (pos >= g_list_length(wm->cws->list))
-		wid = wm->cws->list->data;
-	else if (pos >= 0)
-		wid = g_list_nth_data(wm->cws->list, pos);
+		if (pos < 0) {
+			wid = g_list_last(wm->cws->list)->data;
+			pos = g_list_length(wm->cws->list) - 1;
+		} else if (pos >= g_list_length(wm->cws->list)) {
+			wid = wm->cws->list->data;
+			pos = 0;
+		} else
+			wid = g_list_nth_data(wm->cws->list, pos);
+	} while (urgent && !GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT) && pos != orgpos);
 
 	gnt_wm_raise_window(wm, wid);
 }
@@ -421,7 +426,7 @@
 window_next(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
-	switch_window(wm, 1);
+	switch_window(wm, 1, FALSE);
 	return TRUE;
 }
 
@@ -429,7 +434,7 @@
 window_prev(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
-	switch_window(wm, -1);
+	switch_window(wm, -1, FALSE);
 	return TRUE;
 }
 
@@ -1202,6 +1207,22 @@
 	return ignore_keys ? !(ignore_keys = FALSE) : FALSE;
 }
 
+static gboolean
+window_next_urgent(GntBindable *bindable, GList *n)
+{
+	GntWM *wm = GNT_WM(bindable);
+	switch_window(wm, 1, TRUE);
+	return TRUE;
+}
+
+static gboolean
+window_prev_urgent(GntBindable *bindable, GList *n)
+{
+	GntWM *wm = GNT_WM(bindable);
+	switch_window(wm, -1, TRUE);
+	return TRUE;
+}
+
 #ifdef USE_PYTHON
 static void
 python_script_selected(GntFileSel *fs, const char *path, const char *f, gpointer n)
@@ -1323,6 +1344,7 @@
 {
 	int i;
 	GObjectClass *gclass = G_OBJECT_CLASS(klass);
+	char key[32];
 
 	gclass->dispose = gnt_wm_destroy;
 
@@ -1482,10 +1504,15 @@
 				"\033" "\\", NULL);
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-window", help_for_window,
 				"\033" "|", NULL);
-	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start, 
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start,
 				GNT_KEY_CTRL_G, NULL);
-	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end, 
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end,
 				"\033" GNT_KEY_CTRL_G, NULL);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next-urgent", window_next_urgent,
+				"\033" "\t", NULL);
+	snprintf(key, sizeof(key), "\033%s", GNT_KEY_BACK_TAB);
+	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev-urgent", window_prev_urgent,
+				key[1] ? key : NULL, NULL);
 #ifdef USE_PYTHON
 	gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "run-python", run_python,
 				GNT_KEY_F3, NULL);
--- a/finch/libgnt/gntwm.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/gntwm.h	Mon Jan 07 03:40:27 2008 +0000
@@ -73,7 +73,7 @@
 typedef struct _GntAction
 {
 	const char *label;
-	void (*callback)();
+	void (*callback)(void);
 } GntAction;
 
 struct _GntWM
--- a/finch/libgnt/pygnt/test.py	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/libgnt/pygnt/test.py	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 import gobject
 import gnt
 
--- a/finch/plugins/gntgf.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/finch/plugins/gntgf.c	Mon Jan 07 03:40:27 2008 +0000
@@ -122,7 +122,7 @@
 }
 
 static void
-urgent()
+urgent(void)
 {
 	/* This is from deryni/tuomov's urgent_test.c */
 	Display *dpy;
@@ -322,7 +322,7 @@
 }
 
 static GntWidget *
-config_frame()
+config_frame(void)
 {
 	GntWidget *window, *tree, *check;
 	int i;
--- a/libpurple/account.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/account.c	Mon Jan 07 03:40:27 2008 +0000
@@ -331,6 +331,12 @@
 	if(err == NULL)
 		return node;
 
+	/* It doesn't make sense to have transient errors persist across a
+	 * restart.
+	 */
+	if(!purple_connection_error_is_fatal (err->type))
+		return node;
+
 	child = xmlnode_new_child(node, "type");
 	snprintf(type_str, sizeof(type_str), "%u", err->type);
 	xmlnode_insert_data(child, type_str, -1);
@@ -461,7 +467,7 @@
 }
 
 static void
-schedule_accounts_save()
+schedule_accounts_save(void)
 {
 	if (save_timer == 0)
 		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
@@ -1639,7 +1645,7 @@
 	if (status == NULL)
 	{
 		purple_debug_error("account",
-				   "Invalid status ID %s for account %s (%s)\n",
+				   "Invalid status ID '%s' for account %s (%s)\n",
 				   status_id, purple_account_get_username(account),
 				   purple_account_get_protocol_id(account));
 		return;
--- a/libpurple/blist.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/blist.c	Mon Jan 07 03:40:27 2008 +0000
@@ -298,7 +298,7 @@
 }
 
 static xmlnode *
-blist_to_xmlnode()
+blist_to_xmlnode(void)
 {
 	xmlnode *node, *child, *grandchild;
 	PurpleBlistNode *gnode;
@@ -332,7 +332,7 @@
 }
 
 static void
-purple_blist_sync()
+purple_blist_sync(void)
 {
 	xmlnode *node;
 	char *data;
--- a/libpurple/blist.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/blist.h	Mon Jan 07 03:40:27 2008 +0000
@@ -680,7 +680,8 @@
  *
  * @param g The group
  *
- * @return A list of purple_accounts
+ * @return A GSList of accounts (which must be freed), or NULL if the group
+ *         has no accounts.
  */
 GSList *purple_group_get_accounts(PurpleGroup *g);
 
--- a/libpurple/buddyicon.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/buddyicon.c	Mon Jan 07 03:40:27 2008 +0000
@@ -98,8 +98,7 @@
 {
 	const char *dirname;
 	char *path;
-	FILE *file = NULL;
-
+	
 	g_return_if_fail(img != NULL);
 
 	if (!purple_buddy_icons_is_caching())
@@ -120,24 +119,12 @@
 		}
 	}
 
-	if ((file = g_fopen(path, "wb")) != NULL)
-	{
-		if (!fwrite(purple_imgstore_get_data(img), purple_imgstore_get_size(img), 1, file))
-		{
-			purple_debug_error("buddyicon", "Error writing %s: %s\n",
-			                   path, g_strerror(errno));
-		}
-		else
-			purple_debug_info("buddyicon", "Wrote cache file: %s\n", path);
-
-		fclose(file);
-	}
-	else
-	{
+	if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
+		purple_util_write_data_to_file_absolute(path, purple_imgstore_get_data(img),
+							purple_imgstore_get_size(img));	
+	} else 	{
 		purple_debug_error("buddyicon", "Unable to create file %s: %s\n",
-		                   path, g_strerror(errno));
-		g_free(path);
-		return;
+		                   path, "File already exists.");
 	}
 	g_free(path);
 }
@@ -627,6 +614,10 @@
 				checksum = purple_blist_node_get_string((PurpleBlistNode*)b, "icon_checksum");
 				purple_buddy_icon_set_data(icon, data, len, checksum);
 			}
+			else
+			{
+				delete_buddy_icon_settings((PurpleBlistNode*)b, "buddy_icon");
+			}
 			g_free(path);
 		}
 
--- a/libpurple/certificate.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/certificate.c	Mon Jan 07 03:40:27 2008 +0000
@@ -627,7 +627,7 @@
 
 /** System directory to probe for CA certificates */
 /* This is set in the lazy_init function */
-static const gchar *x509_ca_syspath = NULL;
+static GList *x509_ca_paths = NULL;
 
 /** A list of loaded CAs, populated from the above path whenever the lazy_init
     happens. Contains pointers to x509_ca_elements */
@@ -674,6 +674,7 @@
 	GDir *certdir;
 	const gchar *entry;
 	GPatternSpec *pempat;
+	GList *iter = NULL;
 	
 	if (x509_ca_initialized) return TRUE;
 
@@ -687,54 +688,48 @@
 		return FALSE;
 	}
 
-	/* Attempt to point at the appropriate system path */
-	if (NULL == x509_ca_syspath) {
-#ifdef _WIN32
-		x509_ca_syspath = g_build_filename(DATADIR,
-						   "ca-certs", NULL);
-#else
-		x509_ca_syspath = g_build_filename(DATADIR,
-						   "purple", "ca-certs", NULL);
-#endif
-	}
-
-	/* Populate the certificates pool from the system path */
-	certdir = g_dir_open(x509_ca_syspath, 0, NULL);
-	g_return_val_if_fail(certdir, FALSE);
-
 	/* Use a glob to only read .pem files */
 	pempat = g_pattern_spec_new("*.pem");
-	
-	while ( (entry = g_dir_read_name(certdir)) ) {
-		gchar *fullpath;
-		PurpleCertificate *crt;
 
-		if ( !g_pattern_match_string(pempat, entry) ) {
+	/* Populate the certificates pool from the search path(s) */
+	for (iter = x509_ca_paths; iter; iter = iter->next) {
+		certdir = g_dir_open(iter->data, 0, NULL);
+		if (!certdir) {
+			purple_debug_error("certificate/x509/ca", "Couldn't open location '%s'\n", iter->data);
 			continue;
 		}
 
-		fullpath = g_build_filename(x509_ca_syspath, entry, NULL);
-		
-		/* TODO: Respond to a failure in the following? */
-		crt = purple_certificate_import(x509, fullpath);
+		while ( (entry = g_dir_read_name(certdir)) ) {
+			gchar *fullpath;
+			PurpleCertificate *crt;
+
+			if ( !g_pattern_match_string(pempat, entry) ) {
+				continue;
+			}
+
+			fullpath = g_build_filename(iter->data, entry, NULL);
+
+			/* TODO: Respond to a failure in the following? */
+			crt = purple_certificate_import(x509, fullpath);
 
-		if (x509_ca_quiet_put_cert(crt)) {
-			purple_debug_info("certificate/x509/ca",
-					  "Loaded %s\n",
-					  fullpath);
-		} else {
-			purple_debug_error("certificate/x509/ca",
-					  "Failed to load %s\n",
-					  fullpath);
+			if (x509_ca_quiet_put_cert(crt)) {
+				purple_debug_info("certificate/x509/ca",
+						  "Loaded %s\n",
+						  fullpath);
+			} else {
+				purple_debug_error("certificate/x509/ca",
+						  "Failed to load %s\n",
+						  fullpath);
+			}
+
+			purple_certificate_destroy(crt);
+			g_free(fullpath);
 		}
-
-		purple_certificate_destroy(crt);
-		g_free(fullpath);
+		g_dir_close(certdir);
 	}
 
 	g_pattern_spec_free(pempat);
-	g_dir_close(certdir);
-	
+
 	purple_debug_info("certificate/x509/ca",
 			  "Lazy init completed.\n");
 	x509_ca_initialized = TRUE;
@@ -744,6 +739,17 @@
 static gboolean
 x509_ca_init(void)
 {
+	/* Attempt to point at the appropriate system path */
+	if (NULL == x509_ca_paths) {
+#ifdef _WIN32
+		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
+						   "ca-certs", NULL));
+#else
+		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
+						   "purple", "ca-certs", NULL));
+#endif
+	}
+
 	/* Attempt to initialize now, but if it doesn't work, that's OK;
 	   it will get done later */
 	if ( ! x509_ca_lazy_init()) {
@@ -752,7 +758,7 @@
 				  "dependency is not yet registered. "
 				  "It has been deferred to later.\n");
 	}
-	
+
 	return TRUE;
 }
 
@@ -768,6 +774,9 @@
 	g_list_free(x509_ca_certs);
 	x509_ca_certs = NULL;
 	x509_ca_initialized = FALSE;
+	g_list_foreach(x509_ca_paths, (GFunc)g_free, NULL);
+	g_list_free(x509_ca_paths);
+	x509_ca_paths = NULL;
 }
 
 /** Look up a ca_element by dn */
@@ -1219,6 +1228,9 @@
 }
 
 static void
+x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq);
+
+static void
 x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq)
 {
 	/* TODO: Looking this up by name over and over is expensive.
@@ -1259,8 +1271,8 @@
 	} else {
 		purple_debug_info("certificate/x509/tls_cached",
 				  "Peer cert did NOT match cached\n");
-		/* vrq now becomes the problem of cert_changed */
-		x509_tls_cached_peer_cert_changed(vrq);
+		/* vrq now becomes the problem of the user */
+		x509_tls_cached_unknown_peer(vrq);
 	}
 	
 	purple_certificate_destroy(cached_crt);
@@ -1271,7 +1283,9 @@
 /* For when we've never communicated with this party before */
 /* TODO: Need ways to specify possibly multiple problems with a cert, or at
    least  reprioritize them. For example, maybe the signature ought to be
-   checked BEFORE the hostname checking? */
+   checked BEFORE the hostname checking?
+   Stu thinks we should check the signature before the name, so we do now.
+   The above TODO still stands. */
 static void
 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq)
 {
@@ -1283,35 +1297,6 @@
 
 	peer_crt = (PurpleCertificate *) chain->data;
 
-	/* First, check that the hostname matches */
-	if ( ! purple_certificate_check_subject_name(peer_crt,
-						     vrq->subject_name) ) {
-		gchar *sn = purple_certificate_get_subject_name(peer_crt);
-		gchar *msg;
-		
-		purple_debug_info("certificate/x509/tls_cached",
-				  "Name mismatch: Certificate given for %s "
-				  "has a name of %s\n",
-				  vrq->subject_name, sn);
-
-		/* Prompt the user to authenticate the certificate */
-		/* TODO: Provide the user with more guidance about why he is
-		   being prompted */
-		/* vrq will be completed by user_auth */
-		msg = g_strdup_printf(_("The certificate presented by \"%s\" "
-					"claims to be from \"%s\" instead.  "
-					"This could mean that you are not "
-					"connecting to the service you "
-					"believe you are."),
-				      vrq->subject_name, sn);
-				      
-		x509_tls_cached_user_auth(vrq,msg);
-
-		g_free(sn);
-		g_free(msg);
-		return;
-	} /* if (name mismatch) */
-
 	/* TODO: Figure out a way to check for a bad signature, as opposed to
 	   "not self-signed" */
 	if ( purple_certificate_signed_by(peer_crt, peer_crt) ) {
@@ -1332,7 +1317,7 @@
 
 		g_free(msg);
 		return;
-	} /* if (name mismatch) */
+	} /* if (self signed) */
 	
 	/* Next, check that the certificate chain is valid */
 	if ( ! purple_certificate_check_signature_chain(chain) ) {
@@ -1431,6 +1416,35 @@
 		return;
 	} /* if (CA signature not good) */
 
+	/* Last, check that the hostname matches */
+	if ( ! purple_certificate_check_subject_name(peer_crt,
+						     vrq->subject_name) ) {
+		gchar *sn = purple_certificate_get_subject_name(peer_crt);
+		gchar *msg;
+		
+		purple_debug_info("certificate/x509/tls_cached",
+				  "Name mismatch: Certificate given for %s "
+				  "has a name of %s\n",
+				  vrq->subject_name, sn);
+
+		/* Prompt the user to authenticate the certificate */
+		/* TODO: Provide the user with more guidance about why he is
+		   being prompted */
+		/* vrq will be completed by user_auth */
+		msg = g_strdup_printf(_("The certificate presented by \"%s\" "
+					"claims to be from \"%s\" instead.  "
+					"This could mean that you are not "
+					"connecting to the service you "
+					"believe you are."),
+				      vrq->subject_name, sn);
+				      
+		x509_tls_cached_user_auth(vrq,msg);
+
+		g_free(sn);
+		g_free(msg);
+		return;
+	} /* if (name mismatch) */
+
 	/* If we reach this point, the certificate is good. */
 	/* Look up the local cache and store it there for future use */
 	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,
@@ -1901,3 +1915,10 @@
 	g_byte_array_free(sha_bin, TRUE);
 }
 
+void purple_certificate_add_ca_search_path(const char *path)
+{
+	if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp))
+		return;
+	x509_ca_paths = g_list_append(x509_ca_paths, g_strdup(path));
+}
+
--- a/libpurple/certificate.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/certificate.h	Mon Jan 07 03:40:27 2008 +0000
@@ -786,6 +786,12 @@
 void
 purple_certificate_display_x509(PurpleCertificate *crt);
 
+/**
+ * Add a search path for certificates.
+ *
+ * @param path   Path to search for certificates.
+ */
+void purple_certificate_add_ca_search_path(const char *path);
 
 #ifdef __cplusplus
 }
--- a/libpurple/cipher.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/cipher.c	Mon Jan 07 03:40:27 2008 +0000
@@ -64,6 +64,8 @@
 /*******************************************************************************
  * MD5
  ******************************************************************************/
+#define MD5_HMAC_BLOCK_SIZE	64
+
 struct MD5Context {
 	guint32 total[2];
 	guint32 state[4];
@@ -325,6 +327,13 @@
 	return TRUE;
 }
 
+static size_t
+md5_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return MD5_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps MD5Ops = {
 	NULL,			/* Set option */
 	NULL,			/* Get option */
@@ -340,12 +349,10 @@
 	NULL,			/* get salt size */
 	NULL,			/* set key */
 	NULL,			/* get key size */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,			/* set batch mode */
+	NULL,			/* get batch mode */
+	md5_get_block_size,	/* get block size */
+	NULL			/* set key with len */
 };
 
 /*******************************************************************************
@@ -580,6 +587,13 @@
 	md4_context = NULL;
 }
 
+static size_t
+md4_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return MD4_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps MD4Ops = {
 	NULL,                   /* Set option */
 	NULL,                   /* Get option */
@@ -595,12 +609,202 @@
 	NULL,                   /* get salt size */
 	NULL,                   /* set key */
 	NULL,                   /* get key size */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,                   /* set batch mode */
+	NULL,                   /* get batch mode */
+	md4_get_block_size,     /* get block size */
+	NULL                    /* set key with len */
+};
+
+/*******************************************************************************
+ * HMAC
+ ******************************************************************************/
+
+struct HMAC_Context {
+	PurpleCipherContext *hash;
+	char *name;
+	int blocksize;
+	guchar *opad;
+};
+
+static void
+hmac_init(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+	hctx = g_new0(struct HMAC_Context, 1);
+	purple_cipher_context_set_data(context, hctx);
+	purple_cipher_context_reset(context, extra);
+}
+
+static void
+hmac_reset(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx->name);
+	hctx->name = NULL;
+	if (hctx->hash)
+		purple_cipher_context_destroy(hctx->hash);
+	hctx->hash = NULL;
+	hctx->blocksize = 0;
+	g_free(hctx->opad);
+	hctx->opad = NULL;
+}
+
+static void
+hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (!strcmp(name, "hash")) {
+		g_free(hctx->name);
+		if (hctx->hash)
+			purple_cipher_context_destroy(hctx->hash);
+		hctx->name = g_strdup((char*)value);
+		hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL);
+		hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash);
+	}
+}
+
+static void *
+hmac_get_opt(PurpleCipherContext *context, const gchar *name)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (!strcmp(name, "hash")) {
+		return hctx->name;
+	}
+
+	return NULL;
+}
+
+static void
+hmac_append(PurpleCipherContext *context, const guchar *data, size_t len) 
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	purple_cipher_context_append(hctx->hash, data, len);
+}
+
+static gboolean
+hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	PurpleCipherContext *hash = hctx->hash;
+	guchar *inner_hash;
+	size_t hash_len;
+	gboolean result;
+
+	g_return_val_if_fail(hash != NULL, FALSE);
+
+	inner_hash = g_malloc(100); /* TODO: Should be enough for now... */
+	result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len);
+
+	purple_cipher_context_reset(hash, NULL);
+
+	purple_cipher_context_append(hash, hctx->opad, hctx->blocksize);
+	purple_cipher_context_append(hash, inner_hash, hash_len);
+
+	g_free(inner_hash);
+
+	result = result && purple_cipher_context_digest(hash, in_len, out, out_len);
+
+	return result;
+}
+
+static void
+hmac_uninit(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx;
+
+	purple_cipher_context_reset(context, NULL);
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx);
+}
+
+static void
+hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	int blocksize, i;
+	guchar *ipad;
+	guchar *full_key;
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	g_free(hctx->opad);
+
+	blocksize = hctx->blocksize;
+	ipad = g_malloc(blocksize);
+	hctx->opad = g_malloc(blocksize);
+
+	if (key_len > blocksize) {
+		purple_cipher_context_reset(hctx->hash, NULL);
+		purple_cipher_context_append(hctx->hash, key, key_len);
+		full_key = g_malloc(100); /* TODO: Should be enough for now... */
+		purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len);
+	} else
+		full_key = g_memdup(key, key_len);
+
+    if (key_len < blocksize) {
+    	full_key = g_realloc(full_key, blocksize);
+    	memset(full_key + key_len, 0, blocksize - key_len);
+    }
+
+	for(i = 0; i < blocksize; i++) {
+		ipad[i] = 0x36 ^ full_key[i];
+		hctx->opad[i] = 0x5c ^ full_key[i];
+	}
+
+	g_free(full_key);
+
+	purple_cipher_context_reset(hctx->hash, NULL);
+	purple_cipher_context_append(hctx->hash, ipad, blocksize);
+	g_free(ipad);
+}
+
+static void
+hmac_set_key(PurpleCipherContext *context, const guchar * key)
+{
+	hmac_set_key_with_len(context, key, strlen((char *)key));
+}
+
+static size_t 
+hmac_get_block_size(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	return hctx->blocksize;
+}
+
+static PurpleCipherOps HMACOps = {
+	hmac_set_opt,           /* Set option */
+	hmac_get_opt,           /* Get option */
+	hmac_init,               /* init */
+	hmac_reset,              /* reset */
+	hmac_uninit,             /* uninit */
+	NULL,                   /* set iv */
+	hmac_append,             /* append */
+	hmac_digest,             /* digest */
+	NULL,                   /* encrypt */
+	NULL,                   /* decrypt */
+	NULL,                   /* set salt */
+	NULL,                   /* get salt size */
+	hmac_set_key,           /* set key */
+	NULL,                   /* get key size */
+	NULL,                   /* set batch mode */
+	NULL,                   /* get batch mode */
+	hmac_get_block_size,    /* get block size */
+	hmac_set_key_with_len   /* set key with len */
 };
 
 /******************************************************************************
@@ -986,6 +1190,36 @@
 	return 0;
 }
 
+static gint
+des_decrypt(PurpleCipherContext *context, const guchar data[],
+	    size_t len, guchar output[], size_t *outlen) {
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while(offset+8<=len) {
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+				data+offset,
+				output+offset,
+				1);
+		offset+=8;
+	}
+	*outlen = len;
+	if(offset<len) {
+		*outlen += len - offset;
+		tmp = offset;
+		while(tmp<len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+				buf,
+				output+offset,
+				1);
+	}	
+	return 0;
+}
+
 static void
 des_init(PurpleCipherContext *context, gpointer extra) {
 	struct _des_ctx *mctx;
@@ -1005,32 +1239,380 @@
 }
 
 static PurpleCipherOps DESOps = {
-	NULL,                   /* Set option */
-	NULL,                   /* Get option */
-	des_init,               /* init */
+	NULL,              /* Set option */
+	NULL,              /* Get option */
+	des_init,          /* init */
+ 	NULL,              /* reset */
+	des_uninit,        /* uninit */
+	NULL,              /* set iv */
+	NULL,              /* append */
+	NULL,              /* digest */
+	des_encrypt,       /* encrypt */
+	des_decrypt,       /* decrypt */
+	NULL,              /* set salt */
+	NULL,              /* get salt size */
+	des_set_key,       /* set key */
+	NULL,              /* get key size */
+	NULL,              /* set batch mode */
+	NULL,              /* get batch mode */
+	NULL,              /* get block size */
+	NULL               /* set key with len */
+};
+
+/******************************************************************************
+ * Triple-DES
+ *****************************************************************************/
+
+typedef struct _des3_ctx
+{
+	PurpleCipherBatchMode mode;
+	guchar iv[8];
+	/* First key for encryption */
+	struct _des_ctx key1;
+	/* Second key for decryption */
+	struct _des_ctx key2;
+	/* Third key for encryption */
+	struct _des_ctx key3;
+} des3_ctx[1];
+
+/*
+ *  Fill a DES3 context with subkeys calculated from 3 64bit key.
+ *  Does not check parity bits, but simply ignore them.
+ *  Does not check for weak keys.
+ **/
+static void
+des3_set_key(PurpleCipherContext *context, const guchar * key)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+	int i;
+
+	des_key_schedule (key +  0, ctx->key1.encrypt_subkeys);
+	des_key_schedule (key +  8, ctx->key2.encrypt_subkeys);
+	des_key_schedule (key + 16, ctx->key3.encrypt_subkeys);
+
+	for (i = 0; i < 32; i += 2)
+	{
+		ctx->key1.decrypt_subkeys[i]	= ctx->key1.encrypt_subkeys[30-i];
+		ctx->key1.decrypt_subkeys[i+1]	= ctx->key1.encrypt_subkeys[31-i];
+		ctx->key2.decrypt_subkeys[i]	= ctx->key2.encrypt_subkeys[30-i];
+		ctx->key2.decrypt_subkeys[i+1]	= ctx->key2.encrypt_subkeys[31-i];
+		ctx->key3.decrypt_subkeys[i]	= ctx->key3.encrypt_subkeys[30-i];
+		ctx->key3.decrypt_subkeys[i+1]	= ctx->key3.encrypt_subkeys[31-i];
+	}
+}
+
+static gint
+des3_ecb_encrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while (offset + 8 <= len) {
+		des_ecb_crypt(&ctx->key1,
+		              data+offset,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+		offset += 8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		while (tmp < len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+	}
+	return 0;
+}
+
+static gint
+des3_cbc_encrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8];
+	memcpy(buf, ctx->iv, 8);
+	while (offset + 8 <= len) {
+		for (i = 0; i < 8; i++)
+			buf[i] ^= data[offset + i];
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+		memcpy(buf, output+offset, 8);
+		offset += 8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		i = 0;
+		while (tmp < len) {
+			buf[i++] ^= data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+	}
+	return 0;
+}
+
+static gint
+des3_encrypt(PurpleCipherContext *context, const guchar data[],
+             size_t len, guchar output[], size_t *outlen)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+		return des3_ecb_encrypt(ctx, data, len, output, outlen);
+	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+		return des3_cbc_encrypt(ctx, data, len, output, outlen);
+	} else {
+		g_return_val_if_reached(0);
+	}
+
+	return 0;
+}
+
+static gint
+des3_ecb_decrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while (offset + 8 <= len) {
+		/* NOTE: Apply key in reverse */
+		des_ecb_crypt(&ctx->key3,
+		              data+offset,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		offset+=8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		while (tmp < len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+	}
+	return 0;
+}
+
+static gint
+des3_cbc_decrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	guint8 link[8];
+	memcpy(link, ctx->iv, 8);
+	while (offset + 8 <= len) {
+		des_ecb_crypt(&ctx->key3,
+		              data+offset,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		for (i = 0; i < 8; i++)
+			output[offset + i] ^= link[i];
+		memcpy(link, data + offset, 8);
+		offset+=8;
+	}
+	*outlen = len;
+	if(offset<len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		i = 0;
+		while(tmp<len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		for (i = 0; i < 8; i++)
+			output[offset + i] ^= link[i];
+	}
+	return 0;
+}
+
+static gint
+des3_decrypt(PurpleCipherContext *context, const guchar data[],
+             size_t len, guchar output[], size_t *outlen)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+		return des3_ecb_decrypt(ctx, data, len, output, outlen);
+	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+		return des3_cbc_decrypt(ctx, data, len, output, outlen);
+	} else {
+		g_return_val_if_reached(0);
+	}
+
+	return 0;
+}
+
+static void
+des3_set_batch(PurpleCipherContext *context, PurpleCipherBatchMode mode)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	ctx->mode = mode;
+}
+
+static PurpleCipherBatchMode
+des3_get_batch(PurpleCipherContext *context)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	return ctx->mode;
+}
+
+static void
+des3_set_iv(PurpleCipherContext *context, guchar *iv, size_t len)
+{
+	struct _des3_ctx *ctx;
+
+	g_return_if_fail(len == 8);
+
+	ctx = purple_cipher_context_get_data(context);
+
+	memcpy(ctx->iv, iv, len);
+}
+
+static void
+des3_init(PurpleCipherContext *context, gpointer extra)
+{
+	struct _des3_ctx *mctx;
+	mctx = g_new0(struct _des3_ctx, 1);
+	purple_cipher_context_set_data(context, mctx);
+}
+
+static void
+des3_uninit(PurpleCipherContext *context)
+{
+	struct _des3_ctx *des3_context;
+
+	des3_context = purple_cipher_context_get_data(context);
+	memset(des3_context, 0, sizeof(des3_context));
+
+	g_free(des3_context);
+	des3_context = NULL;
+}
+
+static PurpleCipherOps DES3Ops = {
+	NULL,              /* Set option */
+	NULL,              /* Get option */
+	des3_init,         /* init */
 	NULL,              /* reset */
-	des_uninit,             /* uninit */
-	NULL,                   /* set iv */
-	NULL,             /* append */
-	NULL,             /* digest */
-	des_encrypt,                   /* encrypt */
-	NULL,                   /* decrypt */
-	NULL,                   /* set salt */
-	NULL,                   /* get salt size */
-	des_set_key,		/* set key */
-	NULL,                   /* get key size */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	des3_uninit,       /* uninit */
+	des3_set_iv,       /* set iv */
+	NULL,              /* append */
+	NULL,              /* digest */
+	des3_encrypt,      /* encrypt */
+	des3_decrypt,      /* decrypt */
+	NULL,              /* set salt */
+	NULL,              /* get salt size */
+	des3_set_key,      /* set key */
+	NULL,              /* get key size */
+	des3_set_batch,    /* set batch mode */
+	des3_get_batch,    /* get batch mode */
+	NULL,              /* get block size */
+	NULL               /* set key with len */
 };
 
-
 /*******************************************************************************
  * SHA-1
  ******************************************************************************/
+#define SHA1_HMAC_BLOCK_SIZE	64
 #define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF)
 
 struct SHA1Context {
@@ -1251,6 +1833,13 @@
 	return TRUE;
 }
 
+static size_t
+sha1_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return SHA1_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps SHA1Ops = {
 	sha1_set_opt,	/* Set Option		*/
 	sha1_get_opt,	/* Get Option		*/
@@ -1266,12 +1855,10 @@
 	NULL,			/* get salt size	*/
 	NULL,			/* set key			*/
 	NULL,			/* get key size		*/
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,			/* set batch mode */
+	NULL,			/* get batch mode */
+	sha1_get_block_size,	/* get block size */
+	NULL			/* set key with len */
 };
 
 /*******************************************************************************
@@ -1435,12 +2022,10 @@
 	NULL,          /* get salt size */
 	rc4_set_key,   /* set key       */
 	rc4_get_key_size, /* get key size  */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,          /* set batch mode */
+	NULL,          /* get batch mode */
+	NULL,          /* get block size */
+	NULL           /* set key with len */
 };
 
 /*******************************************************************************
@@ -1510,6 +2095,14 @@
 		caps |= PURPLE_CIPHER_CAPS_SET_KEY;
 	if(ops->get_key_size)
 		caps |= PURPLE_CIPHER_CAPS_GET_KEY_SIZE;
+	if(ops->set_batch_mode)
+		caps |= PURPLE_CIPHER_CAPS_SET_BATCH_MODE;
+	if(ops->get_batch_mode)
+		caps |= PURPLE_CIPHER_CAPS_GET_BATCH_MODE;
+	if(ops->get_block_size)
+		caps |= PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE;
+	if(ops->set_key_with_len)
+		caps |= PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN;
 
 	return caps;
 }
@@ -1636,7 +2229,9 @@
 	purple_ciphers_register_cipher("md5", &MD5Ops);
 	purple_ciphers_register_cipher("sha1", &SHA1Ops);
 	purple_ciphers_register_cipher("md4", &MD4Ops);
+	purple_ciphers_register_cipher("hmac", &HMACOps);
 	purple_ciphers_register_cipher("des", &DESOps);
+	purple_ciphers_register_cipher("des3", &DES3Ops);
 	purple_ciphers_register_cipher("rc4", &RC4Ops);
 }
 
@@ -1967,6 +2562,80 @@
 }
 
 void
+purple_cipher_context_set_batch_mode(PurpleCipherContext *context,
+                                     PurpleCipherBatchMode mode)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_batch_mode)
+		cipher->ops->set_batch_mode(context, mode);
+	else
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "set_batch_mode operation\n", cipher->name);
+}
+
+PurpleCipherBatchMode
+purple_cipher_context_get_batch_mode(PurpleCipherContext *context)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->get_batch_mode)
+		return cipher->ops->get_batch_mode(context);
+	else {
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "get_batch_mode operation\n", cipher->name);
+		return -1;
+	}
+}
+
+size_t
+purple_cipher_context_get_block_size(PurpleCipherContext *context)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->get_block_size)
+		return cipher->ops->get_block_size(context);
+	else {
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "get_block_size operation\n", cipher->name);
+		return -1;
+	}
+}
+
+void
+purple_cipher_context_set_key_with_len(PurpleCipherContext *context,
+                                       const guchar *key, size_t len)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_key_with_len)
+		cipher->ops->set_key_with_len(context, key, len);
+	else
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "set_key_with_len operation\n", cipher->name);
+}
+
+void
 purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data) {
 	g_return_if_fail(context);
 
--- a/libpurple/cipher.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/cipher.h	Mon Jan 07 03:40:27 2008 +0000
@@ -37,26 +37,37 @@
 typedef struct _PurpleCipherOps		PurpleCipherOps;		/**< Ops for a PurpleCipher		*/
 typedef struct _PurpleCipherContext	PurpleCipherContext;	/**< A context for a PurpleCipher	*/
 
+/**
+ * Modes for batch encrypters
+ */
+typedef enum _PurpleCipherBatchMode {
+	PURPLE_CIPHER_BATCH_MODE_ECB,
+	PURPLE_CIPHER_BATCH_MODE_CBC
+} PurpleCipherBatchMode;
 
 /**
  * The operation flags for a cipher
  */
 typedef enum _PurpleCipherCaps {
-	PURPLE_CIPHER_CAPS_SET_OPT			= 1 << 1,		/**< Set option flag	*/
-	PURPLE_CIPHER_CAPS_GET_OPT			= 1 << 2,		/**< Get option flag	*/
-	PURPLE_CIPHER_CAPS_INIT				= 1 << 3,		/**< Init flag			*/
-	PURPLE_CIPHER_CAPS_RESET				= 1 << 4,		/**< Reset flag			*/
-	PURPLE_CIPHER_CAPS_UNINIT				= 1 << 5,		/**< Uninit flag		*/
-	PURPLE_CIPHER_CAPS_SET_IV				= 1 << 6,		/**< Set IV flag		*/
-	PURPLE_CIPHER_CAPS_APPEND				= 1 << 7,		/**< Append flag		*/
-	PURPLE_CIPHER_CAPS_DIGEST				= 1 << 8,		/**< Digest flag		*/
-	PURPLE_CIPHER_CAPS_ENCRYPT			= 1 << 9,		/**< Encrypt flag		*/
-	PURPLE_CIPHER_CAPS_DECRYPT			= 1 << 10,		/**< Decrypt flag		*/
-	PURPLE_CIPHER_CAPS_SET_SALT			= 1 << 11,		/**< Set salt flag		*/
-	PURPLE_CIPHER_CAPS_GET_SALT_SIZE		= 1 << 12,		/**< Get salt size flag	*/
-	PURPLE_CIPHER_CAPS_SET_KEY			= 1 << 13,		/**< Set key flag		*/
-	PURPLE_CIPHER_CAPS_GET_KEY_SIZE		= 1 << 14,		/**< Get key size flag	*/
-	PURPLE_CIPHER_CAPS_UNKNOWN			= 1 << 16		/**< Unknown			*/
+	PURPLE_CIPHER_CAPS_SET_OPT          = 1 << 1,   /**< Set option flag	*/
+	PURPLE_CIPHER_CAPS_GET_OPT          = 1 << 2,   /**< Get option flag	*/
+	PURPLE_CIPHER_CAPS_INIT             = 1 << 3,   /**< Init flag			*/
+	PURPLE_CIPHER_CAPS_RESET            = 1 << 4,   /**< Reset flag			*/
+	PURPLE_CIPHER_CAPS_UNINIT           = 1 << 5,   /**< Uninit flag		*/
+	PURPLE_CIPHER_CAPS_SET_IV           = 1 << 6,   /**< Set IV flag		*/
+	PURPLE_CIPHER_CAPS_APPEND           = 1 << 7,   /**< Append flag		*/
+	PURPLE_CIPHER_CAPS_DIGEST           = 1 << 8,   /**< Digest flag		*/
+	PURPLE_CIPHER_CAPS_ENCRYPT          = 1 << 9,   /**< Encrypt flag		*/
+	PURPLE_CIPHER_CAPS_DECRYPT          = 1 << 10,  /**< Decrypt flag		*/
+	PURPLE_CIPHER_CAPS_SET_SALT         = 1 << 11,  /**< Set salt flag		*/
+	PURPLE_CIPHER_CAPS_GET_SALT_SIZE    = 1 << 12,  /**< Get salt size flag	*/
+	PURPLE_CIPHER_CAPS_SET_KEY          = 1 << 13,  /**< Set key flag		*/
+	PURPLE_CIPHER_CAPS_GET_KEY_SIZE     = 1 << 14,  /**< Get key size flag	*/
+	PURPLE_CIPHER_CAPS_SET_BATCH_MODE   = 1 << 15,  /**< Set batch mode flag */
+	PURPLE_CIPHER_CAPS_GET_BATCH_MODE   = 1 << 16,  /**< Get batch mode flag */
+	PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE   = 1 << 17,  /**< The get block size flag */
+	PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN = 1 << 18,  /**< The set key with length flag */
+	PURPLE_CIPHER_CAPS_UNKNOWN          = 1 << 19   /**< Unknown			*/
 } PurpleCipherCaps;
 
 /**
@@ -105,10 +116,17 @@
 	/** The get key size function */
 	size_t (*get_key_size)(PurpleCipherContext *context);
 
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
+	/** The set batch mode function */
+	void (*set_batch_mode)(PurpleCipherContext *context, PurpleCipherBatchMode mode);
+
+	/** The get batch mode function */
+	PurpleCipherBatchMode (*get_batch_mode)(PurpleCipherContext *context);
+
+	/** The get block size function */
+	size_t (*get_block_size)(PurpleCipherContext *context);
+
+	/** The set key with length function */
+	void (*set_key_with_len)(PurpleCipherContext *context, const guchar *key, size_t len);
 };
 
 #ifdef __cplusplus
@@ -345,7 +363,7 @@
 /**
  * Sets the salt on a context
  *
- * @param context The context who's salt to set
+ * @param context The context whose salt to set
  * @param salt    The salt
  */
 void purple_cipher_context_set_salt(PurpleCipherContext *context, guchar *salt);
@@ -353,7 +371,7 @@
 /**
  * Gets the size of the salt if the cipher supports it
  *
- * @param context The context who's salt size to get
+ * @param context The context whose salt size to get
  *
  * @return The size of the salt
  */
@@ -362,7 +380,7 @@
 /**
  * Sets the key on a context
  *
- * @param context The context who's key to set
+ * @param context The context whose key to set
  * @param key     The key
  */
 void purple_cipher_context_set_key(PurpleCipherContext *context, const guchar *key);
@@ -370,16 +388,53 @@
 /**
  * Gets the key size for a context
  *
- * @param context The context who's key size to get
+ * @param context The context whose key size to get
  *
  * @return The size of the key
  */
 size_t purple_cipher_context_get_key_size(PurpleCipherContext *context);
 
 /**
+ * Sets the batch mode of a context
+ *
+ * @param context The context whose batch mode to set
+ * @param mode    The batch mode under which the cipher should operate
+ *
+ */
+void purple_cipher_context_set_batch_mode(PurpleCipherContext *context, PurpleCipherBatchMode mode);
+
+/**
+ * Gets the batch mode of a context
+ *
+ * @param context The context whose batch mode to get
+ *
+ * @return The batch mode under which the cipher is operating
+ */
+PurpleCipherBatchMode purple_cipher_context_get_batch_mode(PurpleCipherContext *context);
+
+/**
+ * Gets the block size of a context
+ *
+ * @param context The context whose block size to get
+ *
+ * @return The block size of the context
+ */
+size_t purple_cipher_context_get_block_size(PurpleCipherContext *context);
+
+/**
+ * Sets the key with a given length on a context 
+ *
+ * @param context The context whose key to set
+ * @param key     The key
+ * @param len     The length of the key
+ *
+ */
+void purple_cipher_context_set_key_with_len(PurpleCipherContext *context, const guchar *key, size_t len);
+
+/**
  * Sets the cipher data for a context
  *
- * @param context The context who's cipher data to set
+ * @param context The context whose cipher data to set
  * @param data    The cipher data to set
  */
 void purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data);
@@ -387,7 +442,7 @@
 /**
  * Gets the cipher data for a context
  *
- * @param context The context who's cipher data to get
+ * @param context The context whose cipher data to get
  *
  * @return The cipher data
  */
--- a/libpurple/cmds.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/cmds.h	Mon Jan 07 03:40:27 2008 +0000
@@ -30,25 +30,20 @@
 /**************************************************************************/
 /*@{*/
 
-typedef enum _PurpleCmdPriority PurpleCmdPriority;
-typedef enum _PurpleCmdFlag     PurpleCmdFlag;
-typedef enum _PurpleCmdStatus   PurpleCmdStatus;
-typedef enum _PurpleCmdRet      PurpleCmdRet;
-
-enum _PurpleCmdStatus {
+typedef enum _PurpleCmdStatus {
 	PURPLE_CMD_STATUS_OK,
 	PURPLE_CMD_STATUS_FAILED,
 	PURPLE_CMD_STATUS_NOT_FOUND,
 	PURPLE_CMD_STATUS_WRONG_ARGS,
 	PURPLE_CMD_STATUS_WRONG_PRPL,
 	PURPLE_CMD_STATUS_WRONG_TYPE,
-};
+} PurpleCmdStatus;
 
-enum _PurpleCmdRet {
+typedef enum _PurpleCmdRet {
 	PURPLE_CMD_RET_OK,       /**< Everything's okay. Don't look for another command to call. */
 	PURPLE_CMD_RET_FAILED,   /**< The command failed, but stop looking.*/
 	PURPLE_CMD_RET_CONTINUE, /**< Continue, looking for other commands with the same name to call. */
-};
+} PurpleCmdRet;
 
 #define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func)
 
@@ -56,7 +51,7 @@
                                   gchar **args, gchar **error, void *data);
 typedef guint PurpleCmdId;
 
-enum _PurpleCmdPriority {
+typedef enum _PurpleCmdPriority {
 	PURPLE_CMD_P_VERY_LOW  = -1000,
 	PURPLE_CMD_P_LOW       =     0,
 	PURPLE_CMD_P_DEFAULT   =  1000,
@@ -65,7 +60,7 @@
 	PURPLE_CMD_P_ALIAS     =  4000,
 	PURPLE_CMD_P_HIGH      =  5000,
 	PURPLE_CMD_P_VERY_HIGH =  6000,
-};
+} PurpleCmdPriority;
 
 /** 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
@@ -73,7 +68,7 @@
  *
  *  @see purple_cmd_register
  */
-enum _PurpleCmdFlag {
+typedef enum _PurpleCmdFlag {
 	/** Command is usable in IMs. */
 	PURPLE_CMD_FLAG_IM               = 0x01,
 	/** Command is usable in multi-user chats. */
@@ -82,7 +77,7 @@
 	PURPLE_CMD_FLAG_PRPL_ONLY        = 0x04,
 	/** Incorrect arguments to this command should be accepted anyway. */
 	PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08,
-};
+} PurpleCmdFlag;
 
 
 /*@}*/
--- a/libpurple/connection.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/connection.h	Mon Jan 07 03:40:27 2008 +0000
@@ -196,11 +196,11 @@
 	 *  available; on Windows, it uses Win32's network change notification
 	 *  infrastructure.
 	 */
-	void (*network_connected)();
+	void (*network_connected)(void);
 	/** Called when libpurple discovers that the computer's network
 	 *  connection has gone away.
 	 */
-	void (*network_disconnected)();
+	void (*network_disconnected)(void);
 
 	/** Called when an error causes a connection to be disconnected.
 	 *  Called before #disconnected.  This op is intended to replace
--- a/libpurple/conversation.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/conversation.h	Mon Jan 07 03:40:27 2008 +0000
@@ -490,7 +490,8 @@
  *
  * @param conv The conversation.
  *
- * @return The conversation's name.
+ * @return The conversation's name. If the conversation is an IM with a PurpleBuddy,
+ *         then it's the name of the PurpleBuddy.
  */
 const char *purple_conversation_get_name(const PurpleConversation *conv);
 
@@ -718,7 +719,7 @@
  *
  * @param msg   A PurpleConvMessage
  *
- * @return   The name of the sender of the message
+ * @return   The message flags
  *
  * @since 2.2.0
  */
@@ -729,7 +730,7 @@
  *
  * @param msg   A PurpleConvMessage
  *
- * @return   The name of the sender of the message
+ * @return   The timestamp of the message
  *
  * @since 2.2.0
  */
--- a/libpurple/core.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/core.h	Mon Jan 07 03:40:27 2008 +0000
@@ -67,13 +67,14 @@
  * Calls purple_core_quit().  This can be used as the function 
  * passed to purple_timeout_add() when you want to shutdown Purple 
  * in a specified amount of time.  When shutting down Purple 
- * from a plugin, you must use this with a timeout value of 0: 
+ * from a plugin, you must use this instead of purple_core_quit();
+ * for an immediate exit, use a timeout value of 0: 
  *   purple_timeout_add(0, purple_core_quitcb, NULL);
  * This is ensures that code from your plugin is not being 
- * executed when purple_core_quit() is called.  Otherwise you 
- * would get a core dump after purple_core_quit() executes and 
- * control returns to your plugin because purple_core_quit() frees 
- * all plugins.
+ * executed when purple_core_quit() is called.  If the plugin
+ * called purple_core_quit() directly, you would get a core dump
+ * after purple_core_quit() executes and control returns to your
+ * plugin because purple_core_quit() frees all plugins.
  */
 gboolean purple_core_quit_cb(gpointer unused);
 
--- a/libpurple/dnsquery.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/dnsquery.c	Mon Jan 07 03:40:27 2008 +0000
@@ -142,7 +142,7 @@
  */
 #ifdef HAVE_SIGNAL_H
 G_GNUC_NORETURN static void
-trap_gdb_bug()
+trap_gdb_bug(int sig)
 {
 	const char *message =
 		"Purple's DNS child got a SIGTRAP signal.\n"
@@ -286,7 +286,7 @@
  * Begin the functions for dealing with the DNS child processes.
  */
 static void
-cope_with_gdb_brokenness()
+cope_with_gdb_brokenness(void)
 {
 #ifdef __linux__
 	static gboolean already_done = FALSE;
@@ -460,7 +460,7 @@
 static void host_resolved(gpointer data, gint source, PurpleInputCondition cond);
 
 static void
-handle_next_queued_request()
+handle_next_queued_request(void)
 {
 	PurpleDnsQueryData *query_data;
 	PurpleDnsQueryResolverProcess *resolver;
@@ -547,7 +547,7 @@
 	{
 #ifdef HAVE_GETADDRINFO
 		g_snprintf(message, sizeof(message), _("Error resolving %s:\n%s"),
-				query_data->hostname, gai_strerror(err));
+				query_data->hostname, purple_gai_strerror(err));
 #else
 		g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
 				query_data->hostname, err);
@@ -695,7 +695,7 @@
 		}
 		freeaddrinfo(tmp);
 	} else {
-		query_data->error_message = g_strdup_printf(_("Error resolving %s:\n%s"), query_data->hostname, gai_strerror(rc));
+		query_data->error_message = g_strdup_printf(_("Error resolving %s:\n%s"), query_data->hostname, purple_gai_strerror(rc));
 	}
 #else
 	if ((hp = gethostbyname(query_data->hostname))) {
--- a/libpurple/example/nullclient.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/example/nullclient.c	Mon Jan 07 03:40:27 2008 +0000
@@ -167,7 +167,7 @@
 };
 
 static void
-null_ui_init()
+null_ui_init(void)
 {
 	/**
 	 * This should initialize the UI components for all the modules. Here we
@@ -191,7 +191,7 @@
 };
 
 static void
-init_libpurple()
+init_libpurple(void)
 {
 	/* Set a custom user directory (optional) */
 	purple_util_set_user_dir(CUSTOM_USER_DIRECTORY);
@@ -250,14 +250,14 @@
 }
 
 static void
-connect_to_signals_for_demonstration_purposes_only()
+connect_to_signals_for_demonstration_purposes_only(void)
 {
 	static int handle;
 	purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle,
 				PURPLE_CALLBACK(signed_on), NULL);
 }
 
-int main()
+int main(int argc, char *argv[])
 {
 	GList *iter;
 	int i, num;
--- a/libpurple/ft.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/ft.c	Mon Jan 07 03:40:27 2008 +0000
@@ -479,10 +479,11 @@
 		/* Sending a file */
 		/* Check the filename. */
 #ifdef _WIN32
-		if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) {
+		if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
 #else
-		if (g_strrstr(filename, "../")) {
+		if (g_strrstr(filename, "../"))
 #endif
+		{
 			char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
 
 			msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8);
--- a/libpurple/idle.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/idle.c	Mon Jan 07 03:40:27 2008 +0000
@@ -215,8 +215,8 @@
 /*
  * Check idle and set the timer to fire at the next idle-worth event 
  */
-static gint
-check_idleness_timer()
+static gboolean
+check_idleness_timer(void)
 {
 	check_idleness();
 	if (time_until_next_idle_event == 0)
@@ -225,7 +225,7 @@
 	{
 		/* +1 for the boundary,
 		 * +1 more for g_timeout_add_seconds rounding. */
-		idle_timer = purple_timeout_add_seconds(time_until_next_idle_event + 2, check_idleness_timer, NULL);
+		idle_timer = purple_timeout_add_seconds(time_until_next_idle_event + 2, (GSourceFunc)check_idleness_timer, NULL);
 	}
 	return FALSE;
 }
@@ -295,7 +295,7 @@
 }
 
 static void *
-purple_idle_get_handle()
+purple_idle_get_handle(void)
 {
 	static int handle;
 
@@ -307,7 +307,7 @@
 	int idle_poll_minutes = purple_prefs_get_int("/purple/away/mins_before_away");
 
 	 /* +1 more for g_timeout_add_seconds rounding. */
-	idle_timer = purple_timeout_add_seconds((idle_poll_minutes * 60) + 2, check_idleness_timer, NULL);
+	idle_timer = purple_timeout_add_seconds((idle_poll_minutes * 60) + 2, (GSourceFunc)check_idleness_timer, NULL);
 
 	purple_idle_touch();
 
--- a/libpurple/log.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/log.c	Mon Jan 07 03:40:27 2008 +0000
@@ -348,7 +348,6 @@
 				void(*get_log_sets)(PurpleLogSetCallback cb, GHashTable *sets),
 				gboolean(*remove)(PurpleLog *log),
 				gboolean(*is_deletable)(PurpleLog *log))
-{
 #endif
 	PurpleLogLogger *logger;
 	va_list args;
--- a/libpurple/network.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/network.c	Mon Jan 07 03:40:27 2008 +0000
@@ -289,7 +289,7 @@
 	errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
 	if (errnum != 0) {
 #ifndef _WIN32
-		purple_debug_warning("network", "getaddrinfo: %s\n", gai_strerror(errnum));
+		purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum));
 		if (errnum == EAI_SYSTEM)
 			purple_debug_warning("network", "getaddrinfo: system error: %s\n", g_strerror(errno));
 #else
--- a/libpurple/plugin.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugin.c	Mon Jan 07 03:40:27 2008 +0000
@@ -667,7 +667,10 @@
 			}
 			else
 			{
+#if 0
+				/* This isn't necessary. This has already been done when unloading dep_plugin. */
 				plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l);
+#endif
 			}
 		}
 	}
--- a/libpurple/plugins/ciphertest.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugins/ciphertest.c	Mon Jan 07 03:40:27 2008 +0000
@@ -61,7 +61,7 @@
 };
 
 static void
-cipher_test_md5() {
+cipher_test_md5(void) {
 	PurpleCipher *cipher;
 	PurpleCipherContext *context;
 	gchar digest[33];
@@ -113,12 +113,12 @@
 	{"a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"},
 	{"abc", "a9993e364706816aba3e25717850c26c9cd0d89d"} ,
 	{"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1"} ,
-    {NULL, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"},
+	{NULL, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"},
 	{NULL, NULL}
 };
 
 static void
-cipher_test_sha1() {
+cipher_test_sha1(void) {
 	PurpleCipher *cipher;
 	PurpleCipherContext *context;
 	gchar digest[41];
@@ -176,7 +176,7 @@
 }
 
 static void
-cipher_test_digest()
+cipher_test_digest(void)
 {
 	const gchar *nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093";
 	const gchar *client_nonce = "0a4f113b";
--- a/libpurple/plugins/log_reader.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugins/log_reader.c	Mon Jan 07 03:40:27 2008 +0000
@@ -2426,7 +2426,7 @@
 
 }
 
-static void log_reader_init_prefs() {
+static void log_reader_init_prefs(void) {
 	char *path;
 #ifdef _WIN32
 	char *folder;
--- a/libpurple/plugins/perl/common/Prpl.xs	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugins/perl/common/Prpl.xs	Mon Jan 07 03:40:27 2008 +0000
@@ -54,3 +54,20 @@
 	Purple::Account account
 	const char *name
 	time_t login_time
+
+int
+purple_prpl_send_raw(gc, str)
+	Purple::Connection gc
+	const char *str
+PREINIT:
+	PurplePluginProtocolInfo *prpl_info;
+CODE:
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+	if (prpl_info && prpl_info->send_raw != NULL) {
+		RETVAL = prpl_info->send_raw(gc, str, strlen(str));
+	} else {
+		RETVAL = 0;
+	}
+OUTPUT:
+	RETVAL
+
--- a/libpurple/plugins/perl/common/fallback/const-c.inc	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugins/perl/common/fallback/const-c.inc	Mon Jan 07 03:40:27 2008 +0000
@@ -33,7 +33,7 @@
      Regenerate these constant functions by feeding this entire source file to
      perl -x
 
-#!/usr/bin/perl -w
+#!/usr/bin/env perl -w
 use ExtUtils::Constant qw (constant_types C_constant XS_constant);
 
 my $types = {map {($_, 1)} qw(IV)};
--- a/libpurple/plugins/ssl/ssl-nss.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugins/ssl/ssl-nss.c	Mon Jan 07 03:40:27 2008 +0000
@@ -109,7 +109,7 @@
 	}
 }
 
-static gchar *get_error_text()
+static gchar *get_error_text(void)
 {
 	PRInt32 len = PR_GetErrorTextLength();
 	gchar *ret = NULL;
--- a/libpurple/plugins/tcl/tcl.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugins/tcl/tcl.c	Mon Jan 07 03:40:27 2008 +0000
@@ -149,7 +149,7 @@
 	return 0;
 }
 
-static Tcl_Interp *tcl_create_interp()
+static Tcl_Interp *tcl_create_interp(void)
 {
 	Tcl_Interp *interp;
 
--- a/libpurple/plugins/test.pl	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/plugins/test.pl	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/env perl -w
 
 use Gaim;
 
--- a/libpurple/prefs.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/prefs.c	Mon Jan 07 03:40:27 2008 +0000
@@ -438,19 +438,6 @@
 	g_free(filename);
 	prefs_loaded = TRUE;
 
-	/* I introduced a bug in 2.0.0beta2.  This fixes the broken
-	 * scores on upgrade.  This can be removed sometime shortly
-	 * after 2.0.0 final is released. -- rlaager */
-	if (purple_prefs_get_int("/purple/status/scores/offline") == -500 &&
-	    purple_prefs_get_int("/purple/status/scores/available") == 100 &&
-	    purple_prefs_get_int("/purple/status/scores/invisible") == -50 &&
-	    purple_prefs_get_int("/purple/status/scores/away") == -100 &&
-	    purple_prefs_get_int("/purple/status/scores/extended_away") == -200 &&
-	    purple_prefs_get_int("/purple/status/scores/idle") == -400)
-	{
-		purple_prefs_set_int("/purple/status/scores/idle", -10);
-	}
-
 	return TRUE;
 }
 
@@ -914,7 +901,7 @@
 	if(pref) {
 		if(pref->type != PURPLE_PREF_PATH) {
 			purple_debug_error("prefs",
-					"purple_prefs_set_path: %s not a string pref\n", name);
+					"purple_prefs_set_path: %s not a path pref\n", name);
 			return;
 		}
 
--- a/libpurple/protocols/bonjour/bonjour.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Mon Jan 07 03:40:27 2008 +0000
@@ -617,7 +617,7 @@
 #endif
 
 static void
-initialize_default_account_values()
+initialize_default_account_values(void)
 {
 #ifndef _WIN32
 	struct passwd *info;
--- a/libpurple/protocols/bonjour/mdns_win32.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Mon Jan 07 03:40:27 2008 +0000
@@ -30,19 +30,21 @@
 
 static GSList *pending_buddies = NULL;
 
+typedef struct _dnssd_service_ref_handler {
+	DNSServiceRef sdRef;
+	PurpleAccount *account;
+	guint input_handler;
+} DnsSDServiceRefHandlerData;
+
 /* data used by win32 bonjour implementation */
 typedef struct _win32_session_impl_data {
-	DNSServiceRef presence_svc;
-	DNSServiceRef browser_svc;
+	DnsSDServiceRefHandlerData *presence_query;
+	DnsSDServiceRefHandlerData *browser_query;
 	DNSRecordRef buddy_icon_rec;
-
-	guint presence_handler;
-	guint browser_handler;
 } Win32SessionImplData;
 
 typedef struct _win32_buddy_service_resolver_data {
-	DNSServiceRef txt_query;
-	guint txt_query_handler;
+	DnsSDServiceRefHandlerData *txt_query;
 	uint32_t if_idx;
 	gchar *name;
 	gchar *type;
@@ -53,21 +55,20 @@
 
 typedef struct _win32_buddy_impl_data {
 	GSList *resolvers;
-	DNSServiceRef null_query;
-	guint null_query_handler;
+	DnsSDServiceRefHandlerData *null_query;
 } Win32BuddyImplData;
 
 /* data structure for the resolve callback */
 typedef struct _ResolveCallbackArgs {
-	DNSServiceRef resolver;
-	guint resolver_handler;
+	DnsSDServiceRefHandlerData *resolver_query;
 	PurpleAccount *account;
 	BonjourBuddy *bb;
 	Win32SvcResolverData *res_data;
 	gchar *full_service_name;
-	PurpleDnsQueryData *query;
+	PurpleDnsQueryData *dns_query;
 } ResolveCallbackArgs;
 
+
 static gint
 _find_resolver_data(gconstpointer a, gconstpointer b) {
 	const Win32SvcResolverData *rd_a = a;
@@ -87,8 +88,9 @@
 static void
 _cleanup_resolver_data(Win32SvcResolverData *rd) {
 	if (rd->txt_query != NULL) {
-		purple_input_remove(rd->txt_query_handler);
-		DNSServiceRefDeallocate(rd->txt_query);
+		purple_input_remove(rd->txt_query->input_handler);
+		DNSServiceRefDeallocate(rd->txt_query->sdRef);
+		g_free(rd->txt_query);
 	}
 	g_free(rd->name);
 	g_free(rd->type);
@@ -98,7 +100,16 @@
 
 static void
 _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) {
-	DNSServiceProcessResult((DNSServiceRef) data);
+	DnsSDServiceRefHandlerData *srh = data;
+	DNSServiceErrorType errorCode = DNSServiceProcessResult(srh->sdRef);
+	if (errorCode != kDNSServiceErr_NoError) {
+		purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode);
+		/* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */
+		if (errorCode == kDNSServiceErr_Unknown) {
+			purple_connection_error_reason(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Error communicating with local mDNSResponder."));
+		}
+	}
 }
 
 static void
@@ -123,7 +134,7 @@
 	uint32_t ttl, void *context)
 {
 
-	if (kDNSServiceErr_NoError != errorCode) {
+	if (errorCode != kDNSServiceErr_NoError) {
 		purple_debug_error("bonjour", "record query - callback error.\n");
 		/* TODO: Probably should remove the buddy when this happens */
 	} else if (flags & kDNSServiceFlagsAdd) {
@@ -142,9 +153,9 @@
 			bonjour_buddy_got_buddy_icon(bb, rdata, rdlen);
 
 			/* We've got what we need; stop listening */
-			purple_input_remove(idata->null_query_handler);
-			idata->null_query_handler = 0;
-			DNSServiceRefDeallocate(idata->null_query);
+			purple_input_remove(idata->null_query->input_handler);
+			DNSServiceRefDeallocate(idata->null_query->sdRef);
+			g_free(idata->null_query);
 			idata->null_query = NULL;
 		}
 	}
@@ -153,7 +164,7 @@
 static void
 _mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message)
 {
-	ResolveCallbackArgs* args = (ResolveCallbackArgs*) data;
+	ResolveCallbackArgs *args = (ResolveCallbackArgs*) data;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 	gboolean delete_buddy = FALSE;
 	PurpleBuddy *pb;
@@ -168,27 +179,31 @@
 		delete_buddy = TRUE;
 	} else {
 		struct sockaddr_in *addr = g_slist_nth_data(hosts, 1);
+		DNSServiceErrorType errorCode;
+		DNSServiceRef txt_query_sr;
 
 		/* finally, set up the continuous txt record watcher, and add the buddy to purple */
-
-		if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&args->res_data->txt_query, kDNSServiceFlagsLongLivedQuery,
+		errorCode = DNSServiceQueryRecord(&txt_query_sr, kDNSServiceFlagsLongLivedQuery,
 				kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
-				kDNSServiceClass_IN, _mdns_record_query_callback, args->bb)) {
-
+				kDNSServiceClass_IN, _mdns_record_query_callback, args->bb);
+		if (errorCode == kDNSServiceErr_NoError) {
 			const char *ip = inet_ntoa(addr->sin_addr);
 
 			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj);
 
-
 			args->bb->ips = g_slist_prepend(args->bb->ips, g_strdup(ip));
 			args->res_data->ip = args->bb->ips->data;
 
-			args->res_data->txt_query_handler = purple_input_add(DNSServiceRefSockFD(args->res_data->txt_query),
+			args->res_data->txt_query = g_new(DnsSDServiceRefHandlerData, 1);
+			args->res_data->txt_query->sdRef = txt_query_sr;
+			args->res_data->txt_query->account = args->account;
+
+			args->res_data->txt_query->input_handler = purple_input_add(DNSServiceRefSockFD(txt_query_sr),
 				PURPLE_INPUT_READ, _mdns_handle_event, args->res_data->txt_query);
 
 			bonjour_buddy_add_to_purple(args->bb, NULL);
 		} else {
-			purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s\n", args->bb->name);
+			purple_debug_error("bonjour", "Unable to set up record watcher for buddy %s (%d)\n", args->bb->name, errorCode);
 			delete_buddy = TRUE;
 		}
 
@@ -230,21 +245,21 @@
 _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
     const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
 {
-	ResolveCallbackArgs *args = (ResolveCallbackArgs*)context;
+	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 
 	/* remove the input fd and destroy the service ref */
-	purple_input_remove(args->resolver_handler);
-	args->resolver_handler = 0;
-	DNSServiceRefDeallocate(args->resolver);
-	args->resolver = NULL;
+	purple_input_remove(args->resolver_query->input_handler);
+	DNSServiceRefDeallocate(args->resolver_query->sdRef);
+	g_free(args->resolver_query);
+	args->resolver_query = NULL;
 
-	if (kDNSServiceErr_NoError != errorCode)
+	if (errorCode != kDNSServiceErr_NoError)
 		purple_debug_error("bonjour", "service resolver - callback error.\n");
 	else {
 		/* set more arguments, and start the host resolver */
 
-		if ((args->query =
+		if ((args->dns_query =
 				purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)) != NULL) {
 
 			args->full_service_name = g_strdup(fullname);
@@ -286,7 +301,7 @@
 				const char *name, const char *regtype, const char *domain, void *context) {
 
 	/* TODO: deal with collision */
-	if (kDNSServiceErr_NoError != errorCode)
+	if (errorCode != kDNSServiceErr_NoError)
 		purple_debug_error("bonjour", "service advertisement - callback error (%d).\n", errorCode);
 	else
 		purple_debug_info("bonjour", "service advertisement - callback.\n");
@@ -298,26 +313,28 @@
 {
 	PurpleAccount *account = (PurpleAccount*)context;
 
-	if (kDNSServiceErr_NoError != errorCode)
-		purple_debug_error("bonjour", "service browser - callback error\n");
+	if (errorCode != kDNSServiceErr_NoError)
+		purple_debug_error("bonjour", "service browser - callback error (%d)\n", errorCode);
 	else if (flags & kDNSServiceFlagsAdd) {
 		/* A presence service instance has been discovered... check it isn't us! */
 		if (purple_utf8_strcasecmp(serviceName, account->username) != 0) {
+			DNSServiceErrorType resErrorCode;
 			/* OK, lets go ahead and resolve it to add to the buddy list */
 			ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
+			DNSServiceRef resolver_sr;
 
 			purple_debug_info("bonjour", "Received new record for '%s' on iface %u (%s, %s)\n",
 							  serviceName, interfaceIndex, regtype ? regtype : "",
 							  replyDomain ? replyDomain : "");
 
-			if (kDNSServiceErr_NoError == DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype,
-					replyDomain, _mdns_service_resolve_callback, args)) {
+			resErrorCode = DNSServiceResolve(&resolver_sr, 0, 0, serviceName, regtype,
+					replyDomain, _mdns_service_resolve_callback, args);
+			if (resErrorCode == kDNSServiceErr_NoError) {
 				GSList *tmp = pending_buddies;
 				PurpleBuddy *pb;
 				BonjourBuddy* bb = NULL;
 				Win32SvcResolverData *rd;
 				Win32BuddyImplData *idata;
-				gint fd;
 
 				/* Is there an existing buddy? */
 				if ((pb = purple_find_buddy(account, serviceName)))
@@ -344,7 +361,6 @@
 						pb->proto_data = bb;
 				}
 
-
 				rd = g_new0(Win32SvcResolverData, 1);
 				rd->if_idx = interfaceIndex;
 				rd->name = g_strdup(serviceName);
@@ -358,11 +374,14 @@
 				args->res_data = rd;
 				args->account = account;
 
+				args->resolver_query = g_new(DnsSDServiceRefHandlerData, 1);
+				args->resolver_query->sdRef = resolver_sr;
+				args->resolver_query->account = account;
 				/* get a file descriptor for this service ref, and add it to the input list */
-				fd = DNSServiceRefSockFD(args->resolver);
-				args->resolver_handler = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver);
+				args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(resolver_sr),
+					PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query);
 			} else {
-				purple_debug_error("bonjour", "service browser - failed to resolve service.\n");
+				purple_debug_error("bonjour", "service browser - failed to resolve service. (%d)\n", resErrorCode);
 				g_free(args);
 			}
 		}
@@ -432,7 +451,7 @@
 gboolean _mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records) {
 	TXTRecordRef dns_data;
 	gboolean ret = TRUE;
-	DNSServiceErrorType set_ret = kDNSServiceErr_NoError;
+	DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
 	Win32SessionImplData *idata = data->mdns_impl_data;
 
 	g_return_val_if_fail(idata != NULL, FALSE);
@@ -441,44 +460,46 @@
 
 	while (records) {
 		PurpleKeyValuePair *kvp = records->data;
-		set_ret = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value);
-		if (set_ret != kDNSServiceErr_NoError)
+		errorCode = TXTRecordSetValue(&dns_data, kvp->key, strlen(kvp->value), kvp->value);
+		if (errorCode != kDNSServiceErr_NoError)
 			break;
 		records = records->next;
 	}
 
-	if (set_ret != kDNSServiceErr_NoError) {
-		purple_debug_error("bonjour", "Unable to allocate memory for text record.\n");
+	if (errorCode != kDNSServiceErr_NoError) {
+		purple_debug_error("bonjour", "Unable to allocate memory for text record.(%d)\n", errorCode);
 		ret = FALSE;
 	} else {
-		DNSServiceErrorType err = kDNSServiceErr_NoError;
-
 		/* OK, we're done constructing the text record, (re)publish the service */
+		DNSServiceRef presence_sr;
 
 		switch (type) {
 			case PUBLISH_START:
 				purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj);
-				err = DNSServiceRegister(&idata->presence_svc, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE,
+				errorCode = DNSServiceRegister(&presence_sr, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE,
 					NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data),
 					_mdns_service_register_callback, NULL);
 				break;
 
 			case PUBLISH_UPDATE:
 				purple_debug_info("bonjour", "Updating presence.\n");
-				err = DNSServiceUpdateRecord(idata->presence_svc, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
+				errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
 				break;
 		}
 
-		if (err != kDNSServiceErr_NoError) {
-			purple_debug_error("bonjour", "Failed to publish presence service.\n");
+		if (errorCode != kDNSServiceErr_NoError) {
+			purple_debug_error("bonjour", "Failed to publish presence service.(%d)\n", errorCode);
 			ret = FALSE;
 		} else if (type == PUBLISH_START) {
 			/* We need to do this because according to the Apple docs:
 			 * "the client is responsible for ensuring that DNSServiceProcessResult() is called
 			 * whenever there is a reply from the daemon - the daemon may terminate its connection
 			 * with a client that does not process the daemon's responses */
-			idata->presence_handler = purple_input_add(DNSServiceRefSockFD(idata->presence_svc),
-				PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_svc);
+			idata->presence_query = g_new(DnsSDServiceRefHandlerData, 1);
+			idata->presence_query->sdRef = presence_sr;
+			idata->presence_query->account = data->account;
+			idata->presence_query->input_handler = purple_input_add(DNSServiceRefSockFD(presence_sr),
+				PURPLE_INPUT_READ, _mdns_handle_event, idata->presence_query);
 		}
 	}
 
@@ -488,17 +509,24 @@
 }
 
 gboolean _mdns_browse(BonjourDnsSd *data) {
+	DNSServiceErrorType errorCode;
 	Win32SessionImplData *idata = data->mdns_impl_data;
+	DNSServiceRef browser_sr;
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
-	if (DNSServiceBrowse(&idata->browser_svc, 0, 0, ICHAT_SERVICE, NULL,
-				 _mdns_service_browse_callback, data->account)
-			== kDNSServiceErr_NoError) {
-		idata->browser_handler = purple_input_add(DNSServiceRefSockFD(idata->browser_svc),
-			PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_svc);
+	errorCode = DNSServiceBrowse(&browser_sr, 0, 0, ICHAT_SERVICE, NULL,
+		_mdns_service_browse_callback, data->account);
+	if (errorCode == kDNSServiceErr_NoError) {
+		idata->browser_query = g_new(DnsSDServiceRefHandlerData, 1);
+		idata->browser_query->sdRef = browser_sr;
+		idata->browser_query->account = data->account;
+		idata->browser_query->input_handler = purple_input_add(DNSServiceRefSockFD(browser_sr),
+			PURPLE_INPUT_READ, _mdns_handle_event, idata->browser_query);
 		return TRUE;
-	}
+	} else
+		purple_debug_error("bonjour", "Error registering Local Link presence browser. (%d)\n", errorCode);
+
 
 	return FALSE;
 }
@@ -509,14 +537,16 @@
 	if (idata == NULL)
 		return;
 
-	if (idata->presence_svc != NULL) {
-		purple_input_remove(idata->presence_handler);
-		DNSServiceRefDeallocate(idata->presence_svc);
+	if (idata->presence_query != NULL) {
+		purple_input_remove(idata->presence_query->input_handler);
+		DNSServiceRefDeallocate(idata->presence_query->sdRef);
+		g_free(idata->presence_query);
 	}
 
-	if (idata->browser_svc != NULL) {
-		purple_input_remove(idata->browser_handler);
-		DNSServiceRefDeallocate(idata->browser_svc);
+	if (idata->browser_query != NULL) {
+		purple_input_remove(idata->browser_query->input_handler);
+		DNSServiceRefDeallocate(idata->browser_query->sdRef);
+		g_free(idata->browser_query);
 	}
 
 	g_free(idata);
@@ -526,28 +556,30 @@
 
 gboolean _mdns_set_buddy_icon_data(BonjourDnsSd *data, gconstpointer avatar_data, gsize avatar_len) {
 	Win32SessionImplData *idata = data->mdns_impl_data;
-	DNSServiceErrorType err = kDNSServiceErr_NoError;
+	DNSServiceErrorType errorCode = kDNSServiceErr_NoError;
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
 	if (avatar_data != NULL && idata->buddy_icon_rec == NULL) {
 		purple_debug_info("bonjour", "Setting new buddy icon.\n");
-		err = DNSServiceAddRecord(idata->presence_svc, &idata->buddy_icon_rec,
+		errorCode = DNSServiceAddRecord(idata->presence_query->sdRef, &idata->buddy_icon_rec,
 			0, kDNSServiceType_NULL, avatar_len, avatar_data, 0);
 	} else if (avatar_data != NULL) {
 		purple_debug_info("bonjour", "Updating existing buddy icon.\n");
-		err = DNSServiceUpdateRecord(idata->presence_svc, idata->buddy_icon_rec,
+		errorCode = DNSServiceUpdateRecord(idata->presence_query->sdRef, idata->buddy_icon_rec,
 			0, avatar_len, avatar_data, 0);
 	} else if (idata->buddy_icon_rec != NULL) {
 		purple_debug_info("bonjour", "Removing existing buddy icon.\n");
-		DNSServiceRemoveRecord(idata->presence_svc, idata->buddy_icon_rec, 0);
+		errorCode = DNSServiceRemoveRecord(idata->presence_query->sdRef, idata->buddy_icon_rec, 0);
 		idata->buddy_icon_rec = NULL;
 	}
 
-	if (err != kDNSServiceErr_NoError)
-		purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", err);
+	if (errorCode != kDNSServiceErr_NoError) {
+		purple_debug_error("bonjour", "Error (%d) setting buddy icon record.\n", errorCode);
+		return FALSE;
+	}
 
-	return (err == kDNSServiceErr_NoError);
+	return TRUE;
 }
 
 void _mdns_init_buddy(BonjourBuddy *buddy) {
@@ -566,8 +598,9 @@
 	}
 
 	if (idata->null_query != NULL) {
-		purple_input_remove(idata->null_query_handler);
-		DNSServiceRefDeallocate(idata->null_query);
+		purple_input_remove(idata->null_query->input_handler);
+		DNSServiceRefDeallocate(idata->null_query->sdRef);
+		g_free(idata->null_query);
 	}
 
 	g_free(idata);
@@ -583,17 +616,30 @@
 
 	/* Cancel any existing query */
 	if (idata->null_query != NULL) {
-		purple_input_remove(idata->null_query_handler);
-		idata->null_query_handler = 0;
-		DNSServiceRefDeallocate(idata->null_query);
+		purple_input_remove(idata->null_query->input_handler);
+		DNSServiceRefDeallocate(idata->null_query->sdRef);
+		g_free(idata->null_query);
 		idata->null_query = NULL;
 	}
 
-	DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local");
-	if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&idata->null_query, 0, kDNSServiceInterfaceIndexAny, svc_name,
-			kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy)) {
-		idata->null_query_handler = purple_input_add(DNSServiceRefSockFD(idata->null_query),
-			PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query);
+	if (DNSServiceConstructFullName(svc_name, buddy->name, ICHAT_SERVICE, "local") != 0)
+		purple_debug_error("bonjour", "Unable to construct full name to retrieve buddy icon for %s.\n", buddy->name);
+	else {
+		DNSServiceRef null_query_sr;
+
+		DNSServiceErrorType errorCode = DNSServiceQueryRecord(&null_query_sr, 0, kDNSServiceInterfaceIndexAny,
+			svc_name, kDNSServiceType_NULL, kDNSServiceClass_IN, _mdns_record_query_callback, buddy);
+
+		if (errorCode == kDNSServiceErr_NoError) {
+			idata->null_query = g_new(DnsSDServiceRefHandlerData, 1);
+
+			idata->null_query->sdRef = null_query_sr;
+			idata->null_query->account = buddy->account;
+
+			idata->null_query->input_handler = purple_input_add(DNSServiceRefSockFD(null_query_sr),
+				PURPLE_INPUT_READ, _mdns_handle_event, idata->null_query);
+		} else
+			purple_debug_error("bonjour", "Unable to query buddy icon record for %s. (%d)\n", buddy->name, errorCode);
 	}
 
 }
--- a/libpurple/protocols/bonjour/parser.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Mon Jan 07 03:40:27 2008 +0000
@@ -39,7 +39,7 @@
 	for(i=0; i < nb_attributes * 5; i+=5) {
 		if(!xmlStrcmp(attributes[i], (xmlChar*) "from")) {
 			int len = attributes[i+4] - attributes[i+3];
-			bconv->buddy_name = g_strndup(attributes[i+3], len);
+			bconv->buddy_name = g_strndup((char *)attributes[i+3], len);
 			bonjour_jabber_conv_match_by_name(bconv);
 
 			return (bconv->pb != NULL);
--- a/libpurple/protocols/irc/cmds.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/irc/cmds.c	Mon Jan 07 03:40:27 2008 +0000
@@ -367,7 +367,12 @@
 		if (!end)
 			end = cur + strlen(cur);
 		msg = g_strndup(cur, end - cur);
-		buf = irc_format(irc, "vt:", "PRIVMSG", args[0], msg);
+
+		if(!strcmp(cmd, "msg"))
+			buf = irc_format(irc, "vt:", "PRIVMSG", args[0], msg);
+		else /* seding a notice if we get here */
+			buf = irc_format(irc, "vt:", "NOTICE", args[0], msg);
+
 		irc_send(irc, buf);
 		g_free(msg);
 		g_free(buf);
--- a/libpurple/protocols/irc/parse.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/irc/parse.c	Mon Jan 07 03:40:27 2008 +0000
@@ -136,6 +136,7 @@
 	{ "names", "c", irc_cmd_names, N_("names [channel]:  List the users currently in a channel.") },
 	{ "nick", "n", irc_cmd_nick, N_("nick &lt;new nickname&gt;:  Change your nickname.") },
 	{ "nickserv", ":", irc_cmd_service, N_("nickserv: Send a command to nickserv") },
+	{ "notice", "t:", irc_cmd_privmsg, N_("notice &lt;target&lt;:  Send a notice to a user or channel.") },
 	{ "op", ":", irc_cmd_op, N_("op &lt;nick1&gt; [nick2] ...:  Grant channel operator status to someone. You must be a channel operator to do this.") },
 	{ "operwall", ":", irc_cmd_wallops, N_("operwall &lt;message&gt;:  If you don't know what this is, you probably can't use it.") },
 	{ "operserv", ":", irc_cmd_service, N_("operserv: Send a command to operserv") },
--- a/libpurple/protocols/jabber/auth.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/jabber/auth.c	Mon Jan 07 03:40:27 2008 +0000
@@ -330,14 +330,21 @@
 							disallow_plaintext_auth);
 					g_free(msg);
 					return;
-				/* Everything else has failed, so fail the
-				 * connection. Should probably have a better
-				 * error here.
-				 */
+
 				} else {
-					purple_connection_error_reason (js->gc,
-						PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
-						_("Server does not use any supported authentication method"));
+					/* We have no mechs which can work.
+					 * Try falling back on the old jabber:iq:auth method. We get here if the server supports
+					 * one or more sasl mechs, we are compiled with cyrus-sasl support, but we support or can connect with none of
+					 * the offerred mechs. jabberd 2.0 w/ SASL and Apple's iChat Server 10.5 both handle and expect
+					 * jabber:iq:auth in this situation.  iChat Server in particular offers SASL GSSAPI by default, which is often
+					 * not configured on the client side, and expects a fallback to jabber:iq:auth when it (predictably) fails.
+					 *
+					 * Note: xep-0078 points out that using jabber:iq:auth after a sasl failure is wrong. However,
+					 * I believe this refers to actual authentication failure, not a simple lack of concordant mechanisms.
+					 * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers
+					 * which would connect without issue otherwise. -evands
+					 */
+					jabber_auth_start_old(js);
 					return;
 				}
 				/* not reached */
@@ -563,6 +570,75 @@
 	}
 }
 
+/*!
+ * @brief Given the server challenge (message) and the key (password), calculate the HMAC-MD5 digest
+ *
+ * This is the crammd5 response.  Inspired by cyrus-sasl's _sasl_hmac_md5()
+ */
+static void
+auth_hmac_md5(const char *challenge, size_t challenge_len, const char *key, size_t key_len, guchar *digest)
+{
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
+	int i;
+	/* inner padding - key XORd with ipad */
+	unsigned char k_ipad[65];    
+	/* outer padding - key XORd with opad */
+	unsigned char k_opad[65];    
+
+	cipher = purple_ciphers_find_cipher("md5");
+
+	/* if key is longer than 64 bytes reset it to key=MD5(key) */
+	if (strlen(key) > 64) {
+		guchar keydigest[16];
+
+		context = purple_cipher_context_new(cipher, NULL);
+		purple_cipher_context_append(context, (const guchar *)key, strlen(key));
+		purple_cipher_context_digest(context, 16, keydigest, NULL);
+		purple_cipher_context_destroy(context);
+
+		key = (char *)keydigest;
+		key_len = 16;
+	} 
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* start out by storing key in pads */
+	memset(k_ipad, '\0', sizeof k_ipad);
+	memset(k_opad, '\0', sizeof k_opad);
+	memcpy(k_ipad, (void *)key, key_len);
+	memcpy(k_opad, (void *)key, key_len);
+
+	/* XOR key with ipad and opad values */
+	for (i=0; i<64; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/* perform inner MD5 */
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, k_ipad, 64); /* start with inner pad */
+	purple_cipher_context_append(context, (const guchar *)challenge, challenge_len); /* then text of datagram */
+	purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 1st pass */
+	purple_cipher_context_destroy(context);
+
+	/* perform outer MD5 */	
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, k_opad, 64); /* start with outer pad */
+	purple_cipher_context_append(context, digest, 16); /* then results of 1st hash */
+	purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 2nd pass */
+	purple_cipher_context_destroy(context);
+}
+
 static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data)
 {
 	JabberIq *iq;
@@ -608,6 +684,35 @@
 			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
 			jabber_iq_send(iq);
 
+		} else if(js->stream_id && xmlnode_get_child(query, "crammd5")) {
+			const char *challenge;
+			guchar digest[16];
+			char h[17], *p;
+			int i;
+
+			challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge");
+			auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), digest);
+
+			/* Create the response query */
+			iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
+			query = xmlnode_get_child(iq->node, "query");
+
+			x = xmlnode_new_child(query, "username");
+			xmlnode_insert_data(x, js->user->node, -1);
+			x = xmlnode_new_child(query, "resource");
+			xmlnode_insert_data(x, js->user->resource, -1);
+
+			x = xmlnode_new_child(query, "crammd5");
+
+			/* Translate the digest to a hexadecimal notation */
+			p = h;
+			for(i=0; i<16; i++, p+=2)
+				snprintf(p, 3, "%02x", digest[i]);
+			xmlnode_insert_data(x, h, -1);
+
+			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
+			jabber_iq_send(iq);
+
 		} else if(xmlnode_get_child(query, "password")) {
 			if(js->gsc == NULL && !purple_account_get_bool(js->gc->account,
 						"auth_plain_in_clear", FALSE)) {
@@ -972,10 +1077,12 @@
 		}
 	}
 	/* If we've negotiated a security layer, we need to enable it */
-	sasl_getprop(js->sasl, SASL_SSF, &x);
-	if (*(int *)x > 0) {
-		sasl_getprop(js->sasl, SASL_MAXOUTBUF, &x);
-		js->sasl_maxbuf = *(int *)x;
+	if (js->sasl) {
+		sasl_getprop(js->sasl, SASL_SSF, &x);
+		if (*(int *)x > 0) {
+			sasl_getprop(js->sasl, SASL_MAXOUTBUF, &x);
+			js->sasl_maxbuf = *(int *)x;
+		}
 	}
 #endif
 
--- a/libpurple/protocols/jabber/buddy.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Mon Jan 07 03:40:27 2008 +0000
@@ -1153,8 +1153,10 @@
 
 void jabber_vcard_fetch_mine(JabberStream *js)
 {
-	JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "vcard-temp");
-
+	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
+	
+	xmlnode *vcard = xmlnode_new_child(iq->node, "vCard");
+	xmlnode_set_namespace(vcard, "vcard-temp");
 	jabber_iq_set_callback(iq, jabber_vcard_save_mine, NULL);
 
 	jabber_iq_send(iq);
--- a/libpurple/protocols/jabber/jabber.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Jan 07 03:40:27 2008 +0000
@@ -389,9 +389,32 @@
 	g_free(txt); g_free(utf);
 }
 
+static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) 
+{
+	purple_timeout_remove(GPOINTER_TO_INT(timeout));
+	js->keepalive_timeout = -1;
+}
+
+static gboolean jabber_pong_timeout(PurpleConnection *gc)
+{
+	JabberStream *js = gc->proto_data;
+	purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Ping timeout"));
+	js->keepalive_timeout = -1;
+	return FALSE;
+}
+
 void jabber_keepalive(PurpleConnection *gc)
 {
-	jabber_send_raw(gc->proto_data, "\t", -1);
+	JabberStream *js = gc->proto_data;
+	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
+
+	xmlnode *ping = xmlnode_new_child(iq->node, "ping");
+	xmlnode_set_namespace(ping, "urn:xmpp:ping");
+
+	js->keepalive_timeout = purple_timeout_add_seconds(20, (GSourceFunc)(jabber_pong_timeout), gc);
+	jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout));
+	jabber_iq_send(iq);
 }
 
 static void
@@ -592,7 +615,8 @@
 	js->user = jabber_id_new(purple_account_get_username(account));
 	js->next_id = g_random_int();
 	js->write_buffer = purple_circ_buffer_new(512);
-	js->old_length = -1;
+	js->old_length = 0;
+	js->keepalive_timeout = -1;
 
 	if(!js->user) {
 		purple_connection_error_reason (gc,
@@ -1077,7 +1101,7 @@
 			g_free, g_free);
 	js->user = jabber_id_new(purple_account_get_username(account));
 	js->next_id = g_random_int();
-	js->old_length = -1;
+	js->old_length = 0;
 
 	if(!js->user) {
 		purple_connection_error_reason (gc,
@@ -1292,6 +1316,9 @@
 	g_free(js->old_uri);
 	g_free(js->old_track);
 
+	if (js->keepalive_timeout != -1)
+		purple_timeout_remove(js->keepalive_timeout);
+	
 	g_free(js);
 
 	gc->proto_data = NULL;
@@ -1497,10 +1524,16 @@
 				} else
 					purple_notify_user_info_add_pair(user_info, _("Mood"), mood);
 			}
-			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {	
+			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
 				PurpleStatus *tune = purple_presence_get_status(presence, "tune");
 				const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
-				purple_notify_user_info_add_pair(user_info, _("Current media"), title);
+				const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
+				const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
+				char *playing = purple_util_format_song_info(title, artist, album, NULL);
+				if (playing) {
+					purple_notify_user_info_add_pair(user_info, _("Now Listening"), playing);
+					g_free(playing);
+				}
 			}
 		}
 
--- a/libpurple/protocols/jabber/jabber.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Mon Jan 07 03:40:27 2008 +0000
@@ -193,6 +193,9 @@
 	char *old_track;
 	
 	char *host;
+	
+	/* A purple timeout tag for the keepalive */
+	int keepalive_timeout;
 };
 
 typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace);
--- a/libpurple/protocols/jabber/libxmpp.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Mon Jan 07 03:40:27 2008 +0000
@@ -53,7 +53,7 @@
 	OPT_PROTO_SLASH_COMMANDS_NATIVE,
 	NULL,							/* user_splits */
 	NULL,							/* protocol_options */
-	{"png", 32, 32, 96, 96, 8191, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
+	{"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
 	jabber_list_icon,				/* list_icon */
 	jabber_list_emblem,			/* list_emblems */
 	jabber_status_text,				/* status_text */
--- a/libpurple/protocols/jabber/usertune.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/jabber/usertune.c	Mon Jan 07 03:40:27 2008 +0000
@@ -35,11 +35,12 @@
 	xmlnode *tuneinfo, *tune;
 	PurpleJabberTuneInfo tuneinfodata;
 	JabberBuddyResource *resource;
-	
+	gboolean valid = FALSE;
+
 	/* ignore the tune of people not on our buddy list */
 	if (!buddy || !item)
 		return;
-	
+
 	tuneinfodata.artist = NULL;
 	tuneinfodata.title = NULL;
 	tuneinfodata.album = NULL;
@@ -58,36 +59,47 @@
 			if (!strcmp(tuneinfo->name, "artist")) {
 				if (tuneinfodata.artist == NULL) /* only pick the first one */
 					tuneinfodata.artist = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} 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);
+					if (tuneinfodata.time > 0)
+						valid = TRUE;
 				}
 			} else if (!strcmp(tuneinfo->name, "source")) {
 				if (tuneinfodata.album == NULL) /* only pick the first one */
 					tuneinfodata.album = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} else if (!strcmp(tuneinfo->name, "title")) {
 				if (tuneinfodata.title == NULL) /* only pick the first one */
 					tuneinfodata.title = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} else if (!strcmp(tuneinfo->name, "track")) {
 				if (tuneinfodata.track == NULL) /* only pick the first one */
 					tuneinfodata.track = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			} else if (!strcmp(tuneinfo->name, "uri")) {
 				if (tuneinfodata.url == NULL) /* only pick the first one */
 					tuneinfodata.url = xmlnode_get_data(tuneinfo);
+				valid = TRUE;
 			}
 		}
 	}
 
-	purple_prpl_got_user_status(js->gc->account, from, "tune",
-			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);
+	if (valid) {
+		purple_prpl_got_user_status(js->gc->account, from, "tune",
+				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);
+	} else {
+		purple_prpl_got_user_status_deactive(js->gc->account, from, "tune");
+	}
 
 	g_free(tuneinfodata.artist);
 	g_free(tuneinfodata.title);
@@ -119,7 +131,7 @@
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "source"),tuneinfo->album,-1);
 		if(tuneinfo->url && tuneinfo->url[0] != '\0')
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "uri"),tuneinfo->url,-1);
-		if(tuneinfo->time >= 0) {
+		if(tuneinfo->time > 0) {
 			char *length = g_strdup_printf("%d", tuneinfo->time);
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "length"),length,-1);
 			g_free(length);
--- a/libpurple/protocols/msn/contact.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/msn/contact.c	Mon Jan 07 03:40:27 2008 +0000
@@ -1289,16 +1289,16 @@
 				purple_debug_info("MSNCL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
 				msn_group_new(session->userlist, guid, state->new_group_name);
 
-				g_free(guid);
-
 				if (state->action & MSN_ADD_BUDDY) {
 					msn_userlist_add_buddy(session->userlist,
 						state->who,
 						state->new_group_name);
 				} else if (state->action & MSN_MOVE_BUDDY) {
 					msn_add_contact_to_group(session->contact, state, state->who, guid); 
+					g_free(guid);
 					return;
 				}
+				g_free(guid);
 			} else {
 				purple_debug_info("MSNCL", "Adding group %s failed\n",
 					state->new_group_name);
--- a/libpurple/protocols/msn/msn.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Mon Jan 07 03:40:27 2008 +0000
@@ -612,8 +612,8 @@
 			PurpleStatus *tune = purple_presence_get_status(presence, "tune");
 			const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
 			const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
-			currentmedia = g_strdup_printf("%s%s%s", title, artist ? " - " : "",
-					artist ? artist : "");
+			const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
+			currentmedia = purple_util_format_song_info(title, artist, album, NULL);
 			/* We could probably just use user->media.title etc. here */
 		}
 
@@ -662,9 +662,7 @@
 		}
 
 		if (currentmedia) {
-			tmp = g_markup_escape_text(currentmedia, -1);
-			purple_notify_user_info_add_pair(user_info, _("Current media"), tmp);
-			g_free(tmp);
+			purple_notify_user_info_add_pair(user_info, _("Now Listening"), currentmedia);
 			g_free(currentmedia);
 		}
 	}
--- a/libpurple/protocols/msn/soap.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/msn/soap.c	Mon Jan 07 03:40:27 2008 +0000
@@ -813,10 +813,8 @@
 		purple_debug_info("MSN SOAP", "Currently processing another SOAP request\n");
 	} else {
 		purple_debug_info("MSN SOAP", "No requests left to dispatch\n");
+#endif
 	}
-#else
-      }
-#endif
 
 }
 
--- a/libpurple/protocols/msn/soap2.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/msn/soap2.c	Mon Jan 07 03:40:27 2008 +0000
@@ -170,6 +170,9 @@
 {
 	MsnSoapConnection *conn = data;
 
+	/* sslconn already frees the connection in case of error */
+	conn->ssl = NULL;
+
 	g_hash_table_remove(conn->session->soap_table, conn->host);
 }
 
--- a/libpurple/protocols/msnp9/slpcall.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/msnp9/slpcall.c	Mon Jan 07 03:40:27 2008 +0000
@@ -34,7 +34,7 @@
  **************************************************************************/
 
 static char *
-rand_guid()
+rand_guid(void)
 {
 	return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
 			rand() % 0xAAFF + 0x1111,
--- a/libpurple/protocols/myspace/myspace.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Mon Jan 07 03:40:27 2008 +0000
@@ -2912,8 +2912,7 @@
 	menu = g_list_append(menu, act);
 #endif
 
-	act = purple_plugin_action_new(g_strdup_printf("%s",
-				_("Add friends from MySpace.com")), msim_import_friends);
+	act = purple_plugin_action_new(_("Add friends from MySpace.com"), msim_import_friends);
 	menu = g_list_append(menu, act);
 
 	return menu;
--- a/libpurple/protocols/novell/novell.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/novell/novell.c	Mon Jan 07 03:40:27 2008 +0000
@@ -1029,7 +1029,7 @@
  ******************************************************************************/
 
 static char *
-_user_agent_string()
+_user_agent_string(void)
 {
 
 #if !defined(_WIN32)
--- a/libpurple/protocols/oscar/family_auth.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Mon Jan 07 03:40:27 2008 +0000
@@ -196,6 +196,10 @@
  *   unknown= 0x0000008b
  *   serverstore = 0x01
  *
+ * @param truncate_pass Truncate the password to 8 characters.  This
+ *        usually happens for AOL accounts.  We are told that we
+ *        should truncate it if the 0x0017/0x0007 SNAC contains
+ *        a TLV of type 0x0026 with data 0x0000.
  */
 int
 aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key)
@@ -522,8 +526,8 @@
 
 	/*
 	 * If the truncate_pass TLV exists then we should truncate the
-	 * user's password to 8 characters.  This flag is sent when you
-	 * try to log in with an AOL user's screen name.
+	 * user's password to 8 characters.  This flag is sent to us
+	 * when logging in with an AOL user's screen name.
 	 */
 	truncate_pass = aim_tlv_gettlv(tlvlist, 0x0026, 1) != NULL;
 
--- a/libpurple/protocols/oscar/family_icbm.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Mon Jan 07 03:40:27 2008 +0000
@@ -51,10 +51,9 @@
 #include "win32dep.h"
 #endif
 
+#include "util.h"
 /* yaz */
 #include "debug.h"
-#include "../../util.h"
-
 /**
  * Add a standard ICBM header to the given bstream with the given
  * information.
@@ -2378,11 +2377,166 @@
 	sn = byte_stream_getstr(bs, snlen);
 	reason = byte_stream_get16(bs);
 
-	if (channel == 0x0002) { /* File transfer declined */
+	if (channel == 0x0002)
+	{
+		/* parse status note text */
+
+		struct aim_icq_info *info = NULL;
+		struct aim_icq_info *prev_info = NULL;
+		char *response = NULL;
+		char *encoding = NULL;
+		char *stripped_encoding = NULL;
+		char *status_note_text = NULL;
+		char *stripped_status_note_text = NULL;
+		char *status_note = NULL;
+
+		/*
+		 * TODO: Using a while statement here is kind of an ugly hack
+		 *       to be able to use 'break'.  We might as well be using
+		 *       'goto'.  Should probably get rid of this.
+		 */
+		while (reason == 0x0003) /* channel-specific */
+		{
+			guint32 length;
+			guint16 version;
+			guint32 capability;
+			guint8 message_type;
+			guint16 status_code;
+			guint16 text_length;
+			guint32 request_length;
+			guint32 response_length;
+			guint32 encoding_length;
+			PurpleAccount *account;
+			PurpleBuddy *buddy;
+			PurplePresence *presence;
+			PurpleStatus *status;
+
+			for (info = od->icq_info; info != NULL; info = info->next)
+			{
+				if (memcmp(&info->icbm_cookie, cookie, 8) == 0)
+				{
+					if (prev_info == NULL)
+						od->icq_info = info->next;
+					else
+						prev_info->next = info->next;
+
+					break;
+				}
+
+				prev_info = info;
+			}
+
+			if (info == NULL)
+				break;
+
+			if ((length = byte_stream_getle16(bs)) != 27)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 27, received %u.\n", length);
+				break;
+			}
+			if ((version = byte_stream_getle16(bs)) != 9)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect version; expected 9, received %u.\n", version);
+				break;
+			}
+			capability = aim_locate_getcaps(od, bs, 0x10);
+			if (capability != OSCAR_CAPABILITY_EMPTY)
+			{
+				purple_debug_misc("oscar", "clientautoresp: plugin ID is not null.\n");
+				break;
+			}
+			byte_stream_advance(bs, 2); /* unknown */
+			byte_stream_advance(bs, 4); /* client capabilities flags */
+			byte_stream_advance(bs, 1); /* unknown */
+			byte_stream_advance(bs, 2); /* downcouner? */
+
+			if ((length = byte_stream_getle16(bs)) != 14)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect header size; expected 14, received %u.\n", length);
+				break;
+			}
+			byte_stream_advance(bs, 2); /* downcounter? */
+			byte_stream_advance(bs, 12); /* unknown */
+
+			if ((message_type = byte_stream_get8(bs)) != 0x1a)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect message type; expected 0x1a, received 0x%x.\n", message_type);
+				break;
+			}
+			byte_stream_advance(bs, 1); /* message flags */
+			if ((status_code = byte_stream_getle16(bs)) != 0)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect status code; expected 0, received %u.\n", status_code);
+				break;
+			}
+			byte_stream_advance(bs, 2); /* priority code */
+
+			text_length = byte_stream_getle16(bs);
+			byte_stream_advance(bs, text_length); /* text */
+
+			length = byte_stream_getle16(bs);
+			byte_stream_advance(bs, 18); /* unknown */
+			if (length != 18 + 4 + (request_length = byte_stream_getle32(bs)) + 17)
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 18 + 4 + request_length + 17, length);
+				break;
+			}
+			byte_stream_advance(bs, request_length); /* x request */
+			byte_stream_advance(bs, 17); /* unknown */
+
+			length = byte_stream_getle32(bs);
+			response_length = byte_stream_getle32(bs);
+			response = byte_stream_getstr(bs, response_length);
+			if (length != 4 + response_length + 4 + (encoding_length = byte_stream_getle32(bs)))
+			{
+				purple_debug_misc("oscar", "clientautoresp: incorrect block; expected length is %u, got %u.\n", 4 + response_length + 4 + encoding_length, length);
+				break;
+			}
+			encoding = byte_stream_getstr(bs, encoding_length);
+
+			account = purple_connection_get_account(od->gc);
+			stripped_encoding = oscar_encoding_extract(encoding);
+			status_note_text = oscar_encoding_to_utf8(account, stripped_encoding, response, response_length);
+			stripped_status_note_text = purple_markup_strip_html(status_note_text);
+
+			if (stripped_status_note_text != NULL && stripped_status_note_text[0] != 0)
+				status_note = g_strdup_printf("%s: %s", info->status_note_title, stripped_status_note_text);
+			else
+				status_note = g_strdup(info->status_note_title);
+
+			buddy = purple_find_buddy(account, sn);
+			if (buddy == NULL)
+			{
+				purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", sn);
+				break;
+			}
+
+			purple_debug_misc("oscar", "clientautoresp: setting status message to \"%s\".\n", status_note);
+
+			presence = purple_buddy_get_presence(buddy);
+			status = purple_presence_get_active_status(presence);
+
+			purple_prpl_got_user_status(account, sn,
+					purple_status_get_id(status),
+					"message", status_note, NULL);
+
+			break;
+		}
+
+		g_free(status_note);
+		g_free(stripped_status_note_text);
+		g_free(status_note_text);
+		g_free(stripped_encoding);
+		g_free(encoding);
+		g_free(response);
+		g_free(info->status_note_title);
+		g_free(info);
+
 		byte_stream_get16(bs); /* Unknown */
 		byte_stream_get16(bs); /* Unknown */
 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 			ret = userfunc(od, conn, frame, channel, sn, reason, cookie);
+
 	} else if (channel == 0x0004) { /* ICQ message */
 		switch (reason) {
 			case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
--- a/libpurple/protocols/oscar/family_icq.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Mon Jan 07 03:40:27 2008 +0000
@@ -435,6 +435,65 @@
 	return 0;
 }
 
+/*
+ * getstatusnote may be a misleading name because the response
+ * contains a lot of different information but currently it's only
+ * used to get that.
+ */
+int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len)
+{
+	FlapConnection *conn;
+	FlapFrame *frame;
+	aim_snacid_t snacid;
+	int bslen;
+
+	purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin);
+
+	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	{
+		purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n");
+		return -EINVAL;
+	}
+
+	bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin);
+
+	frame = flap_frame_new(od, 0x02, 10 + 4 + bslen);
+
+	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&frame->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	byte_stream_put16(&frame->data, 0x0001);
+	byte_stream_put16(&frame->data, bslen);
+
+	byte_stream_putle16(&frame->data, bslen - 2);
+	byte_stream_putle32(&frame->data, atoi(od->sn));
+	byte_stream_putle16(&frame->data, 0x07d0); /* I command thee. */
+	byte_stream_putle16(&frame->data, snacid); /* eh. */
+	byte_stream_putle16(&frame->data, 0x0fa0); /* shrug. */
+	byte_stream_putle16(&frame->data, 58 + strlen(uin));
+
+	byte_stream_put32(&frame->data, 0x05b90002);    /* don't ask */
+	byte_stream_put32(&frame->data, 0x80000000);
+	byte_stream_put32(&frame->data, 0x00000006);
+	byte_stream_put32(&frame->data, 0x00010002);
+	byte_stream_put32(&frame->data, 0x00020000);
+	byte_stream_put32(&frame->data, 0x04e30000);
+	byte_stream_put32(&frame->data, 0x00020002);
+	byte_stream_put32(&frame->data, 0x00000001);
+
+	byte_stream_put16(&frame->data, 24 + strlen(uin));
+	byte_stream_put32(&frame->data, 0x003c0010);
+	byte_stream_putraw(&frame->data, note_hash, 16); /* status note hash */
+	byte_stream_put16(&frame->data, 0x0032);        /* buddy uin */
+	byte_stream_put16(&frame->data, strlen(uin));
+	byte_stream_putstr(&frame->data, uin);
+
+	flap_connection_send(conn, frame);
+
+	return 0;
+}
+
 static void aim_icq_freeinfo(struct aim_icq_info *info) {
 	int i;
 
@@ -467,6 +526,7 @@
 	g_free(info->workposition);
 	g_free(info->workwebpage);
 	g_free(info->info);
+	g_free(info->status_note_title);
 	g_free(info);
 }
 
@@ -641,6 +701,178 @@
 			info->email = byte_stream_getstr(&qbs, byte_stream_getle16(&qbs));
 			/* Then 0x00 02 00 00 00 00 00 */
 		} break;
+
+		/* status note title and send request for status note text */
+		case 0x0fb4: {
+			GSList *tlvlist;
+			aim_tlv_t *tlv;
+			FlapConnection *conn;
+			char *uin = NULL;
+			char *status_note_title = NULL;
+
+			conn = flap_connection_findbygroup(od, 0x0004);
+			if (conn == NULL)
+			{
+				purple_debug_misc("oscar", "icq/0x0fb4: flap connection was not found.\n");
+				break;
+			}
+
+			byte_stream_advance(&qbs, 0x02); /* length */
+			byte_stream_advance(&qbs, 0x2f); /* unknown stuff */
+
+			tlvlist = aim_tlvlist_read(&qbs);
+
+			tlv = aim_tlv_gettlv(tlvlist, 0x0032, 1);
+			if (tlv != NULL)
+				/* Get user number */
+				uin = aim_tlv_getvalue_as_string(tlv);
+
+			tlv = aim_tlv_gettlv(tlvlist, 0x0226, 1);
+			if (tlv != NULL)
+				/* Get status note title */
+				status_note_title = aim_tlv_getvalue_as_string(tlv);
+
+			aim_tlvlist_free(tlvlist);
+
+			if (uin == NULL || status_note_title == NULL)
+			{
+				purple_debug_misc("oscar", "icq/0x0fb4: uin or "
+						"status_note_title was not found\n");
+				g_free(uin);
+				g_free(status_note_title);
+				break;
+			}
+
+			if (status_note_title[0] == '\0')
+			{
+				PurpleAccount *account;
+				PurpleBuddy *buddy;
+				PurplePresence *presence;
+				PurpleStatus *status;
+
+				account = purple_connection_get_account(od->gc);
+				buddy = purple_find_buddy(account, uin);
+				presence = purple_buddy_get_presence(buddy);
+				status = purple_presence_get_active_status(presence);
+
+				purple_prpl_got_user_status(account, uin,
+						purple_status_get_id(status),
+						"message", NULL, NULL);
+
+				g_free(status_note_title);
+			}
+			else
+			{
+				struct aim_icq_info *info;
+				guint32 data_len;
+				FlapFrame *frame;
+				aim_snacid_t snacid;
+				guchar cookie[8];
+
+				info = g_new0(struct aim_icq_info, 1);
+
+				if (info == NULL)
+				{
+					g_free(uin);
+					g_free(status_note_title);
+
+					break;
+				}
+
+				data_len = 13 + strlen(uin) + 30 + 6 + 4 + 55 + 85 + 4;
+				frame = flap_frame_new(od, 0x0002, 10 + 4 + data_len);
+				snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+
+				aim_putsnac(&frame->data, 0x0004, 0x0006, 0x0000, snacid);
+
+				aim_icbm_makecookie(cookie);
+
+				byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */
+				byte_stream_put16(&frame->data, 0x0002); /* message channel */
+				byte_stream_put8(&frame->data, strlen(uin)); /* uin */
+				byte_stream_putstr(&frame->data, uin);
+
+				byte_stream_put16(&frame->data, 0x0005); /* rendez vous data */
+				byte_stream_put16(&frame->data, 0x00b2);
+				byte_stream_put16(&frame->data, 0x0000); /* request */
+				byte_stream_putraw(&frame->data, cookie, 8); /* ICBM cookie */
+				byte_stream_put32(&frame->data, 0x09461349); /* ICQ server relaying */
+				byte_stream_put16(&frame->data, 0x4c7f);
+				byte_stream_put16(&frame->data, 0x11d1);
+				byte_stream_put32(&frame->data, 0x82224445);
+				byte_stream_put32(&frame->data, 0x53540000);
+
+				byte_stream_put16(&frame->data, 0x000a); /* unknown TLV */
+				byte_stream_put16(&frame->data, 0x0002);
+				byte_stream_put16(&frame->data, 0x0001);
+
+				byte_stream_put16(&frame->data, 0x000f); /* unknown TLV */
+				byte_stream_put16(&frame->data, 0x0000);
+
+				byte_stream_put16(&frame->data, 0x2711); /* extended data */
+				byte_stream_put16(&frame->data, 0x008a);
+				byte_stream_putle16(&frame->data, 0x001b); /* length */
+				byte_stream_putle16(&frame->data, 0x0009); /* version */
+				byte_stream_putle32(&frame->data, 0x00000000); /* plugin: none */
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle16(&frame->data, 0x0000); /* unknown */
+				byte_stream_putle32(&frame->data, 0x00000000); /* client capabilities flags */
+				byte_stream_put8(&frame->data, 0x00); /* unknown */
+				byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */
+				byte_stream_putle16(&frame->data, 0x000e); /* length */
+				byte_stream_putle16(&frame->data, 0x0064); /* downcounter? */
+				byte_stream_putle32(&frame->data, 0x00000000); /* unknown */
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_putle32(&frame->data, 0x00000000);
+				byte_stream_put8(&frame->data, 0x1a); /* message type: plugin message descibed by text string */
+				byte_stream_put8(&frame->data, 0x00); /* message flags */
+				byte_stream_putle16(&frame->data, 0x0000); /* status code */
+				byte_stream_putle16(&frame->data, 0x0001); /* priority code */
+				byte_stream_putle16(&frame->data, 0x0000); /* text length */
+
+				byte_stream_put8(&frame->data, 0x3a); /* message dump */
+				byte_stream_put32(&frame->data, 0x00811a18);
+				byte_stream_put32(&frame->data, 0xbc0e6c18);
+				byte_stream_put32(&frame->data, 0x47a5916f);
+				byte_stream_put32(&frame->data, 0x18dcc76f);
+				byte_stream_put32(&frame->data, 0x1a010013);
+				byte_stream_put32(&frame->data, 0x00000041);
+				byte_stream_put32(&frame->data, 0x77617920);
+				byte_stream_put32(&frame->data, 0x53746174);
+				byte_stream_put32(&frame->data, 0x7573204d);
+				byte_stream_put32(&frame->data, 0x65737361);
+				byte_stream_put32(&frame->data, 0x67650100);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x00000015);
+				byte_stream_put32(&frame->data, 0x00000000);
+				byte_stream_put32(&frame->data, 0x0000000d);
+				byte_stream_put32(&frame->data, 0x00000074);
+				byte_stream_put32(&frame->data, 0x6578742f);
+				byte_stream_put32(&frame->data, 0x782d616f);
+				byte_stream_put32(&frame->data, 0x6c727466);
+
+				byte_stream_put16(&frame->data, 0x0003); /* server ACK requested */
+				byte_stream_put16(&frame->data, 0x0000);
+
+				info->uin = atoi(uin);
+				info->status_note_title = status_note_title;
+
+				memcpy(&info->icbm_cookie, cookie, 8);
+
+				info->next = od->icq_info;
+				od->icq_info = info;
+
+				flap_connection_send(conn, frame);
+			}
+
+			g_free(uin);
+
+		} break;
+
 		} /* End switch statement */
 
 		if (!(snac->flags & 0x0001)) {
--- a/libpurple/protocols/oscar/family_locate.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Mon Jan 07 03:40:27 2008 +0000
@@ -320,10 +320,10 @@
 		cur->away_encoding = g_strdup(userinfo->away_encoding);
 		cur->away_len = userinfo->away_len;
 
-	} else if (!(userinfo->flags & AIM_FLAG_AWAY)) {
+	} else {
 		/*
-		 * We don't have an away message specified in this user_info block.
-		 * If the user is not away, clear any cached away message now.
+		 * We don't have an away message specified in this user_info
+		 * block, so clear any cached away message now.
 		 */
 		if (cur->away) {
 			g_free(cur->away);
@@ -347,41 +347,6 @@
 		userfunc(od, conn, NULL, cur);
 }
 
-void
-aim_locate_dorequest(OscarData *od)
-{
-	struct userinfo_node *cur = od->locate.torequest;
-
-	if (od->locate.waiting_for_response == TRUE)
-		return;
-
-	od->locate.waiting_for_response = TRUE;
-	aim_locate_getinfoshort(od, cur->sn, 0x00000003);
-
-	/* Move this node to the "requested" queue */
-	od->locate.torequest = cur->next;
-	cur->next = od->locate.requested;
-	od->locate.requested = cur;
-}
-
-static gboolean
-purple_reqinfo_timeout_cb(void *data)
-{
-	OscarData *od;
-
-	od = data;
-
-	if (od->locate.torequest == NULL)
-	{
-		od->getinfotimer = 0;
-		return FALSE;
-	}
-
-	aim_locate_dorequest(od);
-
-	return TRUE;
-}
-
 /**
  * Remove this screen name from our queue.  If this info was requested
  * by our info request queue, then pop the next element off of the queue.
@@ -417,19 +382,6 @@
 			cur = cur->next;
 	}
 
-	if (!was_explicit) {
-		od->locate.waiting_for_response = FALSE;
-
-		/*
-		 * Wait a little while then call aim_locate_dorequest(od).
-		 * This keeps us from hitting the rate limit due to
-		 * requesting away messages and info too quickly.
-		 */
-		if (od->getinfotimer == 0)
-			od->getinfotimer = purple_timeout_add(500,
-					purple_reqinfo_timeout_cb, od);
-	}
-
 	return was_explicit;
 }
 
@@ -438,22 +390,18 @@
 {
 	struct userinfo_node *cur;
 
-	/* Make sure we aren't already requesting info for this buddy */
-	cur = od->locate.torequest;
-	while (cur != NULL) {
+	/* Make sure we haven't already requested info for this buddy */
+	for (cur = od->locate.requested; cur != NULL; cur = cur->next)
 		if (aim_sncmp(sn, cur->sn) == 0)
 			return;
-		cur = cur->next;
-	}
 
 	/* Add a new node to our request queue */
 	cur = (struct userinfo_node *)g_malloc(sizeof(struct userinfo_node));
 	cur->sn = g_strdup(sn);
-	cur->next = od->locate.torequest;
-	od->locate.torequest = cur;
+	cur->next = od->locate.requested;
+	od->locate.requested = cur;
 
-	/* Actually request some info up in this piece */
-	aim_locate_dorequest(od);
+	aim_locate_getinfoshort(od, cur->sn, 0x00000003);
 }
 
 aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *sn) {
--- a/libpurple/protocols/oscar/oscar.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Jan 07 03:40:27 2008 +0000
@@ -1743,8 +1743,7 @@
 	int type = 0;
 	gboolean buddy_is_away = FALSE;
 	const char *status_id;
-	gboolean have_status_message = FALSE;
-	char *message = NULL;
+	char *itmsurl = NULL;
 	va_list ap;
 	aim_userinfo_t *info;
 
@@ -1792,20 +1791,10 @@
 			status_id = OSCAR_STATUS_ID_AVAILABLE;
 	}
 
-	/*
-	 * Handle the available message.  If info->status is NULL then the user
-	 * may or may not have an available message, so don't do anything.  If
-	 * info->status is set to the empty string, then the user's client DOES
-	 * support available messages and the user DOES NOT have one set.
-	 * Otherwise info->status contains the available message.
-	 */
-	if (info->status != NULL)
-	{
-		have_status_message = TRUE;
-		if (info->status[0] != '\0')
-			message = oscar_encoding_to_utf8(account, info->status_encoding,
-											 info->status, info->status_len);
-	}
+	if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len)
+		/* Grab the iTunes Music Store URL */
+		itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
+				info->itmsurl, info->itmsurl_len);
 
 	if (info->flags & AIM_FLAG_WIRELESS)
 	{
@@ -1814,38 +1803,27 @@
 		purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE);
 	}
 
-	if (have_status_message)
+	if (status_id == OSCAR_STATUS_ID_AVAILABLE)
 	{
-		if ((!strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE)) && (info->itmsurl != NULL))
-		{
-			char *itmsurl;
-			itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
-					info->itmsurl, info->itmsurl_len);
-			purple_prpl_got_user_status(account, info->sn, status_id,
-					"message", message, "itmsurl", itmsurl, NULL);
-			g_free(itmsurl);
-		}
-		else
-		{
-			purple_prpl_got_user_status(account, info->sn, status_id,
-					"message", message, NULL);
-		}
+		char *message = NULL;
+
+		if (info->status != NULL && info->status[0] != '\0')
+			/* Grab the available message */
+			message = oscar_encoding_to_utf8(account, info->status_encoding,
+					info->status, info->status_len);
+
+		purple_prpl_got_user_status(account, info->sn, status_id,
+				"message", message, "itmsurl", itmsurl, NULL);
+
 		g_free(message);
 	}
 	else
 	{
-		PurpleBuddy *b = purple_find_buddy(account, info->sn);
-		PurpleStatus *status = NULL;
-		const char *active_status_id = NULL;
-
-		if (b != NULL) {
-			status = purple_presence_get_active_status(purple_buddy_get_presence(b));
-			active_status_id = purple_status_get_id(status);
-		}
-
-		if (!active_status_id || strcmp(active_status_id, status_id))
-			purple_prpl_got_user_status(account, info->sn, status_id, NULL);
-	}
+		purple_prpl_got_user_status(account, info->sn, status_id,
+				"itmsurl", itmsurl, NULL);
+	}
+
+	g_free(itmsurl);
 
 	/* Login time stuff */
 	if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
@@ -1900,6 +1878,31 @@
 		g_free(b16);
 	}
 
+	/*
+	 * If we didn't receive a status message with the status change,
+	 * or if the message is empty, and we have a note hash, then
+	 * query the ICQ6 status note.
+	 *
+	 * TODO: We should probably always query the status note regardless
+	 *       of whether they have a status message set, and we should
+	 *       figure out a way to display both the status note and the
+	 *       status message at the same time.
+	 */
+	if (info->status == NULL || info->status[0] == '\0')
+	{
+		struct aim_ssi_item *ssi_item;
+		aim_tlv_t *note_hash;
+
+		ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
+				NULL, info->sn, AIM_SSI_TYPE_BUDDY);
+		if (ssi_item != NULL)
+		{
+			note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1);
+			if (note_hash != NULL)
+				aim_icq_getstatusnote(od, info->sn, note_hash->value, note_hash->length);
+		}
+	}
+
 	return 1;
 }
 
@@ -3026,7 +3029,7 @@
 
 	if (!aim_snvalid_icq(userinfo->sn))
 	{
-		if (strcmp(purple_buddy_get_name(b), userinfo->sn))
+		if (strcmp(purple_buddy_get_name(b), userinfo->sn) != 0)
 			serv_got_alias(gc, purple_buddy_get_name(b), userinfo->sn);
 		else
 			serv_got_alias(gc, purple_buddy_get_name(b), NULL);
@@ -3035,23 +3038,19 @@
 	presence = purple_buddy_get_presence(b);
 	status = purple_presence_get_active_status(presence);
 
-	if (!purple_status_is_available(status) && purple_status_is_online(status))
+	if (purple_status_is_online(status) && !purple_status_is_available(status) &&
+			userinfo->flags & AIM_FLAG_AWAY && userinfo->away_len > 0 &&
+			userinfo->away != NULL && userinfo->away_encoding != NULL)
 	{
-		if ((userinfo->flags & AIM_FLAG_AWAY) &&
-			(userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
-			gchar *charset = oscar_encoding_extract(userinfo->away_encoding);
-			message = oscar_encoding_to_utf8(account, charset,
-			                                 userinfo->away,
-			                                 userinfo->away_len);
-			g_free(charset);
-			purple_status_set_attr_string(status, "message", message);
-			g_free(message);
-		}
-		else
-			/* Set an empty message so that we know not to show "pending" */
-			purple_status_set_attr_string(status, "message", "");
-
-		purple_blist_update_buddy_status(b, status);
+		gchar *charset = oscar_encoding_extract(userinfo->away_encoding);
+		message = oscar_encoding_to_utf8(account, charset,
+		                                 userinfo->away,
+		                                 userinfo->away_len);
+		g_free(charset);
+		purple_prpl_got_user_status(account, userinfo->sn,
+				purple_status_get_id(status),
+				"message", message, NULL);
+		g_free(message);
 	}
 
 	return 1;
@@ -4547,12 +4546,11 @@
 		/* This is needed for us to un-set any previous away message. */
 		away = g_strdup("");
 	}
-	else if ((primitive == PURPLE_STATUS_AWAY) ||
-			 (primitive == PURPLE_STATUS_EXTENDED_AWAY))
+	else
 	{
 		htmlaway = purple_status_get_attr_string(status, "message");
 		if ((htmlaway == NULL) || (*htmlaway == '\0'))
-			htmlaway = _("Away");
+			htmlaway = purple_status_type_get_name(status_type);
 		away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding);
 
 		if (awaylen > od->rights.maxawaymsglen)
@@ -5126,6 +5124,8 @@
 	char *gname, *gname_utf8, *alias, *alias_utf8;
 	PurpleBuddy *b;
 	PurpleGroup *g;
+	struct aim_ssi_item *ssi_item;
+	aim_tlv_t *note_hash;
 	va_list ap;
 	guint16 snac_subtype, type;
 	const char *name;
@@ -5193,6 +5193,21 @@
 
 	}
 
+	ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
+			gname, name, AIM_SSI_TYPE_BUDDY);
+	if (ssi_item != NULL)
+	{
+		note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1);
+		if (note_hash != NULL)
+			aim_icq_getstatusnote(od, name, note_hash->value, note_hash->length);
+	}
+	else
+	{
+		purple_debug_error("oscar", "purple_ssi_parseaddmod: "
+				"Could not find ssi item for oncoming buddy %s, "
+				"group %s\n", name, gname);
+	}
+
 	g_free(gname_utf8);
 	g_free(alias_utf8);
 
--- a/libpurple/protocols/oscar/oscar.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Mon Jan 07 03:40:27 2008 +0000
@@ -3,8 +3,6 @@
  * 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
@@ -311,7 +309,7 @@
 }
 
 #define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036
-#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQ_5_45_3777
+#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3000
 
 typedef enum
 {
@@ -468,7 +466,6 @@
 
 	gboolean icq;
 	guint getblisttimer;
-	guint getinfotimer;
 
 	struct {
 		guint maxwatchers; /* max users who can watch you */
@@ -513,9 +510,7 @@
 
 	struct {
 		struct aim_userinfo_s *userinfo;
-		struct userinfo_node *torequest;
 		struct userinfo_node *requested;
-		gboolean waiting_for_response;
 	} locate;
 
 	/* Server-stored information (ssi) */
@@ -1329,6 +1324,10 @@
 
 	/* we keep track of these in a linked list because we're 1337 */
 	struct aim_icq_info *next;
+
+	/* status note info */
+	guint8 icbm_cookie[8];
+	char *status_note_title;
 };
 
 int aim_icq_reqofflinemsgs(OscarData *od);
@@ -1339,7 +1338,7 @@
 int aim_icq_getalias(OscarData *od, const char *uin);
 int aim_icq_getallinfo(OscarData *od, const char *uin);
 int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias);
-
+int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len);
 
 
 /* 0x0017 - family_auth.c */
--- a/libpurple/protocols/oscar/oscar_data.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/oscar/oscar_data.c	Mon Jan 07 03:40:27 2008 +0000
@@ -97,8 +97,6 @@
 	g_free(od->oldp);
 	if (od->getblisttimer > 0)
 		purple_timeout_remove(od->getblisttimer);
-	if (od->getinfotimer > 0)
-		purple_timeout_remove(od->getinfotimer);
 	while (od->oscar_connections != NULL)
 		flap_connection_destroy(od->oscar_connections->data,
 				OSCAR_DISCONNECT_DONE, NULL);
--- a/libpurple/protocols/qq/file_trans.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/qq/file_trans.c	Mon Jan 07 03:40:27 2008 +0000
@@ -59,7 +59,7 @@
 	return key;
 }
 		
-static guint32 _gen_file_key()
+static guint32 _gen_file_key(void)
 {
 	guint8 seed;
 	
--- a/libpurple/protocols/qq/group_opt.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Mon Jan 07 03:40:27 2008 +0000
@@ -39,37 +39,12 @@
 #include "packet_parse.h"
 #include "utils.h"
 
-/* TODO: can't we use qsort here? */
-/* This implement quick sort algorithm (low->high) */
-static void _quick_sort(gint *numbers, gint left, gint right)
+static int _compare_guint32(const void *a,
+                            const void *b)
 {
-	gint pivot, l_hold, r_hold;
-
-	l_hold = left;
-	r_hold = right;
-	pivot = numbers[left];
-	while (left < right) {
-		while ((numbers[right] >= pivot) && (left < right))
-			right--;
-		if (left != right) {
-			numbers[left] = numbers[right];
-			left++;
-		}
-		while ((numbers[left] <= pivot) && (left < right))
-			left++;
-		if (left != right) {
-			numbers[right] = numbers[left];
-			right--;
-		}
-	}
-	numbers[left] = pivot;
-	pivot = left;
-	left = l_hold;
-	right = r_hold;
-	if (left < pivot)
-		_quick_sort(numbers, left, pivot - 1);
-	if (right > pivot)
-		_quick_sort(numbers, pivot + 1, right);
+	const guint32 *x = a;
+	const guint32 *y = b;
+	return (*x - *y);
 }
 
 static void _sort(guint32 *list)
@@ -77,7 +52,7 @@
 	gint i;
 	for (i = 0; list[i] < 0xffffffff; i++) {;
 	}
-	_quick_sort((gint *) list, 0, i - 1);
+	qsort (list, i, sizeof (guint32), _compare_guint32);
 }
 
 static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members)
--- a/libpurple/protocols/sametime/sametime.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Mon Jan 07 03:40:27 2008 +0000
@@ -3749,7 +3749,7 @@
 
     client = purple_account_get_int(account, MW_KEY_CLIENT, mwLogin_BINARY);
     major = purple_account_get_int(account, MW_KEY_MAJOR, 0x001e);
-    minor = purple_account_get_int(account, MW_KEY_MINOR, 0x001d);
+    minor = purple_account_get_int(account, MW_KEY_MINOR, 0x196f);
 
     DEBUG_INFO("client id: 0x%04x\n", client);
     DEBUG_INFO("client major: 0x%04x\n", major);
@@ -3806,7 +3806,7 @@
 }
 
 
-static int mw_rand() {
+static int mw_rand(void) {
   static int seed = 0;
 
   /* for diversity, not security. don't touch */
@@ -3818,7 +3818,7 @@
 
 
 /** generates a random-ish content id string */
-static char *im_mime_content_id() {
+static char *im_mime_content_id(void) {
   return g_strdup_printf("%03x@%05xmeanwhile",
 			 mw_rand() & 0xfff, mw_rand() & 0xfffff);
 }
@@ -3826,7 +3826,7 @@
 
 /** generates a multipart/related content type with a random-ish
     boundary value */
-static char *im_mime_content_type() {
+static char *im_mime_content_type(void) {
   return g_strdup_printf("multipart/related; boundary=related_MW%03x_%04x",
                          mw_rand() & 0xfff, mw_rand() & 0xffff);
 }
--- a/libpurple/protocols/simple/simple.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/simple/simple.c	Mon Jan 07 03:40:27 2008 +0000
@@ -45,17 +45,17 @@
 #include "dnssrv.h"
 #include "ntlm.h"
 
-static char *gentag() {
+static char *gentag(void) {
 	return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
 }
 
-static char *genbranch() {
+static char *genbranch(void) {
 	return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
 		rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
 		rand() & 0xFFFF, rand() & 0xFFFF);
 }
 
-static char *gencallid() {
+static char *gencallid(void) {
 	return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
 		rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
 		rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
--- a/libpurple/protocols/yahoo/util.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/yahoo/util.c	Mon Jan 07 03:40:27 2008 +0000
@@ -736,12 +736,15 @@
 			} else if (((len - i) >= 4) && !strncmp(&src[i], "&gt;", 4)) {
 				g_string_append_c(dest, '>');
 				i += 3;
-			} else if (((len - i) >= 5) && !strncmp(&src[i], "&amp;", 4)) {
+			} else if (((len - i) >= 5) && !strncmp(&src[i], "&amp;", 5)) {
 				g_string_append_c(dest, '&');
 				i += 4;
-			} else if (((len - i) >= 6) && !strncmp(&src[i], "&quot;", 4)) {
+			} else if (((len - i) >= 6) && !strncmp(&src[i], "&quot;", 6)) {
 				g_string_append_c(dest, '"');
 				i += 5;
+			} else if (((len - i) >= 6) && !strncmp(&src[i], "&apos;", 6)) {
+				g_string_append_c(dest, '\'');
+				i += 5;
 			} else {
 				g_string_append_c(dest, src[i]);
 			}
--- a/libpurple/protocols/zephyr/ZSendList.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/zephyr/ZSendList.c	Mon Jan 07 03:40:27 2008 +0000
@@ -24,7 +24,7 @@
     char *list[];
     int nitems;
     Z_AuthProc cert_routine;
-    Code_t (*send_routine)();
+    Code_t (*send_routine)(void);
 {
     Code_t retval;
     ZNotice_t newnotice;
--- a/libpurple/protocols/zephyr/ZSendNot.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/zephyr/ZSendNot.c	Mon Jan 07 03:40:27 2008 +0000
@@ -20,7 +20,7 @@
 Code_t ZSrvSendNotice(notice, cert_routine, send_routine)
     ZNotice_t *notice;
     Z_AuthProc cert_routine;
-    Code_t (*send_routine)();
+    Code_t (*send_routine)(void);
 {    
     Code_t retval;
     ZNotice_t newnotice;
--- a/libpurple/protocols/zephyr/Zinternal.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/zephyr/Zinternal.c	Mon Jan 07 03:40:27 2008 +0000
@@ -33,8 +33,6 @@
 #include <utmp.h>
 #endif
 
-extern char *inet_ntoa ();
-
 int __Zephyr_fd = -1;
 int __Zephyr_open;
 int __Zephyr_port = -1;
@@ -144,7 +142,7 @@
 
 /* Return 1 if there is a packet waiting, 0 otherwise */
 
-static int Z_PacketWaiting()
+static int Z_PacketWaiting(void)
 {
     struct timeval tv;
     fd_set read;
@@ -158,7 +156,7 @@
 
 /* Wait for a complete notice to become available */
 
-Code_t Z_WaitForComplete()
+Code_t Z_WaitForComplete(void)
 {
     Code_t retval;
 
@@ -195,9 +193,7 @@
  * notices that haven't been touched in a while
  */
 
-static struct _Z_InputQ *Z_SearchQueue(uid, kind)
-    ZUnique_Id_t *uid;
-    ZNotice_Kind_t kind;
+static struct _Z_InputQ *Z_SearchQueue(ZUnique_Id_t *uid, ZNotice_Kind_t kind)
 {
     register struct _Z_InputQ *qptr;
     struct _Z_InputQ *next;
--- a/libpurple/protocols/zephyr/zephyr.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Mon Jan 07 03:40:27 2008 +0000
@@ -53,7 +53,7 @@
 
 extern Code_t ZGetLocations(ZLocations_t *, int *);
 extern Code_t ZSetLocation(char *);
-extern Code_t ZUnsetLocation();
+extern Code_t ZUnsetLocation(void);
 extern Code_t ZGetSubscriptions(ZSubscription_t *, int*);
 extern char __Zephyr_realm[];
 typedef struct _zframe zframe;
@@ -1386,7 +1386,7 @@
 
 #endif /* WIN32 */
 
-static char *get_exposure_level()
+static char *get_exposure_level(void)
 {
 	/* XXX add real error reporting */
 	char *exposure = ZGetVariable("exposure");
@@ -2058,7 +2058,7 @@
 static int zephyr_send_message(zephyr_account *zephyr,char* zclass, char* instance, char* recipient, const char *im, 
 			       const char *sig, char *opcode) ;
 
-static const char * zephyr_get_signature()
+static const char * zephyr_get_signature(void)
 {
 	/* XXX add zephyr error reporting */
 	const char * sig =ZGetVariable("zwrite-signature");
@@ -2676,7 +2676,7 @@
 		return PURPLE_CMD_RET_FAILED;
 }
 
-static void zephyr_register_slash_commands()
+static void zephyr_register_slash_commands(void)
 {
 
 	purple_cmd_register("msg","ws", PURPLE_CMD_P_PRPL,
--- a/libpurple/protocols/zephyr/zephyr.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/protocols/zephyr/zephyr.h	Mon Jan 07 03:40:27 2008 +0000
@@ -163,9 +163,9 @@
 Code_t ZReadAscii16 ZP((char *, int, unsigned short *));
 Code_t ZSendPacket ZP((char*, int, int));
 Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc));
-Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)()));
+Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)(void)));
 Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc));
-Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)()));
+Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)(void)));
 Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc));
 Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc));
 Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems,
--- a/libpurple/purple-remote	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/purple-remote	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 
 import dbus
 import re
--- a/libpurple/purple-send	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/purple-send	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 METHOD_NAME=$1
 
--- a/libpurple/purple-send-async	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/purple-send-async	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 METHOD_NAME=$1
 
--- a/libpurple/purple-url-handler	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/purple-url-handler	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 
 import dbus
 import re
--- a/libpurple/request.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/request.c	Mon Jan 07 03:40:27 2008 +0000
@@ -1208,6 +1208,7 @@
 
 	g_return_val_if_fail(ok_text != NULL,  NULL);
 	g_return_val_if_fail(ok_cb   != NULL,  NULL);
+	g_return_val_if_fail(cancel_text != NULL,  NULL);
 
 	ops = purple_request_get_ui_ops();
 
@@ -1296,6 +1297,7 @@
 	g_return_val_if_fail(fields  != NULL, NULL);
 	g_return_val_if_fail(ok_text != NULL, NULL);
 	g_return_val_if_fail(ok_cb   != NULL, NULL);
+	g_return_val_if_fail(cancel_text != NULL, NULL);
 
 	ops = purple_request_get_ui_ops();
 
--- a/libpurple/request.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/request.h	Mon Jan 07 03:40:27 2008 +0000
@@ -183,39 +183,53 @@
  */
 typedef struct
 {
+	/** @see purple_request_input(). */
 	void *(*request_input)(const char *title, const char *primary,
-						   const char *secondary, const char *default_value,
-						   gboolean multiline, gboolean masked, gchar *hint,
-						   const char *ok_text, GCallback ok_cb,
-						   const char *cancel_text, GCallback cancel_cb,
-						   PurpleAccount *account, const char *who, PurpleConversation *conv,
-						   void *user_data);
+	                       const char *secondary, const char *default_value,
+	                       gboolean multiline, gboolean masked, gchar *hint,
+	                       const char *ok_text, GCallback ok_cb,
+	                       const char *cancel_text, GCallback cancel_cb,
+	                       PurpleAccount *account, const char *who,
+	                       PurpleConversation *conv, void *user_data);
+
+	/** @see purple_request_choice_varg(). */
 	void *(*request_choice)(const char *title, const char *primary,
-							const char *secondary, int default_value,
-							const char *ok_text, GCallback ok_cb,
-							const char *cancel_text, GCallback cancel_cb,
-							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data, va_list choices);
+	                        const char *secondary, int default_value,
+	                        const char *ok_text, GCallback ok_cb,
+	                        const char *cancel_text, GCallback cancel_cb,
+	                        PurpleAccount *account, const char *who,
+	                        PurpleConversation *conv, void *user_data,
+	                        va_list choices);
+
+	/** @see purple_request_action_varg(). */
 	void *(*request_action)(const char *title, const char *primary,
-							const char *secondary, int default_action,
-							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data, size_t action_count,
-							va_list actions);
+	                        const char *secondary, int default_action,
+	                        PurpleAccount *account, const char *who,
+	                        PurpleConversation *conv, void *user_data,
+	                        size_t action_count, va_list actions);
+
+	/** @see purple_request_fields(). */
 	void *(*request_fields)(const char *title, const char *primary,
-							const char *secondary, PurpleRequestFields *fields,
-							const char *ok_text, GCallback ok_cb,
-							const char *cancel_text, GCallback cancel_cb,
-							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data);
+	                        const char *secondary, PurpleRequestFields *fields,
+	                        const char *ok_text, GCallback ok_cb,
+	                        const char *cancel_text, GCallback cancel_cb,
+	                        PurpleAccount *account, const char *who,
+	                        PurpleConversation *conv, void *user_data);
+
+	/** @see purple_request_file(). */
 	void *(*request_file)(const char *title, const char *filename,
-						  gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
-						  PurpleAccount *account, const char *who, PurpleConversation *conv,
-						  void *user_data);
+	                      gboolean savedialog, GCallback ok_cb,
+	                      GCallback cancel_cb, PurpleAccount *account,
+	                      const char *who, PurpleConversation *conv,
+	                      void *user_data);
+
 	void (*close_request)(PurpleRequestType type, void *ui_handle);
+
+	/** @see purple_request_folder(). */
 	void *(*request_folder)(const char *title, const char *dirname,
-							GCallback ok_cb, GCallback cancel_cb,
-							PurpleAccount *account, const char *who, PurpleConversation *conv,
-							void *user_data);
+	                        GCallback ok_cb, GCallback cancel_cb,
+	                        PurpleAccount *account, const char *who,
+	                        PurpleConversation *conv, void *user_data);
 
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
@@ -1159,198 +1173,247 @@
  * Prompts the user for text input.
  *
  * @param handle        The plugin or connection handle.  For some
- *                      things this is EXTREMELY important.  The
- *                      handle is used to programmatically close
- *                      the request dialog when it is no longer
- *                      needed.  For PRPLs this is often a pointer
- *                      to the PurpleConnection instance.  For plugins
- *                      this should be a similar, unique memory
- *                      location.  This value is important because
- *                      it allows a request to be closed, say, when
- *                      you sign offline.  If the request is NOT
- *                      closed it is VERY likely to cause a crash
- *                      whenever the callback handler functions are
- *                      triggered.
- * @param title         The title of the message.
- * @param primary       The main point of the message.
- * @param secondary     The secondary information.
+ *                      things this is <em>extremely</em> important.  The
+ *                      handle is used to programmatically close the request
+ *                      dialog when it is no longer needed.  For PRPLs this
+ *                      is often a pointer to the #PurpleConnection
+ *                      instance.  For plugins this should be a similar,
+ *                      unique memory location.  This value is important
+ *                      because it allows a request to be closed with
+ *                      purple_request_close_with_handle() when, for
+ *                      example, you sign offline.  If the request is
+ *                      <em>not</em> closed it is <strong>very</strong>
+ *                      likely to cause a crash whenever the callback
+ *                      handler functions are triggered.
+ * @param title         The title of the message, or @c NULL if it should have
+ *                      no title.
+ * @param primary       The main point of the message, or @c NULL if you're
+ *                      feeling enigmatic.
+ * @param secondary     Secondary information, or @c NULL if there is none.
  * @param default_value The default value.
- * @param multiline     TRUE if the inputted text can span multiple lines.
- * @param masked        TRUE if the inputted text should be masked in some way.
+ * @param multiline     @c TRUE if the inputted text can span multiple lines.
+ * @param masked        @c TRUE if the inputted text should be masked in some
+ *                      way (such as by displaying characters as stars).  This
+ *                      might be because the input is some kind of password.
  * @param hint          Optionally suggest how the input box should appear.
- *                      Use "html," for example, to allow the user to enter
+ *                      Use "html", for example, to allow the user to enter
  *                      HTML.
- * @param ok_text       The text for the @c OK button.
- * @param ok_cb         The callback for the @c OK button.
- * @param cancel_text   The text for the @c Cancel button.
- * @param cancel_cb     The callback for the @c Cancel button.
- * @param account		The PurpleAccount associated with this request, or NULL if none is
- * @param who			The username of the buddy assocaited with this request, or NULL if none is
- * @param conv			The PurpleConversation associated with this request, or NULL if none is
+ * @param ok_text       The text for the @c OK button, which may not be @c NULL.
+ * @param ok_cb         The callback for the @c OK button, which may not be @c
+ *                      NULL.
+ * @param cancel_text   The text for the @c Cancel button, which may not be @c
+ *                      NULL.
+ * @param cancel_cb     The callback for the @c Cancel button, which may be
+ *                      @c NULL.
+ * @param account       The #PurpleAccount associated with this request, or @c
+ *                      NULL if none is.
+ * @param who           The username of the buddy associated with this request,
+ *                      or @c NULL if none is.
+ * @param conv          The #PurpleConversation associated with this request, or
+ *                      @c NULL if none is.
  * @param user_data     The data to pass to the callback.
  *
  * @return A UI-specific handle.
  */
-void *purple_request_input(void *handle, const char *title,
-						 const char *primary, const char *secondary,
-						 const char *default_value,
-						 gboolean multiline, gboolean masked, gchar *hint,
-						 const char *ok_text, GCallback ok_cb,
-						 const char *cancel_text, GCallback cancel_cb,
-						 PurpleAccount *account, const char *who, PurpleConversation *conv,
-						 void *user_data);
+void *purple_request_input(void *handle, const char *title, const char *primary,
+	const char *secondary, const char *default_value, gboolean multiline,
+	gboolean masked, gchar *hint,
+	const char *ok_text, GCallback ok_cb,
+	const char *cancel_text, GCallback cancel_cb,
+	PurpleAccount *account, const char *who, PurpleConversation *conv,
+	void *user_data);
 
 /**
  * Prompts the user for multiple-choice input.
  *
- * @param handle        The plugin or connection handle.  For some
- *                      things this is EXTREMELY important.  See
- *                      the comments on purple_request_input.
- * @param title         The title of the message.
- * @param primary       The main point of the message.
- * @param secondary     The secondary information.
- * @param default_value The default value.
- * @param ok_text       The text for the @c OK button.
- * @param ok_cb         The callback for the @c OK button.
- * @param cancel_text   The text for the @c Cancel button.
- * @param cancel_cb     The callback for the @c Cancel button.
- * @param account		The PurpleAccount associated with this request, or NULL if none is
- * @param who			The username of the buddy assocaited with this request, or NULL if none is
- * @param conv			The PurpleConversation associated with this request, or NULL if none is
+ * @param handle        The plugin or connection handle.  For some things this
+ *                      is <em>extremely</em> important.  See the comments on
+ *                      purple_request_input().
+ * @param title         The title of the message, or @c NULL if it should have
+ *                      no title.
+ * @param primary       The main point of the message, or @c NULL if you're
+ *                      feeling enigmatic.
+ * @param secondary     Secondary information, or @c NULL if there is none.
+ * @param default_value The default choice; this should be one of the values
+ *                      listed in the varargs.
+ * @param ok_text       The text for the @c OK button, which may not be @c NULL.
+ * @param ok_cb         The callback for the @c OK button, which may not be @c
+ *                      NULL.
+ * @param cancel_text   The text for the @c Cancel button, which may not be @c
+ *                      NULL.
+ * @param cancel_cb     The callback for the @c Cancel button, or @c NULL to
+ *                      do nothing.
+ * @param account       The #PurpleAccount associated with this request, or @c
+ *                      NULL if none is.
+ * @param who           The username of the buddy associated with this request,
+ *                      or @c NULL if none is.
+ * @param conv          The #PurpleConversation associated with this request, or
+ *                      @c NULL if none is.
  * @param user_data     The data to pass to the callback.
- * @param ...           The choices.  This argument list should be
- *                      terminated with a NULL parameter.
+ * @param ...           The choices, which should be pairs of <tt>char *</tt>
+ *                      descriptions and <tt>int</tt> values, terminated with a
+ *                      @c NULL parameter.
  *
  * @return A UI-specific handle.
  */
-void *purple_request_choice(void *handle, const char *title,
-						  const char *primary, const char *secondary,
-						  int default_value,
-						  const char *ok_text, GCallback ok_cb,
-						  const char *cancel_text, GCallback cancel_cb,
-						  PurpleAccount *account, const char *who, PurpleConversation *conv,
-						  void *user_data, ...) G_GNUC_NULL_TERMINATED;
+void *purple_request_choice(void *handle, const char *title, const char *primary,
+	const char *secondary, int default_value,
+	const char *ok_text, GCallback ok_cb,
+	const char *cancel_text, GCallback cancel_cb,
+	PurpleAccount *account, const char *who, PurpleConversation *conv,
+	void *user_data, ...) G_GNUC_NULL_TERMINATED;
 
 /**
  * Prompts the user for multiple-choice input.
  *
- * @param handle        The plugin or connection handle.  For some
- *                      things this is EXTREMELY important.  See
- *                      the comments on purple_request_input.
- * @param title         The title of the message.
- * @param primary       The main point of the message.
- * @param secondary     The secondary information.
- * @param default_value The default value.
- * @param ok_text       The text for the @c OK button.
- * @param ok_cb         The callback for the @c OK button.
- * @param cancel_text   The text for the @c Cancel button.
- * @param cancel_cb     The callback for the @c Cancel button.
- * @param account		The PurpleAccount associated with this request, or NULL if none is
- * @param who			The username of the buddy assocaited with this request, or NULL if none is
- * @param conv			The PurpleConversation associated with this request, or NULL if none is
+ * @param handle        The plugin or connection handle.  For some things this
+ *                      is <em>extremely</em> important.  See the comments on
+ *                      purple_request_input().
+ * @param title         The title of the message, or @c NULL if it should have
+ *                      no title.
+ * @param primary       The main point of the message, or @c NULL if you're
+ *                      feeling enigmatic.
+ * @param secondary     Secondary information, or @c NULL if there is none.
+ * @param default_value The default choice; this should be one of the values
+ *                      listed in the varargs.
+ * @param ok_text       The text for the @c OK button, which may not be @c NULL.
+ * @param ok_cb         The callback for the @c OK button, which may not be @c
+ *                      NULL.
+ * @param cancel_text   The text for the @c Cancel button, which may not be @c
+ *                      NULL.
+ * @param cancel_cb     The callback for the @c Cancel button, or @c NULL to do
+ *                      nothing.
+ * @param account       The #PurpleAccount associated with this request, or @c
+ *                      NULL if none is
+ * @param who           The username of the buddy associated with this request,
+ *                      or @c NULL if none is
+ * @param conv          The #PurpleConversation associated with this request, or
+ *                      @c NULL if none is
  * @param user_data     The data to pass to the callback.
- * @param choices       The choices.  This argument list should be
- *                      terminated with a @c NULL parameter.
+ * @param choices       The choices, which should be pairs of <tt>char *</tt>
+ *                      descriptions and <tt>int</tt> values, terminated with a
+ *                      @c NULL parameter.
  *
  * @return A UI-specific handle.
  */
 void *purple_request_choice_varg(void *handle, const char *title,
-							   const char *primary, const char *secondary,
-							   int default_value,
-							   const char *ok_text, GCallback ok_cb,
-							   const char *cancel_text, GCallback cancel_cb,
-							   PurpleAccount *account, const char *who, PurpleConversation *conv,
-							   void *user_data, va_list choices);
+	const char *primary, const char *secondary, int default_value,
+	const char *ok_text, GCallback ok_cb,
+	const char *cancel_text, GCallback cancel_cb,
+	PurpleAccount *account, const char *who, PurpleConversation *conv,
+	void *user_data, va_list choices);
 
 /**
  * Prompts the user for an action.
  *
  * This is often represented as a dialog with a button for each action.
  *
- * @param handle         The plugin or connection handle.  For some
- *                       things this is EXTREMELY important.  See
- *                       the comments on purple_request_input.
- * @param title          The title of the message.
- * @param primary        The main point of the message.
- * @param secondary      The secondary information.
- * @param default_action The default value.
- * @param account		 The PurpleAccount associated with this request, or NULL if none is
- * @param who			 The username of the buddy assocaited with this request, or NULL if none is
- * @param conv			 The PurpleConversation associated with this request, or NULL if none is
+ * @param handle         The plugin or connection handle.  For some things this
+ *                       is <em>extremely</em> important.  See the comments on
+ *                       purple_request_input().
+ * @param title          The title of the message, or @c NULL if it should have
+ *                       no title.
+ * @param primary        The main point of the message, or @c NULL if you're
+ *                       feeling enigmatic.
+ * @param secondary      Secondary information, or @c NULL if there is none.
+ * @param default_action The default action, zero-indexed; if the third action
+ *                       supplied should be the default, supply <tt>2</tt>.
+ * @param account        The #PurpleAccount associated with this request, or @c
+ *                       NULL if none is.
+ * @param who            The username of the buddy associated with this request,
+ *                       or @c NULL if none is.
+ * @param conv           The #PurpleConversation associated with this request, or
+ *                       @c NULL if none is.
  * @param user_data      The data to pass to the callback.
  * @param action_count   The number of actions.
  * @param ...            A list of actions.  These are pairs of
  *                       arguments.  The first of each pair is the
- *                       string that appears on the button.  It should
+ *                       <tt>char *</tt> that appears on the button.  It should
  *                       have an underscore before the letter you want
  *                       to use as the accelerator key for the button.
- *                       The second of each pair is the callback
+ *                       The second of each pair is the <tt>GCallback</tt>
  *                       function to use when the button is clicked.
  *
  * @return A UI-specific handle.
  */
-void *purple_request_action(void *handle, const char *title,
-						  const char *primary, const char *secondary,
-						  int default_action,
-						  PurpleAccount *account, const char *who, PurpleConversation *conv,
-						  void *user_data, size_t action_count, ...);
+void *purple_request_action(void *handle, const char *title, const char *primary,
+	const char *secondary, int default_action, PurpleAccount *account,
+	const char *who, PurpleConversation *conv, void *user_data,
+	size_t action_count, ...);
 
 /**
  * Prompts the user for an action.
  *
  * This is often represented as a dialog with a button for each action.
  *
- * @param handle         The plugin or connection handle.  For some
- *                       things this is EXTREMELY important.  See
- *                       the comments on purple_request_input.
- * @param title          The title of the message.
- * @param primary        The main point of the message.
- * @param secondary      The secondary information.
- * @param default_action The default value.
- * @param account		 The PurpleAccount associated with this request, or NULL if none is
- * @param who			 The username of the buddy assocaited with this request, or NULL if none is
- * @param conv			 The PurpleConversation associated with this request, or NULL if none is
+ * @param handle         The plugin or connection handle.  For some things this
+ *                       is <em>extremely</em> important.  See the comments on
+ *                       purple_request_input().
+ * @param title          The title of the message, or @c NULL if it should have
+ *                       no title.
+ * @param primary        The main point of the message, or @c NULL if you're
+ *                       feeling enigmatic.
+ * @param secondary      Secondary information, or @c NULL if there is none.
+ * @param default_action The default action, zero-indexed; if the third action
+ *                       supplied should be the default, supply <tt>2</tt>.
+ * @param account        The #PurpleAccount associated with this request, or @c
+ *                       NULL if none is.
+ * @param who            The username of the buddy associated with this request,
+ *                       or @c NULL if none is.
+ * @param conv           The #PurpleConversation associated with this request, or
+ *                       @c NULL if none is.
  * @param user_data      The data to pass to the callback.
  * @param action_count   The number of actions.
- * @param actions        A list of actions and callbacks.
+ * @param actions        A list of actions.  These are pairs of
+ *                       arguments.  The first of each pair is the
+ *                       <tt>char *</tt> that appears on the button.  It should
+ *                       have an underscore before the letter you want
+ *                       to use as the accelerator key for the button.
+ *                       The second of each pair is the <tt>GCallback</tt>
+ *                       function to use when the button is clicked.
  *
  * @return A UI-specific handle.
  */
 void *purple_request_action_varg(void *handle, const char *title,
-							   const char *primary, const char *secondary,
-							   int default_action,
-							   PurpleAccount *account, const char *who, PurpleConversation *conv,
-							   void *user_data, size_t action_count,
-							   va_list actions);
+	const char *primary, const char *secondary, int default_action,
+	PurpleAccount *account, const char *who, PurpleConversation *conv,
+	void *user_data, size_t action_count, va_list actions);
 
 /**
  * Displays groups of fields for the user to fill in.
  *
- * @param handle      The plugin or connection handle.  For some
- *                    things this is EXTREMELY important.  See
- *                    the comments on purple_request_input.
- * @param title       The title of the message.
- * @param primary     The main point of the message.
- * @param secondary   The secondary information.
+ * @param handle      The plugin or connection handle.  For some things this
+ *                    is <em>extremely</em> important.  See the comments on
+ *                    purple_request_input().
+ * @param title       The title of the message, or @c NULL if it should have
+ *                    no title.
+ * @param primary     The main point of the message, or @c NULL if you're
+ *                    feeling enigmatic.
+ * @param secondary   Secondary information, or @c NULL if there is none.
  * @param fields      The list of fields.
- * @param ok_text     The text for the @c OK button.
- * @param ok_cb       The callback for the @c OK button.
- * @param cancel_text The text for the @c Cancel button.
- * @param cancel_cb   The callback for the @c Cancel button.
- * @param account	  The PurpleAccount associated with this request, or NULL if none is
- * @param who		  The username of the buddy associated with this request, or NULL if none is
- * @param conv		  The PurpleConversation associated with this request, or NULL if none is
+ * @param ok_text     The text for the @c OK button, which may not be @c NULL.
+ * @param ok_cb       The callback for the @c OK button, which may not be @c
+ *                    NULL.
+ * @param cancel_text The text for the @c Cancel button, which may not be @c
+ *                    NULL.
+ * @param cancel_cb   The callback for the @c Cancel button, which may be
+ *                    @c NULL.
+ * @param account     The #PurpleAccount associated with this request, or @c
+ *                    NULL if none is
+ * @param who         The username of the buddy associated with this request,
+ *                    or @c NULL if none is
+ * @param conv        The #PurpleConversation associated with this request, or
+ *                    @c NULL if none is
  * @param user_data   The data to pass to the callback.
  *
  * @return A UI-specific handle.
  */
-void *purple_request_fields(void *handle, const char *title,
-						  const char *primary, const char *secondary,
-						  PurpleRequestFields *fields,
-						  const char *ok_text, GCallback ok_cb,
-						  const char *cancel_text, GCallback cancel_cb,
-						  PurpleAccount *account, const char *who, PurpleConversation *conv,
-						  void *user_data);
+void *purple_request_fields(void *handle, const char *title, const char *primary,
+	const char *secondary, PurpleRequestFields *fields,
+	const char *ok_text, GCallback ok_cb,
+	const char *cancel_text, GCallback cancel_cb,
+	PurpleAccount *account, const char *who, PurpleConversation *conv,
+	void *user_data);
 
 /**
  * Closes a request.
@@ -1363,7 +1426,10 @@
 /**
  * Closes all requests registered with the specified handle.
  *
- * @param handle The handle.
+ * @param handle The handle, as supplied as the @a handle parameter to one of the
+ *               <tt>purple_request_*</tt> functions.
+ *
+ * @see purple_request_input().
  */
 void purple_request_close_with_handle(void *handle);
 
@@ -1401,50 +1467,57 @@
  * Displays a file selector request dialog.  Returns the selected filename to
  * the callback.  Can be used for either opening a file or saving a file.
  *
- * @param handle      The plugin or connection handle.  For some
- *                    things this is EXTREMELY important.  See
- *                    the comments on purple_request_input.
- * @param title       The title for the dialog (may be @c NULL)
+ * @param handle      The plugin or connection handle.  For some things this
+ *                    is <em>extremely</em> important.  See the comments on
+ *                    purple_request_input().
+ * @param title       The title of the message, or @c NULL if it should have
+ *                    no title.
  * @param filename    The default filename (may be @c NULL)
  * @param savedialog  True if this dialog is being used to save a file.
  *                    False if it is being used to open a file.
  * @param ok_cb       The callback for the @c OK button.
- * @param cancel_cb   The callback for the @c Cancel button.
- * @param account	  The PurpleAccount associated with this request, or NULL if none is
- * @param who		  The username of the buddy assocaited with this request, or NULL if none is
- * @param conv		  The PurpleConversation associated with this request, or NULL if none is
+ * @param cancel_cb   The callback for the @c Cancel button, which may be @c NULL.
+ * @param account     The #PurpleAccount associated with this request, or @c
+ *                    NULL if none is
+ * @param who         The username of the buddy associated with this request,
+ *                    or @c NULL if none is
+ * @param conv        The #PurpleConversation associated with this request, or
+ *                    @c NULL if none is
  * @param user_data   The data to pass to the callback.
  *
  * @return A UI-specific handle.
  */
 void *purple_request_file(void *handle, const char *title, const char *filename,
-						gboolean savedialog,
-						GCallback ok_cb, GCallback cancel_cb,
-						PurpleAccount *account, const char *who, PurpleConversation *conv,
-						void *user_data);
+	gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
+	PurpleAccount *account, const char *who, PurpleConversation *conv,
+	void *user_data);
 
 /**
  * Displays a folder select dialog. Returns the selected filename to
  * the callback.
  *
- * @param handle      The plugin or connection handle.  For some
- *                    things this is EXTREMELY important.  See
- *                    the comments on purple_request_input.
- * @param title       The title for the dialog (may be @c NULL)
+ * @param handle      The plugin or connection handle.  For some things this
+ *                    is <em>extremely</em> important.  See the comments on
+ *                    purple_request_input().
+ * @param title       The title of the message, or @c NULL if it should have
+ *                    no title.
  * @param dirname     The default directory name (may be @c NULL)
  * @param ok_cb       The callback for the @c OK button.
- * @param cancel_cb   The callback for the @c Cancel button.
- * @param account	  The PurpleAccount associated with this request, or NULL if none is
- * @param who		  The username of the buddy assocaited with this request, or NULL if none is
- * @param conv		  The PurpleConversation associated with this request, or NULL if none is
+ * @param cancel_cb   The callback for the @c Cancel button, which may be @c NULL.
+ * @param account     The #PurpleAccount associated with this request, or @c
+ *                    NULL if none is
+ * @param who         The username of the buddy associated with this request,
+ *                    or @c NULL if none is
+ * @param conv        The #PurpleConversation associated with this request, or
+ *                    @c NULL if none is
  * @param user_data   The data to pass to the callback.
  *
  * @return A UI-specific handle.
  */
 void *purple_request_folder(void *handle, const char *title, const char *dirname,
-						GCallback ok_cb, GCallback cancel_cb,
-						PurpleAccount *account, const char *who, PurpleConversation *conv,
-						void *user_data);
+	GCallback ok_cb, GCallback cancel_cb,
+	PurpleAccount *account, const char *who, PurpleConversation *conv,
+	void *user_data);
 
 /*@}*/
 
--- a/libpurple/roomlist.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/roomlist.c	Mon Jan 07 03:40:27 2008 +0000
@@ -218,6 +218,11 @@
 		prpl_info->roomlist_expand_category(list, category);
 }
 
+GList * purple_roomlist_get_fields(PurpleRoomlist *list)
+{
+	return list->fields;
+}
+
 /*@}*/
 
 /**************************************************************************/
@@ -293,6 +298,26 @@
 	g_hash_table_destroy(components);
 }
 
+PurpleRoomlistRoomType purple_roomlist_room_get_type(PurpleRoomlistRoom *room)
+{
+	return room->type;
+}
+
+const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room)
+{
+	return room->name;
+}
+
+PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room)
+{
+	return room->parent;
+}
+
+GList * purple_roomlist_room_get_fields(PurpleRoomlistRoom *room)
+{
+	return room->fields;
+}
+
 /*@}*/
 
 /**************************************************************************/
@@ -319,6 +344,21 @@
 	return f;
 }
 
+PurpleRoomlistFieldType purple_roomlist_field_get_type(PurpleRoomlistField *field)
+{
+	return field->type;
+}
+
+const char * purple_roomlist_field_get_label(PurpleRoomlistField *field)
+{
+	return field->label;
+}
+
+gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field)
+{
+	return field->hidden;
+}
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/roomlist.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/roomlist.h	Mon Jan 07 03:40:27 2008 +0000
@@ -237,6 +237,15 @@
  */
 void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category);
 
+/**
+ * Get the list of fields for a roomlist.
+ *
+ * @param roomlist  The roomlist, which must not be @c NULL.
+ * @constreturn A list of fields
+ * @since 2.4.0
+ */
+GList * purple_roomlist_get_fields(PurpleRoomlist *roomlist);
+
 /*@}*/
 
 /**************************************************************************/
@@ -273,6 +282,39 @@
  */
 void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room);
 
+/**
+ * Get the type of a room.
+ * @param room  The room, which must not be @c NULL.
+ * @return The type of the room.
+ * @since 2.4.0
+ */
+PurpleRoomlistRoomType purple_roomlist_room_get_type(PurpleRoomlistRoom *room);
+
+/**
+ * Get the name of a room.
+ * @param room  The room, which must not be @c NULL.
+ * @return The name of the room.
+ * @since 2.4.0
+ */
+const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room);
+
+/**
+ * Get the parent of a room.
+ * @param room  The room, which must not be @c NULL.
+ * @return The parent of the room, which can be @c NULL.
+ * @since 2.4.0
+ */
+PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room);
+
+/**
+ * Get the list of fields for a room.
+ *
+ * @param room  The room, which must not be @c NULL.
+ * @constreturn A list of fields
+ * @since 2.4.0
+ */
+GList * purple_roomlist_room_get_fields(PurpleRoomlistRoom *room);
+
 /*@}*/
 
 /**************************************************************************/
@@ -294,6 +336,36 @@
 PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
                                            const gchar *label, const gchar *name,
                                            gboolean hidden);
+
+/**
+ * Get the type of a field.
+ *
+ * @param field  A PurpleRoomlistField, which must not be @c NULL.
+ *
+ * @return  The type of the field.
+ * @since 2.4.0
+ */
+PurpleRoomlistFieldType purple_roomlist_field_get_type(PurpleRoomlistField *field);
+
+/**
+ * Get the label of a field.
+ *
+ * @param field  A PurpleRoomlistField, which must not be @c NULL.
+ *
+ * @return  The label of the field.
+ * @since 2.4.0
+ */
+const char * purple_roomlist_field_get_label(PurpleRoomlistField *field);
+
+/**
+ * Check whether a roomlist-field is hidden.
+ * @param field  A PurpleRoomlistField, which must not be @c NULL.
+ *
+ * @return  @c TRUE if the field is hidden, @c FALSE otherwise.
+ * @since 2.4.0
+ */
+gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/savedstatuses.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/savedstatuses.c	Mon Jan 07 03:40:27 2008 +0000
@@ -190,7 +190,7 @@
  * does the expiration.
  */
 static void
-remove_old_transient_statuses()
+remove_old_transient_statuses(void)
 {
 	GList *l, *next;
 	PurpleSavedStatus *saved_status, *current_status;
--- a/libpurple/signals.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/signals.h	Mon Jan 07 03:40:27 2008 +0000
@@ -44,11 +44,24 @@
 /**************************************************************************/
 /*@{*/
 
-/**
- * Signal Connect Priorities
+/** The priority of a signal connected using purple_signal_connect().
+ *
+ *  @see purple_signal_connect_priority()
  */
 #define PURPLE_SIGNAL_PRIORITY_DEFAULT     0
+/** The largest signal priority; signals with this priority will be called
+ *  <em>last</em>.  (This is highest as in numerical value, not as in order of
+ *  importance.)
+ *
+ *  @see purple_signal_connect_priority().
+ */
 #define PURPLE_SIGNAL_PRIORITY_HIGHEST  9999
+/** The smallest signal priority; signals with this priority will be called
+ *  <em>first</em>.  (This is lowest as in numerical value, not as in order of
+ *  importance.)
+ *
+ *  @see purple_signal_connect_priority().
+ */
 #define PURPLE_SIGNAL_PRIORITY_LOWEST  -9999
 
 /**
@@ -109,19 +122,21 @@
  * @param handle   The handle of the receiver.
  * @param func     The callback function.
  * @param data     The data to pass to the callback function.
- * @param priority The priority with which the handler should be called. Signal handlers are called
- *                 in order from PURPLE_SIGNAL_PRIORITY_LOWEST to PURPLE_SIGNAL_PRIORITY_HIGHEST.
+ * @param priority The priority with which the handler should be called. Signal
+ *                 handlers are called in ascending numerical order of @a
+ *                 priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to
+ *                 #PURPLE_SIGNAL_PRIORITY_HIGHEST.
  *
  * @return The signal handler ID.
  *
  * @see purple_signal_disconnect()
  */
 gulong purple_signal_connect_priority(void *instance, const char *signal,
-				   void *handle, PurpleCallback func, void *data, int priority);
+	void *handle, PurpleCallback func, void *data, int priority);
 
 /**
  * Connects a signal handler to a signal for a particular object.
- * (priority defaults to 0)
+ * (Its priority defaults to 0, aka #PURPLE_SIGNAL_PRIORITY_DEFAULT.)
  * 
  * Take care not to register a handler function twice. Purple will
  * not correct any mistakes for you in this area.
@@ -137,7 +152,7 @@
  * @see purple_signal_disconnect()
  */
 gulong purple_signal_connect(void *instance, const char *signal,
-						   void *handle, PurpleCallback func, void *data);
+	void *handle, PurpleCallback func, void *data);
 
 /**
  * Connects a signal handler to a signal for a particular object.
@@ -153,18 +168,22 @@
  * @param handle   The handle of the receiver.
  * @param func     The callback function.
  * @param data     The data to pass to the callback function.
- * @param priority The order in which the signal should be added to the list
+ * @param priority The priority with which the handler should be called. Signal
+ *                 handlers are called in ascending numerical order of @a
+ *                 priority from #PURPLE_SIGNAL_PRIORITY_LOWEST to
+ *                 #PURPLE_SIGNAL_PRIORITY_HIGHEST.
  *
  * @return The signal handler ID.
  *
  * @see purple_signal_disconnect()
  */
 gulong purple_signal_connect_priority_vargs(void *instance, const char *signal,
-					void *handle, PurpleCallback func, void *data, int priority);
+	void *handle, PurpleCallback func, void *data, int priority);
 
 /**
  * Connects a signal handler to a signal for a particular object.
- * (priority defaults to 0)
+ * (Its priority defaults to 0, aka #PURPLE_SIGNAL_PRIORITY_DEFAULT.)
+ *
  * The signal handler will take a va_args of arguments, instead of
  * individual arguments.
  *
@@ -182,7 +201,7 @@
  * @see purple_signal_disconnect()
  */
 gulong purple_signal_connect_vargs(void *instance, const char *signal,
-								 void *handle, PurpleCallback func, void *data);
+	void *handle, PurpleCallback func, void *data);
 
 /**
  * Disconnects a signal handler from a signal on an object.
--- a/libpurple/status.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/status.c	Mon Jan 07 03:40:27 2008 +0000
@@ -607,13 +607,10 @@
 
 		if (old_status != NULL)
 		{
-			tmp = g_strdup_printf(_("%s changed status from %s to %s"), buddy_alias,
+			tmp = 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));
-			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));
-
+			logtmp = g_markup_escape_text(tmp, -1);
 		}
 		else
 		{
@@ -621,18 +618,15 @@
 
 			if (purple_status_is_active(new_status))
 			{
-				tmp = g_strdup_printf(_("%s is now %s"), buddy_alias,
+				tmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name,
 				                      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));
-
+				logtmp = g_markup_escape_text(tmp, -1);
 			}
 			else
 			{
-				tmp = g_strdup_printf(_("%s is no longer %s"), buddy_alias,
+				tmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name,
 				                      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));
+				logtmp = g_markup_escape_text(tmp, -1);
 			}
 		}
 
@@ -1244,12 +1238,15 @@
 
 			if (log != NULL)
 			{
-				char *tmp = g_strdup_printf(_("%s became idle"),
+				char *tmp, *tmp2;
+				tmp = g_strdup_printf(_("%s became idle"),
 				purple_buddy_get_alias(buddy));
+				tmp2 = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
 
 				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
-				purple_buddy_get_alias(buddy), current_time, tmp);
-				g_free(tmp);
+				purple_buddy_get_alias(buddy), current_time, tmp2);
+				g_free(tmp2);
 			}
 		}
 	}
@@ -1261,12 +1258,15 @@
 
 			if (log != NULL)
 			{
-				char *tmp = g_strdup_printf(_("%s became unidle"),
+				char *tmp, *tmp2;
+				tmp = g_strdup_printf(_("%s became unidle"),
 				purple_buddy_get_alias(buddy));
+				tmp2 = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
 
 				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
-				purple_buddy_get_alias(buddy), current_time, tmp);
-				g_free(tmp);
+				purple_buddy_get_alias(buddy), current_time, tmp2);
+				g_free(tmp2);
 			}
 		}
 	}
@@ -1321,13 +1321,15 @@
 
 			if (log != NULL)
 			{
-				char *msg;
+				char *msg, *tmp;
 
 				if (idle)
-					msg = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
+					tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
 				else
-					msg = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
+					tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
 
+				msg = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
 				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
 				                 purple_account_get_username(account),
 				                 (idle ? idle_time : current_time), msg);
--- a/libpurple/stun.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/stun.c	Mon Jan 07 03:40:27 2008 +0000
@@ -104,7 +104,7 @@
 	g_free(sc);
 }
 
-static void do_callbacks() {
+static void do_callbacks(void) {
 	while(callbacks) {
 		StunCallback cb = callbacks->data;
 		if(cb)
--- a/libpurple/tests/test_cipher.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/tests/test_cipher.c	Mon Jan 07 03:40:27 2008 +0000
@@ -190,6 +190,511 @@
 END_TEST
 
 /******************************************************************************
+ * DES Tests
+ *****************************************************************************/
+#define DES_TEST(in, keyz, out, len) { \
+	PurpleCipher *cipher = NULL; \
+	PurpleCipherContext *context = NULL; \
+	guchar answer[len+1]; \
+	gint ret = 0; \
+	guchar decrypt[len+1] = in; \
+	guchar key[8+1] = keyz;\
+	guchar encrypt[len+1] = out;\
+	size_t outlen; \
+	\
+	cipher = purple_ciphers_find_cipher("des"); \
+	context = purple_cipher_context_new(cipher, NULL); \
+	purple_cipher_context_set_key(context, key); \
+	\
+	ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \
+	\
+	ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \
+	\
+	purple_cipher_context_destroy(context); \
+}
+
+START_TEST(test_des_12345678) {
+	DES_TEST("12345678",
+	         "\x3b\x38\x98\x37\x15\x20\xf7\x5e",
+	         "\x06\x22\x05\xac\x6a\x0d\x55\xdd",
+	         8);
+}
+END_TEST
+
+START_TEST(test_des_abcdefgh) {
+	DES_TEST("abcdefgh",
+	         "\x3b\x38\x98\x37\x15\x20\xf7\x5e",
+	         "\x62\xe0\xc6\x8c\x48\xe4\x75\xed",
+	         8);
+}
+END_TEST
+
+/******************************************************************************
+ * DES3 Tests
+ * See http://csrc.nist.gov/groups/ST/toolkit/examples.html
+ * and some NULL things I made up
+ *****************************************************************************/
+
+#define DES3_TEST(in, key, iv, out, len, mode) { \
+	PurpleCipher *cipher = NULL; \
+	PurpleCipherContext *context = NULL; \
+	guchar answer[len+1]; \
+	guchar decrypt[len+1] = in; \
+	guchar encrypt[len+1] = out; \
+	size_t outlen; \
+	gint ret = 0; \
+	\
+	cipher = purple_ciphers_find_cipher("des3"); \
+	context = purple_cipher_context_new(cipher, NULL); \
+	purple_cipher_context_set_key(context, (guchar *)key); \
+	purple_cipher_context_set_batch_mode(context, (mode)); \
+	purple_cipher_context_set_iv(context, (guchar *)iv, 8); \
+	\
+	ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \
+	\
+	ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \
+	\
+	purple_cipher_context_destroy(context); \
+}
+
+START_TEST(test_des3_ecb_nist1) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+	          "00000000", /* ignored */
+	          "\x71\x47\x72\xF3\x39\x84\x1D\x34\x26\x7F\xCC\x4B\xD2\x94\x9C\xC3"
+	          "\xEE\x11\xC2\x2A\x57\x6A\x30\x38\x76\x18\x3F\x99\xC0\xB6\xDE\x87",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_nist2) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF",
+	          "00000000", /* ignored */
+	          "\x06\xED\xE3\xD8\x28\x84\x09\x0A\xFF\x32\x2C\x19\xF0\x51\x84\x86"
+	          "\x73\x05\x76\x97\x2A\x66\x6E\x58\xB6\xC8\x8C\xF1\x07\x34\x0D\x3D",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_null_key) {
+	DES3_TEST(
+	          "\x16\xf4\xb3\x77\xfd\x4b\x9e\xca",
+	          "\x38\x00\x88\x6a\xef\xcb\x00\xad"
+	          "\x5d\xe5\x29\x00\x7d\x98\x64\x4c"
+	          "\x86\x00\x7b\xd3\xc7\x00\x7b\x32",
+	          "00000000", /* ignored */
+	          "\xc0\x60\x30\xa1\xb7\x25\x42\x44",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_null_text) {
+	DES3_TEST(
+	          "\x65\x73\x34\xc1\x19\x00\x79\x65",
+	          "\x32\x64\xda\x10\x13\x6a\xfe\x1e"
+	          "\x37\x54\xd1\x2c\x41\x04\x10\x40"
+	          "\xaf\x1c\x75\x2b\x51\x3a\x03\xf5",
+	          "00000000", /* ignored */
+	          "\xe5\x80\xf6\x12\xf8\x4e\xd9\x6c",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_null_key_and_text) {
+	DES3_TEST(
+	          "\xdf\x7f\x00\x92\xe7\xc1\x49\xd2",
+	          "\x0e\x41\x00\xc4\x8b\xf0\x6e\xa1"
+	          "\x66\x49\x42\x63\x22\x00\xf0\x99"
+	          "\x6b\x22\xc1\x37\x9c\x00\xe4\x8f",
+	          "00000000", /* ignored */
+	          "\x73\xd8\x1f\x1f\x50\x01\xe4\x79",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_nist1) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+	          "\xF6\x9F\x24\x45\xDF\x4F\x9B\x17",
+	          "\x20\x79\xC3\xD5\x3A\xA7\x63\xE1\x93\xB7\x9E\x25\x69\xAB\x52\x62"
+	          "\x51\x65\x70\x48\x1F\x25\xB5\x0F\x73\xC0\xBD\xA8\x5C\x8E\x0D\xA7",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_nist2) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF",
+	          "\xF6\x9F\x24\x45\xDF\x4F\x9B\x17",
+	          "\x74\x01\xCE\x1E\xAB\x6D\x00\x3C\xAF\xF8\x4B\xF4\x7B\x36\xCC\x21"
+	          "\x54\xF0\x23\x8F\x9F\xFE\xCD\x8F\x6A\xCF\x11\x83\x92\xB4\x55\x81",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_null_key) {
+	DES3_TEST(
+	          "\x16\xf4\xb3\x77\xfd\x4b\x9e\xca",
+	          "\x38\x00\x88\x6a\xef\xcb\x00\xad"
+	          "\x5d\xe5\x29\x00\x7d\x98\x64\x4c"
+	          "\x86\x00\x7b\xd3\xc7\x00\x7b\x32",
+	          "\x31\x32\x33\x34\x35\x36\x37\x38",
+	          "\x52\xe7\xde\x96\x39\x87\x87\xdb",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_null_text) {
+	DES3_TEST(
+	          "\x65\x73\x34\xc1\x19\x00\x79\x65",
+	          "\x32\x64\xda\x10\x13\x6a\xfe\x1e"
+	          "\x37\x54\xd1\x2c\x41\x04\x10\x40"
+	          "\xaf\x1c\x75\x2b\x51\x3a\x03\xf5",
+	          "\x7C\xAF\x0D\x57\x1E\x57\x10\xDA",
+	          "\x40\x12\x0e\x00\x85\xff\x6c\xc2",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_null_key_and_text) {
+	DES3_TEST(
+	          "\xdf\x7f\x00\x92\xe7\xc1\x49\xd2",
+	          "\x0e\x41\x00\xc4\x8b\xf0\x6e\xa1"
+	          "\x66\x49\x42\x63\x22\x00\xf0\x99"
+	          "\x6b\x22\xc1\x37\x9c\x00\xe4\x8f",
+	          "\x01\x19\x0D\x2c\x40\x67\x89\x67",
+	          "\xa7\xc1\x10\xbe\x9b\xd5\x8a\x67",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+/******************************************************************************
+ * HMAC Tests
+ * See RFC2202 and some other NULL tests I made up
+ *****************************************************************************/
+
+#define HMAC_TEST(data, data_len, key, key_len, type, digest) { \
+	PurpleCipher *cipher = NULL; \
+	PurpleCipherContext *context = NULL; \
+	gchar cdigest[41]; \
+	gboolean ret = FALSE; \
+	\
+	cipher = purple_ciphers_find_cipher("hmac"); \
+	context = purple_cipher_context_new(cipher, NULL); \
+	purple_cipher_context_set_option(context, "hash", type); \
+	purple_cipher_context_set_key_with_len(context, (guchar *)key, (key_len)); \
+	\
+	purple_cipher_context_append(context, (guchar *)(data), (data_len)); \
+	ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
+	                                        NULL); \
+	\
+	fail_unless(ret == TRUE, NULL); \
+	fail_unless(strcmp((digest), cdigest) == 0, NULL); \
+	\
+	purple_cipher_context_destroy(context); \
+}
+
+/* HMAC MD5 */
+
+START_TEST(test_hmac_md5_Hi) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          16,
+	          "md5",
+	          "9294727a3638bb1c13f48ef8158bfc9d");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_what) {
+	HMAC_TEST("what do ya want for nothing?",
+	          28,
+	          "Jefe",
+	          4,
+	          "md5",
+	          "750c783e6ab0b503eaa86e310a5db738");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_dd) {
+	HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	          50,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          16,
+	          "md5",
+	          "56be34521d144c88dbb8c733f0e8b3f6");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_cd) {
+	HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+	          50,
+	          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+	          "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+	          "\x15\x16\x17\x18\x19",
+	          25,
+	          "md5",
+	          "697eaf0aca3a3aea3a75164746ffaa79");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_truncation) {
+	HMAC_TEST("Test With Truncation",
+	          20,
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+	          16,
+	          "md5",
+	          "56461ef2342edc00f9bab995690efd4c");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_large_key) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First",
+	          54,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "md5",
+	          "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_large_key_and_data) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+	          73,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "md5",
+	          "6f630fad67cda0ee1fb1f562db3aa53e");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_key) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "md5",
+	          "597bfd644b797a985561eeb03a169e59");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_text) {
+	HMAC_TEST("Hi\x00There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "md5",
+	          "70be8e1b7b50dfcc335d6cd7992c564f");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_key_and_text) {
+	HMAC_TEST("Hi\x00Th\x00re",
+	          8,
+	          "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "md5",
+	          "b31bcbba35a33a067cbba9131cba4889");
+}
+END_TEST
+
+/* HMAC SHA1 */
+
+START_TEST(test_hmac_sha1_Hi) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "sha1",
+	          "b617318655057264e28bc0b6fb378c8ef146be00");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_what) {
+	HMAC_TEST("what do ya want for nothing?",
+	          28,
+	          "Jefe",
+	          4,
+	          "sha1",
+	          "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_dd) {
+	HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	          50,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          20,
+	          "sha1",
+	          "125d7342b9ac11cd91a39af48aa17b4f63f175d3");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_cd) {
+	HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+	          50,
+	          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+	          "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+	          "\x15\x16\x17\x18\x19",
+	          25,
+	          "sha1",
+	          "4c9007f4026250c6bc8414f9bf50c86c2d7235da");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_truncation) {
+	HMAC_TEST("Test With Truncation",
+	          20,
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+	          20,
+	          "sha1",
+	          "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_large_key) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First",
+	          54,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "sha1",
+	          "aa4ae5e15272d00e95705637ce8a3b55ed402112");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_large_key_and_data) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+	          73,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "sha1",
+	          "e8e99d0f45237d786d6bbaa7965c7808bbff1a91");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_key) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "sha1",
+	          "eb62a2e0e33d300be669c52aab3f591bc960aac5");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_text) {
+	HMAC_TEST("Hi\x00There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "sha1",
+	          "31ca58d849e971e418e3439de2c6f83144b6abb7");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_key_and_text) {
+	HMAC_TEST("Hi\x00Th\x00re",
+	          8,
+	          "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "sha1",
+	          "e6b8e2fede87aa09dcb13e554df1435e056eae36");
+}
+END_TEST
+
+/******************************************************************************
  * Suite
  *****************************************************************************/
 Suite *
@@ -227,6 +732,53 @@
 	tcase_add_test(tc, test_sha1_1000_as_1000_times);
 	suite_add_tcase(s, tc);
 
+	/* des tests */
+	tc = tcase_create("DES");
+	tcase_add_test(tc, test_des_12345678);
+	tcase_add_test(tc, test_des_abcdefgh);
+	suite_add_tcase(s, tc);
+
+	/* des3 ecb tests */
+	tc = tcase_create("DES3 ECB");
+	tcase_add_test(tc, test_des3_ecb_nist1);
+	tcase_add_test(tc, test_des3_ecb_nist2);
+	tcase_add_test(tc, test_des3_ecb_null_key);
+	tcase_add_test(tc, test_des3_ecb_null_text);
+	tcase_add_test(tc, test_des3_ecb_null_key_and_text);
+	suite_add_tcase(s, tc);
+	/* des3 cbc tests */
+	tc = tcase_create("DES3 CBC");
+	tcase_add_test(tc, test_des3_cbc_nist1);
+	tcase_add_test(tc, test_des3_cbc_nist2);
+	tcase_add_test(tc, test_des3_cbc_null_key);
+	tcase_add_test(tc, test_des3_cbc_null_text);
+	tcase_add_test(tc, test_des3_cbc_null_key_and_text);
+	suite_add_tcase(s, tc);
+
+	/* hmac tests */
+	tc = tcase_create("HMAC");
+	tcase_add_test(tc, test_hmac_md5_Hi);
+	tcase_add_test(tc, test_hmac_md5_what);
+	tcase_add_test(tc, test_hmac_md5_dd);
+	tcase_add_test(tc, test_hmac_md5_cd);
+	tcase_add_test(tc, test_hmac_md5_truncation);
+	tcase_add_test(tc, test_hmac_md5_large_key);
+	tcase_add_test(tc, test_hmac_md5_large_key_and_data);
+	tcase_add_test(tc, test_hmac_md5_null_key);
+	tcase_add_test(tc, test_hmac_md5_null_text);
+	tcase_add_test(tc, test_hmac_md5_null_key_and_text);
+	tcase_add_test(tc, test_hmac_sha1_Hi);
+	tcase_add_test(tc, test_hmac_sha1_what);
+	tcase_add_test(tc, test_hmac_sha1_dd);
+	tcase_add_test(tc, test_hmac_sha1_cd);
+	tcase_add_test(tc, test_hmac_sha1_truncation);
+	tcase_add_test(tc, test_hmac_sha1_large_key);
+	tcase_add_test(tc, test_hmac_sha1_large_key_and_data);
+	tcase_add_test(tc, test_hmac_sha1_null_key);
+	tcase_add_test(tc, test_hmac_sha1_null_text);
+	tcase_add_test(tc, test_hmac_sha1_null_key_and_text);
+	suite_add_tcase(s, tc);
+
 	return s;
 }
 
--- a/libpurple/upnp.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/upnp.c	Mon Jan 07 03:40:27 2008 +0000
@@ -777,7 +777,7 @@
 
 /* TODO: This could be exported */
 static const gchar *
-purple_upnp_get_internal_ip()
+purple_upnp_get_internal_ip(void)
 {
 	if (control_info.status == PURPLE_UPNP_STATUS_DISCOVERED
 			&& control_info.internalip
--- a/libpurple/util.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/util.c	Mon Jan 07 03:40:27 2008 +0000
@@ -4259,6 +4259,53 @@
 	return g_string_free(workstr, FALSE);
 }
 
+/*
+ * This function is copied from g_strerror() but changed to use
+ * gai_strerror().
+ */
+G_CONST_RETURN gchar *
+purple_gai_strerror(gint errnum)
+{
+	static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT;
+	char *msg;
+	int saved_errno = errno;
+
+	const char *msg_locale;
+
+	msg_locale = gai_strerror(errnum);
+	if (g_get_charset(NULL))
+	{
+		/* This string is already UTF-8--great! */
+		errno = saved_errno;
+		return msg_locale;
+	}
+	else
+	{
+		gchar *msg_utf8 = g_locale_to_utf8(msg_locale, -1, NULL, NULL, NULL);
+		if (msg_utf8)
+		{
+			/* Stick in the quark table so that we can return a static result */
+			GQuark msg_quark = g_quark_from_string(msg_utf8);
+			g_free(msg_utf8);
+
+			msg_utf8 = (gchar *)g_quark_to_string(msg_quark);
+			errno = saved_errno;
+			return msg_utf8;
+		}
+	}
+
+	msg = g_static_private_get(&msg_private);
+	if (!msg)
+	{
+		msg = g_new(gchar, 64);
+		g_static_private_set(&msg_private, msg, g_free);
+	}
+
+	sprintf(msg, "unknown error (%d)", errnum);
+
+	errno = saved_errno;
+	return msg;
+}
 
 char *
 purple_utf8_ncr_encode(const char *str)
@@ -4814,3 +4861,57 @@
 	*newlen = bytes;
 	return utf;
 }
+
+void purple_util_set_current_song(const char *title, const char *artist, const char *album)
+{
+	GList *list = purple_accounts_get_all();
+	for (; list; list = list->next) {
+		PurplePresence *presence;
+		PurpleStatus *tune;
+		PurpleAccount *account = list->data;
+		if (!purple_account_get_enabled(account, purple_core_get_ui()))
+			continue;
+
+		presence = purple_account_get_presence(account);
+		tune = purple_presence_get_status(presence, "tune");
+		if (!tune)
+			continue;
+		if (title) {
+			purple_status_set_active(tune, TRUE);
+			purple_status_set_attr_string(tune, PURPLE_TUNE_TITLE, title);
+			purple_status_set_attr_string(tune, PURPLE_TUNE_ARTIST, artist);
+			purple_status_set_attr_string(tune, PURPLE_TUNE_ALBUM, album);
+		} else {
+			purple_status_set_active(tune, FALSE);
+		}
+	}
+}
+
+char * purple_util_format_song_info(const char *title, const char *artist, const char *album, gpointer unused)
+{
+	GString *string;
+	char *esc;
+
+	if (!title)
+		return NULL;
+
+	esc = g_markup_escape_text(title, -1);
+	string = g_string_new("");
+	g_string_append_printf(string, "%s", esc);
+	g_free(esc);
+
+	if (artist) {
+		esc = g_markup_escape_text(artist, -1);
+		g_string_append_printf(string, _(" - %s"), esc);
+		g_free(esc);
+	}
+
+	if (album) {
+		esc = g_markup_escape_text(album, -1);
+		g_string_append_printf(string, _(" (%s)"), esc);
+		g_free(esc);
+	}
+
+	return g_string_free(string, FALSE);
+}
+
--- a/libpurple/util.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/util.h	Mon Jan 07 03:40:27 2008 +0000
@@ -85,6 +85,31 @@
  */
 void purple_menu_action_free(PurpleMenuAction *act);
 
+/**
+ * Set the appropriate presence values for the currently playing song.
+ *
+ * @param title     The title of the song, @c NULL to unset the value.
+ * @param artist    The artist of the song, can be @c NULL.
+ * @param album     The album of the song, can be @c NULL.
+ * @since 2.4.0
+ */
+void purple_util_set_current_song(const char *title, const char *artist,
+		const char *album);
+
+/**
+ * Format song information.
+ *
+ * @param title     The title of the song, @c NULL to unset the value.
+ * @param artist    The artist of the song, can be @c NULL.
+ * @param album     The album of the song, can be @c NULL.
+ * @param unused    Currently unused, must be @c NULL.
+ *
+ * @return   The formatted string. The caller must #g_free the returned string.
+ * @since 2.4.0
+ */
+char * purple_util_format_song_info(const char *title, const char *artist,
+		const char *album, gpointer unused);
+
 /**************************************************************************/
 /** @name Utility Subsystem                                               */
 /**************************************************************************/
@@ -1110,6 +1135,18 @@
 gchar *purple_utf8_salvage(const char *str);
 
 /**
+ * Return the UTF-8 version of gai_strerror().  It calls gai_strerror()
+ * then converts the result to UTF-8.  This function is analogous to
+ * g_strerror().
+ *
+ * @param errnum The error code.
+ *
+ * @return The UTF-8 error message.
+ * @since 2.4.0
+ */
+G_CONST_RETURN gchar *purple_gai_strerror(gint errnum);
+
+/**
  * Compares two UTF-8 strings case-insensitively.  This string is
  * more expensive than a simple g_utf8_collate() comparison because
  * it calls g_utf8_casefold() on each string, which allocates new
--- a/libpurple/version.h.in	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/version.h.in	Mon Jan 07 03:40:27 2008 +0000
@@ -49,6 +49,34 @@
  */
 const char *purple_version_check(guint required_major, guint required_minor, guint required_micro);
 
+/**
+ * The major version of the running libpurple.  Contrast with
+ * #PURPLE_MAJOR_VERSION, which expands at compile time to the major version of
+ * libpurple being compiled against.
+ *
+ * @since 2.4.0
+ */
+extern const guint purple_major_version;
+
+/**
+ * The minor version of the running libpurple.  Contrast with
+ * #PURPLE_MINOR_VERSION, which expands at compile time to the minor version of
+ * libpurple being compiled against.
+ *
+ * @since 2.4.0
+ */
+extern const guint purple_minor_version;
+
+/**
+ *
+ * The micro version of the running libpurple.  Contrast with
+ * #PURPLE_MICRO_VERSION, which expands at compile time to the micro version of
+ * libpurple being compiled against.
+ *
+ * @since 2.4.0
+ */
+extern const guint purple_micro_version;
+
 #ifdef __cplusplus
 }
 #endif
--- a/libpurple/xmlnode.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/libpurple/xmlnode.c	Mon Jan 07 03:40:27 2008 +0000
@@ -345,6 +345,7 @@
 	g_free(node->name);
 	g_free(node->data);
 	g_free(node->xmlns);
+	g_free(node->prefix);
 
 	if(node->namespace_map)
 		g_hash_table_destroy(node->namespace_map);
@@ -551,6 +552,9 @@
 		g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml);
 	g_free(xml);
 
+	if (len)
+		*len += sizeof("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S) - 1;
+
 	return xml_with_declaration;
 }
 
--- a/pidgin/Makefile.am	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/Makefile.am	Mon Jan 07 03:40:27 2008 +0000
@@ -119,7 +119,8 @@
 	gtkthemes.c \
 	gtkutils.c \
 	gtkwhiteboard.c \
-	minidialog.c
+	minidialog.c \
+	pidgintooltip.c
 
 pidgin_headers = \
 	eggtrayicon.h \
@@ -172,6 +173,7 @@
 	gtkutils.h \
 	gtkwhiteboard.h \
 	minidialog.h \
+	pidgintooltip.h \
 	pidgin.h
 
 pidginincludedir=$(includedir)/pidgin
--- a/pidgin/Makefile.mingw	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/Makefile.mingw	Mon Jan 07 03:40:27 2008 +0000
@@ -95,6 +95,7 @@
 			gtkwhiteboard.c \
 			minidialog.c \
 			pidginstock.c \
+			pidgintooltip.c \
 			win32/MinimizeToTray.c \
 			win32/gtkdocklet-win32.c \
 			win32/gtkwin32dep.c \
--- a/pidgin/gtkaccount.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkaccount.c	Mon Jan 07 03:40:27 2008 +0000
@@ -55,7 +55,6 @@
 	COLUMN_ENABLED,
 	COLUMN_PROTOCOL,
 	COLUMN_DATA,
-	COLUMN_PULSE_DATA,
 	NUM_COLUMNS
 };
 
@@ -139,18 +138,6 @@
 
 } AccountPrefsDialog;
 
-typedef struct
-{
-	GdkPixbuf *online_pixbuf;
-	gboolean pulse_to_grey;
-	float pulse_value;
-	int timeout;
-	PurpleAccount *account;
-	GtkTreeModel *model;
-
-} PidginPulseData;
-
-
 static AccountsWindow *accounts_window = NULL;
 static GHashTable *account_pref_wins;
 
@@ -171,25 +158,7 @@
 add_pref_box(AccountPrefsDialog *dialog, GtkWidget *parent,
 			 const char *text, GtkWidget *widget)
 {
-	GtkWidget *hbox;
-	GtkWidget *label;
-
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
-	gtk_widget_show(hbox);
-
-	label = gtk_label_new_with_mnemonic(text);
-	gtk_size_group_add_widget(dialog->sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
-	gtk_widget_show(label);
-
-	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, PIDGIN_HIG_BORDER);
-	gtk_widget_show(widget);
-	pidgin_set_accessible_label (widget, label);
-
-	return hbox;
+	return pidgin_add_widget_to_vbox(GTK_BOX(parent), text, dialog->sg, widget, TRUE, NULL);
 }
 
 static void
@@ -1121,7 +1090,7 @@
 					 G_CALLBACK(proxy_type_changed_cb), dialog);
 }
 
-static void
+static gboolean
 account_win_destroy_cb(GtkWidget *w, GdkEvent *event,
 					   AccountPrefsDialog *dialog)
 {
@@ -1142,6 +1111,7 @@
 	purple_signals_disconnect_by_handle(dialog);
 
 	g_free(dialog);
+	return FALSE;
 }
 
 static void
@@ -1437,7 +1407,6 @@
 	GtkWidget *win;
 	GtkWidget *main_vbox;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *dbox;
 	GtkWidget *notebook;
 	GtkWidget *button;
@@ -1475,16 +1444,14 @@
 	if ((dialog->plugin = purple_find_prpl(dialog->protocol_id)) != NULL)
 		dialog->prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin);
 
-	dialog->window = win = pidgin_create_window((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
+	dialog->window = win = pidgin_create_dialog((type == PIDGIN_ADD_ACCOUNT_DIALOG) ? _("Add Account") : _("Modify Account"),
 		PIDGIN_HIG_BORDER, "account", FALSE);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(account_win_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	main_vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), main_vbox);
-	gtk_widget_show(main_vbox);
+	main_vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	notebook = gtk_notebook_new();
 	gtk_box_pack_start(GTK_BOX(main_vbox), notebook, FALSE, FALSE, 0);
@@ -1511,8 +1478,6 @@
 	if (!dialog->prpl_info || !dialog->prpl_info->register_user)
 		gtk_widget_hide(button);
 
-
-
 	/* Setup the page with 'Advanced'. */
 	dialog->bottom_vbox = dbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_set_border_width(GTK_CONTAINER(dbox), PIDGIN_HIG_BORDER);
@@ -1524,30 +1489,13 @@
 	add_protocol_options(dialog, dbox);
 	add_proxy_options(dialog, dbox);
 
-	/* Setup the button box */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(main_vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(cancel_account_prefs_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL, G_CALLBACK(cancel_account_prefs_cb), dialog);
 
 	/* Save button */
-	button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE, G_CALLBACK(ok_account_prefs_cb), dialog);
 	if (dialog->account == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
-	gtk_widget_show(button);
-
 	dialog->ok_button = button;
 
 	/* Set up DND */
@@ -1561,9 +1509,6 @@
 	g_signal_connect(G_OBJECT(dialog->window), "drag_data_received",
 			 G_CALLBACK(account_dnd_recv), dialog);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(ok_account_prefs_cb), dialog);
-
 	/* Show the window. */
 	gtk_widget_show(win);
 }
@@ -1575,7 +1520,6 @@
 signed_on_off_cb(PurpleConnection *gc, gpointer user_data)
 {
 	PurpleAccount *account;
-	PidginPulseData *pulse_data;
 	GtkTreeModel *model;
 	GtkTreeIter iter;
 	GdkPixbuf *pixbuf;
@@ -1591,29 +1535,14 @@
 
 	if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index))
 	{
-		gtk_tree_model_get(GTK_TREE_MODEL(accounts_window->model), &iter,
-						   COLUMN_PULSE_DATA, &pulse_data, -1);
-
-		if (pulse_data != NULL)
-		{
-			if (pulse_data->timeout > 0)
-				g_source_remove(pulse_data->timeout);
-
-			g_object_unref(G_OBJECT(pulse_data->online_pixbuf));
-
-			g_free(pulse_data);
-		}
-
 		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 		if ((pixbuf != NULL) && purple_account_is_disconnected(account))
 			gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
 
 		gtk_list_store_set(accounts_window->model, &iter,
 				   COLUMN_ICON, pixbuf,
-				   COLUMN_PULSE_DATA, NULL,
 				   -1);
 
-
 		if (pixbuf != NULL)
 			g_object_unref(G_OBJECT(pixbuf));
 	}
@@ -1812,16 +1741,14 @@
 	}
 }
 
-static gint
+static gboolean
 accedit_win_destroy_cb(GtkWidget *w, GdkEvent *event, AccountsWindow *dialog)
 {
-	/* Since this is called as the window is closing, we don't need
-	 * pidgin_accounts_window_hide() to also dispose of the window */
 	dialog->window = NULL;
 
 	pidgin_accounts_window_hide();
 
-	return 0;
+	return FALSE;
 }
 
 static gboolean
@@ -2155,24 +2082,24 @@
 	GtkTreeViewColumn *column;
 	GtkTreeIter iter;
 	PurpleAccount *account;
-	const gchar *title;
 
 	dialog = (AccountsWindow *)user_data;
 
+	if (event->window != gtk_tree_view_get_bin_window(treeview))
+	    return FALSE;
+
 	/* Figure out which node was clicked */
 	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(dialog->treeview), event->x, event->y, &path, &column, NULL, NULL))
 		return FALSE;
-	title = gtk_tree_view_column_get_title(column);
-	/* The -1 is required because the first two columns of the list
-	 * store are displayed as only one column in the tree view. */
-	column = gtk_tree_view_get_column(treeview, COLUMN_ENABLED-1);
+	if (column == gtk_tree_view_get_column(treeview, 0))
+		return FALSE;
+
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
 	gtk_tree_path_free(path);
 	gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, COLUMN_DATA, &account, -1);
 
 	if ((account != NULL) && (event->button == 1) &&
-		(event->type == GDK_2BUTTON_PRESS) &&
-		(strcmp(gtk_tree_view_column_get_title(column), title)))
+		(event->type == GDK_2BUTTON_PRESS))
 	{
 		pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account);
 		return TRUE;
@@ -2313,7 +2240,6 @@
 	AccountsWindow *dialog;
 	GtkWidget *win;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *sw;
 	GtkWidget *button;
 	int width, height;
@@ -2328,7 +2254,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Accounts"), PIDGIN_HIG_BORDER, "accounts", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -2337,57 +2263,28 @@
 					 G_CALLBACK(configure_cb), accounts_window);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Setup the scrolled window that will contain the list of accounts. */
 	sw = create_accounts_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
 	gtk_widget_show(sw);
 
-	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(add_account_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(add_account_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(modify_account_cb), dialog);
 	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(modify_account_cb), dialog);
 
 	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(ask_delete_account_cb), dialog);
 	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(ask_delete_account_cb), dialog);
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_accounts_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(close_accounts_cb), dialog);
 
 	purple_signal_connect(pidgin_account_get_handle(), "account-modified",
 	                    accounts_window,
--- a/pidgin/gtkblist.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkblist.c	Mon Jan 07 03:40:27 2008 +0000
@@ -59,6 +59,7 @@
 #include "gtkscrollbook.h"
 #include "gtkutils.h"
 #include "pidgin/minidialog.h"
+#include "pidgin/pidgintooltip.h"
 
 #include <gdk/gdkkeysyms.h>
 #include <gtk/gtk.h>
@@ -153,6 +154,7 @@
 static char *pidgin_get_tooltip_text(PurpleBlistNode *node, gboolean full);
 static const char *item_factory_translate_func (const char *path, gpointer func_data);
 static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter);
+static gboolean buddy_is_displayable(PurpleBuddy *buddy);
 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);
@@ -175,7 +177,7 @@
 } PidginBlistNode;
 
 static char dim_grey_string[8] = "";
-static char *dim_grey()
+static char *dim_grey(void)
 {
 	if (!gtkblist)
 		return "dim grey";
@@ -407,6 +409,7 @@
 static void gtk_blist_renderer_editing_cancelled_cb(GtkCellRenderer *renderer, PurpleBuddyList *list)
 {
 	editing_blist = FALSE;
+	g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL);
 	pidgin_blist_refresh(list);
 }
 
@@ -532,12 +535,14 @@
 			if (node_alias && !g_utf8_collate(node_alias, a)) {
 				merges = g_list_append(merges, buddy);
 				i++;
+				g_free(node_alias);
+				break;
 			}
 			g_free(node_alias);
 		}
 	}
 	g_free(a);
-	
+
 	if (i > 1)
 	{
 		char *msg = g_strdup_printf(ngettext("You 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);
@@ -703,12 +708,12 @@
 	pidgin_blist_update(purple_get_blist(), node);
 }
 
-static void gtk_blist_show_systemlog_cb()
+static void gtk_blist_show_systemlog_cb(void)
 {
 	pidgin_syslog_show();
 }
 
-static void gtk_blist_show_onlinehelp_cb()
+static void gtk_blist_show_onlinehelp_cb(void)
 {
 	purple_notify_uri(NULL, PURPLE_WEBSITE "documentation");
 }
@@ -838,20 +843,10 @@
 
 	for (tmp = list; tmp; tmp = tmp->next)
 	{
-		GtkWidget *label;
-		GtkWidget *rowbox;
 		GtkWidget *input;
 
 		pce = tmp->data;
 
-		rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-		gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0);
-
-		label = gtk_label_new_with_mnemonic(pce->label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-		gtk_size_group_add_widget(data->sg, label);
-		gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 		if (pce->is_int)
 		{
 			GtkObject *adjust;
@@ -859,7 +854,7 @@
 										1, 10, 10);
 			input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
 			gtk_widget_set_size_request(input, 50, -1);
-			gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL);
 		}
 		else
 		{
@@ -875,7 +870,7 @@
 				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
 					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
 			}
-			gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
 			g_signal_connect(G_OBJECT(input), "changed",
 							 G_CALLBACK(joinchat_set_sensitive_if_input_cb), data);
 		}
@@ -886,8 +881,6 @@
 			gtk_widget_grab_focus(input);
 			focus = FALSE;
 		}
-		gtk_label_set_mnemonic_widget(GTK_LABEL(label), input);
-		pidgin_set_accessible_label(input, label);
 		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
 		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
 		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
@@ -983,23 +976,14 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
-	rowbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, TRUE, TRUE, 0);
-
 	data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
-	label = gtk_label_new_with_mnemonic(_("_Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(data->sg, label);
-
 	data->account_menu = pidgin_account_option_menu_new(NULL, FALSE,
 			G_CALLBACK(joinchat_select_account_cb),
 			chat_account_filter_func, data);
 	gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label),
-								  GTK_WIDGET(data->account_menu));
-	pidgin_set_accessible_label (data->account_menu, label);
+
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL);
 
 	data->entries_box = gtk_vbox_new(FALSE, 5);
 	gtk_container_add(GTK_CONTAINER(vbox), data->entries_box);
@@ -1112,7 +1096,7 @@
 	}
 }
 
-static void pidgin_blist_add_chat_cb()
+static void pidgin_blist_add_chat_cb(void)
 {
 	GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
 	GtkTreeIter iter;
@@ -1132,7 +1116,7 @@
 	}
 }
 
-static void pidgin_blist_add_buddy_cb()
+static void pidgin_blist_add_buddy_cb(void)
 {
 	GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview));
 	GtkTreeIter iter;
@@ -2571,48 +2555,75 @@
 	int height;
 };
 
+static PangoLayout * create_pango_layout(const char *markup, int *width, int *height)
+{
+	PangoLayout *layout;
+	int w, h;
+
+	layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL);
+	pango_layout_set_markup(layout, markup, -1);
+	pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+	pango_layout_set_width(layout, 300000);
+
+	pango_layout_get_size (layout, &w, &h);
+	if (width)
+		*width = PANGO_PIXELS(w);
+	if (height)
+		*height = PANGO_PIXELS(h);
+	return layout;
+}
+
+static struct tooltip_data * create_tip_for_account(PurpleAccount *account)
+{
+	struct tooltip_data *td = g_new0(struct tooltip_data, 1);
+	td->status_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+		/* Yes, status_icon, not prpl_icon */
+	if (purple_account_is_disconnected(account))
+		gdk_pixbuf_saturate_and_pixelate(td->status_icon, td->status_icon, 0.0, FALSE);
+	td->layout = create_pango_layout(purple_account_get_username(account), &td->width, &td->height);
+	return td;
+}
+
 static struct tooltip_data * create_tip_for_node(PurpleBlistNode *node, gboolean full)
 {
-	char *tooltip_text = NULL;
 	struct tooltip_data *td = g_new0(struct tooltip_data, 1);
 	PurpleAccount *account = NULL;
-	char *tmp, *node_name;
-
-	if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+	char *tmp = NULL, *node_name = NULL, *tooltip_text = NULL;
+
+	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		account = ((PurpleBuddy*)(node))->account;
-	} else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
+	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
 		account = ((PurpleChat*)(node))->account;
 	}
 
 	td->status_icon = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_LARGE);
 	td->avatar = pidgin_blist_get_buddy_icon(node, !full, FALSE);
-	td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+	if (account != NULL) {
+		td->prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+	}
 	tooltip_text = pidgin_get_tooltip_text(node, full);
-	td->layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL);
-	td->name_layout = gtk_widget_create_pango_layout(gtkblist->tipwindow, NULL);
-
-	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+	if (tooltip_text && *tooltip_text) {
+		td->layout = create_pango_layout(tooltip_text, &td->width, &td->height);
+	}
+
+	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		tmp = g_markup_escape_text(purple_buddy_get_name((PurpleBuddy*)node), -1);
-	else
+	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
 		tmp = g_markup_escape_text(purple_chat_get_name((PurpleChat*)node), -1);
+	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+		tmp = g_markup_escape_text(purple_group_get_name((PurpleGroup*)node), -1);
+	} else {
+		/* I don't believe this can happen currently, I think
+		 * everything that calls this function checks for one of the
+		 * above node types first. */
+		tmp = g_strdup(_("Unknown node type"));
+	}
 	node_name = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>", tmp);
 	g_free(tmp);
 
-	pango_layout_set_markup(td->layout, tooltip_text, -1);
-	pango_layout_set_wrap(td->layout, PANGO_WRAP_WORD);
-	pango_layout_set_width(td->layout, 300000);
-
-	pango_layout_get_size (td->layout, &td->width, &td->height);
-	td->width = PANGO_PIXELS(td->width);
-	td->height = PANGO_PIXELS(td->height);
-
-	pango_layout_set_markup(td->name_layout, node_name, -1);
-	pango_layout_set_wrap(td->name_layout, PANGO_WRAP_WORD);
-	pango_layout_set_width(td->name_layout, 300000);
-
-	pango_layout_get_size (td->name_layout, &td->name_width, &td->name_height);
-	td->name_width = PANGO_PIXELS(td->name_width) + SMALL_SPACE + PRPL_SIZE;
-	td->name_height = MAX(PANGO_PIXELS(td->name_height), PRPL_SIZE + SMALL_SPACE);
+	td->name_layout = create_pango_layout(node_name, &td->name_width, &td->name_height);
+	td->name_width += SMALL_SPACE + PRPL_SIZE;
+	td->name_height = MAX(td->name_height, PRPL_SIZE + SMALL_SPACE);
 #if 0  /* PRPL Icon as avatar */
 	if(!td->avatar && full) {
 		td->avatar = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_LARGE);
@@ -2630,7 +2641,8 @@
 	return td;
 }
 
-static void pidgin_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, PurpleBlistNode *node)
+static gboolean
+pidgin_blist_paint_tip(GtkWidget *widget, gpointer null)
 {
 	GtkStyle *style;
 	int current_height, max_width;
@@ -2638,14 +2650,13 @@
 	int max_avatar_width;
 	GList *l;
 	int prpl_col = 0;
-        GtkTextDirection dir = gtk_widget_get_direction(widget);
+	GtkTextDirection dir = gtk_widget_get_direction(widget);
+	int status_size = 0;
 
 	if(gtkblist->tooltipdata == NULL)
-		return;
+		return FALSE;
 
 	style = gtkblist->tipwindow->style;
-	gtk_paint_flat_box(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-			NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1);
 
 	max_text_width = 0;
 	max_avatar_width = 0;
@@ -2657,13 +2668,15 @@
 		max_text_width = MAX(max_text_width,
 				MAX(td->width, td->name_width));
 		max_avatar_width = MAX(max_avatar_width, td->avatar_width);
-	}
-
-	max_width = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
+		if (td->status_icon)
+			status_size = STATUS_SIZE;
+	}
+
+	max_width = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
 	if (dir == GTK_TEXT_DIR_RTL)
 		prpl_col = TOOLTIP_BORDER + max_avatar_width + SMALL_SPACE;
 	else
-		prpl_col = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width - PRPL_SIZE;
+		prpl_col = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width - PRPL_SIZE;
 
 	current_height = 12;
 	for(l = gtkblist->tooltipdata; l; l = l->next)
@@ -2684,14 +2697,16 @@
 		}
 
 #if GTK_CHECK_VERSION(2,2,0)
-		if (dir == GTK_TEXT_DIR_RTL)
-			gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
-					0, 0, max_width - TOOLTIP_BORDER - STATUS_SIZE, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
-		else
-			gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
-					0, 0, TOOLTIP_BORDER, current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
-		if(td->avatar)
-		{
+		if (td->status_icon) {
+			if (dir == GTK_TEXT_DIR_RTL)
+				gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
+				                0, 0, max_width - TOOLTIP_BORDER - status_size, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+			else
+				gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
+				                0, 0, TOOLTIP_BORDER, current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
+		}
+
+		if(td->avatar) {
 			if (dir == GTK_TEXT_DIR_RTL)
 				gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL,
 						td->avatar, 0, 0, TOOLTIP_BORDER, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
@@ -2701,7 +2716,7 @@
 						current_height, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
 		}
 
-		if (!td->avatar_is_prpl_icon)
+		if (!td->avatar_is_prpl_icon && td->prpl_icon)
 			gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->prpl_icon,
 					0, 0,
 					prpl_col,
@@ -2709,41 +2724,49 @@
 					-1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
 
 #else
-		gdk_pixbuf_render_to_drawable(td->status_icon, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 12, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+		if (td->status_icon) {
+			gdk_pixbuf_render_to_drawable(td->status_icon, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 12, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
+		}
 		if(td->avatar)
 			gdk_pixbuf_render_to_drawable(td->avatar,
 					GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0,
 					max_width - (td->avatar_width + TOOLTIP_BORDER),
 					current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
 #endif
-		if (dir == GTK_TEXT_DIR_RTL) {
-			gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					max_width  -(TOOLTIP_BORDER + STATUS_SIZE +SMALL_SPACE) - PANGO_PIXELS(300000),
-					current_height, td->name_layout);
-		} else {
-			gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height, td->name_layout);
+		if (td->name_layout) {
+			if (dir == GTK_TEXT_DIR_RTL) {
+				gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						max_width  -(TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+						current_height, td->name_layout);
+			} else {
+				gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height, td->name_layout);
+			}
 		}
-		if (dir != GTK_TEXT_DIR_RTL) {
-			gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE, current_height + td->name_height, td->layout);
-		} else {
-			gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
-					NULL, gtkblist->tipwindow, "tooltip",
-					max_width - (TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE) - PANGO_PIXELS(300000),
-					current_height + td->name_height,
-					td->layout);
+
+		if (td->layout) {
+			if (dir != GTK_TEXT_DIR_RTL) {
+				gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						TOOLTIP_BORDER + status_size + SMALL_SPACE, current_height + td->name_height, td->layout);
+			} else {
+				gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
+						NULL, gtkblist->tipwindow, "tooltip",
+						max_width - (TOOLTIP_BORDER + status_size + SMALL_SPACE) - PANGO_PIXELS(300000),
+						current_height + td->name_height,
+						td->layout);
+			}
 		}
 
 		current_height += MAX(td->name_height + td->height, td->avatar_height) + TOOLTIP_BORDER;
 	}
-}
-
-
-void pidgin_blist_tooltip_destroy()
+	return FALSE;
+}
+
+static void
+pidgin_blist_destroy_tooltip_data(void)
 {
 	while(gtkblist->tooltipdata) {
 		struct tooltip_data *td = gtkblist->tooltipdata->data;
@@ -2754,17 +2777,94 @@
 			g_object_unref(td->status_icon);
 		if(td->prpl_icon)
 			g_object_unref(td->prpl_icon);
-		g_object_unref(td->layout);
-		g_object_unref(td->name_layout);
+		if (td->layout)
+			g_object_unref(td->layout);
+		if (td->name_layout)
+			g_object_unref(td->name_layout);
 		g_free(td);
 		gtkblist->tooltipdata = g_list_delete_link(gtkblist->tooltipdata, gtkblist->tooltipdata);
 	}
-
-	if (gtkblist->tipwindow == NULL)
-		return;
-
-	gtk_widget_destroy(gtkblist->tipwindow);
-	gtkblist->tipwindow = NULL;
+}
+
+void pidgin_blist_tooltip_destroy()
+{
+	pidgin_blist_destroy_tooltip_data();
+	pidgin_tooltip_destroy();
+}
+
+static gboolean
+pidgin_blist_create_tooltip_for_node(GtkWidget *widget, gpointer data, int *w, int *h)
+{
+	PurpleBlistNode *node = data;
+	int width, height;
+	GList *list;
+	int max_text_width = 0;
+	int max_avatar_width = 0;
+	int status_size = 0;
+
+	if (gtkblist->tooltipdata) {
+		gtkblist->tipwindow = NULL;
+		pidgin_blist_destroy_tooltip_data();
+	}
+
+	gtkblist->tipwindow = widget;
+	if (PURPLE_BLIST_NODE_IS_CHAT(node) ||
+	   PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		struct tooltip_data *td = create_tip_for_node(node, TRUE);
+		gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
+	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+		PurpleGroup *group = (PurpleGroup*)node;
+		GSList *accounts;
+		struct tooltip_data *td = create_tip_for_node(node, TRUE);
+		gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
+
+		/* Accounts with buddies in group */
+		accounts = purple_group_get_accounts(group);
+		for (; accounts != NULL;
+		     accounts = g_slist_delete_link(accounts, accounts)) {
+			PurpleAccount *account = accounts->data;
+			td = create_tip_for_account(account);
+			gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
+		}
+	} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+		PurpleBlistNode *child;
+		PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node);
+		width = height = 0;
+
+		for(child = node->child; child; child = child->next)
+		{
+			if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) {
+				struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child));
+				if (b == (PurpleBuddy *)child) {
+					gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td);
+				} else {
+					gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
+				}
+			}
+		}
+	} else {
+		return FALSE;
+	}
+
+	height = width = 0;
+	for (list = gtkblist->tooltipdata; list; list = list->next) {
+		struct tooltip_data *td = list->data;
+		max_text_width = MAX(max_text_width, MAX(td->width, td->name_width));
+		max_avatar_width = MAX(max_avatar_width, td->avatar_width);
+		height += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE, td->avatar_height),
+				TOOLTIP_BORDER + td->height + td->name_height);
+		if (td->status_icon)
+			status_size = MAX(status_size, STATUS_SIZE);
+	}
+	height += TOOLTIP_BORDER;
+	width = TOOLTIP_BORDER + status_size + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
+
+	if (w)
+		*w = width;
+	if (h)
+		*h = height;
+
+	return TRUE;
 }
 
 static gboolean pidgin_blist_expand_timeout(GtkWidget *tv)
@@ -2826,164 +2926,9 @@
 			 purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline")));
 }
 
-static gboolean pidgin_blist_tooltip_timeout(GtkWidget *tv)
-{
-	GtkTreePath *path;
-	GtkTreeIter iter;
-	PurpleBlistNode *node;
-	GValue val;
-	gboolean editable = FALSE;
-
-	/* If we're editing a cell (e.g. alias editing), don't show the tooltip */
-	g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL);
-	if (editable)
-		return FALSE;
-
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->tip_rect.x, gtkblist->tip_rect.y + (gtkblist->tip_rect.height/2), 
-		&path, NULL, NULL, NULL))
-		return FALSE;
-	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
-	val.g_type = 0;
-	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
-	node = g_value_get_pointer(&val);
-
-	pidgin_blist_draw_tooltip(node, gtkblist->window);
-
-	gtk_tree_path_free(path);
-	return FALSE;
-}
-
 void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget)
 {
-	int scr_w, scr_h, w, h, x, y;
-#if GTK_CHECK_VERSION(2,2,0)
-	int mon_num;
-	GdkScreen *screen = NULL;
-#endif
-	gboolean tooltip_top = FALSE;
-	struct _pidgin_blist_node *gtknode;
-	GdkRectangle mon_size;
-	int sig;
-	const char *name;
-	
-	if (node == NULL)
-		return;
-
-	/*
-	 * Attempt to free the previous tooltip.  I have a feeling
-	 * this is never needed... but just in case.
-	 */
-	pidgin_blist_tooltip_destroy();
-
-	gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
-
-	if(PURPLE_BLIST_NODE_IS_CHAT(node) || PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-		struct tooltip_data *td = create_tip_for_node(node, TRUE);
-		gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
-		w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE +
-			MAX(td->width, td->name_width) + SMALL_SPACE + td->avatar_width + TOOLTIP_BORDER;
-		h = TOOLTIP_BORDER + MAX(td->height + td->name_height, MAX(STATUS_SIZE, td->avatar_height))
-			+ TOOLTIP_BORDER;
-	} else if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
-		PurpleBlistNode *child;
-		PurpleBuddy *b = purple_contact_get_priority_buddy((PurpleContact *)node);
-		int max_text_width = 0;
-		int max_avatar_width = 0;
-		w = h = 0;
-
-		for(child = node->child; child; child = child->next)
-		{
-			if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) {
-				struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child));
-				if (b == (PurpleBuddy *)child) {
-					gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td);
-				} else {
-					gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
-				}
-				max_text_width = MAX(max_text_width, MAX(td->width, td->name_width));
-				max_avatar_width = MAX(max_avatar_width, td->avatar_width);
-				h += MAX(TOOLTIP_BORDER + MAX(STATUS_SIZE,td->avatar_height),
-						TOOLTIP_BORDER + td->height + td->name_height);
-			}
-		}
-		h += TOOLTIP_BORDER;
-		w = TOOLTIP_BORDER + STATUS_SIZE + SMALL_SPACE + max_text_width + SMALL_SPACE + max_avatar_width + TOOLTIP_BORDER;
-	} else {
-		gtk_widget_destroy(gtkblist->tipwindow);
-		gtkblist->tipwindow = NULL;
-		return;
-	}
-
-	if (gtkblist->tooltipdata == NULL) {
-		gtk_widget_destroy(gtkblist->tipwindow);
-		gtkblist->tipwindow = NULL;
-		return;
-	}
-
-	gtknode = node->ui_data;
-
-	name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget)));
-	gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE);
-	gtk_window_set_title(GTK_WINDOW(gtkblist->tipwindow), name ? name : _("Buddy List"));
-	gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE);
-	gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips");
-	g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event",
-			G_CALLBACK(pidgin_blist_paint_tip), NULL);
-	gtk_widget_ensure_style (gtkblist->tipwindow);
-
-#if GTK_CHECK_VERSION(2,2,0)
-	gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL);
-	mon_num = gdk_screen_get_monitor_at_point(screen, x, y);
-	gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size);
-
-	scr_w = mon_size.width + mon_size.x;
-	scr_h = mon_size.height + mon_size.y;
-#else
-	scr_w = gdk_screen_width();
-	scr_h = gdk_screen_height();
-	gdk_window_get_pointer(NULL, &x, &y, NULL);
-	mon_size.x = 0;
-	mon_size.y = 0;
-#endif
-
-#if GTK_CHECK_VERSION(2,2,0)
-	if (w > mon_size.width)
-	  w = mon_size.width - 10;
-
-	if (h > mon_size.height)
-	  h = mon_size.height - 10;
-#endif
-
-	x -= ((w >> 1) + 4);
-
-	if ((y + h + 4) > scr_h || tooltip_top)
-		y = y - h - 5;
-	else
-		y = y + 6;
-
-	if (y < mon_size.y)
-		y = mon_size.y;
-
-	if (y != mon_size.y) {
-		if ((x + w) > scr_w)
-			x -= (x + w + 5) - scr_w;
-		else if (x < mon_size.x)
-			x = mon_size.x;
-	} else {
-		x -= (w / 2 + 10);
-		if (x < mon_size.x)
-			x = mon_size.x;
-	}
-
-	gtk_widget_set_size_request(gtkblist->tipwindow, w, h);
-	gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y);
-	gtk_widget_show(gtkblist->tipwindow);
-
-	/* Hide the tooltip when the widget is destroyed */
-	sig = g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(pidgin_blist_tooltip_destroy), NULL);
-	g_signal_connect_swapped(G_OBJECT(gtkblist->tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig));
-
-	return;
+	pidgin_tooltip_show(widget, node, pidgin_blist_create_tooltip_for_node, pidgin_blist_paint_tip);
 }
 
 static gboolean pidgin_blist_drag_motion_cb(GtkWidget *tv, GdkDragContext *drag_context,
@@ -3033,31 +2978,34 @@
 	return FALSE;
 }
 
-static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null)
-{
-	GtkTreePath *path;
-	int delay;
-
-	delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
-
-	if (delay == 0)
+static gboolean
+pidgin_blist_create_tooltip(GtkWidget *widget, GtkTreePath *path,
+		gpointer null, int *w, int *h)
+{
+	GtkTreeIter iter;
+	PurpleBlistNode *node;
+	GValue val;
+	gboolean editable = FALSE;
+
+	/* If we're editing a cell (e.g. alias editing), don't show the tooltip */
+	g_object_get(G_OBJECT(gtkblist->text_rend), "editable", &editable, NULL);
+	if (editable)
 		return FALSE;
 
-	if (gtkblist->timeout) {
-		if ((event->y > gtkblist->tip_rect.y) && ((event->y - gtkblist->tip_rect.height) < gtkblist->tip_rect.y))
-			return FALSE;
-		/* We've left the cell.  Remove the timeout and create a new one below */
-		pidgin_blist_tooltip_destroy();
-		g_source_remove(gtkblist->timeout);
-	}
-
-	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
-	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &gtkblist->tip_rect);
-
-	if (path)
-		gtk_tree_path_free(path);
-	gtkblist->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_tooltip_timeout, tv);
-
+	if (gtkblist->tooltipdata) {
+		gtkblist->tipwindow = NULL;
+		pidgin_blist_destroy_tooltip_data();
+	}
+
+	gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path);
+	val.g_type = 0;
+	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
+	node = g_value_get_pointer(&val);
+	return pidgin_blist_create_tooltip_for_node(widget, node, w, h);
+}
+
+static gboolean pidgin_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null)
+{
 	if (gtkblist->mouseover_contact) {
 		if ((event->y < gtkblist->contact_rect.y) || ((event->y - gtkblist->contact_rect.height) > gtkblist->contact_rect.y)) {
 			pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact);
@@ -3068,9 +3016,8 @@
 	return FALSE;
 }
 
-static void pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n)
-{
-
+static gboolean pidgin_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n)
+{
 	if (gtkblist->timeout) {
 		g_source_remove(gtkblist->timeout);
 		gtkblist->timeout = 0;
@@ -3081,14 +3028,13 @@
 		gtkblist->drag_timeout = 0;
 	}
 
-	pidgin_blist_tooltip_destroy();
-
 	if (gtkblist->mouseover_contact &&
 		!((e->x > gtkblist->contact_rect.x) && (e->x < (gtkblist->contact_rect.x + gtkblist->contact_rect.width)) &&
 		 (e->y > gtkblist->contact_rect.y) && (e->y < (gtkblist->contact_rect.y + gtkblist->contact_rect.height)))) {
 			pidgin_blist_collapse_contact_cb(NULL, gtkblist->mouseover_contact);
 		gtkblist->mouseover_contact = NULL;
 	}
+	return FALSE;
 }
 
 static void
@@ -3179,7 +3125,7 @@
 		if (g_list_length(purple_connections_get_all()) > 1)
 		{
 			tmp = g_markup_escape_text(chat->account->username, -1);
-			g_string_append_printf(str, _("\n<b>Account:</b> %s"), tmp);
+			g_string_append_printf(str, _("<b>Account:</b> %s"), tmp);
 			g_free(tmp);
 		}
 
@@ -3371,10 +3317,35 @@
 		g_free(tmp);
 
 		purple_notify_user_info_destroy(user_info);
-	}
-
-	purple_signal_emit(pidgin_blist_get_handle(),
-			 "drawing-tooltip", node, str, full);
+	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
+		PurpleGroup *group = (PurpleGroup*)node;
+		PurpleNotifyUserInfo *user_info;
+
+		user_info = purple_notify_user_info_new();
+
+		/* Total buddies (from online accounts) in group */
+		tmp = g_strdup_printf("%d",
+		                      purple_blist_get_group_size(group, FALSE));
+		purple_notify_user_info_add_pair(user_info, _("Total Buddies"),
+		                                 tmp);
+		g_free(tmp);
+
+		/* Online buddies in group */
+		tmp = g_strdup_printf("%d",
+		                      purple_blist_get_group_online_count(group));
+		purple_notify_user_info_add_pair(user_info, _("Online Buddies"),
+		                                 tmp);
+		g_free(tmp);
+
+		tmp = purple_notify_user_info_get_text_with_newline(user_info, "\n");
+		g_string_append(str, tmp);
+		g_free(tmp);
+
+		purple_notify_user_info_destroy(user_info);
+	}
+
+	purple_signal_emit(pidgin_blist_get_handle(), "drawing-tooltip",
+	                   node, str, full);
 
 	return g_string_free(str, FALSE);
 }
@@ -3795,7 +3766,7 @@
 	return text;
 }
 
-static void pidgin_blist_restore_position()
+static void pidgin_blist_restore_position(void)
 {
 	int blist_x, blist_y, blist_width, blist_height;
 
@@ -3942,7 +3913,7 @@
 }
 
 static void
-unseen_conv_menu()
+unseen_conv_menu(void)
 {
 	static GtkWidget *menu = NULL;
 	GList *convs = NULL;
@@ -4228,7 +4199,8 @@
 	pidgin_blist_sort_method_set(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type"));
 }
 
-static void _prefs_change_redo_list()
+static void _prefs_change_redo_list(const char *name, PurplePrefType type,
+                                    gconstpointer val, gpointer data)
 {
 	GtkTreeSelection *sel;
 	GtkTreeIter iter;
@@ -4643,6 +4615,13 @@
 }
 
 static void
+clear_elsewhere_errors(PidginMiniDialog *mini_dialog,
+                       gpointer unused)
+{
+	elsewhere_foreach_account(mini_dialog, purple_account_clear_current_error);
+}
+
+static void
 ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist)
 {
 	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
@@ -4657,6 +4636,12 @@
 	pidgin_mini_dialog_add_button(mini_dialog, _("Re-enable"),
 		reconnect_elsewhere_accounts, NULL);
 
+	/* Make dismissing the dialog clear the errors.  The "destroy" signal
+	 * does not appear to fire at quit, which is fortunate!
+	 */
+	g_signal_connect(G_OBJECT(mini_dialog), "destroy",
+		(GCallback) clear_elsewhere_errors, NULL);
+
 	add_error_dialog(gtkblist, GTK_WIDGET(mini_dialog));
 
 	/* Set priv->signed_on_elsewhere to NULL when the dialog is destroyed */
@@ -4885,6 +4870,9 @@
 #endif
 
 	gtk_tooltips_force_window (tooltips);
+#if GTK_CHECK_VERSION(2, 12, 0)
+	gtk_widget_set_name (tooltips->tip_window, "gtk-tooltips");
+#endif
 	gtk_widget_ensure_style (tooltips->tip_window);
 	style = gtk_widget_get_style (tooltips->tip_window);
 
@@ -5069,8 +5057,9 @@
 	gtk_label_set_line_wrap(GTK_LABEL(gtkblist->headline_label), TRUE);
 	gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_image, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(gtkblist->headline_hbox), gtkblist->headline_label, TRUE, TRUE, 0);
-	g_signal_connect(gtkblist->headline_hbox,
-			 "style-set",
+	g_signal_connect(gtkblist->headline_label,   /* connecting on headline_hbox doesn't work, because
+	                                                the signal is not emitted when theme is changed */
+			"style-set",
 			 G_CALLBACK(headline_style_set),
 			 NULL);
 	g_signal_connect (gtkblist->headline_hbox,
@@ -5152,12 +5141,14 @@
 #ifdef _WIN32
 	g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-begin", G_CALLBACK(pidgin_blist_drag_begin), NULL);
 #endif
-
 	g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-motion", G_CALLBACK(pidgin_blist_drag_motion_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL);
+	g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL);
 
 	/* Tooltips */
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(pidgin_blist_motion_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(pidgin_blist_leave_cb), NULL);
+	pidgin_tooltip_setup_for_treeview(gtkblist->treeview, NULL,
+			pidgin_blist_create_tooltip,
+			pidgin_blist_paint_tip);
 
 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
 
@@ -6505,20 +6496,10 @@
 
 	for (tmp = list; tmp; tmp = tmp->next)
 	{
-		GtkWidget *label;
-		GtkWidget *rowbox;
 		GtkWidget *input;
 
 		pce = tmp->data;
 
-		rowbox = gtk_hbox_new(FALSE, 5);
-		gtk_box_pack_start(GTK_BOX(data->entries_box), rowbox, FALSE, FALSE, 0);
-
-		label = gtk_label_new_with_mnemonic(pce->label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-		gtk_size_group_add_widget(data->sg, label);
-		gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 		if (pce->is_int)
 		{
 			GtkObject *adjust;
@@ -6526,7 +6507,7 @@
 										1, 10, 10);
 			input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
 			gtk_widget_set_size_request(input, 50, -1);
-			gtk_box_pack_end(GTK_BOX(rowbox), input, FALSE, FALSE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL);
 		}
 		else
 		{
@@ -6542,7 +6523,7 @@
 				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
 					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
 			}
-			gtk_box_pack_end(GTK_BOX(rowbox), input, TRUE, TRUE, 0);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
 			g_signal_connect(G_OBJECT(input), "changed",
 							 G_CALLBACK(addchat_set_sensitive_if_input_cb), data);
 		}
@@ -6553,8 +6534,6 @@
 			gtk_widget_grab_focus(input);
 			focus = FALSE;
 		}
-		gtk_label_set_mnemonic_widget(GTK_LABEL(label), input);
-		pidgin_set_accessible_label(input, label);
 		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
 		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
 		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
@@ -6597,7 +6576,6 @@
 	GList *l;
 	PurpleConnection *gc;
 	GtkWidget *label;
-	GtkWidget *rowbox;
 	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *img;
@@ -6673,20 +6651,10 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
-	rowbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_size_group_add_widget(data->sg, label);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 	data->account_menu = pidgin_account_option_menu_new(account, FALSE,
 			G_CALLBACK(addchat_select_account_cb),
 			chat_account_filter_func, data);
-	gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_menu);
-	pidgin_set_accessible_label (data->account_menu, label);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL);
 
 	data->entries_box = gtk_vbox_new(FALSE, 5);
 	gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0);
@@ -6694,36 +6662,17 @@
 
 	rebuild_addchat_entries(data);
 
-	rowbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("A_lias:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_size_group_add_widget(data->sg, label);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 	data->alias_entry = gtk_entry_new();
 	if (alias != NULL)
 		gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias);
-	gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0);
 	gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->alias_entry);
-	pidgin_set_accessible_label (data->alias_entry, label);
+
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_lias:"), data->sg, data->alias_entry, TRUE, NULL);
 	if (name != NULL)
 		gtk_widget_grab_focus(data->alias_entry);
 
-	rowbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Group:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_size_group_add_widget(data->sg, label);
-	gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0);
-
 	data->group_combo = pidgin_text_combo_box_entry_new(group ? group->name : NULL, groups_tree());
-	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);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Group:"), data->sg, data->group_combo, TRUE, NULL);
 	
 	data->autojoin = gtk_check_button_new_with_mnemonic(_("Auto_join when account becomes online."));
 	data->persistent = gtk_check_button_new_with_mnemonic(_("_Hide chat when the window is closed."));
@@ -6997,7 +6946,8 @@
 	pidgin_blist_update_sort_methods();
 }
 
-void pidgin_blist_sort_method_unreg(const char *id){
+void pidgin_blist_sort_method_unreg(const char *id)
+{
 	GList *l = pidgin_blist_sort_methods;
 
 	while(l) {
@@ -7009,6 +6959,7 @@
 			g_free(method);
 			break;
 		}
+		l = l->next;
 	}
 	pidgin_blist_update_sort_methods();
 }
--- a/pidgin/gtkcertmgr.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkcertmgr.c	Mon Jan 07 03:40:27 2008 +0000
@@ -430,7 +430,7 @@
 		/* Set up the display columns */
 		renderer = gtk_cell_renderer_text_new();
 		column = gtk_tree_view_column_new_with_attributes(
-			"Hostname",
+			_("Hostname"),
 			renderer,
 			"text", TPM_HOSTNAME_COLUMN,
 			NULL);
@@ -545,12 +545,13 @@
    So if it is set, don't open another one! */
 CertMgrDialog *certmgr_dialog = NULL;
 
-static void
+static gboolean
 certmgr_close_cb(GtkWidget *w, CertMgrDialog *dlg)
 {
 	/* TODO: Ignoring the arguments to this function may not be ideal,
 	   but there *should* only be "one dialog to rule them all" at a time*/
 	pidgin_certmgr_hide();
+	return FALSE;
 }
 
 void
@@ -559,7 +560,6 @@
 	CertMgrDialog *dlg;
 	GtkWidget *win;
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 
 	/* Enumerate all the certificates on file */
 	{
@@ -599,7 +599,7 @@
 	dlg = certmgr_dialog = g_new0(CertMgrDialog, 1);
 
 	win = dlg->window =
-		pidgin_create_window(_("Certificate Manager"),/* Title */
+		pidgin_create_dialog(_("Certificate Manager"),/* Title */
 				     PIDGIN_HIG_BORDER, /*Window border*/
 				     "certmgr",         /* Role */
 				     TRUE); /* Allow resizing */
@@ -611,9 +611,7 @@
 	gtk_window_set_default_size(GTK_WINDOW(win), 400, 400);
 
 	/* Main vbox */
-	vbox = gtk_vbox_new( FALSE, PIDGIN_HIG_BORDER );
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Notebook of various certificate managers */
 	dlg->notebook = gtk_notebook_new();
@@ -622,19 +620,9 @@
 			   0);
 	gtk_widget_show(dlg->notebook);
 
-	/* Box for the close button */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Close button */
-	dlg->closebutton = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), dlg->closebutton, FALSE, FALSE, 0);
-	gtk_widget_show(dlg->closebutton);
-	g_signal_connect(G_OBJECT(dlg->closebutton), "clicked",
-			 G_CALLBACK(certmgr_close_cb), dlg);
+	dlg->closebutton = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
+			G_CALLBACK(certmgr_close_cb), dlg);
 
 	/* Add the defined certificate managers */
 	/* TODO: Find a way of determining whether each is shown or not */
--- a/pidgin/gtkconn.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkconn.c	Mon Jan 07 03:40:27 2008 +0000
@@ -162,11 +162,6 @@
 		if (info != NULL)
 			g_hash_table_remove(auto_reconns, account);
 
-		/*
-		 * TODO: Do we really want to disable the account when it's
-		 * disconnected by wants_to_die?  This happens when you sign
-		 * on from somewhere else, or when you enter an invalid password.
-		 */
 		purple_account_set_enabled(account, PIDGIN_UI, FALSE);
 	}
 
@@ -182,7 +177,7 @@
 	}
 }
 
-static void pidgin_connection_network_connected ()
+static void pidgin_connection_network_connected (void)
 {
 	GList *list, *l;
 	PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist();
@@ -201,7 +196,7 @@
 	g_list_free(list);
 }
 
-static void pidgin_connection_network_disconnected ()
+static void pidgin_connection_network_disconnected (void)
 {
 	GList *list, *l;
 	PidginBuddyList *gtkblist = pidgin_blist_get_default_gtk_blist();
--- a/pidgin/gtkconv.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkconv.c	Mon Jan 07 03:40:27 2008 +0000
@@ -67,6 +67,7 @@
 #include "gtkthemes.h"
 #include "gtkutils.h"
 #include "pidginstock.h"
+#include "pidgintooltip.h"
 
 #include "gtknickcolors.h"
 
@@ -95,9 +96,10 @@
 
 #define	PIDGIN_CONV_ALL	((1 << 7) - 1)
 
-#define SEND_COLOR "#204a87"
-#define RECV_COLOR "#cc0000"
-#define HIGHLIGHT_COLOR "#AF7F00"
+#define DEFAULT_SEND_COLOR "#204a87"
+#define DEFAULT_RECV_COLOR "#cc0000"
+#define DEFAULT_HIGHLIGHT_COLOR "#AF7F00"
+#define DEFAULT_ACTION_COLOR "#062585"
 
 /* Undef this to turn off "custom-smiley" debug messages */
 #define DEBUG_CUSTOM_SMILEY
@@ -156,6 +158,7 @@
 static void conv_set_unseen(PurpleConversation *gtkconv, PidginUnseenState state);
 static void gtkconv_set_unseen(PidginConversation *gtkconv, PidginUnseenState state);
 static void update_typing_icon(PidginConversation *gtkconv);
+static void update_typing_message(PidginConversation *gtkconv, const char *message);
 static const char *item_factory_translate_func (const char *path, gpointer func_data);
 gboolean pidgin_conv_has_focus(PurpleConversation *conv);
 static void pidgin_conv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data);
@@ -166,8 +169,6 @@
 static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win);
 static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv);
 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,
@@ -1404,6 +1405,7 @@
 	PidginWindow *win = data;
 	PurpleConversation *conv;
 	gboolean logging;
+	PurpleBlistNode *node;
 
 	conv = pidgin_conv_window_get_active_conversation(win);
 
@@ -1414,6 +1416,8 @@
 
 	if (logging == purple_conversation_is_logging(conv))
 		return;
+	
+	node = get_conversation_blist_node(conv);
 
 	if (logging)
 	{
@@ -1437,6 +1441,27 @@
 		/* Disable the logging second, so that the above message can be logged. */
 		purple_conversation_set_logging(conv, FALSE);
 	}
+
+	/* Save the setting IFF it's different than the pref. */
+	switch (conv->type)
+	{
+		case PURPLE_CONV_TYPE_IM:
+			if (logging == purple_prefs_get_bool("/purple/logging/log_ims"))
+				purple_blist_node_remove_setting(node, "enable-logging");
+			else
+				purple_blist_node_set_bool(node, "enable-logging", logging);
+			break;
+
+		case PURPLE_CONV_TYPE_CHAT:
+			if (logging == purple_prefs_get_bool("/purple/logging/log_chats"))
+				purple_blist_node_remove_setting(node, "enable-logging");
+			else
+				purple_blist_node_set_bool(node, "enable-logging", logging);
+			break;
+
+		default:
+			break;
+	}
 }
 
 static void
@@ -2474,7 +2499,6 @@
 {
 	PidginConversation *gtkconv;
 	PidginWindow *win;
-	PurpleBuddy *b;
 	GList *l;
 	GdkPixbuf *status = NULL;
 	GdkPixbuf *infopane_status = NULL;
@@ -2487,13 +2511,18 @@
 	if (conv != gtkconv->active_conv)
 		return;
 
-
 	status = pidgin_conv_get_tab_icon(conv, TRUE);
 	infopane_status = pidgin_conv_get_tab_icon(conv, FALSE);
 
-	b = purple_find_buddy(conv->account, conv->name);
-	if (b)
-		emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b);
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+		PurpleBuddy *b = purple_find_buddy(conv->account, conv->name);
+		if (b)
+			emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b);
+	} else {
+		PurpleChat *c = purple_blist_find_chat(conv->account, conv->name);
+		if (c)
+			emblem = pidgin_blist_get_emblem((PurpleBlistNode*)c);
+	}
 
 	g_return_if_fail(status != NULL);
 
@@ -3444,6 +3473,34 @@
 }
 
 static void
+update_typing_message(PidginConversation *gtkconv, const char *message)
+{
+	GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml));
+	GtkTextMark *stmark, *enmark;
+
+	stmark = gtk_text_buffer_get_mark(buffer, "typing-notification-start");
+	enmark = gtk_text_buffer_get_mark(buffer, "typing-notification-end");
+	if (stmark && enmark) {
+		GtkTextIter start, end;
+		gtk_text_buffer_get_iter_at_mark(buffer, &start, stmark);
+		gtk_text_buffer_get_iter_at_mark(buffer, &end, enmark);
+		gtk_text_buffer_delete_mark(buffer, stmark);
+		gtk_text_buffer_delete_mark(buffer, enmark);
+		gtk_text_buffer_delete(buffer, &start, &end);
+	} else if (message && *message == '\n' && !*(message + 1))
+		message = NULL;
+
+	if (message) {
+		GtkTextIter iter;
+		gtk_text_buffer_get_end_iter(buffer, &iter);
+		gtk_text_buffer_create_mark(buffer, "typing-notification-start", &iter, TRUE);
+		gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, message, -1, "TYPING-NOTIFICATION", NULL);
+		gtk_text_buffer_get_end_iter(buffer, &iter);
+		gtk_text_buffer_create_mark(buffer, "typing-notification-end", &iter, TRUE);
+	}
+}
+
+static void
 update_typing_icon(PidginConversation *gtkconv)
 {
 	PidginWindow *gtkwin;
@@ -3451,6 +3508,7 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 	char *stock_id;
 	const char *tooltip;
+	char *message = NULL;
 
 	gtkwin = gtkconv->win;
 
@@ -3469,6 +3527,7 @@
 			g_source_remove(gtkconv->u.im->typing_timer);
 			gtkconv->u.im->typing_timer = 0;
 		}
+		update_typing_message(gtkconv, "\n");
 		return;
 	}
 
@@ -3478,9 +3537,11 @@
 		}
 		stock_id = PIDGIN_STOCK_ANIMATION_TYPING1;
 		tooltip = _("User is typing...");
+		message = g_strdup_printf(_("\n%s is typing..."), purple_conversation_get_title(conv));
 	} else {
 		stock_id = PIDGIN_STOCK_ANIMATION_TYPING5;
 		tooltip = _("User has typed something and stopped");
+		message = g_strdup_printf(_("\n%s has typed something and stopped"), purple_conversation_get_title(conv));
 		if (gtkconv->u.im->typing_timer != 0) {
 			g_source_remove(gtkconv->u.im->typing_timer);
 			gtkconv->u.im->typing_timer = 0;
@@ -3503,6 +3564,8 @@
 	}
 
 	gtk_widget_show(gtkwin->menu.typing_icon);
+	update_typing_message(gtkconv, message);
+	g_free(message);
 }
 
 static gboolean
@@ -3862,7 +3925,7 @@
 	if (is_me)
 	{
 		GdkColor send_color;
-		gdk_color_parse(SEND_COLOR, &send_color);
+		gdk_color_parse(DEFAULT_SEND_COLOR, &send_color);
 
 #if GTK_CHECK_VERSION(2,6,0)
 		gtk_list_store_insert_with_values(ls, &iter,
@@ -4420,8 +4483,8 @@
 
 	lines = gtk_text_buffer_get_line_count(buffer);
 
-	/* Show a maximum of 4 lines, minimum of 2 */
-	lines = MIN(MAX(lines, 2), 4);
+	/* Show a maximum of 4 lines */
+	lines = MIN(lines, 4);
 	wrapped_lines = MIN(MAX(wrapped_lines, 2), 4);
 
 	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(gtkconv->entry));
@@ -4483,6 +4546,36 @@
 	}
 }
 
+static gboolean
+pidgin_conv_userlist_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
+		gpointer userdata, int *w, int *h)
+{
+	PidginConversation *gtkconv = userdata;
+	GtkTreeIter iter;
+	GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkconv->u.chat->list));
+	PurpleConversation *conv = gtkconv->active_conv;
+	PurpleBlistNode *node;
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleAccount *account = purple_conversation_get_account(conv);
+	char *who = NULL;
+
+	if (account->gc == NULL)
+		return FALSE;
+
+	if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
+		return FALSE;
+
+	gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1);
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl);
+	node = (PurpleBlistNode*)(purple_find_buddy(conv->account, who));
+	if (node && prpl_info && (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME))
+		pidgin_blist_draw_tooltip(node, gtkconv->infopane);
+
+	g_free(who);
+	return FALSE;
+}
+
 static void
 setup_chat_userlist(PidginConversation *gtkconv, GtkWidget *hpaned)
 {
@@ -4537,14 +4630,13 @@
 
 	g_signal_connect(G_OBJECT(list), "button_press_event",
 					 G_CALLBACK(right_click_chat_cb), gtkconv);
-	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_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);
 
+	pidgin_tooltip_setup_for_treeview(list, gtkconv,
+			pidgin_conv_userlist_create_tooltip, NULL);
+
 	rend = gtk_cell_renderer_text_new();
 	g_object_set(rend,
 				 "foreground-set", TRUE,
@@ -4580,32 +4672,12 @@
 	gtk_container_add(GTK_CONTAINER(sw), list);
 }
 
-/* Stuff used to display tooltips on the infopane */
-static struct {
-	int timeout;
-	PidginConversation *gtkconv;   /* This is the Pidgin conversation that
-	                                  triggered the tooltip */
-	int userlistx;
-	int userlisty;
-} tooltip;
-
-static void
-reset_tooltip()
-{
-	if (tooltip.timeout != 0) {
-		g_source_remove(tooltip.timeout);
-		tooltip.timeout = 0;
-	}
-	tooltip.gtkconv = NULL;
-}
-
 static gboolean
-pidgin_conv_tooltip_timeout(PidginConversation *gtkconv)
+pidgin_conv_create_tooltip(GtkWidget *tipwindow, gpointer userdata, int *w, int *h)
 {
 	PurpleBlistNode *node = NULL;
 	PurpleConversation *conv;
-
-	g_return_val_if_fail (tooltip.gtkconv == gtkconv, FALSE);
+	PidginConversation *gtkconv = userdata;
 
 	conv = gtkconv->active_conv;
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
@@ -4628,102 +4700,6 @@
 	return FALSE;
 }
 
-static void
-pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv)
-{
-	pidgin_blist_tooltip_destroy();
-	reset_tooltip();
-}
-
-static gboolean 
-pidgin_conv_motion_cb (GtkWidget *infopane, GdkEventMotion *event, PidginConversation *gtkconv)
-{
-	int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
-
-	pidgin_blist_tooltip_destroy();
-	if (delay == 0)
-		return FALSE;
-
-	if (tooltip.timeout != 0)
-		g_source_remove(tooltip.timeout);
-
-	tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_conv_tooltip_timeout, gtkconv);
-	tooltip.gtkconv = gtkconv;
-	return FALSE;
-}
-
-static gboolean
-pidgin_userlist_tooltip_timeout(PidginConversation *gtkconv)
-{
-	PurplePluginProtocolInfo *prpl_info;
-	PurpleConversation *conv = gtkconv->active_conv;
-	PidginChatPane *gtkchat;
-	PurpleBlistNode *node = NULL;
-	PurpleAccount *account;
-	GtkTreePath *path;
-	GtkTreeIter iter;
-	GtkTreeModel *model;
-	GtkTreeViewColumn *column;
-	gchar *who;
-	int x, y;
-
-	gtkchat = gtkconv->u.chat;
-	account = purple_conversation_get_account(conv);
-
-	if (account->gc == NULL)
-		return FALSE;
-
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl);
-
-	model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
-
-	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);
-	gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1);
-
-	node = (PurpleBlistNode*)(purple_find_buddy(conv->account, who));
-	if (node && prpl_info && (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME))
-		pidgin_blist_draw_tooltip(node, gtkconv->infopane);
-
-	g_free(who);
-	gtk_tree_path_free(path);
-
-
-	return FALSE;
-}
-
-static gboolean
-pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv)
-{
-	PurpleConversation *conv;
-	PurpleAccount *account;
-	int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
-	
-	pidgin_blist_tooltip_destroy();
-	if (delay == 0)
-		return FALSE;
-
-	if (tooltip.timeout != 0)
-		g_source_remove(tooltip.timeout);
-	tooltip.timeout = 0;
-
-	conv = gtkconv->active_conv;
-	account = purple_conversation_get_account(conv);
-
-	if (account->gc == NULL)
-		return FALSE;
-
-	tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_userlist_tooltip_timeout, gtkconv);
-	tooltip.gtkconv = gtkconv;
-	tooltip.userlistx = event->x;
-	tooltip.userlisty = event->y;
-
-	return FALSE;
-}
- 
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
@@ -4757,10 +4733,8 @@
 	g_signal_connect(G_OBJECT(event_box), "button-press-event",
 	                 G_CALLBACK(infopane_press_cb), gtkconv);
 
-	g_signal_connect(G_OBJECT(event_box), "motion-notify-event", 
-			G_CALLBACK(pidgin_conv_motion_cb), gtkconv);
-	g_signal_connect(G_OBJECT(event_box), "leave-notify-event", 
-			G_CALLBACK(pidgin_conv_leave_cb), gtkconv);
+	pidgin_tooltip_setup_for_widget(event_box, gtkconv,
+		pidgin_conv_create_tooltip, NULL);
 
 	gtkconv->infopane = gtk_cell_view_new();
 	gtkconv->infopane_model = gtk_list_store_new(CONV_NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF);
@@ -5064,6 +5038,7 @@
 	GtkWidget *pane = NULL;
 	GtkWidget *tab_cont;
 	PurpleBlistNode *convnode;
+	PurpleValue *value;
 
 	if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) {
 		conv->ui_data = gtkconv;
@@ -5133,6 +5108,13 @@
 	g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received",
 	                 G_CALLBACK(conv_dnd_recv), gtkconv);
 
+	gtk_text_buffer_create_tag(GTK_IMHTML(gtkconv->imhtml)->text_buffer, "TYPING-NOTIFICATION",
+			"foreground", "#888888",
+			"justification", GTK_JUSTIFY_LEFT,  /* XXX: RTL'ify */
+			"weight", PANGO_WEIGHT_BOLD,
+			"scale", PANGO_SCALE_SMALL,
+			NULL);
+
 	/* Setup the container for the tab. */
 	gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 	g_object_set_data(G_OBJECT(tab_cont), "PidginConversation", gtkconv);
@@ -5144,6 +5126,13 @@
 	if (convnode == NULL || !purple_blist_node_get_bool(convnode, "gtk-mute-sound"))
 		gtkconv->make_sound = TRUE;
 
+	if (convnode != NULL &&
+	    (value = g_hash_table_lookup(convnode->settings, "enable-logging")) &&
+	    purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN)
+	{
+		purple_conversation_set_logging(conv, purple_value_get_boolean(value));
+	}
+
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar"))
 		gtk_widget_show(gtkconv->toolbar);
 	else
@@ -5274,9 +5263,6 @@
 		g_source_remove(gtkconv->attach.timer);
 	}
 
-	if (tooltip.gtkconv == gtkconv)
-		reset_tooltip();
-
 	g_free(gtkconv);
 }
 
@@ -5706,6 +5692,7 @@
 		}
 		else {
 			if (purple_message_meify(new_message, -1)) {
+				GdkColor *col;
 				str = g_malloc(1024);
 
 				if (flags & PURPLE_MESSAGE_AUTO_RESP) {
@@ -5718,9 +5705,20 @@
 				}
 
 				if (flags & PURPLE_MESSAGE_NICK)
-					strcpy(color, HIGHLIGHT_COLOR);
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL);
 				else
-					strcpy(color, "#062585");
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "action-name-color", &col, NULL);
+
+				if(col) {
+					g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+						col->red >> 8, col->green >> 8, col->blue >> 8);
+				}
+				else {
+					if (flags & PURPLE_MESSAGE_NICK)
+						strcpy(color, DEFAULT_HIGHLIGHT_COLOR);
+					else
+						strcpy(color, DEFAULT_ACTION_COLOR);
+				}
 			}
 			else {
 				str = g_malloc(1024);
@@ -5732,19 +5730,46 @@
 					g_snprintf(str, 1024, "%s:", alias_escaped);
 					tag_end_offset = 1;
 				}
-				if (flags & PURPLE_MESSAGE_NICK)
-					strcpy(color, HIGHLIGHT_COLOR);
+				if (flags & PURPLE_MESSAGE_NICK) {
+					GdkColor *col;
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL);
+					if(col) {
+						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+							col->red >> 8, col->green >> 8, col->blue >> 8);
+					}
+					else {
+						strcpy(color, DEFAULT_HIGHLIGHT_COLOR);
+					}
+				}
 				else if (flags & PURPLE_MESSAGE_RECV) {
 					if (type == PURPLE_CONV_TYPE_CHAT) {
 						GdkColor *col = get_nick_color(gtkconv, name);
 
 						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
 							   col->red >> 8, col->green >> 8, col->blue >> 8);
-					} else
-						strcpy(color, RECV_COLOR);
+					} else {
+						GdkColor *col;
+						gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "receive-name-color", &col, NULL);
+						if(col) {
+							g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+								col->red >> 8, col->green >> 8, col->blue >> 8);
+						}
+						else {
+							strcpy(color, DEFAULT_RECV_COLOR);
+						}
+					}
 				}
-				else if (flags & PURPLE_MESSAGE_SEND)
-					strcpy(color, SEND_COLOR);
+				else if (flags & PURPLE_MESSAGE_SEND) {
+					GdkColor *col;
+					gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "send-name-color", &col, NULL);
+					if(col) {
+						g_snprintf(color, sizeof(color), "#%02X%02X%02X",
+							col->red >> 8, col->green >> 8, col->blue >> 8);
+					}
+					else {
+						strcpy(color, DEFAULT_SEND_COLOR);
+					}
+				}
 				else {
 					purple_debug_error("gtkconv", "message missing flags\n");
 					strcpy(color, "#000000");
@@ -5865,6 +5890,7 @@
 		(type == PURPLE_CONV_TYPE_IM ? "displayed-im-msg" : "displayed-chat-msg"),
 		account, name, displaying, conv, flags);
 	g_free(displaying);
+	update_typing_message(gtkconv, NULL);
 }
 
 static void
@@ -6958,10 +6984,8 @@
                               GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
 	g_signal_connect(G_OBJECT(event), "button-press-event",
 					 G_CALLBACK(icon_menu), gtkconv);
-	g_signal_connect(G_OBJECT(event), "motion-notify-event",
-			G_CALLBACK(pidgin_conv_motion_cb), gtkconv);
-	g_signal_connect(G_OBJECT(event), "leave-notify-event",
-			G_CALLBACK(pidgin_conv_leave_cb), gtkconv);
+
+	pidgin_tooltip_setup_for_widget(event, gtkconv, pidgin_conv_create_tooltip, NULL);
 	gtk_widget_show(event);
 
 	gtkconv->u.im->icon = gtk_image_new_from_pixbuf(scale);
@@ -7972,6 +7996,7 @@
 		/* Set default tab colors */
 		GString *str = g_string_new(NULL);
 		GtkSettings *settings = gtk_settings_get_default();
+		GtkStyle *parent = gtk_rc_get_style_by_paths(settings, "tab-container.tab-label*", NULL, G_TYPE_NONE), *now;
 		struct {
 			const char *stylename;
 			const char *labelname;
@@ -7986,8 +8011,9 @@
 		};
 		int iter;
 		for (iter = 0; styles[iter].stylename; iter++) {
-			if (!gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE))
-				/* Apparently both ACTIVE and NORMAL are required */
+			now = gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE);
+			if (parent == now ||
+					(parent && now && parent->rc_style == now->rc_style)) {
 				g_string_append_printf(str, "style \"%s\" {\n"
 						"fg[ACTIVE] = \"%s\"\n"
 						"}\n"
@@ -7995,6 +8021,7 @@
 						styles[iter].stylename,
 						styles[iter].color,
 						styles[iter].labelname, styles[iter].stylename);
+			}
 		}
 		gtk_rc_parse_string(str->str);
 		g_string_free(str, TRUE);
@@ -8417,7 +8444,7 @@
 	}
 	
 	if (e->button == 3) {
-		/* Right click was pressed. Popup the Send To menu. */
+		/* Right click was pressed. Popup the context menu. */
 		GtkWidget *menu = gtk_menu_new(), *sub;
 		gboolean populated = populate_menu_with_options(menu, gtkconv, TRUE);
 		sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to));
@@ -8850,7 +8877,7 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 	const char *text = NULL;
 
-	if (!GTK_WIDGET_VISIBLE(gtkconv->tab_label)) {
+	if (!GTK_WIDGET_VISIBLE(gtkconv->infopane)) {
 		/* There's already an entry for alias. Let's not create another one. */
 		return FALSE;
 	}
@@ -9395,6 +9422,7 @@
 		gtkconv->tabby = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 	else
 		gtkconv->tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_widget_set_name(gtkconv->tabby, "tab-container");
 
 	/* select the correct ordering for verticle tabs */
 	if (angle == 90) {
@@ -10062,8 +10090,8 @@
 	GdkColor send_color;
 	time_t breakout_time;
 
-	gdk_color_parse(HIGHLIGHT_COLOR, &nick_highlight);
-	gdk_color_parse(SEND_COLOR, &send_color);
+	gdk_color_parse(DEFAULT_HIGHLIGHT_COLOR, &nick_highlight);
+	gdk_color_parse(DEFAULT_SEND_COLOR, &send_color);
 
 	srand(background.red + background.green + background.blue + 1);
 
--- a/pidgin/gtkdebug.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkdebug.c	Mon Jan 07 03:40:27 2008 +0000
@@ -686,13 +686,11 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/height");
 
-	PIDGIN_DIALOG(win->window);
+	win->window = pidgin_create_dialog(_("Debug Window"), 0, "debug", TRUE);
 	purple_debug_info("gtkdebug", "Setting dimensions to %d, %d\n",
 					width, height);
 
 	gtk_window_set_default_size(GTK_WINDOW(win->window), width, height);
-	gtk_window_set_role(GTK_WINDOW(win->window), "debug");
-	gtk_window_set_title(GTK_WINDOW(win->window), _("Debug Window"));
 
 	g_signal_connect(G_OBJECT(win->window), "delete_event",
 	                 G_CALLBACK(debug_window_destroy), NULL);
@@ -700,7 +698,7 @@
 	                 G_CALLBACK(configure_cb), win);
 
 	handle = pidgin_debug_get_handle();
-	
+
 #ifdef HAVE_REGEX_H
 	/* the list store for all the messages */
 	win->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
@@ -716,8 +714,7 @@
 #endif /* HAVE_REGEX_H */
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(win->window), vbox);
+	vbox = pidgin_dialog_get_vbox(GTK_DIALOG(win->window));
 
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/toolbar")) {
 		/* Setup our top button bar thingie. */
--- a/pidgin/gtkdialogs.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkdialogs.c	Mon Jan 07 03:40:27 2008 +0000
@@ -89,6 +89,7 @@
 	{"Megan 'Cae' Schneider",       N_("support/QA"), NULL},
 	{"Evan Schoenberg",		N_("developer"), NULL},
 	{"Kevin 'SimGuy' Stange",	N_("developer & webmaster"),	NULL},
+	{"Will 'resiak' Thompson",	N_("developer"),	NULL},
 	{"Stu 'nosnilmot' Tomlinson",	N_("developer"), NULL},
 	{"Nathan 'faceprint' Walp",		N_("developer"), NULL},
 	{NULL, NULL, NULL}
@@ -98,8 +99,8 @@
 static const struct developer patch_writers[] = {
 	{"Dennis 'EvilDennisR' Ristuccia",	N_("Senior Contributor/QA"),	NULL},
 	{"Peter 'Fmoo' Ruibal",		NULL,	NULL},
+	{"Elliott 'QuLogic' Sales de Andrade",	NULL,	NULL},
 	{"Gabriel 'Nix' Schulhof", 	NULL, 	NULL},
-	{"Will 'resiak' Thompson",	NULL,	NULL},
 	{NULL, NULL, NULL}
 };
 
@@ -287,7 +288,7 @@
 	}
 }
 
-static void destroy_about()
+static void destroy_about(void)
 {
 	if (about != NULL)
 		gtk_widget_destroy(about);
@@ -336,12 +337,10 @@
 
 void pidgin_dialogs_about()
 {
-	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *logo;
 	GtkWidget *frame;
 	GtkWidget *text;
-	GtkWidget *bbox;
 	GtkWidget *button;
 	GtkTextIter iter;
 	GString *str;
@@ -356,20 +355,12 @@
 		return;
 	}
 
-	PIDGIN_DIALOG(about);
 	tmp = g_strdup_printf(_("About %s"), PIDGIN_NAME);
-	gtk_window_set_title(GTK_WINDOW(about), tmp);
+	about = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "about", TRUE);
 	g_free(tmp);
-	gtk_window_set_role(GTK_WINDOW(about), "about");
-	gtk_window_set_resizable(GTK_WINDOW(about), TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(about), 340, 450);
 
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_set_border_width(GTK_CONTAINER(hbox), PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(about), hbox);
-
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(hbox), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(about), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Generate a logo with a version number */
 	logo = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -708,15 +699,9 @@
 	gtk_text_buffer_place_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)), &iter);
 
 	/* Close Button */
-	bbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(about), GTK_STOCK_CLOSE,
+	                G_CALLBACK(destroy_about), about);
 
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect_swapped(G_OBJECT(button), "clicked",
-							 G_CALLBACK(destroy_about), G_OBJECT(about));
 	g_signal_connect(G_OBJECT(about), "destroy",
 					 G_CALLBACK(destroy_about), G_OBJECT(about));
 
--- a/pidgin/gtkdialogs.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkdialogs.h	Mon Jan 07 03:40:27 2008 +0000
@@ -54,11 +54,10 @@
 
 /* Everything after this should probably be moved elsewhere */
 
-/**
- * Our UI's identifier.
- */
+#ifndef PIDGIN_DISABLE_DEPRECATED
 #define PIDGIN_DIALOG(x)	x = gtk_window_new(GTK_WINDOW_TOPLEVEL); \
 			gtk_window_set_type_hint(GTK_WINDOW(x), GDK_WINDOW_TYPE_HINT_DIALOG)
+#endif
 #define PIDGIN_WINDOW_ICONIFIED(x) (gdk_window_get_state(GTK_WIDGET(x)->window) & GDK_WINDOW_STATE_ICONIFIED)
 
 #endif /* _PIDGINDIALOGS_H_ */
--- a/pidgin/gtkdocklet-x11.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkdocklet-x11.c	Mon Jan 07 03:40:27 2008 +0000
@@ -48,7 +48,7 @@
 static void docklet_x11_create(gboolean);
 
 static gboolean
-docklet_x11_recreate_cb()
+docklet_x11_recreate_cb(gpointer data)
 {
 	docklet_x11_create(TRUE);
 
@@ -79,13 +79,14 @@
 	g_idle_add(docklet_x11_recreate_cb, NULL);
 }
 
-static void
+static gboolean
 docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data)
 {
 	if (event->type != GDK_BUTTON_RELEASE)
-		return;
+		return FALSE;
 
 	pidgin_docklet_clicked(event->button);
+	return TRUE;
 }
 
 static void
@@ -146,7 +147,7 @@
 }
 
 static void
-docklet_x11_blank_icon()
+docklet_x11_blank_icon(void)
 {
 	if (!blank_icon) {
 		GtkIconSize size = GTK_ICON_SIZE_LARGE_TOOLBAR;
@@ -204,7 +205,7 @@
 #endif
 
 static void
-docklet_x11_destroy()
+docklet_x11_destroy(void)
 {
 	g_return_if_fail(docklet != NULL);
 
@@ -229,7 +230,7 @@
 }
 
 static gboolean
-docklet_x11_embed_timeout_cb()
+docklet_x11_embed_timeout_cb(gpointer data)
 {
 	/* The docklet was not embedded within the timeout.
 	 * Remove it as a visibility manager, but leave the plugin
@@ -300,7 +301,7 @@
 }
 
 static void
-docklet_x11_create_ui_op()
+docklet_x11_create_ui_op(void)
 {
 	docklet_x11_create(FALSE);
 }
--- a/pidgin/gtkdocklet.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkdocklet.c	Mon Jan 07 03:40:27 2008 +0000
@@ -62,7 +62,7 @@
  * docklet status and utility functions
  **************************************************************************/
 static gboolean
-docklet_blink_icon()
+docklet_blink_icon(gpointer data)
 {
 	static gboolean blinked = FALSE;
 	gboolean ret = FALSE; /* by default, don't keep blinking */
@@ -108,7 +108,7 @@
 }
 
 static gboolean
-docklet_update_status()
+docklet_update_status(void)
 {
 	GList *convs, *l;
 	int count;
@@ -219,7 +219,7 @@
 }
 
 static gboolean
-online_account_supports_chat()
+online_account_supports_chat(void)
 {
 	GList *c = NULL;
 	c = purple_connections_get_all();
@@ -523,7 +523,7 @@
 }
 
 static GtkWidget *
-docklet_status_submenu()
+docklet_status_submenu(void)
 {
 	GtkWidget *submenu, *menuitem;
 	GList *popular_statuses, *cur;
@@ -667,7 +667,8 @@
 }
 
 static void
-docklet_menu() {
+docklet_menu(void)
+{
 	static GtkWidget *menu = NULL;
 	GtkWidget *menuitem;
 
--- a/pidgin/gtkft.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkft.c	Mon Jan 07 03:40:27 2008 +0000
@@ -156,15 +156,15 @@
 	}
 
 	if (time_remaining != NULL) {
-		if (purple_xfer_get_size(xfer) == 0) {
-			*time_remaining = g_strdup(_("Unknown"));
-		}
-		else if (purple_xfer_is_completed(xfer)) {
+		if (purple_xfer_is_completed(xfer)) {
 			*time_remaining = g_strdup(_("Finished"));
 		}
 		else if (purple_xfer_is_canceled(xfer)) {
 			*time_remaining = g_strdup(_("Canceled"));
 		}
+		else if (purple_xfer_get_size(xfer) == 0 || (kb_sent > 0 && kbps == 0)) {
+			*time_remaining = g_strdup(_("Unknown"));
+		}
 		else if (kb_sent <= 0) {
 			*time_remaining = g_strdup(_("Waiting for transfer to begin"));
 		}
@@ -745,7 +745,6 @@
 	PidginXferDialog *dialog;
 	GtkWidget *window;
 	GtkWidget *vbox1, *vbox2;
-	GtkWidget *bbox;
 	GtkWidget *sw;
 	GtkWidget *button;
 	GtkWidget *expander;
@@ -759,15 +758,13 @@
 		purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished");
 
 	/* Create the window. */
-	dialog->window = window = pidgin_create_window(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
+	dialog->window = window = pidgin_create_dialog(_("File Transfers"), PIDGIN_HIG_BORDER, "file transfer", TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
 
 	/* Create the parent vbox for everything. */
-	vbox1 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox1);
-	gtk_widget_show(vbox1);
+	vbox1 = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Create the main vbox for top half of the window. */
 	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -812,71 +809,35 @@
 	gtk_container_add(GTK_CONTAINER(expander), table);
 	gtk_widget_show(table);
 
-	/* Now the button box for the buttons */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox1), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Open button */
-	button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_OPEN, G_CALLBACK(open_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->open_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(open_button_cb), dialog);
-
 	/* Pause button */
-	button = gtk_button_new_with_mnemonic(_("_Pause"));
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Pause"), G_CALLBACK(pause_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->pause_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pause_button_cb), dialog);
-
 	/* Resume button */
-	button = gtk_button_new_with_mnemonic(_("_Resume"));
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), _("_Resume"), G_CALLBACK(resume_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 	dialog->resume_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(resume_button_cb), dialog);
-
 	/* Remove button */
-	button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_REMOVE, G_CALLBACK(remove_button_cb), dialog);
 	gtk_widget_hide(button);
 	dialog->remove_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(remove_button_cb), dialog);
-
 	/* Stop button */
-	button = gtk_button_new_from_stock(GTK_STOCK_STOP);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, G_CALLBACK(stop_button_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
 	dialog->stop_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(stop_button_cb), dialog);
-
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, G_CALLBACK(close_button_cb), dialog);
 	dialog->close_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_button_cb), dialog);
-
 #ifdef _WIN32
 	g_signal_connect(G_OBJECT(dialog->window), "show",
 		G_CALLBACK(winpidgin_ensure_onscreen), dialog->window);
--- a/pidgin/gtkidle.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkidle.c	Mon Jan 07 03:40:27 2008 +0000
@@ -69,7 +69,7 @@
  */
 #if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT)
 static time_t
-pidgin_get_time_idle()
+pidgin_get_time_idle(void)
 {
 # ifdef HAVE_IOKIT
 	/* Query the IOKit API */
--- a/pidgin/gtkimhtml.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkimhtml.c	Mon Jan 07 03:40:27 2008 +0000
@@ -293,7 +293,7 @@
 #endif
 
 static GtkSmileyTree*
-gtk_smiley_tree_new ()
+gtk_smiley_tree_new (void)
 {
 	return g_new0 (GtkSmileyTree, 1);
 }
@@ -820,7 +820,6 @@
 static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data)
 {
 	GtkWidget *menuitem;
-	GtkWidget *mi, *img;
 
 	menuitem = gtk_menu_item_new_with_mnemonic(_("Paste as Plain _Text"));
 	gtk_widget_show(menuitem);
@@ -1020,7 +1019,7 @@
 static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext)
 {
 	GtkTextIter iter;
-	GtkIMHtmlOptions flags = plaintext ? 0 : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
+	GtkIMHtmlOptions flags = plaintext ? GTK_IMHTML_NO_SMILEY : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
 
 	if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL))
 		gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
@@ -1144,14 +1143,13 @@
 #ifdef _WIN32
 	/* If we're on windows, let's see if we can get data from the HTML Format
 	   clipboard before we try to paste from the GTK buffer */
-	if (!clipboard_paste_html_win32(imhtml)) {
+	if (!clipboard_paste_html_win32(imhtml))
 #endif
+	{
 	GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
 	gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
 				       paste_received_cb, imhtml);
-#ifdef _WIN32
 	}
-#endif
 	g_signal_stop_emission_by_name(imhtml, "paste-clipboard");
 }
 
@@ -1405,6 +1403,22 @@
 	                                        _("Hyperlink prelight color"),
 	                                        _("Color to draw hyperlinks when mouse is over them."),
 	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("send-name-color",
+	                                        _("Sent Message Name Color"),
+	                                        _("Color to draw the name of a message you sent."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("receive-name-color",
+	                                        _("Received Message Name Color"),
+	                                        _("Color to draw the name of a message you received."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("highlight-name-color",
+	                                        _("\"Attention\" Name Color"),
+	                                        _("Color to draw the name of a message you received containing your name."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
+	gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("action-name-color",
+	                                        _("Action Message Name Color"),
+	                                        _("Color to draw the name of an action message."),
+	                                        GDK_TYPE_COLOR, G_PARAM_READABLE));
 
 	binding_set = gtk_binding_set_by_class (parent_class);
 	gtk_binding_entry_add_signal (binding_set, GDK_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD);
@@ -2990,6 +3004,7 @@
 			pos += tlen;
 			g_free(tag); /* This was allocated back in VALID_TAG() */
 		} else if (imhtml->edit.link == NULL &&
+				!(options & GTK_IMHTML_NO_SMILEY) &&
 				gtk_imhtml_is_smiley(imhtml, fonts, c, &smilelen)) {
 			GtkIMHtmlFontDetail *fd;
 			gchar *sml = NULL;
@@ -3475,6 +3490,9 @@
 		return;
 	}
 #endif /* FILECHOOSER */
+#if 0 /* mismatched curly braces */
+	}
+#endif
 
 	/*
 	 * XXX - We should probably prompt the user to determine if they really
@@ -4267,33 +4285,6 @@
 	g_object_unref(object);
 }
 
-static void populate_popup_cb(GtkTextView *textview, GtkMenu *menu, gpointer nul)
-{
-	GtkWidget *mi, *img;
-	
-	mi = gtk_menu_item_new();
-	gtk_widget_show(mi);
-	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
-
-	img = gtk_image_new_from_stock(GTK_STOCK_BOLD, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_mnemonic(_("_Font"));
-	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
-	gtk_widget_show(mi);
-	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
-
-	img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_mnemonic(_("_Insert"));
-	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
-	gtk_widget_show(mi);
-	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
-
-	img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, GTK_ICON_SIZE_MENU);
-	mi = gtk_image_menu_item_new_with_mnemonic(_("S_mile!"));
-	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
-	gtk_widget_show(mi);
-	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
-}
-
 static void imhtml_toggle_bold(GtkIMHtml *imhtml)
 {
 	GtkTextIter start, end;
--- a/pidgin/gtkimhtml.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkimhtml.h	Mon Jan 07 03:40:27 2008 +0000
@@ -225,7 +225,8 @@
 	GTK_IMHTML_RETURN_LOG          = 1 << 7,
 	GTK_IMHTML_USE_POINTSIZE       = 1 << 8,
 	GTK_IMHTML_NO_FORMATTING       = 1 << 9,
-	GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10
+	GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10,
+	GTK_IMHTML_NO_SMILEY           = 1 << 11,
 } GtkIMHtmlOptions;
 
 enum {
--- a/pidgin/gtkimhtmltoolbar.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Mon Jan 07 03:40:27 2008 +0000
@@ -99,7 +99,7 @@
 	gtk_widget_grab_focus(toolbar->imhtml);
 }
 
-static void
+static gboolean
 destroy_toolbar_font(GtkWidget *widget, GdkEvent *event,
 					 GtkIMHtmlToolbar *toolbar)
 {
@@ -111,6 +111,7 @@
 		gtk_widget_destroy(toolbar->font_dialog);
 		toolbar->font_dialog = NULL;
 	}
+	return FALSE;
 }
 
 static void
@@ -191,7 +192,7 @@
 	gtk_widget_grab_focus(toolbar->imhtml);
 }
 
-static void
+static gboolean
 destroy_toolbar_fgcolor(GtkWidget *widget, GdkEvent *event,
 						GtkIMHtmlToolbar *toolbar)
 {
@@ -203,6 +204,7 @@
 		gtk_widget_destroy(toolbar->fgcolor_dialog);
 		toolbar->fgcolor_dialog = NULL;
 	}
+	return FALSE;
 }
 
 static void cancel_toolbar_fgcolor(GtkWidget *widget,
@@ -263,7 +265,7 @@
 	gtk_widget_grab_focus(toolbar->imhtml);
 }
 
-static void
+static gboolean
 destroy_toolbar_bgcolor(GtkWidget *widget, GdkEvent *event,
 						GtkIMHtmlToolbar *toolbar)
 {
@@ -279,6 +281,7 @@
 		gtk_widget_destroy(toolbar->bgcolor_dialog);
 		toolbar->bgcolor_dialog = NULL;
 	}
+	return FALSE;
 }
 
 static void
@@ -467,10 +470,11 @@
 	GtkTextMark *ins;
 
 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-	if (response != GTK_RESPONSE_ACCEPT) {
+	if (response != GTK_RESPONSE_ACCEPT)
 #else /* FILECHOOSER */
-	if (response != GTK_RESPONSE_OK) {
+	if (response != GTK_RESPONSE_OK)
 #endif /* FILECHOOSER */
+	{
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
 		return;
 	}
@@ -574,11 +578,12 @@
 	}
 }
 
-static void
+static gboolean
 close_smiley_dialog(GtkWidget *widget, GdkEvent *event,
 					GtkIMHtmlToolbar *toolbar)
 {
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smiley), FALSE);
+	return FALSE;
 }
 
 
@@ -704,10 +709,8 @@
 		smileys = smileys->next;
 	}
 
-	PIDGIN_DIALOG(dialog);
+	dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE);
 
-	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
-	gtk_window_set_role(GTK_WINDOW(dialog), "smiley_dialog");
 	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
 
 	if (unique_smileys != NULL) {
@@ -764,18 +767,15 @@
 	}
 
 	g_signal_connect(G_OBJECT(dialog), "key-press-event", (GCallback)smiley_dialog_input_cb, toolbar);
-	gtk_container_add(GTK_CONTAINER(dialog), smiley_table);
+	gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(dialog))), smiley_table);
 
 	gtk_widget_show(smiley_table);
 
-	gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
-
 	/* connect signals */
 	g_signal_connect(G_OBJECT(dialog), "delete_event",
 					 G_CALLBACK(close_smiley_dialog), toolbar);
 
 	/* show everything */
-	gtk_window_set_title(GTK_WINDOW(dialog), _("Smile!"));
 	gtk_widget_show_all(dialog);
 	gtk_window_set_transient_for(GTK_WINDOW(dialog),
 			GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar))));
@@ -1096,8 +1096,8 @@
 		{PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, do_small, &toolbar->smaller_size, _("Decrease Font Size")},
 		{"", NULL, NULL, NULL},
 		{PIDGIN_STOCK_TOOLBAR_FONT_FACE, toggle_font, &toolbar->font, _("Font Face")},
-		{PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")},
-		{PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")},
+		{PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")},
+		{PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")},
 		{"", NULL, NULL, NULL},
 		{PIDGIN_STOCK_CLEAR, clear_formatting_cb, &toolbar->clear, _("Reset Formatting")},
 		{"", NULL, NULL, NULL},
--- a/pidgin/gtkmain.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkmain.c	Mon Jan 07 03:40:27 2008 +0000
@@ -174,7 +174,7 @@
  * be wise to move this code into gtksound.c.
  */
 static void
-clean_pid()
+clean_pid(void)
 {
 	int status;
 	pid_t pid;
@@ -243,7 +243,7 @@
 #endif
 
 static int
-ui_main()
+ui_main(void)
 {
 #ifndef _WIN32
 	GList *icons = NULL;
@@ -361,7 +361,7 @@
 	gtk_main_quit();
 }
 
-static GHashTable *pidgin_ui_get_info()
+static GHashTable *pidgin_ui_get_info(void)
 {
 	if(NULL == ui_info) {
 		ui_info = g_hash_table_new(g_str_hash, g_str_equal);
@@ -399,6 +399,7 @@
 	if (terse) {
 		text = g_strdup_printf(_("%s %s. Try `%s -h' for more information.\n"), PIDGIN_NAME, DISPLAY_VERSION, name);
 	} else {
+#ifndef WIN32
 		text = g_strdup_printf(_("%s %s\n"
 		       "Usage: %s [OPTION]...\n\n"
 		       "  -c, --config=DIR    use DIR for config files\n"
@@ -408,10 +409,20 @@
 		       "  -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"
+		       "  -v, --version       display the current version and exit\n"), PIDGIN_NAME, DISPLAY_VERSION, name);
+#else
+		text = g_strdup_printf(_("%s %s\n"
+		       "Usage: %s [OPTION]...\n\n"
+		       "  -c, --config=DIR    use DIR for config files\n"
+		       "  -d, --debug         print debugging messages to stdout\n"
+		       "  -h, --help          display this help and exit\n"
+		       "  -m, --multiple      do not ensure single instance\n"
+		       "  -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"
+		       "  -v, --version       display the current version and exit\n"), PIDGIN_NAME, DISPLAY_VERSION, name);
 #endif
-		       "  -v, --version       display the current version and exit\n"), PIDGIN_NAME, DISPLAY_VERSION, name);
 	}
 
 	purple_print_utf8_to_console(stdout, text);
--- a/pidgin/gtknotify.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtknotify.c	Mon Jan 07 03:40:27 2008 +0000
@@ -166,16 +166,18 @@
 	mail_dialog = NULL;
 }
 
-static void
+static gboolean
 formatted_close_cb(GtkWidget *win, GdkEvent *event, void *user_data)
 {
 	purple_notify_close(PURPLE_NOTIFY_FORMATTED, win);
+	return FALSE;
 }
 
-static void
+static gboolean
 searchresults_close_cb(PidginNotifySearchResultsData *data, GdkEvent *event, gpointer user_data)
 {
 	purple_notify_close(PURPLE_NOTIFY_SEARCHRESULTS, data);
+	return FALSE;
 }
 
 static void
@@ -284,6 +286,8 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return dialog;
@@ -328,7 +332,7 @@
 }
 
 static GtkWidget *
-pidgin_get_mail_dialog()
+pidgin_get_mail_dialog(void)
 {
 	if (mail_dialog == NULL) {
 		GtkWidget *dialog = NULL;
@@ -600,7 +604,7 @@
 }
 
 static GtkIMHtmlOptions
-notify_imhtml_options()
+notify_imhtml_options(void)
 {
 	GtkIMHtmlOptions options = 0;
 
@@ -684,6 +688,8 @@
 	g_object_set_data(G_OBJECT(window), "info-widget", imhtml);
 
 	/* Show the window */
+	pidgin_auto_parent_window(window);
+
 	gtk_widget_show(window);
 
 	return window;
@@ -894,6 +900,8 @@
 	pidgin_notify_searchresults_new_rows(gc, results, data);
 
 	/* Show the window */
+	pidgin_auto_parent_window(window);
+
 	gtk_widget_show(window);
 	return data;
 }
--- a/pidgin/gtkplugin.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkplugin.c	Mon Jan 07 03:40:27 2008 +0000
@@ -31,6 +31,7 @@
 #include "debug.h"
 #include "prefs.h"
 #include "request.h"
+#include "pidgintooltip.h"
 
 #include <string.h>
 
@@ -531,6 +532,58 @@
 	plugin_dialog_response_cb(dialog, PIDGIN_RESPONSE_CONFIGURE, sel);
 }
 
+static gboolean
+pidgin_plugins_paint_tooltip(GtkWidget *tipwindow, gpointer data)
+{
+	PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin");
+	gtk_paint_layout(tipwindow->style, tipwindow->window, GTK_STATE_NORMAL, FALSE,
+			NULL, tipwindow, "tooltip",
+			6, 6, layout);
+	return TRUE;
+}
+
+static gboolean
+pidgin_plugins_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
+		gpointer data, int *w, int *h)
+{
+	GtkTreeIter iter;
+	GtkTreeView *treeview = GTK_TREE_VIEW(data);
+	PurplePlugin *plugin = NULL;
+	GtkTreeModel *model = gtk_tree_view_get_model(treeview);
+	PangoLayout *layout;
+	int width, height;
+	char *markup, *name, *desc, *author;
+
+	if (!gtk_tree_model_get_iter(model, &iter, path))
+		return FALSE;
+
+	gtk_tree_model_get(model, &iter, 2, &plugin, -1);
+
+	markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>Description:</b> %s\n<b>Author:</b> %s",
+			name = g_markup_escape_text(purple_plugin_get_name(plugin), -1),
+			desc = g_markup_escape_text(purple_plugin_get_description(plugin), -1),
+			author = g_markup_escape_text(purple_plugin_get_author(plugin), -1));
+
+	layout = gtk_widget_create_pango_layout(tipwindow, NULL);
+	pango_layout_set_markup(layout, markup, -1);
+	pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+	pango_layout_set_width(layout, 600000);
+	pango_layout_get_size(layout, &width, &height);
+	g_object_set_data_full(G_OBJECT(tipwindow), "tooltip-plugin", layout, g_object_unref);
+
+	if (w)
+		*w = PANGO_PIXELS(width) + 12;
+	if (h)
+		*h = PANGO_PIXELS(height) + 12;
+
+	g_free(markup);
+	g_free(name);
+	g_free(desc);
+	g_free(author);
+
+	return TRUE;
+}
+
 void pidgin_plugin_dialog_show()
 {
 	GtkWidget *sw;
@@ -613,6 +666,10 @@
 	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view),
 				pidgin_tree_view_search_equal_func, NULL, NULL);
 
+	pidgin_tooltip_setup_for_treeview(event_view, event_view,
+			pidgin_plugins_create_tooltip,
+			pidgin_plugins_paint_tooltip);
+
 	expander = gtk_expander_new(_("<b>Plugin Details</b>"));
 	gtk_expander_set_use_markup(GTK_EXPANDER(expander), TRUE);
 	plugin_details = gtk_label_new(NULL);
--- a/pidgin/gtkpluginpref.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkpluginpref.c	Mon Jan 07 03:40:27 2008 +0000
@@ -93,22 +93,6 @@
 		case PURPLE_PLUGIN_PREF_NONE:
 		default:
 			if (format == PURPLE_STRING_FORMAT_TYPE_NONE)
-				box = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-			else
-				box = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-
-			gtk_widget_show(box);
-			gtk_box_pack_start(GTK_BOX(parent), box, FALSE, FALSE, 0);
-
-			gtk_label = gtk_label_new_with_mnemonic(pref_label);
-			gtk_misc_set_alignment(GTK_MISC(gtk_label), 0, 0.5);
-			gtk_widget_show(gtk_label);
-			gtk_box_pack_start(GTK_BOX(box), gtk_label, FALSE, FALSE, 0);
-
-			if(sg)
-				gtk_size_group_add_widget(sg, gtk_label);
-
-			if (format == PURPLE_STRING_FORMAT_TYPE_NONE)
 			{				
 				entry = gtk_entry_new();
 				gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_string(pref_name));
@@ -123,9 +107,7 @@
 				g_signal_connect(G_OBJECT(entry), "changed",
 								 G_CALLBACK(entry_cb),
 								 (gpointer)pref_name);
-				gtk_label_set_mnemonic_widget(GTK_LABEL(gtk_label), entry);
-				gtk_widget_show(entry);
-				gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0);
+				pidgin_add_widget_to_vbox(GTK_BOX(parent), pref_label, sg, entry, TRUE, NULL);
 			}
 			else
 			{
@@ -135,6 +117,19 @@
 				GtkWidget *toolbar;
 				GtkWidget *frame;
 
+				box = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+
+				gtk_widget_show(box);
+				gtk_box_pack_start(GTK_BOX(parent), box, FALSE, FALSE, 0);
+
+				gtk_label = gtk_label_new_with_mnemonic(pref_label);
+				gtk_misc_set_alignment(GTK_MISC(gtk_label), 0, 0.5);
+				gtk_widget_show(gtk_label);
+				gtk_box_pack_start(GTK_BOX(box), gtk_label, FALSE, FALSE, 0);
+
+				if(sg)
+					gtk_size_group_add_widget(sg, gtk_label);
+
 				hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 				gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
 				gtk_widget_show(hbox);
--- a/pidgin/gtkpounce.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkpounce.c	Mon Jan 07 03:40:27 2008 +0000
@@ -1317,7 +1317,6 @@
 pidgin_pounces_manager_show(void)
 {
 	PouncesManager *dialog;
-	GtkWidget *bbox;
 	GtkWidget *button;
 	GtkWidget *list;
 	GtkWidget *vbox;
@@ -1334,7 +1333,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/pounces/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Buddy Pounces"), PIDGIN_HIG_BORDER, "pounces", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1343,61 +1342,33 @@
 					 G_CALLBACK(pounces_manager_configure_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* List of saved buddy pounces */
 	list = create_pounces_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
 
-	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
+	/* Add button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD, G_CALLBACK(pounces_manager_add_cb), dialog);
+	gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL));
 
-	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_set_sensitive(button, (purple_accounts_get_all() != NULL));
 	purple_signal_connect(purple_connections_get_handle(), "signed-on",
 						pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button);
 	purple_signal_connect(purple_connections_get_handle(), "signed-off",
 						pounces_manager, PURPLE_CALLBACK(pounces_manager_connection_cb), button);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_add_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
-	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY, G_CALLBACK(pounces_manager_modify_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_modify_cb), dialog);
+	dialog->modify_button = button;
 
 	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
-	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE, G_CALLBACK(pounces_manager_delete_cb), dialog);
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_delete_cb), dialog);
+	dialog->delete_button = button;
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(pounces_manager_close_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE, G_CALLBACK(pounces_manager_close_cb), dialog);
 
 	gtk_widget_show(win);
 }
--- a/pidgin/gtkprefs.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkprefs.c	Mon Jan 07 03:40:27 2008 +0000
@@ -89,23 +89,12 @@
 pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title,
 		const char *key, int min, int max, GtkSizeGroup *sg)
 {
-	GtkWidget *hbox;
-	GtkWidget *label;
 	GtkWidget *spin;
 	GtkObject *adjust;
 	int val;
 
 	val = purple_prefs_get_int(key);
 
-	hbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5);
-	gtk_widget_show(hbox);
-
-	label = gtk_label_new_with_mnemonic(title);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_widget_show(label);
-
 	adjust = gtk_adjustment_new(val, min, max, 1, 1, 1);
 	spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
 	g_object_set_data(G_OBJECT(spin), "val", (char *)key);
@@ -113,21 +102,11 @@
 		gtk_widget_set_size_request(spin, 50, -1);
 	else
 		gtk_widget_set_size_request(spin, 60, -1);
-	gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0);
 	g_signal_connect(G_OBJECT(adjust), "value-changed",
 					 G_CALLBACK(update_spin_value), GTK_WIDGET(spin));
 	gtk_widget_show(spin);
 
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), spin);
-
-	if (sg) {
-		gtk_size_group_add_widget(sg, label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-	}
-
-	pidgin_set_accessible_label (spin, label);
-
-	return hbox;
+	return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL);
 }
 
 static void
@@ -141,37 +120,18 @@
 pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title,
 							 const char *key, GtkSizeGroup *sg)
 {
-	GtkWidget *hbox, *label, *entry;
+	GtkWidget *entry;
 	const gchar *value;
 
 	value = purple_prefs_get_string(key);
 
-	hbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(page), hbox, FALSE, FALSE, 0);
-	gtk_widget_show(hbox);
-
-	label = gtk_label_new_with_mnemonic(title);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_widget_show(label);
-
 	entry = gtk_entry_new();
 	gtk_entry_set_text(GTK_ENTRY(entry), value);
-	gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
 	g_signal_connect(G_OBJECT(entry), "changed",
 					 G_CALLBACK(entry_set), (char*)key);
 	gtk_widget_show(entry);
 
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
-
-	if(sg) {
-		gtk_size_group_add_widget(sg, label);
-		gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-	}
-
-	pidgin_set_accessible_label(entry, label);
-
-	return hbox;
+	return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL);
 }
 
 static void
@@ -205,7 +165,6 @@
 {
 	GtkWidget  *dropdown, *opt, *menu;
 	GtkWidget  *label = NULL;
-	GtkWidget  *hbox;
 	gchar      *text;
 	const char *stored_str = NULL;
 	int         stored_int = 0;
@@ -215,19 +174,6 @@
 
 	g_return_val_if_fail(menuitems != NULL, NULL);
 
-	if (title != NULL) {
-		hbox = gtk_hbox_new(FALSE, 5);
-		/*gtk_container_add (GTK_CONTAINER (box), hbox);*/
-		gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
-		gtk_widget_show(hbox);
-
-		label = gtk_label_new_with_mnemonic(title);
-		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-		gtk_widget_show(label);
-	} else {
-		hbox = box;
-	}
-
 #if 0 /* GTK_CHECK_VERSION(2,4,0) */
 	if(type == PURPLE_PREF_INT)
 		model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
@@ -239,11 +185,6 @@
 	menu = gtk_menu_new();
 #endif
 
-	if (label != NULL) {
-		gtk_label_set_mnemonic_widget(GTK_LABEL(label), dropdown);
-		pidgin_set_accessible_relations (dropdown, label);
-	}
-
 	if (type == PURPLE_PREF_INT)
 		stored_int = purple_prefs_get_int(key);
 	else if (type == PURPLE_PREF_STRING)
@@ -293,8 +234,8 @@
 	}
 
 	gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu);
-	gtk_box_pack_start(GTK_BOX(hbox), dropdown, FALSE, FALSE, 0);
-	gtk_widget_show(dropdown);
+
+	pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label);
 
 	return label;
 }
@@ -412,7 +353,7 @@
 	gtk_tree_path_free(path);
 }
 
-static GtkTreeRowReference *theme_refresh_theme_list()
+static GtkTreeRowReference *theme_refresh_theme_list(void)
 {
 	GdkPixbuf *pixbuf;
 	GSList *themes;
@@ -676,7 +617,7 @@
 }
 
 static GtkWidget *
-theme_page()
+theme_page(void)
 {
 	GtkWidget *add_button, *remove_button;
 	GtkWidget *hbox_buttons;
@@ -877,7 +818,7 @@
 }
 
 static GtkWidget *
-interface_page()
+interface_page(void)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox;
@@ -973,7 +914,7 @@
 #endif
 
 static GtkWidget *
-conv_page()
+conv_page(void)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox;
@@ -986,7 +927,6 @@
 
 #if GTK_CHECK_VERSION(2,4,0)
 	GtkWidget *hbox;
-	GtkWidget *label;
 	GtkWidget *font_button;
 	const char *font_name;
 #endif
@@ -1029,19 +969,15 @@
 		fontpref = pidgin_prefs_checkbox(_("Use document font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox);
 	else
 		fontpref = pidgin_prefs_checkbox(_("Use font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox);
-	hbox = gtk_hbox_new(FALSE, 3);
-	label = gtk_label_new_with_mnemonic(_("Conversation _font:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
 	font_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font");
 	font_button = gtk_font_button_new_with_font(font_name ? font_name : NULL);
 	gtk_font_button_set_show_style(GTK_FONT_BUTTON(font_button), TRUE);
-	gtk_box_pack_start(GTK_BOX(hbox), font_button, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Conversation _font:"), NULL, font_button, FALSE, NULL);
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font"))
 		gtk_widget_set_sensitive(hbox, FALSE);
 	g_signal_connect(G_OBJECT(fontpref), "clicked", G_CALLBACK(pidgin_toggle_sensitive), hbox);
 	g_signal_connect(G_OBJECT(font_button), "font-set", G_CALLBACK(pidgin_custom_font_set), NULL);
-	gtk_widget_show_all(hbox);
 #endif
 
 	vbox = pidgin_make_frame(ret, _("Default Formatting"));
@@ -1140,7 +1076,7 @@
 }
 
 static GtkWidget *
-network_page()
+network_page(void)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox, *hbox, *entry;
@@ -1414,7 +1350,7 @@
 	return FALSE;
 }
 
-static GList *get_available_browsers()
+static GList *get_available_browsers(void)
 {
 	struct browser {
 		char *name;
@@ -1480,7 +1416,7 @@
 }
 
 static GtkWidget *
-browser_page()
+browser_page(void)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox;
@@ -1523,28 +1459,16 @@
 									browser_changed1_cb, hbox);
 	}
 
-	hbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-	label = gtk_label_new_with_mnemonic(_("_Manual:\n(%s for URL)"));
-	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	entry = gtk_entry_new();
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
-
 	if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"), "custom"))
 		gtk_widget_set_sensitive(hbox, FALSE);
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/browsers/browser",
 								browser_changed2_cb, hbox);
-
-	gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
-
 	gtk_entry_set_text(GTK_ENTRY(entry),
 					   purple_prefs_get_path(PIDGIN_PREFS_ROOT "/browsers/command"));
 	g_signal_connect(G_OBJECT(entry), "focus-out-event",
 					 G_CALLBACK(manual_browser_set), NULL);
-	pidgin_set_accessible_label (entry, label);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Manual:\n(%s for URL)"), sg, entry, TRUE, NULL);
 
 	gtk_widget_show_all(ret);
 	g_object_unref(sg);
@@ -1553,7 +1477,7 @@
 #endif /*_WIN32*/
 
 static GtkWidget *
-logging_page()
+logging_page(void)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox;
@@ -1781,7 +1705,7 @@
 }
 
 static GtkWidget *
-sound_page()
+sound_page(void)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox, *sw, *button;
@@ -1827,33 +1751,20 @@
 	gtk_size_group_add_widget(sg, dd);
 	gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
 
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("Sound c_ommand:\n(%s for filename)"));
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
 	entry = gtk_entry_new();
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
-
 	gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);
 	cmd = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/sound/command");
 	if(cmd)
 		gtk_entry_set_text(GTK_ENTRY(entry), cmd);
-
-	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
 	g_signal_connect(G_OBJECT(entry), "changed",
 					 G_CALLBACK(sound_cmd_yeah), NULL);
 
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Sound c_ommand:\n(%s for filename)"), sg, entry, TRUE, NULL);
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
 								sound_changed1_cb, hbox);
 	gtk_widget_set_sensitive(hbox,
 			!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"),
 					"custom"));
-
-	pidgin_set_accessible_label (entry, label);
 #endif /* _WIN32 */
 
 	vbox = pidgin_make_frame (ret, _("Sound Options"));
@@ -1867,13 +1778,6 @@
 				NULL);
 
 #ifdef USE_GSTREAMER
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("Volume:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
 	sw = gtk_hscale_new_with_range(0.0, 100.0, 5.0);
 	gtk_range_set_increments(GTK_RANGE(sw), 5.0, 25.0);
 	gtk_range_set_value(GTK_RANGE(sw), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume"));
@@ -1883,7 +1787,7 @@
 	g_signal_connect (G_OBJECT (sw), "value-changed",
 			  G_CALLBACK (prefs_sound_volume_changed),
 			  NULL);
-	gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0);
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Volume:"), NULL, sw, TRUE, NULL);
 
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
 								sound_changed3_cb, hbox);
@@ -2008,11 +1912,10 @@
 }
 
 static GtkWidget *
-away_page()
+away_page(void)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox;
-	GtkWidget *hbox;
 	GtkWidget *dd;
 	GtkWidget *label;
 	GtkWidget *button;
@@ -2063,22 +1966,13 @@
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive), select);
 
-	hbox = gtk_hbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(vbox), hbox);
-
-	label = gtk_label_new_with_mnemonic(_("Change _status to:"));
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	/* TODO: Show something useful if we don't have any saved statuses. */
+	menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away));
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Change _status to:"), sg, menu, TRUE, &label);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(pidgin_toggle_sensitive), menu);
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive), label);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
-	/* TODO: Show something useful if we don't have any saved statuses. */
-	menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away));
-	gtk_box_pack_start(GTK_BOX(hbox), menu, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(pidgin_toggle_sensitive), menu);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu);
 
 	if (!purple_prefs_get_bool("/purple/away/away_when_idle")) {
 		gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE);
@@ -2092,22 +1986,13 @@
 	button = pidgin_prefs_checkbox(_("Use status from last _exit at startup"),
 		"/purple/savedstatus/startup_current_status", vbox);
 
-	hbox = gtk_hbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(vbox), hbox);
-
-	label = gtk_label_new_with_mnemonic(_("Status to a_pply at startup:"));
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	/* TODO: Show something useful if we don't have any saved statuses. */
+	menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus));
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(pidgin_toggle_sensitive), menu);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Status to a_pply at startup:"), sg, menu, TRUE, &label);
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(pidgin_toggle_sensitive), label);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
-	/* TODO: Show something useful if we don't have any saved statuses. */
-	menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus));
-	gtk_box_pack_start(GTK_BOX(hbox), menu, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(button), "clicked",
-			 G_CALLBACK(pidgin_toggle_sensitive), menu);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), menu);
 
 	if (purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) {
 		gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE);
@@ -2133,7 +2018,7 @@
 #endif
 }
 
-static void prefs_notebook_init() {
+static void prefs_notebook_init(void) {
 	prefs_notebook_add_page(_("Interface"), interface_page(), notebook_page++);
 	prefs_notebook_add_page(_("Conversations"), conv_page(), notebook_page++);
 	prefs_notebook_add_page(_("Smiley Themes"), theme_page(), notebook_page++);
@@ -2153,7 +2038,6 @@
 void pidgin_prefs_show(void)
 {
 	GtkWidget *vbox;
-	GtkWidget *bbox;
 	GtkWidget *notebook;
 	GtkWidget *button;
 
@@ -2169,31 +2053,20 @@
 	/* Back to instant-apply! I win!  BU-HAHAHA! */
 
 	/* Create the window */
-	prefs = pidgin_create_window(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
+	prefs = pidgin_create_dialog(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
 	g_signal_connect(G_OBJECT(prefs), "destroy",
 					 G_CALLBACK(delete_prefs), NULL);
 
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(prefs), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(prefs), FALSE, PIDGIN_HIG_BORDER);
 
 	/* The notebook */
 	prefsnotebook = notebook = gtk_notebook_new ();
 	gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
 	gtk_widget_show(prefsnotebook);
 
-	/* The buttons to press! */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
-	gtk_widget_show (bbox);
-
-	button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(prefs), GTK_STOCK_CLOSE, NULL, NULL);
 	g_signal_connect_swapped(G_OBJECT(button), "clicked",
 							 G_CALLBACK(gtk_widget_destroy), prefs);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
 
 	prefs_notebook_init();
 
--- a/pidgin/gtkprivacy.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkprivacy.c	Mon Jan 07 03:40:27 2008 +0000
@@ -45,6 +45,7 @@
 	GtkWidget *add_button;
 	GtkWidget *remove_button;
 	GtkWidget *clear_button;
+	GtkWidget *close_button;
 
 	GtkWidget *button_box;
 	GtkWidget *allow_widget;
@@ -259,19 +260,22 @@
 
 	gtk_widget_hide(dialog->allow_widget);
 	gtk_widget_hide(dialog->block_widget);
-	gtk_widget_hide(dialog->button_box);
+	gtk_widget_hide_all(dialog->button_box);
 
 	if (new_type == PURPLE_PRIVACY_ALLOW_USERS) {
 		gtk_widget_show(dialog->allow_widget);
-		gtk_widget_show(dialog->button_box);
+		gtk_widget_show_all(dialog->button_box);
 		dialog->in_allow_list = TRUE;
 	}
 	else if (new_type == PURPLE_PRIVACY_DENY_USERS) {
 		gtk_widget_show(dialog->block_widget);
-		gtk_widget_show(dialog->button_box);
+		gtk_widget_show_all(dialog->button_box);
 		dialog->in_allow_list = FALSE;
 	}
 
+	gtk_widget_show_all(dialog->close_button);
+	gtk_widget_show(dialog->button_box);
+
 	purple_blist_schedule_save();
 	pidgin_blist_refresh(purple_get_blist());
 }
@@ -355,8 +359,6 @@
 privacy_dialog_new(void)
 {
 	PidginPrivacyDialog *dialog;
-	GtkWidget *bbox;
-	GtkWidget *hbox;
 	GtkWidget *vbox;
 	GtkWidget *button;
 	GtkWidget *dropdown;
@@ -367,15 +369,13 @@
 
 	dialog = g_new0(PidginPrivacyDialog, 1);
 
-	dialog->win = pidgin_create_window(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
+	dialog->win = pidgin_create_dialog(_("Privacy"), PIDGIN_HIG_BORDER, "privacy", TRUE);
 
 	g_signal_connect(G_OBJECT(dialog->win), "delete_event",
 					 G_CALLBACK(destroy_cb), dialog);
 
 	/* Main vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(dialog->win), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog->win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* Description label */
 	label = gtk_label_new(
@@ -385,22 +385,10 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 	gtk_widget_show(label);
 
-	/* Hbox for the accounts drop-down and label. */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-	gtk_widget_show(hbox);
-
-	/* "Set privacy for:" label */
-	label = gtk_label_new(_("Set privacy for:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_widget_show(label);
-
 	/* Accounts drop-down */
 	dropdown = pidgin_account_option_menu_new(NULL, FALSE,
 												G_CALLBACK(select_account_cb), NULL, dialog);
-	gtk_box_pack_start(GTK_BOX(hbox), dropdown, FALSE, FALSE, 0);
-	gtk_widget_show(dropdown);
-	pidgin_set_accessible_label (dropdown, label);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Set privacy for:"), NULL, dropdown, TRUE, NULL);
 	dialog->account = pidgin_account_option_menu_get_selected(dropdown);
 
 	/* Add the drop-down list with the allow/block types. */
@@ -433,52 +421,27 @@
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->block_widget, TRUE, TRUE, 0);
 
 	/* Add the button box for Add, Remove, Clear */
-	dialog->button_box = bbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_SPREAD);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+	dialog->button_box = pidgin_dialog_get_action_area(GTK_DIALOG(dialog->win));
 
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_ADD, G_CALLBACK(add_cb), dialog);
 	dialog->add_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(add_cb), dialog);
 
 	/* Remove button */
-	button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_REMOVE, G_CALLBACK(remove_cb), dialog);
 	dialog->remove_button = button;
 	gtk_widget_set_sensitive(button, FALSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(remove_cb), dialog);
 
 	/* Clear button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLEAR, G_CALLBACK(clear_cb), dialog);
 	dialog->clear_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(clear_cb), dialog);
-
-	/* Another button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
-	gtk_widget_show(bbox);
 
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
+	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLOSE, G_CALLBACK(close_cb), dialog);
+	dialog->close_button = button;
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(close_cb), dialog);
-
+	type_changed_cb(GTK_OPTION_MENU(dialog->type_menu), dialog);
+#if 0
 	if (dialog->account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) {
 		gtk_widget_show(dialog->allow_widget);
 		gtk_widget_show(dialog->button_box);
@@ -489,7 +452,7 @@
 		gtk_widget_show(dialog->button_box);
 		dialog->in_allow_list = FALSE;
 	}
-
+#endif
 	return dialog;
 }
 
--- a/pidgin/gtkrequest.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkrequest.c	Mon Jan 07 03:40:27 2008 +0000
@@ -251,11 +251,12 @@
 	purple_request_close(PURPLE_REQUEST_FIELDS, data);
 }
 
-static void
+static gboolean
 destroy_multifield_cb(GtkWidget *dialog, GdkEvent *event,
 					  PidginRequestData *data)
 {
 	multifield_cancel_cb(NULL, data);
+	return FALSE;
 }
 
 
@@ -439,6 +440,8 @@
 	pidgin_set_accessible_label (entry, label);
 	data->u.input.entry = entry;
 
+	pidgin_auto_parent_window(dialog);
+
 	/* Show everything. */
 	gtk_widget_show(dialog);
 
@@ -546,6 +549,8 @@
 	g_object_set_data(G_OBJECT(dialog), "radio", radio);
 
 	/* Show everything. */
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return data;
@@ -661,6 +666,8 @@
 		gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_action);
 
 	/* Show everything. */
+	pidgin_auto_parent_window(dialog);
+
 	gtk_widget_show_all(dialog);
 
 	return data;
@@ -1059,7 +1066,6 @@
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
 	GtkWidget *hbox;
-	GtkWidget *bbox;
 	GtkWidget *frame;
 	GtkWidget *label;
 	GtkWidget *table;
@@ -1089,9 +1095,9 @@
 
 
 #ifdef _WIN32
-	data->dialog = win = pidgin_create_window(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+	data->dialog = win = pidgin_create_dialog(PIDGIN_ALERT_TITLE, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
 #else /* !_WIN32 */
-	data->dialog = win = pidgin_create_window(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
+	data->dialog = win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, "multifield", TRUE) ;
 #endif /* _WIN32 */
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -1099,7 +1105,7 @@
 
 	/* Setup the main horizontal box */
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), hbox);
+	gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(win))), hbox);
 	gtk_widget_show(hbox);
 
 	/* Dialog icon. */
@@ -1382,39 +1388,21 @@
 
 	g_object_unref(sg);
 
-	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(text_to_stock(cancel_text));
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(multifield_cancel_cb), data);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(cancel_text), G_CALLBACK(multifield_cancel_cb), data);
 	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 
 	/* OK button */
-	button = gtk_button_new_from_stock(text_to_stock(ok_text));
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_show(button);
-
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), text_to_stock(ok_text), G_CALLBACK(multifield_ok_cb), data);
 	data->ok_button = button;
-
 	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
 	gtk_window_set_default(GTK_WINDOW(win), button);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(multifield_ok_cb), data);
-
 	if (!purple_request_fields_all_required_filled(fields))
 		gtk_widget_set_sensitive(button, FALSE);
 
+	pidgin_auto_parent_window(win);
+
 	gtk_widget_show(win);
 
 	return data;
@@ -1502,7 +1490,9 @@
 	}
 
 #endif /* FILECHOOSER */
-
+#if 0 /* mismatched curly braces */
+	}
+#endif
 	if ((data->u.file.savedialog == TRUE) &&
 		(g_file_test(data->u.file.name, G_FILE_TEST_EXISTS))) {
 		purple_request_action(data, NULL, _("That file already exists"),
@@ -1516,7 +1506,7 @@
 }
 
 #if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-static void
+static gboolean
 file_cancel_cb(PidginRequestData *data)
 {
 	generic_response_start(data);
@@ -1525,6 +1515,7 @@
 		((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL);
 
 	purple_request_close(data->type, data);
+	return FALSE;
 }
 #endif /* FILECHOOSER */
 
@@ -1622,6 +1613,8 @@
 					 G_CALLBACK(file_ok_check_if_exists_cb), data);
 #endif /* FILECHOOSER */
 
+	pidgin_auto_parent_window(filesel);
+
 	data->dialog = filesel;
 	gtk_widget_show(filesel);
 
@@ -1673,6 +1666,8 @@
 #endif
 
 	data->dialog = dirsel;
+	pidgin_auto_parent_window(dirsel);
+
 	gtk_widget_show(dirsel);
 
 	return (void *)data;
--- a/pidgin/gtkroomlist.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkroomlist.c	Mon Jan 07 03:40:27 2008 +0000
@@ -28,6 +28,8 @@
 #include "pidgin.h"
 #include "gtkutils.h"
 #include "pidginstock.h"
+#include "pidgintooltip.h"
+
 #include "debug.h"
 #include "account.h"
 #include "connection.h"
@@ -340,41 +342,20 @@
 	}
 }
 
-static void pidgin_roomlist_tooltip_destroy(PidginRoomlist *grl)
-{
-	if ((grl == NULL) || (grl->tipwindow == NULL))
-		return;
-
-	gtk_widget_destroy(grl->tipwindow);
-	grl->tipwindow = NULL;
-}
-
-static void pidgin_roomlist_tooltip_destroy_cb(GObject *object, PidginRoomlist *grl)
-{
-	if ((grl == NULL) || (grl->tipwindow == NULL))
-		return;
-
-	if (grl->timeout)
-		g_source_remove(grl->timeout);
-	grl->timeout = 0;
-
-	pidgin_roomlist_tooltip_destroy(grl);
-}
-
 #define SMALL_SPACE 6
 #define TOOLTIP_BORDER 12
 
-static void pidgin_roomlist_paint_tip(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+static gboolean
+pidgin_roomlist_paint_tooltip(GtkWidget *widget, gpointer user_data)
 {
-	PidginRoomlist *grl = (PidginRoomlist *)user_data;
+	PurpleRoomlist *list = user_data;
+	PidginRoomlist *grl = list->ui_data;
 	GtkStyle *style;
 	int current_height, max_width;
 	int max_text_width;
 	GtkTextDirection dir = gtk_widget_get_direction(GTK_WIDGET(grl->tree));
 
 	style = grl->tipwindow->style;
-	gtk_paint_flat_box(style, grl->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
-			NULL, grl->tipwindow, "tooltip", 0, 0, -1, -1);
 
 	max_text_width = 0;
 
@@ -404,15 +385,13 @@
 				current_height + grl->tip_name_height,
 				grl->tip_layout);
 	}
-
+	return FALSE;
 }
 
-static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list)
+static gboolean pidgin_roomlist_create_tip(PurpleRoomlist *list, GtkTreePath *path)
 {
 	PidginRoomlist *grl = list->ui_data;
-	GtkWidget *tv = grl->tree;
 	PurpleRoomlistRoom *room;
-	GtkTreePath *path;
 	GtkTreeIter iter;
 	GValue val;
 	gchar *name, *tmp, *node_name;
@@ -421,10 +400,11 @@
 	gint j;
 	gboolean first = TRUE;
 
+#if 0
 	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), grl->tip_rect.x, grl->tip_rect.y + (grl->tip_rect.height/2),
 		&path, NULL, NULL, NULL))
 		return FALSE;
-
+#endif
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(grl->model), &iter, path);
 
 	val.g_type = 0;
@@ -460,8 +440,6 @@
 		g_free(label);
 	}
 
-	gtk_tree_path_free(path);
-
 	grl->tip_layout = gtk_widget_create_pango_layout(grl->tipwindow, NULL);
 	grl->tip_name_layout = gtk_widget_create_pango_layout(grl->tipwindow, NULL);
 
@@ -492,156 +470,22 @@
 	return TRUE;
 }
 
-static void pidgin_roomlist_draw_tooltip(PurpleRoomlist *list, GtkWidget *widget)
+static gboolean
+pidgin_roomlist_create_tooltip(GtkWidget *widget, GtkTreePath *path,
+		gpointer data, int *w, int *h)
 {
+	PurpleRoomlist *list = data;
 	PidginRoomlist *grl = list->ui_data;
-	int scr_w, scr_h, w, h, x, y;
-#if GTK_CHECK_VERSION(2,2,0)
-	int mon_num;
-	GdkScreen *screen = NULL;
-#endif
-	GdkRectangle mon_size;
-	int sig;
-	const char *name;
-
-	pidgin_roomlist_tooltip_destroy(grl);
-	grl->tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
-	gtk_widget_ensure_style (grl->tipwindow);
-
-	if (!pidgin_roomlist_create_tip(list)) {
-		pidgin_roomlist_tooltip_destroy(grl);
-		return;
-	}
-
-	name = gtk_window_get_title(GTK_WINDOW(gtk_widget_get_toplevel(widget)));
-	gtk_widget_set_app_paintable(grl->tipwindow, TRUE);
-	gtk_window_set_title(GTK_WINDOW(grl->tipwindow), name ? name : _("Room List"));
-	gtk_window_set_resizable(GTK_WINDOW(grl->tipwindow), FALSE);
-	gtk_widget_set_name(grl->tipwindow, "gtk-tooltips");
-	g_signal_connect(G_OBJECT(grl->tipwindow), "expose_event",
-			G_CALLBACK(pidgin_roomlist_paint_tip), grl);
-
-	w = TOOLTIP_BORDER + SMALL_SPACE +
-		MAX(grl->tip_width, grl->tip_name_width) + TOOLTIP_BORDER;
-	h = TOOLTIP_BORDER + grl->tip_height + grl->tip_name_height
-		+ TOOLTIP_BORDER;
-
-#if GTK_CHECK_VERSION(2,2,0)
-	gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL);
-	mon_num = gdk_screen_get_monitor_at_point(screen, x, y);
-	gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size);
-
-	scr_w = mon_size.width + mon_size.x;
-	scr_h = mon_size.height + mon_size.y;
-#else
-	scr_w = gdk_screen_width();
-	scr_h = gdk_screen_height();
-	gdk_window_get_pointer(NULL, &x, &y, NULL);
-	mon_size.x = 0;
-	mon_size.y = 0;
-#endif
-
-#if GTK_CHECK_VERSION(2,2,0)
-	if (w > mon_size.width)
-		w = mon_size.width - 10;
-
-	if (h > mon_size.height)
-		h = mon_size.height - 10;
-#endif
-	x -= ((w >> 1) + 4);
-
-	if ((y + h + 4) > scr_h)
-		y = y - h - 5;
-	else
-		y = y + 6;
-
-	if (y < mon_size.y)
-		y = mon_size.y;
-
-	if (y != mon_size.y) {
-		if ((x + w) > scr_w)
-			x -= (x + w + 5) - scr_w;
-		else if (x < mon_size.x)
-			x = mon_size.x;
-	} else {
-		x -= (w / 2 + 10);
-		if (x < mon_size.x)
-			x = mon_size.x;
-	}
-
-	gtk_widget_set_size_request(grl->tipwindow, w, h);
-	gtk_window_move(GTK_WINDOW(grl->tipwindow), x, y);
-	gtk_widget_show(grl->tipwindow);
-
-	/* Hide the tooltip when the widget is destroyed */
-	sig = g_signal_connect(G_OBJECT(widget), "destroy", G_CALLBACK(pidgin_roomlist_tooltip_destroy_cb), grl);
-	g_signal_connect_swapped(G_OBJECT(grl->tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig));
-}
-
-static gboolean pidgin_roomlist_tooltip_timeout(PurpleRoomlist *list)
-{
-	PidginRoomlist *grl = list->ui_data;
-	GtkWidget *tv = grl->tree;
-	GtkTreePath *path;
-
-	pidgin_roomlist_tooltip_destroy(grl);
-
-	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), grl->tip_rect.x, grl->tip_rect.y + (grl->tip_rect.height/2),
-	                                   &path, NULL, NULL, NULL))
+	grl->tipwindow = widget;
+	if (!pidgin_roomlist_create_tip(data, path))
 		return FALSE;
-
-	pidgin_roomlist_draw_tooltip(list, GTK_WIDGET(grl->tree));
-
-	return FALSE;
-}
-
-static gboolean row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer user_data)
-{
-	PurpleRoomlist *list = user_data;
-	PidginRoomlist *grl = list->ui_data;
-	GtkTreePath *path;
-	int delay;
-
-	/* XXX: should this be using the blist delay pref? */
-	delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
-
-	if (delay == 0)
-		return FALSE;
-
-	if (grl->timeout) {
-		if ((event->y > grl->tip_rect.y) && ((event->y - grl->tip_rect.height) < grl->tip_rect.y))
-			return FALSE;
-		/* We've left the cell.  Remove the timeout and create a new one below */
-		pidgin_roomlist_tooltip_destroy(grl);
-	}
-
-	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
-
-	if (path == NULL) {
-		pidgin_roomlist_tooltip_destroy(grl);
-		return FALSE;
-	}
-
-	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &grl->tip_rect);
-
-	if (path)
-		gtk_tree_path_free(path);
-	grl->timeout = g_timeout_add(delay, (GSourceFunc)pidgin_roomlist_tooltip_timeout, list);
-
-	return FALSE;
-}
-
-static void row_leave_cb(GtkWidget *tv, GdkEventCrossing *e, gpointer user_data)
-{
-	PurpleRoomlist *list = user_data;
-	PidginRoomlist *grl = list->ui_data;
-
-	if (grl->timeout) {
-		g_source_remove(grl->timeout);
-		grl->timeout = 0;
-	}
-
-	pidgin_roomlist_tooltip_destroy(grl);
+	if (w)
+		*w = TOOLTIP_BORDER + SMALL_SPACE +
+			MAX(grl->tip_width, grl->tip_name_width) + TOOLTIP_BORDER;
+	if (h)
+		*h = TOOLTIP_BORDER + grl->tip_height + grl->tip_name_height
+			+ TOOLTIP_BORDER;
+	return TRUE;
 }
 
 static gboolean account_filter_func(PurpleAccount *account)
@@ -677,48 +521,30 @@
 	GtkWidget *window;
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
-	GtkWidget *account_hbox;
 	GtkWidget *bbox;
-	GtkWidget *label;
 
 	dialog = g_new0(PidginRoomlistDialog, 1);
 	dialog->account = account;
 
 	/* Create the window. */
-	dialog->window = window = pidgin_create_window(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
+	dialog->window = window = pidgin_create_dialog(_("Room List"), PIDGIN_HIG_BORDER, "room list", TRUE);
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(delete_win_cb), dialog);
 
 	/* Create the parent vbox for everything. */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(window), vbox);
-	gtk_widget_show(vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
 
 	vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(vbox), vbox2);
 	gtk_widget_show(vbox2);
 
 	/* accounts dropdown list */
-	account_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox2), account_hbox, FALSE, FALSE, 0);
-	gtk_widget_show(account_hbox);
-
-	label = gtk_label_new(NULL);
-	gtk_box_pack_start(GTK_BOX(account_hbox), label, FALSE, FALSE, 0);
-	gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Account:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_widget_show(label);
-
 	dialog->account_widget = pidgin_account_option_menu_new(dialog->account, FALSE,
 	                         G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog);
-
 	if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */
 		dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget);
-
-	gtk_box_pack_start(GTK_BOX(account_hbox), dialog->account_widget, TRUE, TRUE, 0);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(dialog->account_widget));
-	gtk_widget_show(dialog->account_widget);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL);
 
 	/* scrolled window */
 	dialog->sw = gtk_scrolled_window_new(NULL, NULL);
@@ -738,19 +564,14 @@
 	gtk_widget_show(dialog->progress);
 
 	/* button box */
-	bbox = gtk_hbutton_box_new();
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window));
 	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
 	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-	gtk_widget_show(bbox);
 
 	/* stop button */
-	dialog->stop_button = gtk_button_new_from_stock(GTK_STOCK_STOP);
-	gtk_box_pack_start(GTK_BOX(bbox), dialog->stop_button, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(dialog->stop_button), "clicked",
+	dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP,
 	                 G_CALLBACK(stop_button_cb), dialog);
 	gtk_widget_set_sensitive(dialog->stop_button, FALSE);
-	gtk_widget_show(dialog->stop_button);
 
 	/* list button */
 	dialog->list_button = pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH,
@@ -779,11 +600,8 @@
 	gtk_widget_show(dialog->join_button);
 
 	/* close button */
-	dialog->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), dialog->close_button, FALSE, FALSE, 0);
-	g_signal_connect(G_OBJECT(dialog->close_button), "clicked",
+	dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE,
 					 G_CALLBACK(close_button_cb), dialog);
-	gtk_widget_show(dialog->close_button);
 
 	/* show the dialog window and return the dialog */
 	gtk_widget_show(dialog->window);
@@ -967,6 +785,9 @@
 	g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), list);
 	g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(row_leave_cb), list);
 #endif
+	pidgin_tooltip_setup_for_treeview(tree, list,
+		pidgin_roomlist_create_tooltip,
+		pidgin_roomlist_paint_tooltip);
 
 	/* Enable CTRL+F searching */
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), NAME_COLUMN);
--- a/pidgin/gtksavedstatuses.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtksavedstatuses.c	Mon Jan 07 03:40:27 2008 +0000
@@ -594,7 +594,7 @@
 	width  = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/width");
 	height = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/status/dialog/height");
 
-	dialog->window = win = pidgin_create_window(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Saved Statuses"), PIDGIN_HIG_BORDER, "statuses", TRUE);
 	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
@@ -603,18 +603,14 @@
 					 G_CALLBACK(configure_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	/* List of saved status states */
 	list = create_saved_status_list(dialog);
 	gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 0);
 
 	/* Button box. */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win));
 
 	/* Use button */
 	button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE,
@@ -627,36 +623,23 @@
 					 G_CALLBACK(status_window_use_cb), dialog);
 
 	/* Add button */
-	button = gtk_button_new_from_stock(GTK_STOCK_ADD);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_add_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_ADD,
+			G_CALLBACK(status_window_add_cb), dialog);
 
 	/* Modify button */
-	button = gtk_button_new_from_stock(PIDGIN_STOCK_MODIFY);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), PIDGIN_STOCK_MODIFY,
+			G_CALLBACK(status_window_modify_cb), dialog);
 	dialog->modify_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+
+	/* Delete button */
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_DELETE,
+			G_CALLBACK(status_window_delete_cb), dialog);
+	dialog->delete_button = button;
 	gtk_widget_set_sensitive(button, FALSE);
 
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_modify_cb), dialog);
-
-	/* Delete button */
-	button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
-	dialog->delete_button = button;
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-	gtk_widget_set_sensitive(button, FALSE);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_delete_cb), dialog);
-
 	/* Close button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_window_close_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
+			G_CALLBACK(status_window_close_cb), dialog);
 
 	purple_signal_connect(purple_savedstatuses_get_handle(),
 			"savedstatus-changed", status_window,
@@ -1109,7 +1092,6 @@
 	GtkWidget *entry;
 	GtkWidget *frame;
 	GtkWidget *hbox;
-	GtkWidget *label;
 	GtkWidget *sw;
 	GtkWidget *text;
 	GtkWidget *toolbar;
@@ -1147,64 +1129,40 @@
 	if (edit)
 		dialog->original_title = g_strdup(purple_savedstatus_get_title(saved_status));
 
-	dialog->window = win = pidgin_create_window(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
+	dialog->window = win = pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(status_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
 	/* Title */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Title:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	entry = gtk_entry_new();
 	dialog->title = GTK_ENTRY(entry);
 	if ((saved_status != NULL)
 			&& !purple_savedstatus_is_transient(saved_status)
 			&& (purple_savedstatus_get_title(saved_status) != NULL))
 		gtk_entry_set_text(GTK_ENTRY(entry), purple_savedstatus_get_title(saved_status));
-	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
 	g_signal_connect(G_OBJECT(entry), "changed",
 					 G_CALLBACK(editor_title_changed_cb), dialog);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Title:"), sg, entry, TRUE, NULL);
 
 	/* Status type */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Status:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	if (saved_status != NULL)
 		dropdown = create_status_type_menu(purple_savedstatus_get_type(saved_status));
 	else
 		dropdown = create_status_type_menu(PURPLE_STATUS_AWAY);
 	dialog->type = GTK_OPTION_MENU(dropdown);
-	gtk_box_pack_start(GTK_BOX(hbox), dropdown, TRUE, TRUE, 0);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Status:"), sg, dropdown, TRUE, NULL);
 
 	/* Status message */
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
-
-	label = gtk_label_new_with_mnemonic(_("_Message:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-
 	frame = pidgin_create_imhtml(TRUE, &text, &toolbar, NULL);
 	dialog->message = GTK_IMHTML(text);
-	gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
+	hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Message:"), sg, frame, TRUE, NULL);
+	gtk_container_child_set(GTK_CONTAINER(vbox), hbox, "expand", TRUE, "fill", TRUE, NULL);
 	focus_chain = g_list_prepend(focus_chain, dialog->message);
 	gtk_container_set_focus_chain(GTK_CONTAINER(hbox), focus_chain);
 	g_list_free(focus_chain);
@@ -1257,23 +1215,18 @@
 		(saved_status != NULL) && purple_savedstatus_has_substatuses(saved_status));
 
 	/* Button box */
-	bbox = gtk_hbutton_box_new();
+	bbox = pidgin_dialog_get_action_area(GTK_DIALOG(win));
 	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
 	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
 
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_editor_cancel_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL,
+			G_CALLBACK(status_editor_cancel_cb), dialog);
 
 	/* Use button */
 	button = pidgin_pixbuf_button_from_stock(_("_Use"), GTK_STOCK_EXECUTE,
 										   PIDGIN_BUTTON_HORIZONTAL);
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(status_editor_ok_cb), dialog);
 
@@ -1284,19 +1237,15 @@
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	if (dialog->original_title == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(status_editor_ok_cb), dialog);
 
 	/* Save button */
-	button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	dialog->save_button = GTK_BUTTON(button);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_SAVE,
+			G_CALLBACK(status_editor_ok_cb), dialog);
 	if (dialog->original_title == NULL)
 		gtk_widget_set_sensitive(button, FALSE);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(status_editor_ok_cb), dialog);
+	dialog->save_button = GTK_BUTTON(button);
 
 	gtk_widget_show_all(win);
 	g_object_unref(sg);
@@ -1447,8 +1396,6 @@
 	char *tmp;
 	SubStatusEditor *dialog;
 	GtkSizeGroup *sg;
-	GtkWidget *bbox;
-	GtkWidget *button;
 	GtkWidget *combo;
 	GtkWidget *hbox;
 	GtkWidget *frame;
@@ -1486,15 +1433,14 @@
 	dialog->account = account;
 
 	tmp = g_strdup_printf(_("Status for %s"), purple_account_get_username(account));
-	dialog->window = win = pidgin_create_window(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
+	dialog->window = win = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
 	g_free(tmp);
 
 	g_signal_connect(G_OBJECT(win), "delete_event",
 					 G_CALLBACK(substatus_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
-	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(win), vbox);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
 
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
@@ -1543,25 +1489,13 @@
 	dialog->toolbar = GTK_IMHTMLTOOLBAR(toolbar);
 	gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);
 
-	/* Button box */
-	bbox = gtk_hbutton_box_new();
-	gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
-	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
-
 	/* Cancel button */
-	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(substatus_editor_cancel_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CANCEL,
+			G_CALLBACK(substatus_editor_cancel_cb), dialog);
 
 	/* OK button */
-	button = gtk_button_new_from_stock(GTK_STOCK_OK);
-	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
-
-	g_signal_connect(G_OBJECT(button), "clicked",
-					 G_CALLBACK(substatus_editor_ok_cb), dialog);
+	pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_OK,
+			G_CALLBACK(substatus_editor_ok_cb), dialog);
 
 	/* Seed the input widgets with the current values */
 
--- a/pidgin/gtkscrollbook.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkscrollbook.c	Mon Jan 07 03:40:27 2008 +0000
@@ -64,7 +64,7 @@
 	return scroll_book_type;
 }
 
-static void
+static gboolean
 scroll_left_cb(PidginScrollBook *scroll_book)
 {
 	int index;
@@ -72,9 +72,10 @@
 
 	if (index > 0)
 		gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index - 1);
+	return TRUE;
 }
 
-static void
+static gboolean
 scroll_right_cb(PidginScrollBook *scroll_book)
 {
 	int index, count;
@@ -87,6 +88,7 @@
 
 	if (index + 1 < count)
 		gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index + 1);
+	return TRUE;
 }
 
 static void
@@ -136,10 +138,11 @@
 	refresh_scroll_box(scroll_book, index, count);
 }
 
-static void
+static gboolean
 scroll_close_cb(PidginScrollBook *scroll_book)
 {
 	gtk_widget_destroy(gtk_notebook_get_nth_page(GTK_NOTEBOOK(scroll_book->notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook))));
+	return FALSE;
 }
 
 static void
--- a/pidgin/gtksession.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtksession.c	Mon Jan 07 03:40:27 2008 +0000
@@ -124,7 +124,7 @@
 	purple_debug(PURPLE_DEBUG_INFO, NULL, "done.\n");
 }
 
-static void ice_init() {
+static void ice_init(void) {
 	IceIOErrorHandler default_handler;
 
 	ice_installed_io_error_handler = IceSetIOErrorHandler(NULL);
--- a/pidgin/gtkstatusbox.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkstatusbox.c	Mon Jan 07 03:40:27 2008 +0000
@@ -119,7 +119,10 @@
 	DATA_COLUMN,
 
 	/**
- 	 * This column stores the GdkPixbuf for the status emblem. Currently only 'saved' is stored
+ 	 * This column stores the GdkPixbuf for the status emblem. Currently only 'saved' is stored.
+	 * In the GtkTreeModel for the dropdown, this is the stock-id (gchararray), and for the
+	 * GtkTreeModel for the cell_view (for the account-specific statusbox), this is the prpl-icon
+	 * (GdkPixbuf) of the account.
  	 */
 	EMBLEM_COLUMN,
 
@@ -606,7 +609,7 @@
 	char aa_color[8];
 	PurpleSavedStatus *saved_status;
 	char *primary, *secondary, *text;
-	GdkPixbuf *pixbuf;
+	GdkPixbuf *pixbuf, *emblem = NULL;
 	GtkTreePath *path;
 	gboolean account_status = FALSE;
 	PurpleAccount *acct = (status_box->token_status_account) ? status_box->token_status_account : status_box->account;
@@ -703,6 +706,7 @@
 		text = g_strdup_printf("%s - <span size=\"smaller\" color=\"%s\">%s</span>",
 				       purple_account_get_username(status_box->account),
 				       aa_color, secondary ? secondary : primary);
+		emblem = pidgin_create_prpl_icon(status_box->account, PIDGIN_PRPL_ICON_SMALL);
 	} else if (secondary != NULL) {
 		text = g_strdup_printf("%s<span size=\"smaller\" color=\"%s\"> - %s</span>",
 				       primary, aa_color, secondary);
@@ -719,10 +723,14 @@
 	gtk_list_store_set(status_box->store, &(status_box->iter),
 			   ICON_COLUMN, pixbuf,
 			   TEXT_COLUMN, text,
+			   EMBLEM_COLUMN, emblem,
+			   EMBLEM_VISIBLE_COLUMN, (emblem != NULL),
 			   -1);
 	if ((status_box->typing == 0) && (!status_box->connecting))
 		g_object_unref(pixbuf);
 	g_free(text);
+	if (emblem)
+		g_object_unref(emblem);
 
 	/* Make sure to activate the only row in the tree view */
 	path = gtk_tree_path_new_from_string("0");
@@ -941,7 +949,7 @@
 
 /* This returns NULL if the active accounts don't have identical
  * statuses and a token account if they do */
-static PurpleAccount* check_active_accounts_for_identical_statuses()
+static PurpleAccount* check_active_accounts_for_identical_statuses(void)
 {
 	PurpleAccount *acct = NULL, *acct2;
 	GList *tmp, *tmp2, *active_accts = purple_accounts_get_all_active();
@@ -1100,7 +1108,7 @@
 	return TRUE;
 }
 
-static int imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box)
+static gboolean imhtml_remove_focus(GtkWidget *w, GdkEventKey *event, PidginStatusBox *status_box)
 {
 	if (event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab)
 	{
@@ -1690,7 +1698,7 @@
 	status_box->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
 
 	status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, 
-					       G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN);
+					       G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);
 	status_box->dropdown_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, 
 							G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN);
 
@@ -1775,10 +1783,13 @@
 
 	status_box->icon_rend = gtk_cell_renderer_pixbuf_new();
 	status_box->text_rend = gtk_cell_renderer_text_new();
+	emblem_rend = gtk_cell_renderer_pixbuf_new();
 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, FALSE);
 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), status_box->text_rend, TRUE);
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, FALSE);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, "pixbuf", ICON_COLUMN, NULL);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->text_rend, "markup", TEXT_COLUMN, NULL);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, "pixbuf", EMBLEM_COLUMN, "visible", EMBLEM_VISIBLE_COLUMN, NULL);
 #if GTK_CHECK_VERSION(2, 6, 0)
 	g_object_set(status_box->text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 #endif
--- a/pidgin/gtkthemes.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkthemes.c	Mon Jan 07 03:40:27 2008 +0000
@@ -168,7 +168,7 @@
 }
 
 static void
-pidgin_smiley_themes_remove_non_existing()
+pidgin_smiley_themes_remove_non_existing(void)
 {
 	static struct smiley_theme *theme = NULL;
 	GSList *iter = NULL;
--- a/pidgin/gtkutils.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkutils.c	Mon Jan 07 03:40:27 2008 +0000
@@ -132,12 +132,9 @@
 	}
 }
 
-GtkWidget *
-pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable)
+static
+void pidgin_window_init(GtkWindow *wnd, const char *title, guint border_width, const char *role, gboolean resizable)
 {
-	GtkWindow *wnd = NULL;
-
-	wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
 	if (title)
 		gtk_window_set_title(wnd, title);
 #ifdef _WIN32
@@ -148,11 +145,63 @@
 	if (role)
 		gtk_window_set_role(wnd, role);
 	gtk_window_set_resizable(wnd, resizable);
+}
+
+GtkWidget *
+pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable)
+{
+	GtkWindow *wnd = NULL;
+
+	wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+	pidgin_window_init(wnd, title, border_width, role, resizable);
+
+	return GTK_WIDGET(wnd);
+}
+
+GtkWidget *
+pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable)
+{
+	GtkWindow *wnd = NULL;
+
+	wnd = GTK_WINDOW(gtk_dialog_new());
+	pidgin_window_init(wnd, title, border_width, role, resizable);
+	g_object_set(G_OBJECT(wnd), "has-separator", FALSE, NULL);
 
 	return GTK_WIDGET(wnd);
 }
 
 GtkWidget *
+pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing)
+{
+	GtkBox *vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox);
+	gtk_box_set_homogeneous(vbox, homogeneous);
+	gtk_box_set_spacing(vbox, spacing);
+	return GTK_WIDGET(vbox);
+}
+
+GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog)
+{
+	return GTK_DIALOG(dialog)->vbox;
+}
+
+GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog)
+{
+	return GTK_DIALOG(dialog)->action_area;
+}
+
+GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
+		GCallback callback, gpointer callbackdata)
+{
+	GtkWidget *button = gtk_button_new_from_stock(label);
+	GtkWidget *bbox = pidgin_dialog_get_action_area(dialog);
+	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
+	if (callback)
+		g_signal_connect(G_OBJECT(button), "clicked", callback, callbackdata);
+	gtk_widget_show(button);
+	return button;
+}
+
+GtkWidget *
 pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret)
 {
 	GtkWidget *frame;
@@ -1399,7 +1448,7 @@
 			char *str;
 
 			str = g_strdup_printf(_("The following error has occurred loading %s: %s"),
-						data->filename, strerror(errno));
+						data->filename, g_strerror(errno));
 			purple_notify_error(NULL, NULL,
 					  _("Failed to load image"),
 					  str);
@@ -1652,23 +1701,23 @@
 	GdkPixbuf *pixbuf = NULL;
 
 	if (prim == PURPLE_STATUS_UNAVAILABLE)
-        	pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_BUSY,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_AWAY)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AWAY,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_EXTENDED_AWAY)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_XA,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_INVISIBLE)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_INVISIBLE,
-                                                 icon_size, "GtkWidget");
-        else if (prim == PURPLE_STATUS_OFFLINE)
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_OFFLINE,
-                                                 icon_size, "GtkWidget");
-        else
-                pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AVAILABLE,
-                                                 icon_size, "GtkWidget");
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_BUSY,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_AWAY)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AWAY,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_EXTENDED_AWAY)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_XA,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_INVISIBLE)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_INVISIBLE,
+				icon_size, "GtkWidget");
+	else if (prim == PURPLE_STATUS_OFFLINE)
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_OFFLINE,
+				icon_size, "GtkWidget");
+	else
+		pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AVAILABLE,
+				icon_size, "GtkWidget");
 	return pixbuf;
 
 }
@@ -2306,6 +2355,9 @@
 	}
 
 #endif /* FILECHOOSER */
+#if 0 /* mismatched curly braces */
+	}
+#endif
 	if (dialog->callback)
 		dialog->callback(filename, dialog->data);
 	gtk_widget_destroy(dialog->icon_filesel);
@@ -2895,7 +2947,7 @@
 GSList *minidialogs = NULL;
 
 static void *
-pidgin_utils_get_handle()
+pidgin_utils_get_handle(void)
 {
 	static int handle;
 
@@ -3269,6 +3321,144 @@
 	gtk_entry_set_text(GTK_ENTRY(GTK_BIN((widget))->child), (text));
 }
 
+GtkWidget *
+pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label)
+{
+	GtkWidget *hbox;
+	GtkWidget *label = NULL;
+
+	if (widget_label) {
+		hbox = gtk_hbox_new(FALSE, 5);
+		gtk_widget_show(hbox);
+		gtk_box_pack_start(vbox, hbox, FALSE, FALSE, 0);
+
+		label = gtk_label_new_with_mnemonic(widget_label);
+		gtk_widget_show(label);
+		if (sg) {
+			gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+			gtk_size_group_add_widget(sg, label);
+		}
+		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	} else {
+		hbox = GTK_WIDGET(vbox);
+	}
+
+	gtk_widget_show(widget);
+	gtk_box_pack_start(GTK_BOX(hbox), widget, expand, TRUE, 0);
+	if (label) {
+		gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
+		pidgin_set_accessible_label (widget, label);
+	}
+
+	if (p_label)
+		(*p_label) = label;
+	return hbox;
+}
+
+gboolean pidgin_auto_parent_window(GtkWidget *widget)
+{
+#if 0
+	/* This looks at the most recent window that received focus, and makes
+	 * that the parent window. */
+#ifndef _WIN32
+	static GdkAtom _WindowTime = GDK_NONE;
+	static GdkAtom _Cardinal = GDK_NONE;
+	GList *windows = NULL;
+	GtkWidget *parent = NULL;
+	time_t window_time = 0;
+
+	windows = gtk_window_list_toplevels();
+
+	if (_WindowTime == GDK_NONE) {
+		_WindowTime = gdk_x11_xatom_to_atom(gdk_x11_get_xatom_by_name("_NET_WM_USER_TIME"));
+	}
+	if (_Cardinal == GDK_NONE) {
+		_Cardinal = gdk_atom_intern("CARDINAL", FALSE);
+	}
+
+	while (windows) {
+		GtkWidget *window = windows->data;
+		guchar *data = NULL;
+		int al = 0;
+		time_t value;
+
+		windows = g_list_delete_link(windows, windows);
+
+		if (window == widget ||
+				!GTK_WIDGET_VISIBLE(window))
+			continue;
+
+		if (!gdk_property_get(window->window, _WindowTime, _Cardinal, 0, sizeof(time_t), FALSE,
+				NULL, NULL, &al, &data))
+			continue;
+		value = *(time_t *)data;
+		if (window_time < value) {
+			window_time = value;
+			parent = window;
+		}
+		g_free(data);
+	}
+	if (windows)
+		g_list_free(windows);
+	if (parent) {
+		if (!gtk_get_current_event() && gtk_window_has_toplevel_focus(GTK_WINDOW(parent))) {
+			/* The window is in focus, and the new window was not triggered by a keypress/click
+			 * event. So do not set it transient, to avoid focus stealing and all that.
+			 */
+			return FALSE;
+		}
+		gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
+		return TRUE;
+	}
+	return FALSE;
+#endif
+#else
+	/* This finds the currently active window and makes that the parent window. */
+	GList *windows = NULL;
+	GtkWidget *parent = NULL;
+	GdkEvent *event = gtk_get_current_event();
+	GdkWindow *menu = NULL;
+
+	if (event == NULL)
+		/* The window was not triggered by a user action. */
+		return FALSE;
+
+	/* We need to special case events from a popup menu. */
+	if (event->type == GDK_BUTTON_RELEASE) {
+		/* XXX: Neither of the following works:
+			menu = event->button.window;
+			menu = gdk_window_get_parent(event->button.window);
+			menu = gdk_window_get_toplevel(event->button.window);
+		*/
+	} else if (event->type == GDK_KEY_PRESS)
+		menu = event->key.window;
+
+	windows = gtk_window_list_toplevels();
+	while (windows) {
+		GtkWidget *window = windows->data;
+		windows = g_list_delete_link(windows, windows);
+
+		if (window == widget ||
+				!GTK_WIDGET_VISIBLE(window)) {
+			continue;
+		}
+
+		if (gtk_window_has_toplevel_focus(GTK_WINDOW(window)) ||
+				(menu && menu == window->window)) {
+			parent = window;
+			break;
+		}
+	}
+	if (windows)
+		g_list_free(windows);
+	if (parent) {
+		gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
+		return TRUE;
+	}
+	return FALSE;
+#endif
+}
+
 gchar *
 pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis)
 {
--- a/pidgin/gtkutils.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/gtkutils.h	Mon Jan 07 03:40:27 2008 +0000
@@ -121,6 +121,61 @@
 GtkWidget *pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable);
 
 /**
+ * Creates a new dialog window
+ *
+ * @param title        The window title, or @c NULL
+ * @param border_width The window's desired border width
+ * @param role         A string indicating what the window is responsible for doing, or @c NULL
+ * @param resizable    Whether the window should be resizable (@c TRUE) or not (@c FALSE)
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable);
+
+/**
+ * Retrieves the main content box (vbox) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ * @param homogeneous  TRUE if all children are to be given equal space allotments. 
+ * @param spacing      the number of pixels to place by default between children
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing);
+
+/**
+ * Retrieves the main content box (vbox) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog);
+
+/**
+ * Add a button to a dialog created by #pidgin_create_dialog.
+ *
+ * @param dialog         The dialog window
+ * @param label          The stock-id or the label for the button
+ * @param callback       The callback function for the button
+ * @param callbackdata   The user data for the callback function
+ *
+ * @return The created button.
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
+		GCallback callback, gpointer callbackdata);
+
+/**
+ * Retrieves the action area (button box) from a pidgin dialog window
+ *
+ * @param dialog       The dialog window
+ *
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog);
+
+/**
  * Toggles the sensitivity of a widget.
  *
  * @param widget    @c NULL. Used for signal handlers.
@@ -319,16 +374,20 @@
 gboolean pidgin_screenname_autocomplete_default_filter(const PidginBuddyCompletionEntry *completion_entry, gpointer all_accounts);
 
 /**
+ * Add autocompletion of screenames to an entry.
+ *
  * @deprecated
- * Add autocompletion of screenames to an entry.
- * The usage of this function is deprecated. For new code, use the equivalent:
- * pidgin_setup_screenname_autocomplete_with_filter(entry, optmenu, pidgin_screenname_autocomplete_default_filter, GINT_TO_POINTER(all))
+ *   For new code, use the equivalent:
+ *   #pidgin_setup_screenname_autocomplete_with_filter(@a entry, @a optmenu,
+ *   #pidgin_screenname_autocomplete_default_filter, <tt>GINT_TO_POINTER(@a
+ *   all)</tt>)
  *
  * @param entry     The GtkEntry on which to setup autocomplete.
- * @param optmenu   A menu for accounts, returned by pidgin_account_option_menu_new().
- *                  If @a optmenu is not @c NULL, it'll be updated when a screenname is chosen
- *                  from the autocomplete list.
- * @param all       Whether to include screennames from disconnected accounts. 
+ * @param optmenu   A menu for accounts, returned by
+ *                  pidgin_account_option_menu_new().  If @a optmenu is not @c
+ *                  NULL, it'll be updated when a screenname is chosen from the
+ *                  autocomplete list.
+ * @param all       Whether to include screennames from disconnected accounts.
  */
 void pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *optmenu, gboolean all);
 
@@ -451,7 +510,7 @@
 
 /**
  * A valid GtkMenuPositionFunc.  This is used to determine where 
- * to draw context menu's when the menu is activated with the 
+ * to draw context menus when the menu is activated with the 
  * keyboard (shift+F10).  If the menu is activated with the mouse, 
  * then you should just use GTK's built-in position function, 
  * because it does a better job of positioning the menu.
@@ -725,6 +784,31 @@
  */
 void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);
 
+/**
+ * Automatically make a window transient to a suitable parent window.
+ *
+ * @param window    The window to make transient.
+ *
+ * @return  Whether the window was made transient or not.
+ * @since 2.4.0
+ */
+gboolean pidgin_auto_parent_window(GtkWidget *window);
+
+/**
+ * Add a labelled widget to a GtkVBox
+ *
+ * @param vbox         The GtkVBox to add the widget to.
+ * @param widget_label The label to give the widget.
+ * @param sg           The GtkSizeGroup to add the label to.
+ * @param widget       The GtkWidget to add
+ * @param expand       Whether to expand the widget horizontally.
+ * @param p_label      Place to store a pointer to the GtkLabel, or NULL if you don't care.
+ *
+ * @return  A GtkHBox already added to the GtkVBox containing the GtkLabel and the GtkWidget.
+ * @since 2.4.0
+ */
+GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label);
+
 gchar *pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis);
 
 #endif /* _PIDGINUTILS_H_ */
--- a/pidgin/minidialog.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/minidialog.c	Mon Jan 07 03:40:27 2008 +0000
@@ -35,7 +35,41 @@
 #include "pidgin/pidgin.h"
 #include "pidgin/pidginstock.h"
 
-G_DEFINE_TYPE (PidginMiniDialog, pidgin_mini_dialog, GTK_TYPE_VBOX)
+static void     pidgin_mini_dialog_init       (PidginMiniDialog      *self);
+static void     pidgin_mini_dialog_class_init (PidginMiniDialogClass *klass);
+
+static gpointer pidgin_mini_dialog_parent_class = NULL;
+
+static void
+pidgin_mini_dialog_class_intern_init (gpointer klass)
+{
+	pidgin_mini_dialog_parent_class = g_type_class_peek_parent (klass);
+	pidgin_mini_dialog_class_init ((PidginMiniDialogClass*) klass);
+}
+
+GType
+pidgin_mini_dialog_get_type (void)
+{
+	static GType g_define_type_id = 0;
+	if (G_UNLIKELY (g_define_type_id == 0))
+	{
+		static const GTypeInfo g_define_type_info = {
+			sizeof (PidginMiniDialogClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) pidgin_mini_dialog_class_intern_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,   /* class_data */
+			sizeof (PidginMiniDialog),
+			0,      /* n_preallocs */
+			(GInstanceInitFunc) pidgin_mini_dialog_init,
+			NULL,
+		};
+		g_define_type_id = g_type_register_static (GTK_TYPE_VBOX,
+			"PidginMiniDialog", &g_define_type_info, 0);
+	}
+	return g_define_type_id;
+}
 
 enum
 {
--- a/pidgin/pidgincombobox.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/pidgincombobox.c	Mon Jan 07 03:40:27 2008 +0000
@@ -2980,7 +2980,7 @@
 
   g_return_if_fail (link != NULL);
 
-  combo_box->priv->cells = g_slist_remove_link (combo_box->priv->cells, link);
+  combo_box->priv->cells = g_slist_delete_link (combo_box->priv->cells, link);
   combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info,
                                            position);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgintooltip.c	Mon Jan 07 03:40:27 2008 +0000
@@ -0,0 +1,363 @@
+/**
+ * @file pidgintooltip.c Pidgin Tooltip API
+ * @ingroup 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.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "internal.h"
+#include "prefs.h"
+#include "pidgin.h"
+#include "pidgintooltip.h"
+#include "debug.h"
+
+struct
+{
+	GtkWidget *widget;
+	int timeout;
+	GdkRectangle tip_rect;
+	GtkWidget *tipwindow;
+	PidginTooltipPaint paint_tooltip;
+} pidgin_tooltip;
+
+typedef struct
+{
+	GtkWidget *widget;
+	gpointer userdata;
+	PidginTooltipPaint paint_tooltip;
+	union {
+		struct {
+			PidginTooltipCreateForTree create_tooltip;
+			GtkTreePath *path;
+		} treeview;
+		struct {
+			PidginTooltipCreate create_tooltip;
+		} widget;
+	} common;
+} PidginTooltipData;
+
+static void
+destroy_tooltip_data(PidginTooltipData *data)
+{
+	gtk_tree_path_free(data->common.treeview.path);
+	g_free(data);
+}
+
+void pidgin_tooltip_destroy()
+{
+	if (pidgin_tooltip.timeout > 0) {
+		g_source_remove(pidgin_tooltip.timeout);
+		pidgin_tooltip.timeout = 0;
+	}
+	if (pidgin_tooltip.tipwindow) {
+		gtk_widget_destroy(pidgin_tooltip.tipwindow);
+		pidgin_tooltip.tipwindow = NULL;
+	}
+}
+
+static gboolean
+pidgin_tooltip_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+	if (pidgin_tooltip.paint_tooltip) {
+		gtk_paint_flat_box(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+				NULL, widget, "tooltip", 0, 0, -1, -1);
+		pidgin_tooltip.paint_tooltip(widget, data);
+	}
+	return FALSE;
+}
+
+static GtkWidget*
+setup_tooltip_window(void)
+{
+	const char *name;
+	GtkWidget *tipwindow;
+
+	tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
+	name = gtk_window_get_title(GTK_WINDOW(pidgin_tooltip.widget));
+#if GTK_CHECK_VERSION(2,10,0)
+	gtk_window_set_type_hint(GTK_WINDOW(tipwindow), GDK_WINDOW_TYPE_HINT_TOOLTIP);
+#endif
+	gtk_widget_set_app_paintable(tipwindow, TRUE);
+	gtk_window_set_title(GTK_WINDOW(tipwindow), name ? name : _("Pidgin Tooltip"));
+	gtk_window_set_resizable(GTK_WINDOW(tipwindow), FALSE);
+	gtk_widget_set_name(tipwindow, "gtk-tooltips");
+	gtk_widget_ensure_style(tipwindow);
+	gtk_widget_realize(tipwindow);
+	return tipwindow;
+}
+
+static void
+setup_tooltip_window_position(gpointer data, int w, int h)
+{
+	int sig;
+	int scr_w, scr_h, x, y;
+#if GTK_CHECK_VERSION(2,2,0)
+	int mon_num;
+	GdkScreen *screen = NULL;
+#endif
+	GdkRectangle mon_size;
+	GtkWidget *tipwindow = pidgin_tooltip.tipwindow;
+
+#if GTK_CHECK_VERSION(2,2,0)
+	gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL);
+	mon_num = gdk_screen_get_monitor_at_point(screen, x, y);
+	gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size);
+
+	scr_w = mon_size.width + mon_size.x;
+	scr_h = mon_size.height + mon_size.y;
+#else
+	scr_w = gdk_screen_width();
+	scr_h = gdk_screen_height();
+	gdk_window_get_pointer(NULL, &x, &y, NULL);
+	mon_size.x = 0;
+	mon_size.y = 0;
+#endif
+
+#if GTK_CHECK_VERSION(2,2,0)
+	if (w > mon_size.width)
+		w = mon_size.width - 10;
+
+	if (h > mon_size.height)
+		h = mon_size.height - 10;
+#endif
+	x -= ((w >> 1) + 4);
+
+	if ((y + h + 4) > scr_h)
+		y = y - h - 5;
+	else
+		y = y + 6;
+
+	if (y < mon_size.y)
+		y = mon_size.y;
+
+	if (y != mon_size.y) {
+		if ((x + w) > scr_w)
+			x -= (x + w + 5) - scr_w;
+		else if (x < mon_size.x)
+			x = mon_size.x;
+	} else {
+		x -= (w / 2 + 10);
+		if (x < mon_size.x)
+			x = mon_size.x;
+	}
+
+	gtk_widget_set_size_request(tipwindow, w, h);
+	gtk_window_move(GTK_WINDOW(tipwindow), x, y);
+	gtk_widget_show(tipwindow);
+
+	g_signal_connect(G_OBJECT(tipwindow), "expose_event",
+			G_CALLBACK(pidgin_tooltip_expose_event), data);
+
+	/* Hide the tooltip when the widget is destroyed */
+	sig = g_signal_connect(G_OBJECT(pidgin_tooltip.widget), "destroy", G_CALLBACK(pidgin_tooltip_destroy), NULL);
+	g_signal_connect_swapped(G_OBJECT(tipwindow), "destroy", G_CALLBACK(g_source_remove), GINT_TO_POINTER(sig));
+}
+
+void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata,
+		PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip)
+{
+	GtkWidget *tipwindow;
+	int w, h;
+
+	pidgin_tooltip_destroy();
+
+	pidgin_tooltip.widget = gtk_widget_get_toplevel(widget);
+	pidgin_tooltip.tipwindow = tipwindow = setup_tooltip_window();
+	pidgin_tooltip.paint_tooltip = paint_tooltip;
+
+	if (!create_tooltip(tipwindow, userdata, &w, &h)) {
+		pidgin_tooltip_destroy();
+		return;
+	}
+	setup_tooltip_window_position(userdata, w, h);
+}
+
+static void
+reset_data_treepath(PidginTooltipData *data)
+{
+	gtk_tree_path_free(data->common.treeview.path);
+	data->common.treeview.path = NULL;
+}
+
+static void
+pidgin_tooltip_draw(PidginTooltipData *data)
+{
+	GtkWidget *tipwindow;
+	int w, h;
+
+	pidgin_tooltip_destroy();
+
+	pidgin_tooltip.widget = gtk_widget_get_toplevel(data->widget);
+	pidgin_tooltip.tipwindow = tipwindow = setup_tooltip_window();
+	pidgin_tooltip.paint_tooltip = data->paint_tooltip;
+
+	if (!data->common.widget.create_tooltip(tipwindow, data->userdata, &w, &h)) {
+		if (tipwindow == pidgin_tooltip.tipwindow)
+			pidgin_tooltip_destroy();
+		return;
+	}
+
+	setup_tooltip_window_position(data->userdata, w, h);
+}
+
+static void
+pidgin_tooltip_draw_tree(PidginTooltipData *data)
+{
+	GtkWidget *tipwindow;
+	GtkTreePath *path = NULL;
+	int w, h;
+
+	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(data->widget),
+				pidgin_tooltip.tip_rect.x,
+				pidgin_tooltip.tip_rect.y + (pidgin_tooltip.tip_rect.height/2),
+				&path, NULL, NULL, NULL)) {
+		pidgin_tooltip_destroy();
+		return;
+	}
+
+	if (data->common.treeview.path) {
+		if (gtk_tree_path_compare(data->common.treeview.path, path) == 0) {
+			gtk_tree_path_free(path);
+			return;
+		}
+		gtk_tree_path_free(data->common.treeview.path);
+		data->common.treeview.path = NULL;
+	}
+
+	pidgin_tooltip_destroy();
+
+	pidgin_tooltip.widget = gtk_widget_get_toplevel(data->widget);
+	pidgin_tooltip.tipwindow = tipwindow = setup_tooltip_window();
+	pidgin_tooltip.paint_tooltip = data->paint_tooltip;
+
+	if (!data->common.treeview.create_tooltip(tipwindow, path, data->userdata, &w, &h)) {
+		if (tipwindow == pidgin_tooltip.tipwindow)
+			pidgin_tooltip_destroy();
+		gtk_tree_path_free(path);
+		return;
+	}
+
+	setup_tooltip_window_position(data->userdata, w, h);
+
+	data->common.treeview.path = path;
+	g_signal_connect_swapped(G_OBJECT(pidgin_tooltip.tipwindow), "destroy",
+			G_CALLBACK(reset_data_treepath), data);
+}
+
+static gboolean
+pidgin_tooltip_timeout(gpointer data)
+{
+	PidginTooltipData *tdata = data;
+	pidgin_tooltip.timeout = 0;
+	if (GTK_IS_TREE_VIEW(tdata->widget))
+		pidgin_tooltip_draw_tree(data);
+	else
+		pidgin_tooltip_draw(data);
+	return FALSE;
+}
+
+static gboolean
+row_motion_cb(GtkWidget *tv, GdkEventMotion *event, gpointer userdata)
+{
+	GtkTreePath *path;
+	int delay;
+
+	if (event->window != gtk_tree_view_get_bin_window(GTK_TREE_VIEW(tv)))
+		return FALSE;    /* The cursor is probably on the TreeView's header. */
+
+	/* XXX: probably use something more generic? */
+	delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
+	if (delay == 0)
+		return FALSE;
+
+	if (pidgin_tooltip.timeout) {
+		if ((event->y >= pidgin_tooltip.tip_rect.y) && ((event->y - pidgin_tooltip.tip_rect.height) <= pidgin_tooltip.tip_rect.y))
+			return FALSE;
+		/* We've left the cell.  Remove the timeout and create a new one below */
+		pidgin_tooltip_destroy();
+	}
+
+	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL);
+
+	if (path == NULL) {
+		pidgin_tooltip_destroy();
+		return FALSE;
+	}
+
+	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &pidgin_tooltip.tip_rect);
+	gtk_tree_path_free(path);
+
+	pidgin_tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_tooltip_timeout, userdata);
+
+	return FALSE;
+}
+
+static gboolean
+widget_leave_cb(GtkWidget *tv, GdkEvent *event, gpointer userdata)
+{
+	pidgin_tooltip_destroy();
+	return FALSE;
+}
+
+gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata,
+		PidginTooltipCreateForTree create_tooltip, PidginTooltipPaint paint_tooltip)
+{
+	PidginTooltipData *tdata = g_new0(PidginTooltipData, 1);
+	tdata->widget = tree;
+	tdata->userdata = userdata;
+	tdata->common.treeview.create_tooltip = create_tooltip;
+	tdata->paint_tooltip = paint_tooltip;
+
+	g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), tdata);
+	g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL);
+	g_signal_connect_swapped(G_OBJECT(tree), "destroy", G_CALLBACK(destroy_tooltip_data), tdata);
+	return TRUE;
+}
+
+static gboolean
+widget_motion_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+	int delay = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay");
+
+	pidgin_tooltip_destroy();
+	if (delay == 0)
+		return FALSE;
+
+	pidgin_tooltip.timeout = g_timeout_add(delay, (GSourceFunc)pidgin_tooltip_timeout, data);
+	return FALSE;
+}
+
+gboolean pidgin_tooltip_setup_for_widget(GtkWidget *widget, gpointer userdata,
+		PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip)
+{
+	PidginTooltipData *wdata = g_new0(PidginTooltipData, 1);
+	wdata->widget = widget;
+	wdata->userdata = userdata;
+	wdata->common.widget.create_tooltip = create_tooltip;
+	wdata->paint_tooltip = paint_tooltip;
+
+	g_signal_connect(G_OBJECT(widget), "motion-notify-event", G_CALLBACK(widget_motion_cb), wdata);
+	g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL);
+	g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(g_free), wdata);
+	return TRUE;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgintooltip.h	Mon Jan 07 03:40:27 2008 +0000
@@ -0,0 +1,112 @@
+/**
+ * @file pidgintooltip.h Pidgin Tooltip API
+ * @ingroup 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.
+ *
+ * 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 _PIDGIN_TOOLTIP_H_
+#define _PIDGIN_TOOLTIP_H_
+
+#include <gtk/gtk.h>
+
+/**
+ * @param tipwindow  The window for the tooltip.
+ * @param path       The GtkTreePath representing the row under the cursor.
+ * @param userdata   The userdata set during pidgin_tooltip_setup_for_treeview.
+ * @param w          The value of this should be set to the desired width of the tooltip window.
+ * @param h          The value of this should be set to the desired height of the tooltip window.
+ *
+ * @return  @c TRUE if the tooltip was created correctly, @c FALSE otherwise.
+ * @since 2.4.0
+ */
+typedef gboolean (*PidginTooltipCreateForTree)(GtkWidget *tipwindow,
+			GtkTreePath *path, gpointer userdata, int *w, int *h);
+
+/**
+ * @param tipwindow  The window for the tooltip.
+ * @param userdata   The userdata set during pidgin_tooltip_show.
+ * @param w          The value of this should be set to the desired width of the tooltip window.
+ * @param h          The value of this should be set to the desired height of the tooltip window.
+ *
+ * @return  @c TRUE if the tooltip was created correctly, @c FALSE otherwise.
+ * @since 2.4.0
+ */
+typedef gboolean (*PidginTooltipCreate)(GtkWidget *tipwindow,
+			gpointer userdata, int *w, int *h);
+
+/**
+ * @param  tipwindow   The window for the tooltip.
+ * @param  userdata    The userdata set during pidgin_tooltip_setup_for_treeview or pidgin_tooltip_show.
+ *
+ * @return  @c TRUE if the tooltip was painted correctly, @c FALSE otherwise.
+ * @since 2.4.0
+ */
+typedef gboolean (*PidginTooltipPaint)(GtkWidget *tipwindow, gpointer userdata);
+
+/**
+ * Setup tooltip drawing functions for a treeview.
+ *
+ * @param tree         The treeview
+ * @param userdata     The userdata to send to the callback functions
+ * @param create_cb    Callback function to create the tooltip for a GtkTreePath
+ * @param paint_cb     Callback function to paint the tooltip
+ *
+ * @return   @c TRUE if the tooltip callbacks were setup correctly.
+ * @since 2.4.0
+ */
+gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata,
+		PidginTooltipCreateForTree create_cb, PidginTooltipPaint paint_cb);
+
+/**
+ * Setup tooltip drawing functions for any widget.
+ *
+ * @param widget       The widget
+ * @param userdata     The userdata to send to the callback functions
+ * @param create_cb    Callback function to create the tooltip for the widget
+ * @param paint_cb     Callback function to paint the tooltip
+ *
+ * @return   @c TRUE if the tooltip callbacks were setup correctly.
+ * @since 2.4.0
+ */
+gboolean pidgin_tooltip_setup_for_widget(GtkWidget *widget, gpointer userdata,
+		PidginTooltipCreate create_tooltip, PidginTooltipPaint paint_tooltip);
+
+/**
+ * Destroy the tooltip.
+ * @since 2.4.0
+ */
+void pidgin_tooltip_destroy(void);
+
+/**
+ * Create and show a tooltip.
+ *
+ * @param widget      The widget the tooltip is for
+ * @param userdata    The userdata to send to the callback functions
+ * @param create_cb    Callback function to create the tooltip from the GtkTreePath
+ * @param paint_cb     Callback function to paint the tooltip
+ *
+ * @since 2.4.0
+ */
+void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata,
+		PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb);
+
+#endif
--- a/pidgin/plugins/cap/cap.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/cap/cap.c	Mon Jan 07 03:40:27 2008 +0000
@@ -434,26 +434,6 @@
 	stats->last_seen = time(NULL);
 }
 
-static void buddy_idle(PurpleBuddy *buddy, gboolean old_idle, gboolean idle) {
-}
-
-#if 0
-static void blist_node_extended_menu(PurpleBlistNode *node, GList **menu) {
-	PurpleBuddy *buddy;
-	PurpleMenuAction *menu_action;
-	purple_debug_info("cap", "got extended blist menu\n");
-	purple_debug_info("cap", "is buddy: %d\n", PURPLE_BLIST_NODE_IS_BUDDY(node));
-	purple_debug_info("cap", "is contact: %d\n", PURPLE_BLIST_NODE_IS_CONTACT(node));
-	purple_debug_info("cap", "is group: %d\n", PURPLE_BLIST_NODE_IS_GROUP(node));
-	/* Probably only concerned with buddy/contact types. Contacts = meta-buddies (grouped msn/jabber/etc.) */
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-	buddy = (PurpleBuddy *)node;
-	menu_action = purple_menu_action_new(_("Display Statistics"),
-			PURPLE_CALLBACK(display_statistics_action_cb), NULL, NULL);
-	*menu = g_list_append(*menu, menu_action);
-}
-#endif
-
 /* drawing-tooltip */
 static void drawing_tooltip(PurpleBlistNode *node, GString *text, gboolean full) {
 	if(node->type == PURPLE_BLIST_BUDDY_NODE) {
@@ -662,15 +642,6 @@
 	/* result = dbi_conn_queryf(_conn, "insert into cap_message values(\'%s\', \'%s\', %d, now());", sender, receiver, count); */
 }
 
-/* Callbacks */
-void display_statistics_action_cb(PurpleBlistNode *node, gpointer data) {
-	PurpleBuddy *buddy;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-	buddy = (PurpleBuddy *)node;
-	purple_debug_info("cap", "Statistics for %s requested.\n", buddy->name);
-}
-
 /* Purple plugin specific code */
 
 static gboolean plugin_load(PurplePlugin *plugin) {
@@ -714,9 +685,6 @@
 	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", plugin,
 			PURPLE_CALLBACK(buddy_signed_off), NULL);
 
-	/*purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin,
-			PURPLE_CALLBACK(blist_node_extended_menu), NULL);*/
-
 	purple_signal_connect(pidgin_blist_get_handle(), "drawing-tooltip", plugin,
 			PURPLE_CALLBACK(drawing_tooltip), NULL);
 
@@ -726,9 +694,6 @@
 	purple_signal_connect(purple_connections_get_handle(), "signed-off", plugin,
 			PURPLE_CALLBACK(signed_off), NULL);
 
-	purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", plugin,
-			PURPLE_CALLBACK(buddy_idle), NULL);
-
 	_signals_connected = TRUE;
 }
 
@@ -765,9 +730,6 @@
 	purple_signal_disconnect(purple_blist_get_handle(), "buddy-signed-off", plugin,
 			PURPLE_CALLBACK(buddy_signed_off));
 
-	/*purple_signal_disconnect(purple_blist_get_handle(), "blist-node-extended-menu", plugin,
-			PURPLE_CALLBACK(blist_node_extended_menu));*/
-
 	purple_signal_disconnect(pidgin_blist_get_handle(), "drawing-tooltip", plugin,
 			PURPLE_CALLBACK(drawing_tooltip));
 
@@ -777,9 +739,6 @@
 	purple_signal_disconnect(purple_connections_get_handle(), "signed-off", plugin,
 			PURPLE_CALLBACK(signed_off));
 
-	purple_signal_disconnect(purple_blist_get_handle(), "buddy-idle-changed", plugin,
-			PURPLE_CALLBACK(buddy_idle));
-
 	_signals_connected = FALSE;
 }
 
--- a/pidgin/plugins/cap/cap.h	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/cap/cap.h	Mon Jan 07 03:40:27 2008 +0000
@@ -98,7 +98,6 @@
 static void buddy_signed_on(PurpleBuddy *buddy);
 /* buddy-signed-off */
 static void buddy_signed_off(PurpleBuddy *buddy);
-static void buddy_idle(PurpleBuddy *buddy, gboolean old_idle, gboolean idle);
 /* drawing-tooltip */
 static void drawing_tooltip(PurpleBlistNode *node, GString *text, gboolean full);
 /* signed-on */
@@ -107,21 +106,20 @@
 static void signed_off(PurpleConnection *gc);
 static void reset_all_last_message_times(gpointer key, gpointer value, gpointer user_data);
 static PurpleStatus * get_status_for(PurpleBuddy *buddy);
-static void create_tables();
-static gboolean create_database_connection();
-static void destroy_database_connection();
+static void create_tables(void);
+static gboolean create_database_connection(void);
+static void destroy_database_connection(void);
 static guint word_count(const gchar *string);
 static void insert_status_change(CapStatistics *statistics);
 static void insert_status_change_from_purple_status(CapStatistics *statistics, PurpleStatus *status);
 static void insert_word_count(const char *sender, const char *receiver, guint count);
-void display_statistics_action_cb(PurpleBlistNode *node, gpointer data);
 static gboolean plugin_load(PurplePlugin *plugin);
 static void add_plugin_functionality(PurplePlugin *plugin);
 static void cancel_conversation_timeouts(gpointer key, gpointer value, gpointer user_data);
 static void remove_plugin_functionality(PurplePlugin *plugin);
 static void write_stats_on_unload(gpointer key, gpointer value, gpointer user_data);
 static gboolean plugin_unload(PurplePlugin *plugin);
-static CapPrefsUI * create_cap_prefs_ui();
+static CapPrefsUI * create_cap_prefs_ui(void);
 static void cap_prefs_ui_destroy_cb(GtkObject *object, gpointer user_data);
 static void numeric_spinner_prefs_cb(GtkSpinButton *spinbutton, gpointer user_data);
 static GtkWidget * get_config_frame(PurplePlugin *plugin);
--- a/pidgin/plugins/crazychat/cc_network.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/crazychat/cc_network.c	Mon Jan 07 03:40:27 2008 +0000
@@ -529,7 +529,7 @@
 	while (total < len) {
 		n = send(s, buf + total, bytesleft, 0);
 		if (n == -1) {
-			Debug("ERROR: %s\n", strerror(errno));
+			Debug("ERROR: %s\n", g_strerror(errno));
 			return -1;
 		}
 		total += n;
--- a/pidgin/plugins/markerline.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/markerline.c	Mon Jan 07 03:40:27 2008 +0000
@@ -190,13 +190,13 @@
 }
 
 static void
-detach_from_all_windows()
+detach_from_all_windows(void)
 {
 	g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)detach_from_pidgin_window, NULL);
 }
 
 static void
-attach_to_all_windows()
+attach_to_all_windows(void)
 {
 	g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)attach_to_pidgin_window, NULL);
 }
--- a/pidgin/plugins/pidginrc.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/pidginrc.c	Mon Jan 07 03:40:27 2008 +0000
@@ -30,17 +30,29 @@
 static const gchar *color_prefs[] = {
 	"/plugins/gtk/purplerc/color/GtkWidget::cursor-color",
 	"/plugins/gtk/purplerc/color/GtkWidget::secondary-cursor-color",
-	"/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-color"
+	"/plugins/gtk/purplerc/color/GtkIMHtml::hyperlink-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::send-name-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::receive-name-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::highlight-name-color",
+	"/plugins/gtk/purplerc/color/GtkIMHtml::action-name-color"
 };
 static const gchar *color_prefs_set[] = {
 	"/plugins/gtk/purplerc/set/color/GtkWidget::cursor-color",
 	"/plugins/gtk/purplerc/set/color/GtkWidget::secondary-cursor-color",
-	"/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color"
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::send-name-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::receive-name-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::highlight-name-color",
+	"/plugins/gtk/purplerc/set/color/GtkIMHtml::action-name-color"
 };
 static const gchar *color_names[] = {
 	N_("Cursor Color"),
 	N_("Secondary Cursor Color"),
-	N_("Hyperlink Color")
+	N_("Hyperlink Color"),
+	N_("Sent Message Name Color"),
+	N_("Received Message Name Color"),
+	N_("Highlighted Message Name Color"),
+	N_("Action Message Name Color")
 };
 static GtkWidget *color_widgets[G_N_ELEMENTS(color_prefs)];
 
@@ -83,7 +95,7 @@
 */
 
 static GString *
-make_gtkrc_string()
+make_gtkrc_string(void)
 {
 	gint i;
 	gchar *prefbase = NULL;
@@ -173,7 +185,7 @@
 }
 
 static void
-purplerc_make_changes()
+purplerc_make_changes(void)
 {
 	GString *str = make_gtkrc_string();
 #if GTK_CHECK_VERSION(2,4,0)
--- a/pidgin/plugins/spellchk.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/spellchk.c	Mon Jan 07 03:40:27 2008 +0000
@@ -695,7 +695,7 @@
 	return 1;
 }
 
-static void load_conf()
+static void load_conf(void)
 {
 	/* Corrections to change "...", "(c)", "(r)", and "(tm)" to their
 	 * Unicode character equivalents were not added here even though
@@ -1912,7 +1912,7 @@
 	save_list();
 }
 
-static void list_add_new()
+static void list_add_new(void)
 {
 	GtkTreeIter iter;
 	const char *word = gtk_entry_get_text(GTK_ENTRY(bad_entry));
@@ -2015,7 +2015,7 @@
 	gtk_tree_row_reference_free(row_reference);
 }
 
-static void list_delete()
+static void list_delete(void)
 {
 	GtkTreeSelection *sel;
 	GSList *list = NULL;
@@ -2161,14 +2161,13 @@
 get_config_frame(PurplePlugin *plugin)
 {
 	GtkWidget *ret, *vbox, *win;
-	GtkWidget *hbox, *label;
+	GtkWidget *hbox;
 	GtkWidget *button;
 	GtkSizeGroup *sg;
 	GtkSizeGroup *sg2;
 	GtkCellRenderer *renderer;
 	GtkTreeViewColumn *column;
 	GtkWidget *vbox2;
-	GtkWidget *hbox2;
 	GtkWidget *vbox3;
 
 	ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
@@ -2275,37 +2274,15 @@
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 	sg2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
-	hbox2 = gtk_hbox_new(FALSE, 2);
-	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
-	gtk_widget_show(hbox2);
-
-	label = gtk_label_new_with_mnemonic(_("You _type:"));
-	gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-
 	bad_entry = gtk_entry_new();
 	/* Set a minimum size. Since they're in a size group, the other entry will match up. */
 	gtk_widget_set_size_request(bad_entry, 350, -1);
-	gtk_box_pack_start(GTK_BOX(hbox2), bad_entry, TRUE, TRUE, 0);
 	gtk_size_group_add_widget(sg2, bad_entry);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), bad_entry);
-	gtk_widget_show(bad_entry);
-
-	hbox2 = gtk_hbox_new(FALSE, 2);
-	gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, FALSE, 0);
-	gtk_widget_show(hbox2);
-
-	label = gtk_label_new_with_mnemonic(_("You _send:"));
-	gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);
-	gtk_size_group_add_widget(sg, label);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _type:"), sg, bad_entry, FALSE, NULL);
 
 	good_entry = gtk_entry_new();
-	gtk_box_pack_start(GTK_BOX(hbox2), good_entry, TRUE, TRUE, 0);
 	gtk_size_group_add_widget(sg2, good_entry);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), good_entry);
-	gtk_widget_show(good_entry);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("You _send:"), sg, good_entry, FALSE, NULL);
 
 	/* Created here so it can be passed to whole_words_button_toggled. */
 	case_toggle = gtk_check_button_new_with_mnemonic(_("_Exact case match (uncheck for automatic case handling)"));
--- a/pidgin/plugins/ticker/ticker.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/ticker/ticker.c	Mon Jan 07 03:40:27 2008 +0000
@@ -65,7 +65,7 @@
 	return TRUE; /* don't actually destroy the window */
 }
 
-static void buddy_ticker_create_window() {
+static void buddy_ticker_create_window(void) {
 	if(tickerwindow) {
 		gtk_widget_show(tickerwindow);
 		return;
@@ -215,7 +215,7 @@
 	buddy_ticker_update_contact(c);
 }
 
-static void buddy_ticker_show()
+static void buddy_ticker_show(void)
 {
 	PurpleBuddyList *list = purple_get_blist();
 	PurpleBlistNode *gnode, *cnode, *bnode;
--- a/pidgin/plugins/xmppconsole.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/plugins/xmppconsole.c	Mon Jan 07 03:40:27 2008 +0000
@@ -727,7 +727,7 @@
 }
 
 static void 
-create_console() 
+create_console(PurplePluginAction *action) 
 {
 	GtkWidget *vbox = gtk_vbox_new(FALSE, 6);
 	GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Mon Jan 07 03:40:27 2008 +0000
@@ -699,6 +699,7 @@
     Delete "$INSTDIR\ca-certs\Equifax_Secure_CA.pem"
     Delete "$INSTDIR\ca-certs\GTE_CyberTrust_Global_Root.pem"
     Delete "$INSTDIR\ca-certs\Microsoft_Secure_Server_Authority.pem"
+    Delete "$INSTDIR\ca-certs\StartCom_Free_SSL_CA.pem"
     Delete "$INSTDIR\ca-certs\Verisign_Class3_Extended_Validation_CA.pem"
     Delete "$INSTDIR\ca-certs\Verisign_Class3_Primary_CA.pem"
     Delete "$INSTDIR\ca-certs\Verisign_RSA_Secure_Server_CA.pem"
@@ -1154,11 +1155,21 @@
 !macro RunCheckMacro UN
 Function ${UN}RunCheck
   Push $R0
-  System::Call 'kernel32::OpenMutex(i 2031617, b 0, t "pidgin_is_running") i .R0'
-  IntCmp $R0 0 done
-    MessageBox MB_OK|MB_ICONEXCLAMATION $(PIDGIN_IS_RUNNING) /SD IDOK
+  Push $R1
+
+  IntOp $R1 0 + 0
+  retry_runcheck:
+  ; Close the Handle (needed if we're retrying)
+  IntCmp $R1 0 +2
+    System::Call 'kernel32::CloseHandle(i $R1) i .R1'
+  System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_is_running") i .R1 ?e'
+  Pop $R0
+  IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume
+    MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(PIDGIN_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck
     Abort
+
   done:
+  Pop $R1
   Pop $R0
 FunctionEnd
 !macroend
@@ -1169,10 +1180,16 @@
   Push $R0
   Push $R1
   Push $R2
-  System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_installer_running") i .r1 ?e'
+
+  IntOp $R1 0 + 0
+  retry_runcheck:
+  ; Close the Handle (needed if we're retrying)
+  IntCmp $R1 0 +2
+    System::Call 'kernel32::CloseHandle(i $R1) i .R1'
+  System::Call 'kernel32::CreateMutexA(i 0, i 0, t "pidgin_installer_running") i .R1 ?e'
   Pop $R0
-  StrCmp $R0 0 +3
-    MessageBox MB_OK|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDOK
+  IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume
+    MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck
     Abort
   Call RunCheck
   StrCpy $name "Pidgin ${PIDGIN_VERSION}"
--- a/pidgin/win32/winpidgin.c	Mon Dec 17 08:20:42 2007 +0000
+++ b/pidgin/win32/winpidgin.c	Mon Jan 07 03:40:27 2008 +0000
@@ -445,27 +445,31 @@
 #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13)
 #define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14)
 
-static BOOL winpidgin_set_running() {
+static BOOL winpidgin_set_running(BOOL fail_if_running) {
 	HANDLE h;
 
 	if ((h = CreateMutex(NULL, FALSE, "pidgin_is_running"))) {
-		if (GetLastError() == ERROR_ALREADY_EXISTS) {
-			HWND msg_win;
+		DWORD err = GetLastError();
+		if (err == ERROR_ALREADY_EXISTS) {
+			if (fail_if_running) {
+				HWND msg_win;
 
-			printf("An instance of Pidgin is already running.\n");
+				printf("An instance of Pidgin is already running.\n");
 
-			if((msg_win = FindWindowEx(HWND_MESSAGE, NULL, TEXT("WinpidginMsgWinCls"), NULL)))
-				if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
-					return FALSE;
+				if((msg_win = FindWindowEx(HWND_MESSAGE, NULL, TEXT("WinpidginMsgWinCls"), NULL)))
+					if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
+						return FALSE;
 
-			/* If we get here, the focus request wasn't successful */
+				/* If we get here, the focus request wasn't successful */
 
-			MessageBox(NULL,
-				"An instance of Pidgin is already running",
-				NULL, MB_OK | MB_TOPMOST);
+				MessageBox(NULL,
+					"An instance of Pidgin is already running",
+					NULL, MB_OK | MB_TOPMOST);
 
-			return FALSE;
-		}
+				return FALSE;
+			}
+		} else if (err != ERROR_SUCCESS)
+			printf("Error (%u) accessing \"pidgin_is_running\" mutex.\n", (UINT) err);
 	}
 	return TRUE;
 }
@@ -628,8 +632,8 @@
 
 	winpidgin_set_locale();
 	/* If help, version or multiple flag used, do not check Mutex */
-	if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v") && !strstr(lpszCmdLine, "-m"))
-		if (!getenv("PIDGIN_MULTI_INST") && !winpidgin_set_running())
+	if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v"))
+		if (!winpidgin_set_running(getenv("PIDGIN_MULTI_INST") == NULL && strstr(lpszCmdLine, "-m") == NULL))
 			return 0;
 
 	/* Now we are ready for Pidgin .. */
--- a/po/POTFILES.in	Mon Dec 17 08:20:42 2007 +0000
+++ b/po/POTFILES.in	Mon Jan 07 03:40:27 2008 +0000
@@ -12,6 +12,7 @@
 finch/gntpounce.c
 finch/gntprefs.c
 finch/gntrequest.c
+finch/gntroomlist.c
 finch/gntsound.c
 finch/gntstatus.c
 finch/gntui.c
@@ -179,6 +180,7 @@
 libpurple/sslconn.c
 libpurple/status.c
 libpurple/util.c
+libpurple/win32/libc_interface.c
 pidgin.desktop.in
 pidgin/eggtrayicon.c
 pidgin/gtkaccount.c
--- a/po/check_po.pl	Mon Dec 17 08:20:42 2007 +0000
+++ b/po/check_po.pl	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/env perl -w
 #
 # check_po.pl  -  check po file translations for likely errors
 #
--- a/po/de.po	Mon Dec 17 08:20:42 2007 +0000
+++ b/po/de.po	Mon Jan 07 03:40:27 2008 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-12-06 15:10+0100\n"
-"PO-Revision-Date: 2007-12-06 15:10+0100\n"
+"POT-Creation-Date: 2008-01-05 13:31+0100\n"
+"PO-Revision-Date: 2008-01-05 13:31+0100\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -1019,6 +1019,9 @@
 msgid "Open File..."
 msgstr "Datei öffnen..."
 
+msgid "Choose Location..."
+msgstr "Wählen Sie einen Ort..."
+
 msgid "Buddy logs in"
 msgstr "Buddy meldet sich an"
 
@@ -1433,19 +1436,6 @@
 msgstr "Ze_rtifikat ansehen..."
 
 #. Prompt the user to authenticate the certificate
-#. TODO: Provide the user with more guidance about why he is
-#. being prompted
-#. vrq will be completed by user_auth
-#, c-format
-msgid ""
-"The certificate presented by \"%s\" claims to be from \"%s\" instead.  This "
-"could mean that you are not connecting to the service you believe you are."
-msgstr ""
-"Das Zertifikat, welches von „%s“ präsentiert wurde, behauptet stattdessen "
-"von „%s“ zu kommen.  Das kann bedeuten, dass Sie tatsächlich nicht mit dem "
-"Dienst verbunden sind, mit dem Sie glauben verbunden zu sein."
-
-#. Prompt the user to authenticate the certificate
 #. vrq will be completed by user_auth
 #, c-format
 msgid ""
@@ -1495,6 +1485,19 @@
 msgid "Invalid certificate authority signature"
 msgstr "Unbekannte Zertifizierungsstellensignatur"
 
+#. Prompt the user to authenticate the certificate
+#. TODO: Provide the user with more guidance about why he is
+#. being prompted
+#. vrq will be completed by user_auth
+#, c-format
+msgid ""
+"The certificate presented by \"%s\" claims to be from \"%s\" instead.  This "
+"could mean that you are not connecting to the service you believe you are."
+msgstr ""
+"Das Zertifikat, welches von „%s“ präsentiert wurde, behauptet stattdessen "
+"von „%s“ zu kommen.  Das kann bedeuten, dass Sie tatsächlich nicht mit dem "
+"Dienst verbunden sind, mit dem Sie glauben verbunden zu sein."
+
 #. Make messages
 #, c-format
 msgid ""
@@ -3282,6 +3285,10 @@
 msgid "nickserv: Send a command to nickserv"
 msgstr "nickserv: Sendet ein Kommando zum Nickserv"
 
+msgid "notice &lt;target&lt;:  Send a notice to a user or channel."
+msgstr ""
+"notice &lt;Ziel&gt;:  Sende eine Notiz an einen Benutzer oder an einen Kanal."
+
 msgid ""
 "op &lt;nick1&gt; [nick2] ...:  Grant channel operator status to someone. You "
 "must be a channel operator to do this."
@@ -3412,12 +3419,12 @@
 msgid "Plaintext Authentication"
 msgstr "Klartext-Authentifizierung"
 
+msgid "Invalid response from server."
+msgstr "Ungültige Serverantwort."
+
 msgid "Server does not use any supported authentication method"
 msgstr "Der Server benutzt keine der unterstützten Authentifizierungsmethoden"
 
-msgid "Invalid response from server."
-msgstr "Ungültige Serverantwort."
-
 msgid ""
 "This server requires plaintext authentication over an unencrypted "
 "connection.  Allow this and continue authentication?"
@@ -3830,6 +3837,9 @@
 msgid "Write error"
 msgstr "Schreibfehler"
 
+msgid "Ping timeout"
+msgstr "Ping-Zeitüberschreitung"
+
 msgid "Read Error"
 msgstr "Fehler beim Lesen"
 
@@ -3965,8 +3975,8 @@
 msgid "Mood"
 msgstr "Stimmung"
 
-msgid "Current media"
-msgstr "Aktuelles Medium"
+msgid "Now Listening"
+msgstr ""
 
 msgid "Mood Text"
 msgstr "Stimmungstext"
@@ -9513,26 +9523,14 @@
 msgstr "Musik hören"
 
 #, c-format
-msgid "%s changed status from %s to %s"
-msgstr "%s hat den Status von %s zu %s geändert"
-
-#, c-format
 msgid "%s (%s) changed status from %s to %s"
 msgstr "%s (%s) hat den Status von %s zu %s geändert"
 
 #, c-format
-msgid "%s is now %s"
-msgstr "%s ist jetzt %s"
-
-#, c-format
 msgid "%s (%s) is now %s"
 msgstr "%s (%s) ist jetzt %s"
 
 #, c-format
-msgid "%s is no longer %s"
-msgstr "%s ist nicht mehr %s"
-
-#, c-format
 msgid "%s (%s) is no longer %s"
 msgstr "%s (%s) ist nicht mehr %s"
 
@@ -9638,6 +9636,36 @@
 msgid "Unable to connect to %s: %s"
 msgstr "Verbindung zu %s nicht möglich: %s"
 
+#, c-format
+msgid " - %s"
+msgstr " - %s"
+
+#, c-format
+msgid " (%s)"
+msgstr " (%s)"
+
+#. 10053
+#, c-format
+msgid "Connection interrupted by other software on your computer."
+msgstr ""
+"Die Verbindung wurde von einer anderen Software auf ihrem Computer "
+"unterbrochen."
+
+#. 10054
+#, c-format
+msgid "Remote host closed connection."
+msgstr "Der entfernte Host hat die Verbindung beendet."
+
+#. 10060
+#, c-format
+msgid "Connection timed out."
+msgstr "Verbindungsabbruch wegen Zeitüberschreitung."
+
+#. 10061
+#, c-format
+msgid "Connection refused."
+msgstr "Verbindung abgelehnt."
+
 msgid "Internet Messenger"
 msgstr "Internet-Sofortnachrichtendienst"
 
@@ -9892,6 +9920,12 @@
 "Sie sind im Moment nicht mit einem Konto angemeldet, welches benutzt werden "
 "kann, um diesen Buddy hinzuzufügen."
 
+#. I don't believe this can happen currently, I think
+#. * everything that calls this function checks for one of the
+#. * above node types first.
+msgid "Unknown node type"
+msgstr "Unbekannter Knotentyp"
+
 #. Buddies menu
 msgid "/_Buddies"
 msgstr "/_Buddys"
@@ -9993,12 +10027,8 @@
 msgstr "/Hilfe/Ü_ber"
 
 #, c-format
-msgid ""
-"\n"
-"<b>Account:</b> %s"
-msgstr ""
-"\n"
-"<b>Konto:</b> %s"
+msgid "<b>Account:</b> %s"
+msgstr "<b>Konto:</b> %s"
 
 #, c-format
 msgid ""
@@ -10029,6 +10059,12 @@
 msgid "Rockin'"
 msgstr "Abgefahren"
 
+msgid "Total Buddies"
+msgstr "Buddy-Anzahl"
+
+msgid "Online Buddies"
+msgstr "Online-Buddys"
+
 #, c-format
 msgid "Idle %dd %dh %02dm"
 msgstr "Untätig %dd %dh %02dm"
@@ -10447,6 +10483,14 @@
 msgid "User has typed something and stopped"
 msgstr "Benutzer hat etwas getippt und wartet nun"
 
+#, c-format
+msgid ""
+"\n"
+"%s has typed something and stopped"
+msgstr ""
+"\n"
+"%s hat etwas getippt und wartet nun"
+
 #. Build the Send To menu
 msgid "S_end To"
 msgstr "S_enden an"
@@ -11084,6 +11128,34 @@
 msgstr ""
 "Farbe zum Darstellen von Hyperlinks, wenn sich die Maus darüber befindet."
 
+msgid "Sent Message Name Color"
+msgstr "Farbe des Absendernamens für gesendete Nachrichten"
+
+msgid "Color to draw the name of a message you sent."
+msgstr ""
+"Farbe, mit der der Name in einer gesendeten Nachricht dargestellt wird."
+
+msgid "Received Message Name Color"
+msgstr "Farbe des Absendernamens für empfangene Nachrichten"
+
+msgid "Color to draw the name of a message you received."
+msgstr ""
+"Farbe, mit der der Name in einer empfangenen Nachricht dargestellt wird."
+
+msgid "\"Attention\" Name Color"
+msgstr "Farbe des Absendernamens für \"Achtung\"-Nachrichten"
+
+msgid "Color to draw the name of a message you received containing your name."
+msgstr ""
+"Farbe, mit der der Name in einer Nachricht dargestellt wird, die Ihren Namen "
+"enthält."
+
+msgid "Action Message Name Color"
+msgstr "Farbe des Absendernamens für Aktions-Nachrichten"
+
+msgid "Color to draw the name of an action message."
+msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird."
+
 msgid "_Copy E-Mail Address"
 msgstr "Kopiere _E-Mail-Adresse"
 
@@ -11137,15 +11209,6 @@
 msgid "_Save Image..."
 msgstr "Bild _speichern..."
 
-msgid "_Font"
-msgstr "_Schrift"
-
-msgid "_Insert"
-msgstr "_Einfügen"
-
-msgid "S_mile!"
-msgstr "_Lächeln!"
-
 msgid "Select Font"
 msgstr "Schriftart wählen"
 
@@ -11174,6 +11237,9 @@
 msgid "Insert Link"
 msgstr "Link einfügen"
 
+msgid "_Insert"
+msgstr "_Einfügen"
+
 #, c-format
 msgid "Failed to store image: %s\n"
 msgstr "Speichern des Bildes fehlgeschlagen: %s\n"
@@ -11181,12 +11247,14 @@
 msgid "Insert Image"
 msgstr "Bild einfügen"
 
+msgid "Smile!"
+msgstr "Lächeln!"
+
 msgid "This theme has no available smileys."
 msgstr "Dieses Thema verfügt über keine Smileys."
 
-#. show everything
-msgid "Smile!"
-msgstr "Lächeln!"
+msgid "_Font"
+msgstr "_Schrift"
 
 msgid "Group Items"
 msgstr "Elemente gruppieren"
@@ -11386,6 +11454,33 @@
 
 #, c-format
 msgid ""
+"%s %s\n"
+"Usage: %s [OPTION]...\n"
+"\n"
+"  -c, --config=DIR    use DIR for config files\n"
+"  -d, --debug         print debugging messages to stdout\n"
+"  -h, --help          display this help and exit\n"
+"  -m, --multiple      do not ensure single instance\n"
+"  -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"
+"  -v, --version       display the current version and exit\n"
+msgstr ""
+"%s %s\n"
+"Benutzung: %s [OPTION]...\n"
+"\n"
+"  -c, --config=VERZ   benutze VERZ als Konfigurationsverzeichnis\n"
+"  -d, --debug         gibt Debugging-Meldungen nach stdout aus\n"
+"  -h, --help          zeigt diese Hilfe und beendet das Programm\n"
+"  -m, --multiple      mehrere Instanzen erlauben\n"
+"  -n, --nologin       nicht automatisch anmelden\n"
+"  -l, --login[=NAME]  automatische Anmeldung (optionales Argument \n"
+"                      NAME bestimmt Konto(n), die benutzt werden\n"
+"                      sollen, getrennt durch Kommata)\n"
+"  -v, --version       zeigt aktuelle Version und beendet das Programm\n"
+
+#, c-format
+msgid ""
 "%s %s has segfaulted and attempted to dump a core file.\n"
 "This is a bug in the software and has happened through\n"
 "no fault of your own.\n"
@@ -11694,8 +11789,8 @@
 "This is how your outgoing message text will appear when you use protocols "
 "that support formatting."
 msgstr ""
-"So wird der ausgehende Nachrichtentext aussehen, wenn Sie "
-"Protokollebenutzen, die Formatierung unterstützen."
+"So wird der ausgehende Nachrichtentext aussehen, wenn Sie Protokolle "
+"benutzen, die Formatierung unterstützen."
 
 msgid "Cannot start proxy configuration program."
 msgstr "Kann das Proxy-Konfigurationsprogramm nicht starten."
@@ -11952,7 +12047,6 @@
 msgid "Changes to privacy settings take effect immediately."
 msgstr "Einstellungen bzgl. der Privatsphäre werden sofort wirksam."
 
-#. "Set privacy for:" label
 msgid "Set privacy for:"
 msgstr "Setze Privatsphäre für:"
 
@@ -12773,6 +12867,9 @@
 msgid "Hyperlink Color"
 msgstr "Hyperlink-Farbe"
 
+msgid "Highlighted Message Name Color"
+msgstr "Farbe des Absendernamens für hervorgehobene Nachrichten"
+
 msgid "GtkTreeView Horizontal Separation"
 msgstr "GtkTreeview horizontaler Abstand"
 
@@ -12780,7 +12877,7 @@
 msgstr "Unterhaltungseintrag"
 
 msgid "Request Dialog"
-msgstr "Dialog anfordern"
+msgstr "Anfrage-Dialog"
 
 msgid "Notify Dialog"
 msgstr "Benachrichtigungsdialog"
--- a/po/stats.pl	Mon Dec 17 08:20:42 2007 +0000
+++ b/po/stats.pl	Mon Jan 07 03:40:27 2008 +0000
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 # Copyright 2003-2005 Nathan Walp <faceprint@faceprint.com>
 #
--- a/share/ca-certs/Makefile.am	Mon Dec 17 08:20:42 2007 +0000
+++ b/share/ca-certs/Makefile.am	Mon Jan 07 03:40:27 2008 +0000
@@ -3,6 +3,7 @@
 		Equifax_Secure_CA.pem \
 		GTE_CyberTrust_Global_Root.pem \
 		Microsoft_Secure_Server_Authority.pem \
+		StartCom_Free_SSL_CA.pem \
 		Verisign_RSA_Secure_Server_CA.pem \
 		Verisign_Class3_Primary_CA.pem
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/StartCom_Free_SSL_CA.pem	Mon Jan 07 03:40:27 2008 +0000
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFFjCCBH+gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsDELMAkGA1UEBhMCSUwx
+DzANBgNVBAgTBklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0
+Q29tIEx0ZC4xGjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBG
+cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS
+YWRtaW5Ac3RhcnRjb20ub3JnMB4XDTA1MDMxNzE3Mzc0OFoXDTM1MDMxMDE3Mzc0
+OFowgbAxCzAJBgNVBAYTAklMMQ8wDQYDVQQIEwZJc3JhZWwxDjAMBgNVBAcTBUVp
+bGF0MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMRowGAYDVQQLExFDQSBBdXRob3Jp
+dHkgRGVwLjEpMCcGA1UEAxMgRnJlZSBTU0wgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkxITAfBgkqhkiG9w0BCQEWEmFkbWluQHN0YXJ0Y29tLm9yZzCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEA7YRgACOeyEpRKSfeOqE5tWmrCbIvNP1h3D3TsM+x
+18LEwrHkllbEvqoUDufMOlDIOmKdw6OsWXuO7lUaHEe+o5c5s7XvIywI6Nivcy+5
+yYPo7QAPyHWlLzRMGOh2iCNJitu27Wjaw7ViKUylS7eYtAkUEKD4/mJ2IhULpNYI
+LzUCAwEAAaOCAjwwggI4MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgHmMB0G
+A1UdDgQWBBQcicOWzL3+MtUNjIExtpidjShkjTCB3QYDVR0jBIHVMIHSgBQcicOW
+zL3+MtUNjIExtpidjShkjaGBtqSBszCBsDELMAkGA1UEBhMCSUwxDzANBgNVBAgT
+BklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4x
+GjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBGcmVlIFNTTCBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYSYWRtaW5Ac3Rh
+cnRjb20ub3JnggEAMB0GA1UdEQQWMBSBEmFkbWluQHN0YXJ0Y29tLm9yZzAdBgNV
+HRIEFjAUgRJhZG1pbkBzdGFydGNvbS5vcmcwEQYJYIZIAYb4QgEBBAQDAgAHMC8G
+CWCGSAGG+EIBDQQiFiBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAy
+BglghkgBhvhCAQQEJRYjaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL2NhLWNybC5j
+cmwwKAYJYIZIAYb4QgECBBsWGWh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy8wOQYJ
+YIZIAYb4QgEIBCwWKmh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9pbmRleC5waHA/
+YXBwPTExMTANBgkqhkiG9w0BAQQFAAOBgQBscSXhnjSRIe/bbL0BCFaPiNhBOlP1
+ct8nV0t2hPdopP7rPwl+KLhX6h/BquL/lp9JmeaylXOWxkjHXo0Hclb4g4+fd68p
+00UOpO6wNnQt8M2YI3s3S9r+UZjEHjQ8iP2ZO1CnwYszx8JSFhKVU2Ui77qLzmLb
+cCOxgN8aIDjnfg==
+-----END CERTIFICATE-----