changeset 25985:c4fd9222dda1

propagate from branch 'im.pidgin.pidgin' (head 303af74a38e7b313d4fb0be4d4054a16cb13d819) to branch 'im.pidgin.cpw.darkrain42.xmpp.bosh' (head 650d82b8a5f0c8860804dd8004cd54badea48e1e)
author Paul Aurich <paul@darkrain42.org>
date Sat, 07 Mar 2009 01:59:40 +0000
parents 0e93bbb7f5ca (diff) 9122bf747d3a (current diff)
children 2597de135090
files .todo libpurple/protocols/jabber/Makefile.am libpurple/protocols/jabber/Makefile.mingw libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/caps.c libpurple/protocols/jabber/data.c libpurple/protocols/jabber/disco.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h libpurple/protocols/jabber/libxmpp.c libpurple/protocols/jabber/message.c libpurple/protocols/jabber/message.h libpurple/protocols/jabber/pep.c libpurple/protocols/jabber/presence.c libpurple/protocols/jabber/roster.c libpurple/protocols/jabber/usermood.c libpurple/protocols/jabber/usernick.c libpurple/protocols/jabber/usertune.c
diffstat 270 files changed, 11502 insertions(+), 4188 deletions(-) [+]
line wrap: on
line diff
--- a/.mtn-ignore	Sat Mar 07 01:36:57 2009 +0000
+++ b/.mtn-ignore	Sat Mar 07 01:59:40 2009 +0000
@@ -10,6 +10,7 @@
 .*\.def$
 .*\.dll$
 .*\.exe$
+.*\.loT$
 intltool-.*
 Doxyfile(\.mingw)?$
 aclocal.m4
--- a/.todo	Sat Mar 07 01:36:57 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<todo version="0.1.19">
-    <title>
-        Gaim TODO List
-    </title>
-    <link filename="./libpurple/protocols/oscar/.todo" priority="medium" time="0"/>
-    <link filename="./libpurple/protocols/jabber/.todo" priority="medium" time="1176995038"/>
-</todo>
--- a/COPYRIGHT	Sat Mar 07 01:36:57 2009 +0000
+++ b/COPYRIGHT	Sat Mar 07 01:59:40 2009 +0000
@@ -57,6 +57,7 @@
 Philip Brown
 Dan Bruce
 Norbert Buchmuller
+Johannes Buchner
 Sean Burke
 Thomas Butter
 Trevor Caira
@@ -87,6 +88,7 @@
 Lorenzo Colitti
 Collabora Ltd.
 Jeff Connelly
+Chris Connett
 Nathan Conrad
 Felipe Contreras
 Alex Converse
--- a/ChangeLog	Sat Mar 07 01:36:57 2009 +0000
+++ b/ChangeLog	Sat Mar 07 01:59:40 2009 +0000
@@ -1,5 +1,24 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.6.0 (??/??/2009):
+	General:
+	* Theme support in libpurple thanks to Justin Rodriguez's summer of code
+	  project.  With some minor additions and clean ups from Paul Aurich.
+
+	XMPP:
+	* Add support for in-band bytestreams (XEP-0047).
+	* Add support for attention (XEP-0224).
+
+	Pidgin:
+	* Added -f command line option to tell Pidgin to ignore NetworkManager
+	  and assume it has a valid network connection.
+	* Allow plugins to specify custom link types to the GtkIMHtml widget.
+	* The status message input box at the bottom of the buddy list expands
+	  correctly when starting a new line of text.
+	* Pressing the Enter key in the message entry box of the New Status
+	  dialog and various other dialogs now causes the cursor to move to
+	  the next line.
+
 version 2.5.5 (03/01/2009):
 	libpurple:
 	* Fix a crash when removing an account with an unknown protocol id.
@@ -669,13 +688,13 @@
 version 2.2.0 (09/13/2007):
 	http://developer.pidgin.im/query?status=closed&milestone=2.2.0
 
-	Libpurple:
+	libpurple:
 	* New protocol plugin: MySpaceIM (Jeff Connelly, Google Summer of
 	  Code)
 	* XMPP enhancements. See
-	  http://www.adiumx.com/blog/2007/07/soc-xmpp-update.php (Andreas 
+	  http://www.adiumx.com/blog/2007/07/soc-xmpp-update.php (Andreas
 	  Monitzer, Google Summer of Code for Adium)
-	* Certificate management. Libpurple will validate certificates on
+	* Certificate management. libpurple will validate certificates on
 	  SSL-encrypted protocols (William Ehlhardt, Google Summer of Code)
 	* Some adjustments were made to fix sending messages when using
 	  the MSN HTTP method. (Laszlo Pandy)
--- a/ChangeLog.API	Sat Mar 07 01:36:57 2009 +0000
+++ b/ChangeLog.API	Sat Mar 07 01:59:40 2009 +0000
@@ -1,5 +1,73 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.6.0 (??/??/2009):
+	libpurple:
+		Added:
+		* PURPLE_BLIST_NODE
+		* PURPLE_GROUP
+		* PURPLE_CONTACT
+		* PURPLE_BUDDY
+		* PURPLE_CHAT
+		* purple_buddy_get_protocol_data
+		* purple_buddy_set_protocol_data
+		* purple_buddy_get_local_buddy_alias
+		* purple_blist_get_buddies
+		* purple_blist_get_ui_data
+		* purple_blist_set_ui_data
+		* purple_blist_node_get_ui_data
+		* purple_blist_node_set_ui_data
+		* purple_connection_get_protocol_data
+		* purple_connection_set_protocol_data
+		* purple_global_proxy_set_info
+		* purple_log_get_activity_score
+		* purple_network_force_online
+		* purple_request_field_get_group
+		* purple_request_field_get_ui_data
+		* purple_request_field_set_ui_data
+		* purple_strequal
+		* xmlnode_from_file
+
+		Deprecated:
+		* purple_buddy_get_local_alias
+		* purple_notify_user_info_remove_entry
+		* purple_status_type_set_primary_attr
+		* purple_status_type_add_attr
+		* purple_status_type_add_attrs
+		* purple_status_type_add_attrs_vargs
+		* purple_status_type_get_primary_attr
+		* purple_status_set_attr_boolean
+		* purple_status_set_attr_int
+		* purple_status_set_attr_string
+		* purple_presence_add_status
+		* purple_presence_add_list
+
+	pidgin:
+		Added:
+		* gtk_imhtml_class_register_protocol
+		* gtk_imhtml_link_get_url, gtk_imhtml_link_get_text_tag,
+		  gtk_imhtml_link_activate functions to process GtkIMHtmlLink
+		  objects from GtkIMHtml protocol callbacks.
+		* gtk_imhtml_set_return_inserts_newline
+		* pidgin_blist_set_theme
+		* pidgin_blist_get_theme
+		* pidgin_sound_is_customized
+		* pidgin_utils_init, pidgin_utils_uninit
+
+	perl:
+		Changed:
+		* Made a bunch of functions act more perl-like. Call the new()
+		  functions as Class->new(...) instead of Class::new(...):
+			* Purple::Request::Fields::new
+			* Purple::Request::Field::new
+			* Purple::Request::Field::account_new
+			* Purple::Request::Field::bool_new
+			* Purple::Request::Field::choice_new
+			* Purple::Request::Field::int_new
+			* Purple::Request::Field::label_new
+			* Purple::Request::Field::list_new
+			* Purple::Request::Field::string_new
+			* Purple::Request::Field::group_new
+
 version 2.5.5 (03/01/2009):
 	libpurple:
 		Changed:
--- a/ChangeLog.win32	Sat Mar 07 01:36:57 2009 +0000
+++ b/ChangeLog.win32	Sat Mar 07 01:59:40 2009 +0000
@@ -1,7 +1,10 @@
+version 2.6.0 (??/??/2009):
+
 version 2.5.5 (03/01/2009):
 	* Remove the "Flash window when chat messages are received" pref from
 	  the Windows Pidgin Options plugin - the Message Notification plugin
 	  does this (and much more).
+	* Updated GTK+ to 2.14.7
 
 version 2.5.4 (01/12/2009):
 	* Fix the "Hang on Exit" issue that a number of users encountered.
--- a/Makefile.am	Sat Mar 07 01:36:57 2009 +0000
+++ b/Makefile.am	Sat Mar 07 01:59:40 2009 +0000
@@ -10,6 +10,7 @@
 		README.mingw \
 		config.h.mingw \
 		doxy2devhelp.xsl \
+		fix-casts.sh \
 		gaim.pc.in \
 		gaim-uninstalled.pc.in \
 		intltool-extract.in \
--- a/Makefile.mingw	Sat Mar 07 01:36:57 2009 +0000
+++ b/Makefile.mingw	Sat Mar 07 01:59:40 2009 +0000
@@ -2,7 +2,7 @@
 #
 # Author: hermanator12002@yahoo.com
 # Date 9/11/02
-# Description: Top Makefile for win32 (mingw) port of Pidgin and LibPurple
+# Description: Top Makefile for win32 (mingw) port of Pidgin and libpurple
 #
 
 PIDGIN_TREE_TOP := .
--- a/configure.ac	Sat Mar 07 01:36:57 2009 +0000
+++ b/configure.ac	Sat Mar 07 01:59:40 2009 +0000
@@ -43,20 +43,20 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [5])
+m4_define([purple_lt_current], [6])
 m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [5])
-m4_define([purple_micro_version], [5])
-m4_define([purple_version_suffix], [])
+m4_define([purple_minor_version], [6])
+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], [5])
+m4_define([gnt_lt_current], [6])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [5])
-m4_define([gnt_micro_version], [5])
-m4_define([gnt_version_suffix], [])
+m4_define([gnt_minor_version], [6])
+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])
 m4_define([gnt_display_version], gnt_version[]m4_ifdef([gnt_version_suffix],[gnt_version_suffix]))
@@ -1103,6 +1103,7 @@
 		msnp9)		dynamic_msn=yes ;;
 		myspace)	dynamic_myspace=yes ;;
 		novell)		dynamic_novell=yes ;;
+		null)		dynamic_null=yes ;;
 		oscar)		dynamic_oscar=yes ;;
 		aim)		dynamic_oscar=yes ;;
 		icq)		dynamic_oscar=yes ;;
@@ -1117,21 +1118,6 @@
 		*)			echo "Invalid dynamic protocol $i!!" ; exit ;;
 	esac
 done
-AM_CONDITIONAL(DYNAMIC_BONJOUR, test "x$dynamic_bonjour" = "xyes"  -a [ "x$avahiincludes" = "xyes" -a "x$avahilibs " = "xyes" ] )
-AM_CONDITIONAL(DYNAMIC_GG, test "x$dynamic_gg" = "xyes")
-AM_CONDITIONAL(DYNAMIC_IRC, test "x$dynamic_irc" = "xyes")
-AM_CONDITIONAL(DYNAMIC_JABBER, test "x$dynamic_jabber" = "xyes")
-AM_CONDITIONAL(DYNAMIC_MSN, test "x$dynamic_msn" = "xyes")
-AM_CONDITIONAL(DYNAMIC_MYSPACE, test "x$dynamic_myspace" = "xyes")
-AM_CONDITIONAL(DYNAMIC_NOVELL, test "x$dynamic_novell" = "xyes")
-AM_CONDITIONAL(DYNAMIC_OSCAR, test "x$dynamic_oscar" = "xyes")
-AM_CONDITIONAL(DYNAMIC_QQ, test "x$dynamic_qq" = "xyes")
-AM_CONDITIONAL(DYNAMIC_SAMETIME, test "x$dynamic_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
-AM_CONDITIONAL(DYNAMIC_SILC, test "x$dynamic_silc" = "xyes" -a "x$have_silc" = "xyes")
-AM_CONDITIONAL(DYNAMIC_SIMPLE, test "x$dynamic_simple" = "xyes")
-AM_CONDITIONAL(DYNAMIC_TOC, test "x$dynamic_toc" = "xyes")
-AM_CONDITIONAL(DYNAMIC_YAHOO, test "x$dynamic_yahoo" = "xyes")
-AM_CONDITIONAL(DYNAMIC_ZEPHYR, test "x$dynamic_zephyr" = "xyes")
 
 AC_ARG_ENABLE(plugins, [AC_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes)
 AC_ARG_WITH(krb4, [AC_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no")
--- a/doc/TCL-HOWTO.dox	Sat Mar 07 01:36:57 2009 +0000
+++ b/doc/TCL-HOWTO.dox	Sat Mar 07 01:59:40 2009 +0000
@@ -332,7 +332,7 @@
 callbacks will live in the namespace @c event underneath that
 namespace.  To briefly illustrate, the signal @c receiving-im-msg is
 provided with three arguments; the account on which the IM was
-received, the screen name of the user sending the IM, and the text of
+received, the name of the buddy sending the IM, and the text of
 the IM.  These arguments live in the variables @c event::account,
 @c event::sender, and @c event::buffer, respectively.  Therefore a callback
 which notifies the user of an incoming IM containing the word 'shizzle'
--- a/doc/funniest_home_convos.txt	Sat Mar 07 01:36:57 2009 +0000
+++ b/doc/funniest_home_convos.txt	Sat Mar 07 01:59:40 2009 +0000
@@ -565,3 +565,10 @@
              for using pidgen
 22:36 <user> why do they think this is a bad client? does it have history?
 
+--
+
+15:45 <deryni> We've had a Grand Plugin Database Plan for approximately forever.
+15:45 <SimGuy> ah, the GPDP
+15:46 <khc> well, there was a Grand Smiley Theme Database
+15:47 <SimGuy> the GSTD sounds like a bad acronym
+15:47 <khc> I realized after typing that
--- a/doc/notify-signals.dox	Sat Mar 07 01:36:57 2009 +0000
+++ b/doc/notify-signals.dox	Sat Mar 07 01:59:40 2009 +0000
@@ -18,7 +18,7 @@
   @note
     If adding a PurpleNotifyUserInfoEntry, be sure not to free it -- PurpleNotifyUserInfo assumes responsibility for its objects.
   @param account   The account on which the info was obtained.
-  @param who       The screen name of the user whose info is to be displayed.
+  @param who       The name of the buddy whose info is to be displayed.
   @param user_info The information to be displayed, as PurpleNotifyUserInfoEntry objects
  @endsignaldef
 
--- a/doc/pidgin.1.in	Sat Mar 07 01:36:57 2009 +0000
+++ b/doc/pidgin.1.in	Sat Mar 07 01:59:40 2009 +0000
@@ -128,11 +128,11 @@
 .TP
 .B Alias
 Create an alias for this buddy.  This will show an editable text field where
-the buddy's screen name was displayed.  In this field one can give this
+the buddy's name was displayed.  In this field one can give this
 buddy an alternate, more friendly name to appear on the buddy list and in
 conversations.
 
-For example, if a buddy's name screen name was jsmith1281xx and his real
+For example, if a buddy's name was jsmith1281xx and his real
 name was 'John Q. Smith,' one could create an alias as to identify the
 buddy by his common name.
 .LP
@@ -150,7 +150,7 @@
 Clicking \fIDelete\fR will delete the currently selected account.
 Clicking \fIAdd\fR or \fIModify\fR will invoke a \fBModify Account\fR
 window.  Here, the user  can add or alter account information.  When creating
-a new account, the user will submit a screen name and password.  The user will
+a new account, the user will submit a username and password.  The user will
 also choose the protocol for the account.
 
 If \fIRemember Password\fR is chosen, the password will be saved in
@@ -545,7 +545,7 @@
 .br
   \fI~/.purple/status.xml\fR: stores the user's away messages.
 .br
-  \fI~/.purple/logs/PROTOCOL/ACCOUNT/SCREENNAME/DATE.{html,txt}\fR: conversation logs.
+  \fI~/.purple/logs/PROTOCOL/ACCOUNT/BUDDYNAME/DATE.{html,txt}\fR: conversation logs.
 
 .SH DIRECTORIES
   \fI@prefix@/lib/pidgin/\fR: Pidgin's plugins directory.
--- a/finch/gntaccount.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/finch/gntaccount.c	Sat Mar 07 01:59:40 2009 +0000
@@ -65,7 +65,7 @@
 	GntWidget *window;
 
 	GntWidget *protocol;
-	GntWidget *screenname;
+	GntWidget *username;
 	GntWidget *password;
 	GntWidget *alias;
 
@@ -118,8 +118,8 @@
 	plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol));
 	prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
 
-	/* Screenname && user-splits */
-	value = gnt_entry_get_text(GNT_ENTRY(dialog->screenname));
+	/* Username && user-splits */
+	value = gnt_entry_get_text(GNT_ENTRY(dialog->username));
 
 	if (value == NULL || *value == '\0')
 	{
@@ -326,7 +326,7 @@
 	}
 
 	if (username != NULL)
-		gnt_entry_set_text(GNT_ENTRY(dialog->screenname), username);
+		gnt_entry_set_text(GNT_ENTRY(dialog->username), username);
 
 	g_free(username);
 }
@@ -546,7 +546,7 @@
 	gnt_box_set_pad(GNT_BOX(hbox), 0);
 	gnt_box_add_widget(GNT_BOX(window), hbox);
 
-	dialog->screenname = entry = gnt_entry_new(NULL);
+	dialog->username = entry = gnt_entry_new(NULL);
 	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Username:")));
 	gnt_box_add_widget(GNT_BOX(hbox), entry);
 
--- a/finch/gntblist.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/finch/gntblist.c	Sat Mar 07 01:59:40 2009 +0000
@@ -357,7 +357,7 @@
 	int color = 0;
 
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node))
-		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);
+		node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
 	if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
 		return 0;
 
@@ -388,7 +388,7 @@
 	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);
+		node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
 		fnode = FINCH_GET_DATA(node);
 		if (fnode && fnode->signed_timer)
 			flag |= GNT_TEXT_FLAG_BLINK;
@@ -886,7 +886,7 @@
 	const char *name = NULL;
 
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node))
-		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);  /* XXX: this can return NULL?! */
+		node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));  /* XXX: this can return NULL?! */
 
 	if (node == NULL)
 		return NULL;
@@ -1027,7 +1027,7 @@
 		return;
 
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node))
-		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);
+		node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
 
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
 	{
@@ -1438,16 +1438,16 @@
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		PurpleBuddy *b = (PurpleBuddy*) node;
 		type = PURPLE_LOG_IM;
-		name = g_strdup(b->name);
-		account = b->account;
+		name = g_strdup(purple_buddy_get_name(b));
+		account = purple_buddy_get_account(b);
 	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
 		PurpleChat *c = (PurpleChat*) node;
 		PurplePluginProtocolInfo *prpl_info = NULL;
 		type = PURPLE_LOG_CHAT;
-		account = c->account;
+		account = purple_chat_get_account(c);
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
 		if (prpl_info && prpl_info->get_chat_name) {
-			name = prpl_info->get_chat_name(c->components);
+			name = prpl_info->get_chat_name(purple_chat_get_components(c));
 		}
 	} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		finch_log_show_contact((PurpleContact *)node);
@@ -1571,8 +1571,8 @@
 		ggblist->tagged = g_list_prepend(ggblist->tagged, node);
 	}
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node))
-		node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);
-	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+		update_buddy_display(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)), ggblist);
+	else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
 		update_buddy_display((PurpleBuddy*)node, ggblist);
 	else
 		update_node_display(node, ggblist);
@@ -1612,7 +1612,7 @@
 				purple_blist_add_group((PurpleGroup*)node, (PurpleBlistNode*)tg);
 			} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 				update_buddy_display(purple_contact_get_priority_buddy((PurpleContact*)node), ggblist);
-				if ((PurpleBlistNode*)tg == target) {
+				if (PURPLE_BLIST_NODE(tg) == target) {
 					/* The target is a group, just add the contact to the group. */
 					purple_blist_add_contact((PurpleContact*)node, tg, NULL);
 				} else if (tc) {
@@ -1624,7 +1624,7 @@
 				}
 			} else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 				update_buddy_display((PurpleBuddy*)node, ggblist);
-				if ((PurpleBlistNode*)tg == target) {
+				if (PURPLE_BLIST_NODE(tg) == target) {
 					/* The target is a group. Add this buddy in a new contact under this group. */
 					purple_blist_add_buddy((PurpleBuddy*)node, NULL, tg, NULL);
 				} else if (PURPLE_BLIST_NODE_IS_CONTACT(target)) {
@@ -1639,7 +1639,7 @@
 				}
 			} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
 				update_node_display(node, ggblist);
-				if ((PurpleBlistNode*)tg == target)
+				if (PURPLE_BLIST_NODE(tg) == target)
 					purple_blist_add_chat((PurpleChat*)node, tg, NULL);
 				else
 					purple_blist_add_chat((PurpleChat*)node, NULL, target);
@@ -1685,7 +1685,7 @@
 		create_group_menu(GNT_MENU(context), NULL);
 		title = g_strdup(_("Buddy List"));
 	} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
-		ggblist->cnode = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);
+		ggblist->cnode = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
 		create_buddy_menu(GNT_MENU(context), (PurpleBuddy*)ggblist->cnode);
 		title = g_strdup(purple_contact_get_alias((PurpleContact*)node));
 	} else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
@@ -2415,8 +2415,8 @@
 
 	switch (purple_blist_node_get_type(n1)) {
 		case PURPLE_BLIST_CONTACT_NODE:
-			n1 = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)n1);
-			n2 = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)n2);
+			n1 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n1)));
+			n2 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n2)));
 			/* now compare the presence of the priority buddies */
 		case PURPLE_BLIST_BUDDY_NODE:
 			ret = purple_presence_compare(purple_buddy_get_presence((PurpleBuddy*)n1),
--- a/finch/gntconv.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/finch/gntconv.c	Sat Mar 07 01:59:40 2009 +0000
@@ -496,8 +496,9 @@
 	buddies = purple_find_buddies(account, name);
 	for (cur = buddies; cur != NULL; cur = cur->next) {
 		PurpleBlistNode *node = cur->data;
-		if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL))) {
-			finch_log_show_contact((PurpleContact *)node->parent);
+		if ((node != NULL) &&
+				(purple_blist_node_get_sibling_prev(node) || purple_blist_node_get_sibling_next(node))) {
+			finch_log_show_contact((PurpleContact *)purple_blist_node_get_parent(node));
 			g_slist_free(buddies);
 			return;
 		}
@@ -529,7 +530,7 @@
 	gnt_menuitem_set_submenu(item, GNT_MENU(sub));
 
 	for (; buds; buds = g_slist_delete_link(buds, buds)) {
-		PurpleBlistNode *node = (PurpleBlistNode *)purple_buddy_get_contact((PurpleBuddy *)buds->data);
+		PurpleBlistNode *node = PURPLE_BLIST_NODE(purple_buddy_get_contact(PURPLE_BUDDY(buds->data)));
 		for (node = purple_blist_node_get_first_child(node); node != NULL;
 				node = purple_blist_node_get_sibling_next(node)) {
 			PurpleBuddy *buddy = (PurpleBuddy *)node;
--- a/finch/gntlog.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/finch/gntlog.c	Sat Mar 07 01:59:40 2009 +0000
@@ -49,7 +49,7 @@
 
 struct log_viewer_hash_t {
 	PurpleLogType type;
-	char *screenname;
+	char *username;
 	PurpleAccount *account;
 	PurpleContact *contact;
 };
@@ -62,7 +62,7 @@
 		return g_direct_hash(viewer->contact);
 
 	if (viewer->account) {
-		return g_str_hash(viewer->screenname) +
+		return g_str_hash(viewer->username) +
 			g_str_hash(purple_account_get_username(viewer->account));
 	}
 
@@ -88,10 +88,10 @@
 			return FALSE;
 	}
 
-	if (a->screenname && b->screenname) {
-		normal = g_strdup(purple_normalize(a->account, a->screenname));
+	if (a->username && b->username) {
+		normal = g_strdup(purple_normalize(a->account, a->username));
 		ret = (a->account == b->account) &&
-			!strcmp(normal, purple_normalize(b->account, b->screenname));
+			!strcmp(normal, purple_normalize(b->account, b->username));
 		g_free(normal);
 	} else {
 		ret = (a == b);
@@ -155,7 +155,7 @@
 		lv = g_hash_table_lookup(log_viewers, ht);
 		g_hash_table_remove(log_viewers, ht);
 
-		g_free(ht->screenname);
+		g_free(ht->username);
 		g_free(ht);
 	} else
 		syslog_viewer = NULL;
@@ -284,7 +284,7 @@
 				if (!purple_prefs_get_bool("/purple/logging/log_chats"))
 					log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
 			}
-			g_free(ht->screenname);
+			g_free(ht->username);
 			g_free(ht);
 		}
 
@@ -365,31 +365,31 @@
 	*list = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, set->name, set->account), *list);
 }
 
-void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account)
+void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *account)
 {
 	struct log_viewer_hash_t *ht;
 	FinchLogViewer *lv = NULL;
-	const char *name = screenname;
+	const char *name = username;
 	char *title;
 	GList *logs = NULL;
 	int size = 0;
 
 	if (type != PURPLE_LOG_IM) {
 		g_return_if_fail(account != NULL);
-		g_return_if_fail(screenname != NULL);
+		g_return_if_fail(username != NULL);
 	}
 
 	ht = g_new0(struct log_viewer_hash_t, 1);
 
 	ht->type = type;
-	ht->screenname = g_strdup(screenname);
+	ht->username = g_strdup(username);
 	ht->account = account;
 
 	if (log_viewers == NULL) {
 		log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
 	} else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
 		gnt_window_present(lv->window);
-		g_free(ht->screenname);
+		g_free(ht->username);
 		g_free(ht);
 		return;
 	}
@@ -397,7 +397,7 @@
 	if (type == PURPLE_LOG_CHAT) {
 		PurpleChat *chat;
 
-		chat = purple_blist_find_chat(account, screenname);
+		chat = purple_blist_find_chat(account, username);
 		if (chat != NULL)
 			name = purple_chat_get_name(chat);
 
@@ -405,8 +405,8 @@
 	} else {
 		PurpleBuddy *buddy;
 
-		if (screenname) {
-			buddy = purple_find_buddy(account, screenname);
+		if (username) {
+			buddy = purple_find_buddy(account, username);
 			if (buddy != NULL)
 				name = purple_buddy_get_contact_alias(buddy);
 			title = g_strdup_printf(_("Conversations with %s"), name);
@@ -415,9 +415,9 @@
 		}
 	}
 
-	if (screenname) {
-		logs = purple_log_get_logs(type, screenname, account);
-		size = purple_log_get_total_size(type, screenname, account);
+	if (username) {
+		logs = purple_log_get_logs(type, username, account);
+		size = purple_log_get_total_size(type, username, account);
 	} else {
 		/* This will happen only for IMs */
 		GHashTable *table = purple_log_get_log_sets();
@@ -458,12 +458,16 @@
 
 	for (child = purple_blist_node_get_first_child((PurpleBlistNode*)contact); child;
 			child = purple_blist_node_get_sibling_next(child)) {
+		const char *name;
+		PurpleAccount *account;
 		if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
 			continue;
 
-		logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, ((PurpleBuddy *)child)->name,
-						((PurpleBuddy *)child)->account), logs);
-		total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, ((PurpleBuddy *)child)->name, ((PurpleBuddy *)child)->account);
+		name = purple_buddy_get_name((PurpleBuddy *)child);
+		account = purple_buddy_get_account((PurpleBuddy *)child);
+		logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, name,
+						account), logs);
+		total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, name, account);
 	}
 	logs = g_list_sort(logs, purple_log_compare);
 
--- a/finch/gntlog.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/finch/gntlog.h	Sat Mar 07 01:59:40 2009 +0000
@@ -50,7 +50,7 @@
 
 
 
-void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account);
+void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *account);
 void finch_log_show_contact(PurpleContact *contact);
 
 void finch_syslog_show(void);
--- a/finch/gntrequest.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/finch/gntrequest.c	Sat Mar 07 01:59:40 2009 +0000
@@ -39,6 +39,12 @@
 #include "debug.h"
 #include "util.h"
 
+/* XXX: Until gobjectification ... */
+#undef FINCH_GET_DATA
+#undef FINCH_SET_DATA
+#define FINCH_GET_DATA(obj)  purple_request_field_get_ui_data(obj)
+#define FINCH_SET_DATA(obj, data)  purple_request_field_set_ui_data(obj, data)
+
 typedef struct
 {
 	void *user_data;
@@ -393,11 +399,11 @@
 }
 
 static void
-update_selected_account(GntEntry *screenname, const char *start, const char *end,
+update_selected_account(GntEntry *username, const char *start, const char *end,
 		GntComboBox *accountlist)
 {
 	GList *accounts = gnt_tree_get_rows(GNT_TREE(accountlist->dropdown));
-	const char *name = gnt_entry_get_text(screenname);
+	const char *name = gnt_entry_get_text(username);
 	while (accounts) {
 		if (purple_find_buddy(accounts->data, name)) {
 			gnt_combo_box_set_selected(accountlist, accounts->data);
@@ -419,7 +425,7 @@
 }
 
 static GntWidget*
-create_string_field(PurpleRequestField *field, GntWidget **screenname)
+create_string_field(PurpleRequestField *field, GntWidget **username)
 {
 	const char *hint = purple_request_field_get_type_hint(field);
 	GntWidget *entry = gnt_entry_new(
@@ -435,8 +441,8 @@
 			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;
+		if (username)
+			*username = entry;
 	} else if (hint && !strcmp(hint, "group")) {
 		PurpleBlistNode *node;
 		for (node = purple_blist_get_root(); node;
@@ -569,7 +575,7 @@
 {
 	GntWidget *window, *box;
 	GList *grlist;
-	GntWidget *screenname = NULL, *accountlist = NULL;
+	GntWidget *username = NULL, *accountlist = NULL;
 
 	window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_FIELDS);
 
@@ -617,7 +623,7 @@
 			}
 			else if (type == PURPLE_REQUEST_FIELD_STRING)
 			{
-				FINCH_SET_DATA(field, create_string_field(field, &screenname));
+				FINCH_SET_DATA(field, create_string_field(field, &username));
 			}
 			else if (type == PURPLE_REQUEST_FIELD_INTEGER)
 			{
@@ -633,7 +639,8 @@
 			}
 			else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
 			{
-				accountlist = FINCH_SET_DATA(field, create_account_field(field));
+				accountlist = create_account_field(field);
+				FINCH_SET_DATA(field, accountlist);
 			}
 			else
 			{
@@ -655,8 +662,8 @@
 	setup_default_callback(window, cancel_cb, userdata);
 	gnt_widget_show(window);
 
-	if (screenname && accountlist) {
-		g_signal_connect(screenname, "completion", G_CALLBACK(update_selected_account), accountlist);
+	if (username && accountlist) {
+		g_signal_connect(username, "completion", G_CALLBACK(update_selected_account), accountlist);
 	}
 
 	g_object_set_data(G_OBJECT(window), "fields", allfields);
--- a/finch/plugins/grouping.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/finch/plugins/grouping.c	Sat Mar 07 01:59:40 2009 +0000
@@ -87,7 +87,7 @@
 
 	switch (purple_blist_node_get_type(node)) {
 		case PURPLE_BLIST_CONTACT_NODE:
-			node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node);
+			node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
 			ret = PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node) ? &online : &offline;
 			break;
 		case PURPLE_BLIST_BUDDY_NODE:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fix-casts.sh	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if [ $# -eq 0 ]; then
+	echo "Usage: `basename "$0"` PurpleFoo..."
+	echo
+	echo "This script searches the *current working directory* and replaces casts"
+	echo "with GObject-style type checking and casting macros."
+	echo 'For example, "(PurpleBuddy *)b" becomes "PURPLE_BUDDY(b)".'
+	exit 0
+fi
+
+for struct in $* ; do
+	cast=`echo $struct | sed "s|[A-Z]|_\0|g" | tr "a-z" "A-Z" | sed "s|^_||"`
+	for file in `grep -rl "([[:space:]]*$struct[[:space:]]*\*[[:space:]]*)" . --include=*.c --exclude=purple-client-bindings.c` ; do
+		sed -i "s|([[:space:]]*$struct[[:space:]]*\*[[:space:]]*)[[:space:]]*(|$cast(|g" $file
+		sed -i "s|([[:space:]]*$struct[[:space:]]*\*[[:space:]]*)[[:space:]]*\([^(][^,);]*\)|$cast(\1)|g" $file
+	done
+done
--- a/libpurple/Makefile.am	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/Makefile.am	Sat Mar 07 01:59:40 2009 +0000
@@ -75,7 +75,12 @@
 	stringref.c \
 	stun.c \
 	sound.c \
+	sound-theme.c \
+	sound-theme-loader.c \
 	sslconn.c \
+	theme.c \
+	theme-loader.c \
+	theme-manager.c \
 	upnp.c \
 	util.c \
 	value.c \
@@ -128,7 +133,12 @@
 	stringref.h \
 	stun.h \
 	sound.h \
+	sound-theme.h \
+	sound-theme-loader.h \
 	sslconn.h \
+	theme.h \
+	theme-loader.h \
+	theme-manager.h \
 	upnp.h \
 	util.h \
 	value.h \
--- a/libpurple/Makefile.mingw	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/Makefile.mingw	Sat Mar 07 01:59:40 2009 +0000
@@ -1,7 +1,7 @@
 #
 # Makefile.mingw
 #
-# Description: Makefile for win32 (mingw) version of LibPurple
+# Description: Makefile for win32 (mingw) version of libpurple
 #
 
 PIDGIN_TREE_TOP := ..
@@ -67,10 +67,15 @@
 			signals.c \
 			smiley.c \
 			sound.c \
+			sound-theme.c \
+			sound-theme-loader.c \
 			sslconn.c \
 			status.c \
 			stringref.c \
 			stun.c \
+			theme.c \
+			theme-loader.c \
+			theme-manager.c \
 			upnp.c \
 			util.c \
 			value.c \
--- a/libpurple/account.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/account.c	Sat Mar 07 01:59:40 2009 +0000
@@ -178,9 +178,7 @@
 	{
 		const char *string_value = purple_value_get_string(attr_value);
 		const char *default_string_value = purple_value_get_string(default_value);
-		if (((string_value == NULL) && (default_string_value == NULL)) ||
-			((string_value != NULL) && (default_string_value != NULL) &&
-			 !strcmp(string_value, default_string_value)))
+		if (purple_strequal(string_value, default_string_value))
 			return NULL;
 		value = g_strdup(purple_value_get_string(attr_value));
 	}
@@ -511,11 +509,11 @@
 			/* Ignore this setting */
 			continue;
 
-		if (!strcmp(str_type, "string"))
+		if (purple_strequal(str_type, "string"))
 			type = PURPLE_PREF_STRING;
-		else if (!strcmp(str_type, "int"))
+		else if (purple_strequal(str_type, "int"))
 			type = PURPLE_PREF_INT;
-		else if (!strcmp(str_type, "bool"))
+		else if (purple_strequal(str_type, "bool"))
 			type = PURPLE_PREF_BOOLEAN;
 		else
 			/* Ignore this setting */
@@ -660,17 +658,17 @@
 	child = xmlnode_get_child(node, "type");
 	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
 	{
-		if (!strcmp(data, "global"))
+		if (purple_strequal(data, "global"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_USE_GLOBAL);
-		else if (!strcmp(data, "none"))
+		else if (purple_strequal(data, "none"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
-		else if (!strcmp(data, "http"))
+		else if (purple_strequal(data, "http"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_HTTP);
-		else if (!strcmp(data, "socks4"))
+		else if (purple_strequal(data, "socks4"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS4);
-		else if (!strcmp(data, "socks5"))
+		else if (purple_strequal(data, "socks5"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS5);
-		else if (!strcmp(data, "envvar"))
+		else if (purple_strequal(data, "envvar"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_USE_ENVVAR);
 		else
 		{
@@ -2027,7 +2025,7 @@
 	{
 		PurpleStatusType *status_type = (PurpleStatusType *)l->data;
 
-		if (!strcmp(purple_status_type_get_id(status_type), id))
+		if (purple_strequal(purple_status_type_get_id(status_type), id))
 			return status_type;
 	}
 
@@ -2238,9 +2236,9 @@
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
-	
+
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);      
+	        prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2255,20 +2253,20 @@
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
-	
+
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);      
+	        prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-		
+
 	if (prpl_info) {
 		GList *cur, *groups = NULL;
 
 		/* Make a list of what group each buddy is in */
 		for (cur = buddies; cur != NULL; cur = cur->next) {
-			PurpleBlistNode *node = cur->data;
-			groups = g_list_append(groups, node->parent->parent);
+			PurpleBuddy *buddy = cur->data;
+			groups = g_list_append(groups, purple_buddy_get_group(buddy));
 		}
 
 		if (prpl_info->add_buddies != NULL)
@@ -2294,13 +2292,13 @@
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
-	
+
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);      
+	        prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-		
+
 	if (prpl_info && prpl_info->remove_buddy)
 		prpl_info->remove_buddy(gc, buddy, group);
 }
@@ -2311,13 +2309,13 @@
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
-	
+
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);      
+	        prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-		
+
 	if (prpl_info) {
 		if (prpl_info->remove_buddies)
 			prpl_info->remove_buddies(gc, buddies, groups);
@@ -2339,9 +2337,9 @@
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
-	
+
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);      
+	        prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2357,11 +2355,11 @@
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
-	
+
 	purple_account_set_password(account, new_pw);
-	
+
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);      
+	        prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2375,15 +2373,15 @@
 	PurpleConnection *gc;
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurplePlugin *prpl = NULL;
-	
+
 	g_return_val_if_fail(account, FALSE);
 	g_return_val_if_fail(buddy, FALSE);
 
 	gc = purple_account_get_connection(account);
 	if (gc == NULL)
 		return FALSE;
-	
-	prpl = purple_connection_get_prpl(gc);      
+
+	prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2518,23 +2516,26 @@
 	purple_accounts_remove(account);
 
 	/* Remove this account's buddies */
-	for (gnode = purple_blist_get_root(); gnode != NULL; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root();
+	     gnode != NULL;
+		 gnode = purple_blist_node_get_sibling_next(gnode))
+	{
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
 
-		cnode = gnode->child;
+		cnode = purple_blist_node_get_first_child(gnode);
 		while (cnode) {
-			PurpleBlistNode *cnode_next = cnode->next;
+			PurpleBlistNode *cnode_next = purple_blist_node_get_sibling_next(cnode);
 
 			if(PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
-				bnode = cnode->child;
+				bnode = purple_blist_node_get_first_child(cnode);
 				while (bnode) {
-					PurpleBlistNode *bnode_next = bnode->next;
+					PurpleBlistNode *bnode_next = purple_blist_node_get_sibling_next(bnode);
 
 					if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) {
 						PurpleBuddy *b = (PurpleBuddy *)bnode;
 
-						if (b->account == account)
+						if (purple_buddy_get_account(b) == account)
 							purple_blist_remove_buddy(b);
 					}
 					bnode = bnode_next;
@@ -2542,7 +2543,7 @@
 			} else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) {
 				PurpleChat *c = (PurpleChat *)cnode;
 
-				if (c->account == account)
+				if (purple_chat_get_account(c) == account)
 					purple_blist_remove_chat(c);
 			}
 			cnode = cnode_next;
@@ -2633,11 +2634,11 @@
 
 	for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
 		account = (PurpleAccount *)l->data;
-		if (protocol_id && strcmp(account->protocol_id, protocol_id))
+		if (protocol_id && !purple_strequal(account->protocol_id, protocol_id))
 		  continue;
 
 		who = g_strdup(purple_normalize(account, name));
-		if (!strcmp(purple_normalize(account, purple_account_get_username(account)), who)) {
+		if (purple_strequal(purple_normalize(account, purple_account_get_username(account)), who)) {
 			g_free(who);
 			return account;
 		}
--- a/libpurple/account.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/account.h	Sat Mar 07 01:59:40 2009 +0000
@@ -260,7 +260,7 @@
  * Notifies the user that a remote user has wants to add the local user
  * to his or her buddy list and requires authorization to do so.
  *
- * This will present a dialog informing the user of this and ask if the 
+ * This will present a dialog informing the user of this and ask if the
  * user authorizes or denies the remote user from adding him.
  *
  * @param account      The account that was added
--- a/libpurple/blist.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/blist.c	Sat Mar 07 01:59:40 2009 +0000
@@ -82,7 +82,7 @@
 
 static guint _purple_blist_hbuddy_equal(struct _purple_hbuddy *hb1, struct _purple_hbuddy *hb2)
 {
-	return ((!strcmp(hb1->name, hb2->name)) && hb1->account == hb2->account && hb1->group == hb2->group);
+	return (purple_strequal(hb1->name, hb2->name) && hb1->account == hb2->account && hb1->group == hb2->group);
 }
 
 static void _purple_blist_hbuddy_free_key(struct _purple_hbuddy *hb)
@@ -382,11 +382,11 @@
 	if (!value)
 		return;
 
-	if (!type || !strcmp(type, "string"))
+	if (!type || purple_strequal(type, "string"))
 		purple_blist_node_set_string(node, name, value);
-	else if (!strcmp(type, "bool"))
+	else if (purple_strequal(type, "bool"))
 		purple_blist_node_set_bool(node, name, atoi(value));
-	else if (!strcmp(type, "int"))
+	else if (purple_strequal(type, "int"))
 		purple_blist_node_set_int(node, name, atoi(value));
 
 	g_free(value);
@@ -453,9 +453,9 @@
 	for (x = cnode->child; x; x = x->next) {
 		if (x->type != XMLNODE_TYPE_TAG)
 			continue;
-		if (!strcmp(x->name, "buddy"))
+		if (purple_strequal(x->name, "buddy"))
 			parse_buddy(group, contact, x);
-		else if (!strcmp(x->name, "setting"))
+		else if (purple_strequal(x->name, "setting"))
 			parse_setting((PurpleBlistNode*)contact, x);
 	}
 
@@ -528,12 +528,12 @@
 	for (cnode = groupnode->child; cnode; cnode = cnode->next) {
 		if (cnode->type != XMLNODE_TYPE_TAG)
 			continue;
-		if (!strcmp(cnode->name, "setting"))
+		if (purple_strequal(cnode->name, "setting"))
 			parse_setting((PurpleBlistNode*)group, cnode);
-		else if (!strcmp(cnode->name, "contact") ||
-				!strcmp(cnode->name, "person"))
+		else if (purple_strequal(cnode->name, "contact") ||
+				purple_strequal(cnode->name, "person"))
 			parse_contact(group, cnode);
-		else if (!strcmp(cnode->name, "chat"))
+		else if (purple_strequal(cnode->name, "chat"))
 			parse_chat(group, cnode);
 	}
 }
@@ -590,11 +590,11 @@
 				if (x->type != XMLNODE_TYPE_TAG)
 					continue;
 
-				if (!strcmp(x->name, "permit")) {
+				if (purple_strequal(x->name, "permit")) {
 					name = xmlnode_get_data(x);
 					purple_privacy_permit_add(account, name, TRUE);
 					g_free(name);
-				} else if (!strcmp(x->name, "block")) {
+				} else if (purple_strequal(x->name, "block")) {
 					name = xmlnode_get_data(x);
 					purple_privacy_deny_add(account, name, TRUE);
 					g_free(name);
@@ -699,6 +699,24 @@
 	return purplebuddylist ? purplebuddylist->root : NULL;
 }
 
+GHashTable *
+purple_blist_get_buddies()
+{
+	return purplebuddylist ? purplebuddylist->buddies : NULL;
+}
+
+void *
+purple_blist_get_ui_data()
+{
+	return purplebuddylist->ui_data;
+}
+
+void
+purple_blist_set_ui_data(void *ui_data)
+{
+	purplebuddylist->ui_data = ui_data;
+}
+
 void purple_blist_show()
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
@@ -774,12 +792,28 @@
 	return node? node->prev : NULL;
 }
 
+void *
+purple_blist_node_get_ui_data(const PurpleBlistNode *node)
+{
+	g_return_val_if_fail(node, NULL);
+
+	return node->ui_data;
+}
+
+void
+purple_blist_node_set_ui_data(PurpleBlistNode *node, void *ui_data) {
+	g_return_if_fail(node);
+
+	node->ui_data = ui_data;
+}
+
 void
 purple_blist_update_buddy_status(PurpleBuddy *buddy, PurpleStatus *old_status)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
 	PurplePresence *presence;
 	PurpleStatus *status;
+	PurpleBlistNode *cnode;
 
 	g_return_if_fail(buddy != NULL);
 
@@ -794,16 +828,18 @@
 
 		purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy);
 
-		((PurpleContact*)((PurpleBlistNode*)buddy)->parent)->online++;
-		if (((PurpleContact*)((PurpleBlistNode*)buddy)->parent)->online == 1)
-			((PurpleGroup *)((PurpleBlistNode *)buddy)->parent->parent)->online++;
+		cnode = buddy->node.parent;
+		if (++(PURPLE_CONTACT(cnode)->online) == 1)
+			PURPLE_GROUP(cnode->parent)->online++;
 	} else if (!purple_status_is_online(status) &&
 				purple_status_is_online(old_status)) {
+
 		purple_blist_node_set_int(&buddy->node, "last_seen", time(NULL));
 		purple_signal_emit(purple_blist_get_handle(), "buddy-signed-off", buddy);
-		((PurpleContact*)((PurpleBlistNode*)buddy)->parent)->online--;
-		if (((PurpleContact*)((PurpleBlistNode*)buddy)->parent)->online == 0)
-			((PurpleGroup *)((PurpleBlistNode *)buddy)->parent->parent)->online--;
+
+		cnode = buddy->node.parent;
+		if (--(PURPLE_CONTACT(cnode)->online) == 0)
+			PURPLE_GROUP(cnode->parent)->online--;
 	} else {
 		purple_signal_emit(purple_blist_get_handle(),
 		                 "buddy-status-changed", buddy, old_status,
@@ -1025,7 +1061,7 @@
 	g_return_if_fail(source != NULL);
 	g_return_if_fail(new_name != NULL);
 
-	if (*new_name == '\0' || !strcmp(new_name, source->name))
+	if (*new_name == '\0' || purple_strequal(new_name, source->name))
 		return;
 
 	dest = purple_find_group(new_name);
@@ -1092,7 +1128,7 @@
 
 	/* Notify all PRPLs */
 	/* TODO: Is this condition needed?  Seems like it would always be TRUE */
-	if(old_name && source && strcmp(source->name, old_name)) {
+	if(old_name && purple_strequal(source->name, old_name)) {
 		for (accts = purple_group_get_accounts(source); accts; accts = g_slist_remove(accts, accts->data)) {
 			PurpleAccount *account = accts->data;
 			PurpleConnection *gc = NULL;
@@ -1101,7 +1137,7 @@
 			GList *l = NULL, *buddies = NULL;
 
 			gc = purple_account_get_connection(account);
-			
+
 			if(gc)
 				prpl = purple_connection_get_prpl(gc);
 
@@ -1166,17 +1202,17 @@
 	return chat;
 }
 
-PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *screenname, const char *alias)
+PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
 	PurpleBuddy *buddy;
 
 	g_return_val_if_fail(account != NULL, FALSE);
-	g_return_val_if_fail(screenname != NULL, FALSE);
+	g_return_val_if_fail(name != NULL, FALSE);
 
 	buddy = g_new0(PurpleBuddy, 1);
 	buddy->account  = account;
-	buddy->name     = g_strdup(screenname);
+	buddy->name     = g_strdup(name);
 	buddy->alias    = g_strdup(alias);
 	buddy->presence = purple_presence_new_for_buddy(buddy);
 	((PurpleBlistNode *)buddy)->type = PURPLE_BLIST_BUDDY_NODE;
@@ -1232,6 +1268,23 @@
 	return buddy->icon;
 }
 
+gpointer
+purple_buddy_get_protocol_data(const PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	return buddy->proto_data;
+}
+
+void
+purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data)
+{
+	g_return_if_fail(buddy != NULL);
+
+	buddy->proto_data = data;
+}
+
+
 void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node)
 {
 	PurpleBlistNode *cnode = (PurpleBlistNode*)chat;
@@ -1339,7 +1392,7 @@
 		g = (PurpleGroup*)node->parent->parent;
 	} else if (contact) {
 		c = contact;
-		g = (PurpleGroup *)((PurpleBlistNode *)c)->parent;
+		g = PURPLE_GROUP(PURPLE_BLIST_NODE(c)->parent);
 	} else {
 		g = group;
 		if (g == NULL)
@@ -1421,16 +1474,14 @@
 	}
 
 	if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
-		((PurpleContact*)bnode->parent)->online++;
-		if (((PurpleContact*)bnode->parent)->online == 1)
-			((PurpleGroup*)bnode->parent->parent)->online++;
+		if (++(PURPLE_CONTACT(bnode->parent)->online) == 1)
+			PURPLE_GROUP(bnode->parent->parent)->online++;
 	}
 	if (purple_account_is_connected(buddy->account)) {
-		((PurpleContact*)bnode->parent)->currentsize++;
-		if (((PurpleContact*)bnode->parent)->currentsize == 1)
-			((PurpleGroup*)bnode->parent->parent)->currentsize++;
+		if (++(PURPLE_CONTACT(bnode->parent)->currentsize) == 1)
+			PURPLE_GROUP(bnode->parent->parent)->currentsize++;
 	}
-	((PurpleContact*)bnode->parent)->totalsize++;
+	PURPLE_CONTACT(bnode->parent)->totalsize++;
 
 	hb = g_new(struct _purple_hbuddy, 1);
 	hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
@@ -1546,7 +1597,7 @@
 	g_return_if_fail(contact != NULL);
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT((PurpleBlistNode*)contact));
 
-	if ((PurpleBlistNode*)contact == node)
+	if (PURPLE_BLIST_NODE(contact) == node)
 		return;
 
 	if (node && (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
@@ -2056,6 +2107,12 @@
 	return buddy->name;
 }
 
+const char *purple_buddy_get_local_buddy_alias(PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy, NULL);
+	return buddy->alias;
+}
+
 const char *purple_buddy_get_server_alias(PurpleBuddy *buddy)
 {
         g_return_val_if_fail(buddy != NULL, NULL);
@@ -2300,7 +2357,7 @@
 {
 	g_return_val_if_fail(buddy != NULL, NULL);
 
-	return (PurpleContact*)((PurpleBlistNode*)buddy)->parent;
+	return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy)->parent);
 }
 
 PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy)
--- a/libpurple/blist.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/blist.h	Sat Mar 07 01:59:40 2009 +0000
@@ -75,12 +75,37 @@
 
 } PurpleBlistNodeFlags;
 
+/**
+ * @since 2.6.0
+ */
+#define PURPLE_BLIST_NODE(obj) ((PurpleBlistNode *)(obj))
+
 #define PURPLE_BLIST_NODE_HAS_FLAG(b, f) (purple_blist_node_get_flags((PurpleBlistNode*)(b)) & (f))
 #define PURPLE_BLIST_NODE_SHOULD_SAVE(b) (! PURPLE_BLIST_NODE_HAS_FLAG(b, PURPLE_BLIST_NODE_FLAG_NO_SAVE))
 
 #define PURPLE_BLIST_NODE_NAME(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CHAT_NODE  ? purple_chat_get_name((PurpleChat*)n) :        \
 				     purple_blist_node_get_type(n) == PURPLE_BLIST_BUDDY_NODE ? purple_buddy_get_name((PurpleBuddy*)n) : NULL)
 
+/**
+ * @since 2.6.0
+ */
+#define PURPLE_GROUP(obj) ((PurpleGroup *)(obj))
+
+/**
+ * @since 2.6.0
+ */
+#define PURPLE_CONTACT(obj) ((PurpleContact *)(obj))
+
+/**
+ * @since 2.6.0
+ */
+#define PURPLE_BUDDY(obj) ((PurpleBuddy *)(obj))
+
+/**
+ * @since 2.6.0
+ */
+#define PURPLE_CHAT(obj) ((PurpleChat *)(obj))
+
 #include "account.h"
 #include "buddyicon.h"
 #include "status.h"
@@ -111,7 +136,7 @@
  */
 struct _PurpleBuddy {
 	PurpleBlistNode node;                     /**< The node that this buddy inherits from */
-	char *name;                             /**< The screenname of the buddy. */
+	char *name;                             /**< The name of the buddy. */
 	char *alias;                            /**< The user-set alias of the buddy */
 	char *server_alias;                     /**< The server-specified alias of the buddy.  (i.e. MSN "Friendly Names") */
 	void *proto_data;                       /**< This allows the prpl to associate whatever data it wants with a buddy */
@@ -156,9 +181,6 @@
 	PurpleAccount *account; /**< The account this chat is attached to */
 };
 
-#endif /* PURPLE_HIDE_STRUCTS && PURPLE_BLIST_STRUCTS */
-
-
 /**
  * The Buddy List
  */
@@ -168,6 +190,8 @@
 	void *ui_data;                /**< UI-specific data. */
 };
 
+#endif /* PURPLE_HIDE_STRUCTS && PURPLE_BLIST_STRUCTS */
+
 /**
  * Buddy list UI operations.
  *
@@ -236,6 +260,33 @@
 PurpleBlistNode *purple_blist_get_root(void);
 
 /**
+ * Returns the hash table of every buddy in the list.
+ *
+ * @return The hash table of every buddy in the list.
+ *
+ * @since 2.6.0
+ */
+GHashTable *purple_blist_get_buddies(void);
+
+/**
+ * Returns the UI data for the list.
+ *
+ * @return The UI data for the list.
+ *
+ * @since 2.6.0
+ */
+void *purple_blist_get_ui_data(void);
+
+/**
+ * Sets the UI data for the list.
+ *
+ * @param ui_data The UI data for the list.
+ *
+ * @since 2.6.0
+ */
+void purple_blist_set_ui_data(void *ui_data);
+
+/**
  * Returns the next node of a given node. This function is to be used to iterate
  * over the tree returned by purple_get_blist.
  *
@@ -302,6 +353,25 @@
 PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node);
 
 /**
+ * Returns the UI data of a given node.
+ *
+ * @param node The node.
+ * @return The UI data.
+ * @since 2.6.0
+ */
+void *purple_blist_node_get_ui_data(const PurpleBlistNode *node);
+
+/**
+ * Sets the UI data of a given node.
+ *
+ * @param node The node.
+ * @param ui_data The UI data.
+ *
+ * @since 2.6.0
+ */
+void purple_blist_node_set_ui_data(PurpleBlistNode *node, void *ui_data);
+
+/**
  * Shows the buddy list, creating a new one if necessary.
  */
 void purple_blist_show(void);
@@ -331,6 +401,7 @@
  * Updates a node's custom icon.
  *
  * @param node  The PurpleBlistNode whose custom icon has changed.
+ *
  * @since 2.5.0
  */
 void purple_blist_update_node_icon(PurpleBlistNode *node);
@@ -423,11 +494,11 @@
  * Creates a new buddy
  *
  * @param account    The account this buddy will get added to
- * @param screenname The screenname of the new buddy
+ * @param name       The name of the new buddy
  * @param alias      The alias of the new buddy (or NULL if unaliased)
  * @return           A newly allocated buddy
  */
-PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *screenname, const char *alias);
+PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias);
 
 /**
  * Sets a buddy's icon.
@@ -470,6 +541,32 @@
 PurpleBuddyIcon *purple_buddy_get_icon(const PurpleBuddy *buddy);
 
 /**
+ * Returns a buddy's protocol-specific data.
+ *
+ * This should only be called from the associated prpl.
+ *
+ * @param buddy The buddy.
+ * @return      The protocol data.
+ *
+ * @see purple_buddy_set_protocol_data()
+ * @since 2.6.0
+ */
+gpointer purple_buddy_get_protocol_data(const PurpleBuddy *buddy);
+
+/**
+ * Sets a buddy's protocol-specific data.
+ *
+ * This should only be called from the associated prpl.
+ *
+ * @param buddy The buddy.
+ * @param data  The data.
+ *
+ * @see purple_buddy_get_protocol_data()
+ * @since 2.6.0
+ */
+void purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data);
+
+/**
  * Returns a buddy's contact.
  *
  * @param buddy The buddy.
@@ -659,15 +756,18 @@
  */
 const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy);
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
 /**
  * Returns the correct alias for this user, ignoring server aliases.  Used
  * when a user-recognizable name is required.  In order: buddy's alias; buddy's
  * contact alias; buddy's user name.
- * 
+ *
  * @param buddy  The buddy whose alias will be returned.
  * @return       The appropriate name or alias.
+ * @deprecated   Try purple_buddy_get_alias(), if server aliases are okay.
  */
 const char *purple_buddy_get_local_alias(PurpleBuddy *buddy);
+#endif
 
 /**
  * Returns the correct name to display for a buddy. In order of precedence:
@@ -680,6 +780,16 @@
 const char *purple_buddy_get_alias(PurpleBuddy *buddy);
 
 /**
+ * Returns the local alias for the buddy, or @c NULL if none exists.
+ *
+ * @param buddy  The buddy
+ * @return       The local alias for the buddy
+ *
+ * @since 2.6.0
+ */
+const char *purple_buddy_get_local_buddy_alias(PurpleBuddy *buddy);
+
+/**
  * Returns the correct name to display for a blist chat.
  *
  * @param chat   The chat whose name will be returned.
@@ -688,19 +798,19 @@
 const char *purple_chat_get_name(PurpleChat *chat);
 
 /**
- * Finds the buddy struct given a screenname and an account
+ * Finds the buddy struct given a name and an account
  *
  * @param account The account this buddy belongs to
- * @param name    The buddy's screenname
+ * @param name    The buddy's name
  * @return        The buddy or NULL if the buddy does not exist
  */
 PurpleBuddy *purple_find_buddy(PurpleAccount *account, const char *name);
 
 /**
- * Finds the buddy struct given a screenname, an account, and a group
+ * Finds the buddy struct given a name, an account, and a group
  *
  * @param account The account this buddy belongs to
- * @param name    The buddy's screenname
+ * @param name    The buddy's name
  * @param group   The group to look in
  * @return        The buddy or NULL if the buddy does not exist in the group
  */
@@ -708,10 +818,10 @@
 		PurpleGroup *group);
 
 /**
- * Finds all PurpleBuddy structs given a screenname and an account
+ * Finds all PurpleBuddy structs given a name and an account
  *
  * @param account The account this buddy belongs to
- * @param name    The buddy's screenname (or NULL to return all buddies in the account)
+ * @param name    The buddy's name (or NULL to return all buddies in the account)
  *
  * @return        A GSList of buddies (which must be freed), or NULL if the buddy doesn't exist
  */
@@ -751,6 +861,7 @@
  * @param chat  The chat.
  *
  * @return  The account the chat belongs to.
+ *
  * @since 2.4.0
  */
 PurpleAccount *purple_chat_get_account(PurpleChat *chat);
@@ -761,6 +872,7 @@
  * @param chat  The chat.
  *
  * @constreturn  The hashtable.
+ *
  * @since 2.4.0
  */
 GHashTable *purple_chat_get_components(PurpleChat *chat);
@@ -979,6 +1091,7 @@
  * @param node The node.
  *
  * @return The type of the node.
+ *
  * @since 2.1.0
  */
 PurpleBlistNodeType purple_blist_node_get_type(PurpleBlistNode *node);
--- a/libpurple/buddyicon.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/buddyicon.c	Sat Mar 07 01:59:40 2009 +0000
@@ -153,7 +153,7 @@
 {
 	const char *dirname;
 	char *path;
-	
+
 	g_return_if_fail(img != NULL);
 
 	if (!purple_buddy_icons_is_caching())
@@ -175,7 +175,7 @@
 	}
 
 	purple_util_write_data_to_file_absolute(path, purple_imgstore_get_data(img),
-											purple_imgstore_get_size(img));	
+											purple_imgstore_get_size(img));
 	g_free(path);
 }
 
@@ -453,7 +453,7 @@
 
 	if (conv != NULL)
 		purple_conv_im_set_icon(PURPLE_CONV_IM(conv), icon_to_set);
-	
+
 	/* icon's refcount was incremented above */
 	if (icon) purple_buddy_icon_unref(icon);
 }
@@ -757,7 +757,7 @@
 		g_hash_table_insert(pointer_icon_cache, account, img);
 	else
 		g_hash_table_remove(pointer_icon_cache, account);
-	
+
 	if (purple_account_is_connected(account))
 	{
 		PurpleConnection *gc;
@@ -889,7 +889,9 @@
 
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		PurpleBlistNode *child;
-		for (child = node->child ; child ; child = child->next)
+		for (child = purple_blist_node_get_first_child(node);
+		     child;
+			 child = purple_blist_node_get_sibling_next(child))
 		{
 			PurpleBuddy *buddy;
 			PurpleConversation *conv;
@@ -986,7 +988,7 @@
 {
 	purple_blist_node_remove_setting(node, setting_name);
 
-	if (!strcmp(setting_name, "buddy_icon"))
+	if (purple_strequal(setting_name, "buddy_icon"))
 	{
 		purple_blist_node_remove_setting(node, "avatar_hash");
 		purple_blist_node_remove_setting(node, "icon_checksum");
@@ -1083,7 +1085,7 @@
 
 		g_free(new_filename);
 
-		if (!strcmp(setting_name, "buddy_icon"))
+		if (purple_strequal(setting_name, "buddy_icon"))
 		{
 			const char *hash;
 
@@ -1098,7 +1100,7 @@
 				PurpleAccount *account = purple_buddy_get_account((PurpleBuddy *)node);
 				const char *prpl_id = purple_account_get_protocol_id(account);
 
-				if (!strcmp(prpl_id, "prpl-yahoo"))
+				if (purple_strequal(prpl_id, "prpl-yahoo"))
 				{
 					int checksum = purple_blist_node_get_int(node, "icon_checksum");
 					if (checksum != 0)
--- a/libpurple/certificate.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/certificate.c	Sat Mar 07 01:59:40 2009 +0000
@@ -51,7 +51,7 @@
 {
 	PurpleCertificateVerificationRequest *vrq;
 	PurpleCertificateScheme *scheme;
-	
+
 	g_return_if_fail(subject_name != NULL);
 	/* If you don't have a cert to check, why are you requesting that it
 	   be verified? */
@@ -97,10 +97,10 @@
 				  "Failed to verify certificate for %s\n",
 				  vrq->subject_name);
 	}
-		
-		
-		
-	
+
+
+
+
 	/* Pass the results on to the request's callback */
 	(vrq->cb)(st, vrq->cb_data);
 
@@ -154,7 +154,7 @@
 purple_certificate_destroy (PurpleCertificate *crt)
 {
 	PurpleCertificateScheme *scheme;
-	
+
 	if (NULL == crt) return;
 
 	scheme = crt->scheme;
@@ -206,7 +206,7 @@
 			  "Checking signature chain for uid=%s\n",
 			  uid);
 	g_free(uid);
-	
+
 	/* If this is a single-certificate chain, say that it is valid */
 	if (chain->next == NULL) {
 		purple_debug_info("certificate",
@@ -218,9 +218,9 @@
 	crt = (PurpleCertificate *)(chain->data);
 	/* And start with the second certificate in the chain */
 	for ( cur = chain->next; cur; cur = cur->next ) {
-		
+
 		issuer = (PurpleCertificate *)(cur->data);
-		
+
 		/* Check the signature for this link */
 		if (! purple_certificate_signed_by(crt, issuer) ) {
 			uid = purple_certificate_get_unique_id(issuer);
@@ -228,7 +228,7 @@
 					  "...Bad or missing signature by %s\nChain is INVALID\n",
 					  uid);
 			g_free(uid);
-		
+
 			return FALSE;
 		}
 
@@ -237,7 +237,7 @@
 				  "...Good signature by %s\n",
 				  uid);
 		g_free(uid);
-		
+
 		/* The issuer is now the next crt whose signature is to be
 		   checked */
 		crt = issuer;
@@ -283,7 +283,7 @@
 	g_return_val_if_fail(crt->scheme, NULL);
 
 	scheme = crt->scheme;
-	
+
 	g_return_val_if_fail(scheme->get_fingerprint_sha1, NULL);
 
 	fpr = (scheme->get_fingerprint_sha1)(crt);
@@ -354,7 +354,7 @@
 	g_return_val_if_fail(crt, FALSE);
 
 	scheme = crt->scheme;
-	
+
 	g_return_val_if_fail(scheme, FALSE);
 
 	/* If both provided references are NULL, what are you doing calling
@@ -371,7 +371,7 @@
 {
 	gchar *path;
 	gchar *esc_scheme_name, *esc_name, *esc_id;
-	
+
 	g_return_val_if_fail(pool, NULL);
 	g_return_val_if_fail(pool->scheme_name, NULL);
 	g_return_val_if_fail(pool->name, NULL);
@@ -380,7 +380,7 @@
 	esc_scheme_name = pool ? g_strdup(purple_escape_filename(pool->scheme_name)) : NULL;
 	esc_name = pool ? g_strdup(purple_escape_filename(pool->name)) : NULL;
 	esc_id = id ? g_strdup(purple_escape_filename(id)) : NULL;
-	
+
 	path = g_build_filename(purple_user_dir(),
 				"certificates", /* TODO: constantize this? */
 				esc_scheme_name,
@@ -404,7 +404,7 @@
 	if (purple_certificate_find_scheme(pool->scheme_name) == NULL) {
 		return FALSE;
 	}
-	
+
 	return TRUE;
 }
 
@@ -441,7 +441,7 @@
 purple_certificate_pool_store(PurpleCertificatePool *pool, const gchar *id, PurpleCertificate *crt)
 {
 	gboolean ret = FALSE;
-	
+
 	g_return_val_if_fail(pool, FALSE);
 	g_return_val_if_fail(id, FALSE);
 	g_return_val_if_fail(pool->put_cert, FALSE);
@@ -461,13 +461,13 @@
 	}
 
 	return ret;
-}	
+}
 
 gboolean
 purple_certificate_pool_delete(PurpleCertificatePool *pool, const gchar *id)
 {
 	gboolean ret = FALSE;
-	
+
 	g_return_val_if_fail(pool, FALSE);
 	g_return_val_if_fail(id, FALSE);
 	g_return_val_if_fail(pool->delete_cert, FALSE);
@@ -496,7 +496,7 @@
 purple_certificate_pool_destroy_idlist(GList *idlist)
 {
 	GList *l;
-	
+
 	/* Iterate through and free them strings */
 	for ( l = idlist; l; l = l->next ) {
 		g_free(l->data);
@@ -520,7 +520,7 @@
 			  vrq->subject_name, id);
 
 	/* Signal what happened back to the caller */
-	if (1 == id) {		
+	if (1 == id) {
 		/* Accepted! */
 		purple_certificate_verify_complete(vrq,
 						   PURPLE_CERTIFICATE_VALID);
@@ -557,11 +557,11 @@
 	} else {
 		cn_match = _("(DOES NOT MATCH)");
 	}
-	
+
 	/* Make messages */
 	primary = g_strdup_printf(_("%s has presented the following certificate for just-this-once use:"), vrq->subject_name);
 	secondary = g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn, cn_match, sha_asc);
-	
+
 	/* Make a semi-pretty display */
 	purple_request_accept_cancel(
 		vrq->cb_data, /* TODO: Find what the handle ought to be */
@@ -575,7 +575,7 @@
 		vrq,
 		x509_singleuse_verify_cb,
 		x509_singleuse_verify_cb );
-	
+
 	/* Cleanup */
 	g_free(primary);
 	g_free(secondary);
@@ -644,13 +644,13 @@
 
 	/* lazy_init calls this function, so calling lazy_init here is a
 	   Bad Thing */
-	
+
 	g_return_val_if_fail(crt, FALSE);
 	g_return_val_if_fail(crt->scheme, FALSE);
 	/* Make sure that this is some kind of X.509 certificate */
 	/* TODO: Perhaps just check crt->scheme->name instead? */
 	g_return_val_if_fail(crt->scheme == purple_certificate_find_scheme(x509_ca.scheme_name), FALSE);
-	
+
 	el = g_new0(x509_ca_element, 1);
 	el->dn = purple_certificate_get_unique_id(crt);
 	el->crt = purple_certificate_copy(crt);
@@ -675,7 +675,7 @@
 	const gchar *entry;
 	GPatternSpec *pempat;
 	GList *iter = NULL;
-	
+
 	if (x509_ca_initialized) return TRUE;
 
 	/* Check that X.509 is registered */
@@ -791,7 +791,7 @@
 
 	for (cur = lst; cur; cur = cur->next) {
 		x509_ca_element *el = cur->data;
-		if (el->dn && !strcmp(dn, el->dn)) {
+		if (purple_strequal(dn, el->dn)) {
 			return el;
 		}
 	}
@@ -832,7 +832,7 @@
 	} else {
 		crt = NULL;
 	}
-	
+
 	return crt;
 }
 
@@ -840,7 +840,7 @@
 x509_ca_put_cert(const gchar *id, PurpleCertificate *crt)
 {
 	gboolean ret = FALSE;
-	
+
 	g_return_val_if_fail(x509_ca_lazy_init(), FALSE);
 
 	/* TODO: This is a quick way of doing this. At some point the change
@@ -854,7 +854,7 @@
 x509_ca_delete_cert(const gchar *id)
 {
 	x509_ca_element *el;
-	
+
 	g_return_val_if_fail(x509_ca_lazy_init(), FALSE);
 	g_return_val_if_fail(id, FALSE);
 
@@ -870,7 +870,7 @@
 	/* Unlink it from the memory cache and destroy it */
 	x509_ca_certs = g_list_remove(x509_ca_certs, el);
 	x509_ca_element_free(el);
-	
+
 	return TRUE;
 }
 
@@ -878,7 +878,7 @@
 x509_ca_get_idlist(void)
 {
 	GList *l, *idlist;
-	
+
 	g_return_val_if_fail(x509_ca_lazy_init(), NULL);
 
 	idlist = NULL;
@@ -886,7 +886,7 @@
 		x509_ca_element *el = l->data;
 		idlist = g_list_prepend(idlist, g_strdup(el->dn));
 	}
-	
+
 	return idlist;
 }
 
@@ -921,7 +921,7 @@
 {
 	gchar *poolpath;
 	int ret;
-	
+
 	/* Set up key cache here if it isn't already done */
 	poolpath = purple_certificate_pool_mkpath(&x509_tls_peers, NULL);
 	ret = purple_build_dir(poolpath, 0700); /* Make it this user only */
@@ -937,13 +937,13 @@
 {
 	gchar *keypath;
 	gboolean ret = FALSE;
-	
+
 	g_return_val_if_fail(id, FALSE);
 
 	keypath = purple_certificate_pool_mkpath(&x509_tls_peers, id);
 
 	ret = g_file_test(keypath, G_FILE_TEST_IS_REGULAR);
-	
+
 	g_free(keypath);
 	return ret;
 }
@@ -954,14 +954,14 @@
 	PurpleCertificateScheme *x509;
 	PurpleCertificate *crt;
 	gchar *keypath;
-	
+
 	g_return_val_if_fail(id, NULL);
 
 	/* Is it in the pool? */
 	if ( !x509_tls_peers_cert_in_pool(id) ) {
 		return NULL;
 	}
-	
+
 	/* Look up the X.509 scheme */
 	x509 = purple_certificate_find_scheme("x509");
 	g_return_val_if_fail(x509, NULL);
@@ -990,7 +990,7 @@
 	/* Work out the filename and export */
 	keypath = purple_certificate_pool_mkpath(&x509_tls_peers, id);
 	ret = purple_certificate_export(keypath, crt);
-	
+
 	g_free(keypath);
 	return ret;
 }
@@ -1012,7 +1012,7 @@
 	}
 
 	/* OK, so work out the keypath and delete the thing */
-	keypath = purple_certificate_pool_mkpath(&x509_tls_peers, id);	
+	keypath = purple_certificate_pool_mkpath(&x509_tls_peers, id);
 	if ( unlink(keypath) != 0 ) {
 		purple_debug_error("certificate/tls_peers",
 				   "Unlink of %s failed!\n",
@@ -1047,7 +1047,7 @@
 	while ( (entry = g_dir_read_name(dir)) != NULL ) {
 		/* Unescape the filename */
 		const char *unescaped = purple_unescape_filename(entry);
-		
+
 		/* Copy the entry name into our list (GLib owns the original
 		   string) */
 		idlist = g_list_prepend(idlist, g_strdup(unescaped));
@@ -1055,7 +1055,7 @@
 
 	/* Release the directory */
 	g_dir_close(dir);
-	
+
 	return idlist;
 }
 
@@ -1143,7 +1143,7 @@
 
 	g_return_if_fail(c);
 	g_return_if_fail(c->vrq);
-	
+
 	vrq = c->vrq;
 
 	x509_tls_cached_ua_ctx_free(c);
@@ -1155,7 +1155,7 @@
 		purple_debug_info("certificate/x509/tls_cached",
 				  "User ACCEPTED cert\nCaching first in chain for future use as %s...\n",
 				  cache_id);
-		
+
 		purple_certificate_pool_store(tls_peers, cache_id,
 					      vrq->cert_chain->data);
 
@@ -1195,7 +1195,7 @@
 	/* Make messages */
 	primary = g_strdup_printf(_("Accept certificate for %s?"),
 				  vrq->subject_name);
-		
+
 	/* Make a semi-pretty display */
 	purple_request_action(
 		vrq->cb_data, /* TODO: Find what the handle ought to be */
@@ -1211,7 +1211,7 @@
 		_("Accept"), x509_tls_cached_user_auth_accept_cb,
 		_("Reject"),  x509_tls_cached_user_auth_reject_cb,
 		_("_View Certificate..."), x509_tls_cached_show_cert);
-	
+
 	/* Cleanup */
 	g_free(primary);
 }
@@ -1225,7 +1225,7 @@
 			  "Certificate for %s does not match cached. "
 			  "Auto-rejecting!\n",
 			  vrq->subject_name);
-	
+
 	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
 	return;
 }
@@ -1245,7 +1245,7 @@
 	/* The peer's certificate should be the first in the list */
 	PurpleCertificate *peer_crt =
 		(PurpleCertificate *) vrq->cert_chain->data;
-	
+
 	PurpleCertificate *cached_crt;
 	GByteArray *peer_fpr, *cached_fpr;
 
@@ -1278,7 +1278,7 @@
 		/* vrq now becomes the problem of the user */
 		x509_tls_cached_unknown_peer(vrq);
 	}
-	
+
 	purple_certificate_destroy(cached_crt);
 	g_byte_array_free(peer_fpr, TRUE);
 	g_byte_array_free(cached_fpr, TRUE);
@@ -1305,7 +1305,7 @@
 	   "not self-signed" */
 	if ( purple_certificate_signed_by(peer_crt, peer_crt) ) {
 		gchar *msg;
-		
+
 		purple_debug_info("certificate/x509/tls_cached",
 				  "Certificate for %s is self-signed.\n",
 				  vrq->subject_name);
@@ -1316,13 +1316,13 @@
 					"is self-signed. It cannot be "
 					"automatically checked."),
 				      vrq->subject_name);
-				      
+
 		x509_tls_cached_user_auth(vrq,msg);
 
 		g_free(msg);
 		return;
 	} /* if (self signed) */
-	
+
 	/* Next, check that the certificate chain is valid */
 	if ( ! purple_certificate_check_signature_chain(chain) ) {
 		/* TODO: Tell the user where the chain broke? */
@@ -1390,7 +1390,7 @@
 	}
 
 	g_free(ca_id);
-	
+
 	/* Check the signature */
 	if ( !purple_certificate_signed_by(end_crt, ca_crt) ) {
 		/* TODO: If signed_by ever returns a reason, maybe mention
@@ -1406,7 +1406,7 @@
 					  "Authority from which it claims to "
 					  "have a signature."),
 					vrq->subject_name);
-		
+
 		purple_notify_error(NULL, /* TODO: Probably wrong */
 				    _("SSL Certificate Error"),
 				    _("Invalid certificate authority"
@@ -1425,7 +1425,7 @@
 						     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",
@@ -1441,7 +1441,7 @@
 					"connecting to the service you "
 					"believe you are."),
 				      vrq->subject_name, sn);
-				      
+
 		x509_tls_cached_user_auth(vrq,msg);
 
 		g_free(sn);
@@ -1465,7 +1465,7 @@
 				   "Unable to locate tls_peers certificate "
 				   "cache.\n");
 	}
-	
+
 	/* Whew! Done! */
 	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
 }
@@ -1481,7 +1481,7 @@
 	purple_debug_info("certificate/x509/tls_cached",
 			  "Starting verify for %s\n",
 			  vrq->subject_name);
-	
+
 	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,tls_peers_name);
 
 	if (!tls_peers) {
@@ -1494,7 +1494,7 @@
 		x509_tls_cached_unknown_peer(vrq);
 		return;
 	}
-	
+
 	/* Check if the peer has a certificate cached already */
 	purple_debug_info("certificate/x509/tls_cached",
 			  "Checking for cached cert...\n");
@@ -1583,7 +1583,7 @@
 			     name);
 
 	/* TODO: Signalling and such? */
-	
+
 	return NULL;
 }
 
@@ -1611,7 +1611,7 @@
 	purple_debug_info("certificate",
 			  "CertificateScheme %s registered\n",
 			  scheme->name);
-	
+
 	return TRUE;
 }
 
@@ -1664,7 +1664,7 @@
 			     scheme_name, ver_name);
 
 	/* TODO: Signalling and such? */
-	
+
 	return NULL;
 }
 
@@ -1742,7 +1742,7 @@
 			     scheme_name, pool_name);
 
 	/* TODO: Signalling and such? */
-	
+
 	return NULL;
 
 }
@@ -1830,11 +1830,11 @@
 	}
 
 	cert_pools = g_list_remove(cert_pools, pool);
-	
+
 	/* TODO: Signalling? */
 	purple_signal_unregister(pool, "certificate-stored");
 	purple_signal_unregister(pool, "certificate-deleted");
-		
+
 	purple_debug_info("certificate",
 			  "CertificatePool %s unregistered\n",
 			  pool->name);
--- a/libpurple/certificate.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/certificate.h	Sat Mar 07 01:59:40 2009 +0000
@@ -60,7 +60,7 @@
 typedef void (*PurpleCertificateVerifiedCallback)
 		(PurpleCertificateVerificationStatus st,
 		 gpointer userdata);
-							  
+
 /** A certificate instance
  *
  *  An opaque data structure representing a single certificate under some
@@ -96,7 +96,7 @@
 
 	/** Internal pool data */
 	gpointer data;
-	
+
 	/**
 	 * Set up the Pool's internal state
 	 *
@@ -249,7 +249,7 @@
 
 	/** Retrieve the certificate activation/expiration times */
 	gboolean (* get_times)(PurpleCertificate *crt, time_t *activation, time_t *expiration);
-	
+
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
 	void (*_purple_reserved3)(void);
@@ -276,7 +276,7 @@
 
 	/** Name of the Verifier - case insensitive */
 	gchar *name;
-	
+
 	/**
 	 * Start the verification process
 	 *
@@ -326,14 +326,14 @@
 	 * For X.509 certificates, this is the Common Name
 	 */
 	gchar *subject_name;
-	
+
 	/** List of certificates in the chain to be verified (such as that returned by purple_ssl_get_peer_certificates )
 	 *
 	 * This is most relevant for X.509 certificates used in SSL sessions.
 	 * The list order should be: certificate, issuer, issuer's issuer, etc.
 	 */
 	GList *cert_chain;
-	
+
 	/** Internal data used by the Verifier code */
 	gpointer data;
 
@@ -437,7 +437,7 @@
  *
  * @return TRUE if 'crt' has a valid signature made by 'issuer',
  *         otherwise FALSE
- * @todo Find a way to give the reason (bad signature, not the issuer, etc.) 
+ * @todo Find a way to give the reason (bad signature, not the issuer, etc.)
  */
 gboolean
 purple_certificate_signed_by(PurpleCertificate *crt, PurpleCertificate *issuer);
@@ -523,7 +523,7 @@
 /**
  * Check the subject name against that on the certificate
  * @param crt   Certificate instance
- * @param name  Name to check. 
+ * @param name  Name to check.
  * @return TRUE if it is a match, else FALSE
  */
 gboolean
--- a/libpurple/cipher.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/cipher.c	Sat Mar 07 01:59:40 2009 +0000
@@ -512,7 +512,7 @@
 }
 
 static void
-md4_append(PurpleCipherContext *context, const guchar *data, size_t len) 
+md4_append(PurpleCipherContext *context, const guchar *data, size_t len)
 {
 	struct MD4_Context *mctx = purple_cipher_context_get_data(context);
 	const guint32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
@@ -551,7 +551,7 @@
 	char *p = (char *)mctx->block + offset;
 	int padding = 56 - (offset + 1);
 
-	
+
 	if(in_len<16) return FALSE;
 	if(out_len) *out_len = 16;
 	*p++ = 0x80;
@@ -659,7 +659,7 @@
 
 	hctx = purple_cipher_context_get_data(context);
 
-	if (!strcmp(name, "hash")) {
+	if (purple_strequal(name, "hash")) {
 		g_free(hctx->name);
 		if (hctx->hash)
 			purple_cipher_context_destroy(hctx->hash);
@@ -676,7 +676,7 @@
 
 	hctx = purple_cipher_context_get_data(context);
 
-	if (!strcmp(name, "hash")) {
+	if (purple_strequal(name, "hash")) {
 		return hctx->name;
 	}
 
@@ -684,7 +684,7 @@
 }
 
 static void
-hmac_append(PurpleCipherContext *context, const guchar *data, size_t len) 
+hmac_append(PurpleCipherContext *context, const guchar *data, size_t len)
 {
 	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
 
@@ -778,7 +778,7 @@
 	hmac_set_key_with_len(context, key, strlen((char *)key));
 }
 
-static size_t 
+static size_t
 hmac_get_block_size(PurpleCipherContext *context)
 {
 	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
@@ -1022,11 +1022,11 @@
  *			  16 encryption rounds.
  *			  To calculate subkeys for decryption the caller
  *    			  have to reorder the generated subkeys.
- *     
+ *
  *        rawkey:	    8 Bytes of key data
  *        subkey:	    Array of at least 32 guint32s. Will be filled
  *    		    with calculated subkeys.
- *     
+ *
  **/
 static void
 des_key_schedule (const guint8 * rawkey, guint32 * subkey)
@@ -1186,7 +1186,7 @@
 				buf,
 				output+offset,
 				0);
-	}	
+	}
 	return 0;
 }
 
@@ -1216,7 +1216,7 @@
 				buf,
 				output+offset,
 				1);
-	}	
+	}
 	return 0;
 }
 
@@ -1692,11 +1692,11 @@
 
 	ctx = purple_cipher_context_get_data(context);
 
-	if(!strcmp(name, "sizeHi")) {
+	if(purple_strequal(name, "sizeHi")) {
 		ctx->sizeHi = GPOINTER_TO_INT(value);
-	} else if(!strcmp(name, "sizeLo")) {
+	} else if(purple_strequal(name, "sizeLo")) {
 		ctx->sizeLo = GPOINTER_TO_INT(value);
-	} else if(!strcmp(name, "lenW")) {
+	} else if(purple_strequal(name, "lenW")) {
 		ctx->lenW = GPOINTER_TO_INT(value);
 	}
 }
@@ -1707,11 +1707,11 @@
 
 	ctx = purple_cipher_context_get_data(context);
 
-	if(!strcmp(name, "sizeHi")) {
+	if(purple_strequal(name, "sizeHi")) {
 		return GINT_TO_POINTER(ctx->sizeHi);
-	} else if(!strcmp(name, "sizeLo")) {
+	} else if(purple_strequal(name, "sizeLo")) {
 		return GINT_TO_POINTER(ctx->sizeLo);
-	} else if(!strcmp(name, "lenW")) {
+	} else if(purple_strequal(name, "lenW")) {
 		return GINT_TO_POINTER(ctx->lenW);
 	}
 
@@ -1942,12 +1942,12 @@
 
 	ctx = purple_cipher_context_get_data(context);
 
-	if(!strcmp(name, "key_len")) {
+	if(purple_strequal(name, "key_len")) {
 		ctx->key_len = GPOINTER_TO_INT(value);
 	}
 }
 
-static size_t 
+static size_t
 rc4_get_key_size (PurpleCipherContext *context)
 {
 	struct RC4Context *ctx;
@@ -1967,7 +1967,7 @@
 
 	ctx = purple_cipher_context_get_data(context);
 
-	if(!strcmp(name, "key_len")) {
+	if(purple_strequal(name, "key_len")) {
 		return GINT_TO_POINTER(ctx->key_len);
 	}
 
--- a/libpurple/cipher.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/cipher.h	Sat Mar 07 01:59:40 2009 +0000
@@ -422,7 +422,7 @@
 size_t purple_cipher_context_get_block_size(PurpleCipherContext *context);
 
 /**
- * Sets the key with a given length on a context 
+ * Sets the key with a given length on a context
  *
  * @param context The context whose key to set
  * @param key     The key
--- a/libpurple/circbuffer.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/circbuffer.c	Sat Mar 07 01:59:40 2009 +0000
@@ -44,7 +44,7 @@
 static void grow_circ_buffer(PurpleCircBuffer *buf, gsize len) {
 	int in_offset = 0, out_offset = 0;
 	int start_buflen;
-	
+
 	g_return_if_fail(buf != NULL);
 
 	start_buflen = buf->buflen;
@@ -94,7 +94,7 @@
 	int len_stored;
 
 	g_return_if_fail(buf != NULL);
-	
+
 	/* Grow the buffer, if necessary */
 	if ((buf->buflen - buf->bufused) < len)
 		grow_circ_buffer(buf, len);
--- a/libpurple/cmds.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/cmds.c	Sat Mar 07 01:59:40 2009 +0000
@@ -236,7 +236,7 @@
 	for (l = cmds; l; l = l->next) {
 		c = l->data;
 
-		if (strcmp(c->cmd, cmd) != 0)
+		if (!purple_strequal(c->cmd, cmd))
 			continue;
 
 		found = TRUE;
@@ -250,8 +250,8 @@
 
 		right_type = TRUE;
 
-		if ((c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) && c->prpl_id &&
-		    (strcmp(c->prpl_id, prpl_id) != 0))
+		if ((c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) &&
+		    !purple_strequal(c->prpl_id, prpl_id))
 			continue;
 
 		right_prpl = TRUE;
@@ -320,8 +320,8 @@
 			if (!(c->flags & PURPLE_CMD_FLAG_CHAT))
 				continue;
 
-		if (conv && (c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) && c->prpl_id &&
-		    (strcmp(c->prpl_id, purple_account_get_protocol_id(purple_conversation_get_account(conv))) != 0))
+		if (conv && (c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) &&
+		    !purple_strequal(c->prpl_id, purple_account_get_protocol_id(purple_conversation_get_account(conv))))
 			continue;
 
 		ret = g_list_append(ret, c->cmd);
@@ -342,7 +342,7 @@
 	for (l = cmds; l; l = l->next) {
 		c = l->data;
 
-		if (cmd && (strcmp(cmd, c->cmd) != 0))
+		if (cmd && !purple_strequal(cmd, c->cmd))
 			continue;
 
 		if (conv && (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM))
@@ -352,8 +352,8 @@
 			if (!(c->flags & PURPLE_CMD_FLAG_CHAT))
 				continue;
 
-		if (conv && (c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) && c->prpl_id &&
-		    (strcmp(c->prpl_id, purple_account_get_protocol_id(purple_conversation_get_account(conv))) != 0))
+		if (conv && (c->flags & PURPLE_CMD_FLAG_PRPL_ONLY) &&
+		    !purple_strequal(c->prpl_id, purple_account_get_protocol_id(purple_conversation_get_account(conv))))
 			continue;
 
 		ret = g_list_append(ret, c->help);
--- a/libpurple/connection.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/connection.c	Sat Mar 07 01:59:40 2009 +0000
@@ -191,16 +191,16 @@
 	PurpleConnection *gc;
 	PurplePlugin *prpl;
 	PurplePluginProtocolInfo *prpl_info;
-	
+
 	g_return_if_fail(account != NULL);
-		
+
 	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
-	
+
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 	else {
 		gchar *message;
-		
+
 		message = g_strdup_printf(_("Missing protocol plugin for %s"),
 								  purple_account_get_username(account));
 		purple_notify_error(NULL, _("Unregistration Error"), message, NULL);
@@ -212,7 +212,7 @@
 		prpl_info->unregister_user(account, cb, user_data);
 		return;
 	}
-	
+
 	if (((password == NULL) || (*password == '\0')) &&
 		!(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
 		!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
@@ -221,10 +221,10 @@
 						   "a password.\n", purple_account_get_username(account));
 		return;
 	}
-	
+
 	gc = g_new0(PurpleConnection, 1);
 	PURPLE_DBUS_REGISTER_POINTER(gc, PurpleConnection);
-	
+
 	gc->prpl = prpl;
 	if ((password != NULL) && (*password != '\0'))
 		gc->password = g_strdup(password);
@@ -232,11 +232,11 @@
 	purple_connection_set_state(gc, PURPLE_CONNECTING);
 	connections = g_list_append(connections, gc);
 	purple_account_set_connection(account, gc);
-	
+
 	purple_signal_emit(purple_connections_get_handle(), "signing-on", gc);
-	
+
 	purple_debug_info("connection", "Unregistering.  gc = %p\n", gc);
-	
+
 	prpl_info->unregister_user(account, cb, user_data);
 }
 
@@ -285,7 +285,7 @@
 	buddies = purple_find_buddies(account, NULL);
 	while (buddies != NULL) {
 		PurpleBuddy *buddy = buddies->data;
-		buddy->proto_data = NULL;
+		purple_buddy_set_protocol_data(buddy, NULL);
 		buddies = g_slist_delete_link(buddies, buddies);
 	}
 
@@ -427,6 +427,13 @@
 	gc->display_name = g_strdup(name);
 }
 
+void
+purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data) {
+	g_return_if_fail(connection != NULL);
+
+	connection->proto_data = proto_data;
+}
+
 PurpleConnectionState
 purple_connection_get_state(const PurpleConnection *gc)
 {
@@ -467,6 +474,13 @@
 	return gc->display_name;
 }
 
+void *
+purple_connection_get_protocol_data(const PurpleConnection *connection) {
+	g_return_val_if_fail(connection != NULL, NULL);
+
+	return connection->proto_data;
+}
+
 void
 purple_connection_update_progress(PurpleConnection *gc, const char *text,
 								size_t step, size_t count)
--- a/libpurple/connection.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/connection.h	Sat Mar 07 01:59:40 2009 +0000
@@ -42,7 +42,7 @@
 	PURPLE_CONNECTION_FORMATTING_WBFO = 0x0008, /**< The text buffer must be formatted as a whole */
 	PURPLE_CONNECTION_NO_NEWLINES = 0x0010, /**< No new lines are allowed in outgoing messages */
 	PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */
-	PURPLE_CONNECTION_NO_URLDESC = 0x0040,  /**< Connection does not support descriptions with links */ 
+	PURPLE_CONNECTION_NO_URLDESC = 0x0040,  /**< Connection does not support descriptions with links */
 	PURPLE_CONNECTION_NO_IMAGES = 0x0080,  /**< Connection does not support sending of images */
 	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100 /**< Connection supports sending and receiving custom smileys */
 
@@ -56,7 +56,9 @@
 
 } PurpleConnectionState;
 
-/** Possible errors that can cause a connection to be closed.
+/**
+ * Possible errors that can cause a connection to be closed.
+ *
  *  @since 2.3.0
  */
 typedef enum
@@ -70,7 +72,7 @@
 	PURPLE_CONNECTION_ERROR_INVALID_USERNAME = 1,
 	/** The username, password or some other credential was incorrect.  Use
 	 *  #PURPLE_CONNECTION_ERROR_INVALID_USERNAME instead if the username
-         *  is known to be invalid.
+	 *  is known to be invalid.
 	 */
 	PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED = 2,
 	/** libpurple doesn't speak any of the authentication methods the
@@ -92,7 +94,7 @@
 	PURPLE_CONNECTION_ERROR_NAME_IN_USE = 6,
 
 	/** The username/server/other preference for the account isn't valid.
-	 *  For instance, on IRC the screen name cannot contain white space.
+	 *  For instance, on IRC the username cannot contain white space.
 	 *  This reason should not be used for incorrect passwords etc: use
 	 *  #PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED for that.
 	 *
@@ -148,71 +150,84 @@
 #include "status.h"
 #include "sslconn.h"
 
-/** Connection UI operations.  Used to notify the user of changes to
- *  connections, such as being disconnected, and to respond to the
- *  underlying network connection appearing and disappearing.  UIs should
- *  call #purple_connections_set_ui_ops() with an instance of this struct.
+/**
+ * Connection UI operations.  Used to notify the user of changes to
+ * connections, such as being disconnected, and to respond to the
+ * underlying network connection appearing and disappearing.  UIs should
+ * call #purple_connections_set_ui_ops() with an instance of this struct.
  *
- *  @see @ref ui-ops
+ * @see @ref ui-ops
  */
 typedef struct
 {
-	/** When an account is connecting, this operation is called to notify
-	 *  the UI of what is happening, as well as which @a step out of @a
-	 *  step_count has been reached (which might be displayed as a progress
-	 *  bar).
-	 *  @see #purple_connection_update_progress
+	/**
+	 * When an account is connecting, this operation is called to notify
+	 * the UI of what is happening, as well as which @a step out of @a
+	 * step_count has been reached (which might be displayed as a progress
+	 * bar).
+	 * @see #purple_connection_update_progress
 	 */
 	void (*connect_progress)(PurpleConnection *gc,
 	                         const char *text,
 	                         size_t step,
 	                         size_t step_count);
 
-	/** Called when a connection is established (just before the
-	 *  @ref signed-on signal).
+	/**
+	 * Called when a connection is established (just before the
+	 * @ref signed-on signal).
 	 */
 	void (*connected)(PurpleConnection *gc);
-	/** Called when a connection is ended (between the @ref signing-off
-	 *  and @ref signed-off signals).
+
+	/**
+	 * Called when a connection is ended (between the @ref signing-off
+	 * and @ref signed-off signals).
 	 */
 	void (*disconnected)(PurpleConnection *gc);
 
-	/** Used to display connection-specific notices.  (Pidgin's Gtk user
-	 *  interface implements this as a no-op; #purple_connection_notice(),
-	 *  which uses this operation, is not used by any of the protocols
-	 *  shipped with libpurple.)
+	/**
+	 * Used to display connection-specific notices.  (Pidgin's Gtk user
+	 * interface implements this as a no-op; #purple_connection_notice(),
+	 * which uses this operation, is not used by any of the protocols
+	 * shipped with libpurple.)
 	 */
 	void (*notice)(PurpleConnection *gc, const char *text);
 
-	/** Called when an error causes a connection to be disconnected.
-	 *  Called before #disconnected.
-	 *  @param text  a localized error message.
-	 *  @see #purple_connection_error
-	 *  @deprecated in favour of
-	 *              #PurpleConnectionUiOps.report_disconnect_reason.
+	/**
+	 * Called when an error causes a connection to be disconnected.
+	 * Called before #disconnected.
+	 * @param text  a localized error message.
+	 * @see #purple_connection_error
+	 * @deprecated in favour of
+	 *             #PurpleConnectionUiOps.report_disconnect_reason.
 	 */
 	void (*report_disconnect)(PurpleConnection *gc, const char *text);
 
-	/** Called when libpurple discovers that the computer's network
-	 *  connection is active.  On Linux, this uses Network Manager if
-	 *  available; on Windows, it uses Win32's network change notification
-	 *  infrastructure.
+	/**
+	 * Called when libpurple discovers that the computer's network
+	 * connection is active.  On Linux, this uses Network Manager if
+	 * available; on Windows, it uses Win32's network change notification
+	 * infrastructure.
 	 */
 	void (*network_connected)(void);
-	/** Called when libpurple discovers that the computer's network
-	 *  connection has gone away.
+
+	/**
+	 * Called when libpurple discovers that the computer's network
+	 * connection has gone away.
 	 */
 	void (*network_disconnected)(void);
 
-	/** Called when an error causes a connection to be disconnected.
+	/**
+	 * Called when an error causes a connection to be disconnected.
 	 *  Called before #disconnected.  This op is intended to replace
 	 *  #report_disconnect.  If both are implemented, this will be called
 	 *  first; however, there's no real reason to implement both.
+	 *
 	 *  @param reason  why the connection ended, if known, or
 	 *                 #PURPLE_CONNECTION_ERROR_OTHER_ERROR, if not.
 	 *  @param text  a localized message describing the disconnection
 	 *               in more detail to the user.
 	 *  @see #purple_connection_error_reason
+	 *
 	 *  @since 2.3.0
 	 */
 	void (*report_disconnect_reason)(PurpleConnection *gc,
@@ -354,6 +369,16 @@
 void purple_connection_set_display_name(PurpleConnection *gc, const char *name);
 
 /**
+ * Sets the protocol data for a connection.
+ *
+ * @param connection The PurpleConnection.
+ * @param proto_data The protocol data to set for the connection.
+ *
+ * @since 2.6.0
+ */
+void purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data);
+
+/**
  * Returns the connection state.
  *
  * @param gc The connection.
@@ -385,6 +410,7 @@
  * @param gc The connection.
  *
  * @return The protocol plugin.
+ *
  * @since 2.4.0
  */
 PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc);
@@ -408,6 +434,17 @@
 const char *purple_connection_get_display_name(const PurpleConnection *gc);
 
 /**
+ * Gets the protocol data from a connection.
+ *
+ * @param connection The PurpleConnection.
+ *
+ * @return The protocol data for the connection.
+ *
+ * @since 2.6.0
+ */
+void *purple_connection_get_protocol_data(const PurpleConnection *connection);
+
+/**
  * Updates the connection progress.
  *
  * @param gc    The connection.
@@ -450,6 +487,7 @@
  * @param gc          the connection which is closing.
  * @param reason      why the connection is closing.
  * @param description a non-@c NULL localized description of the error.
+ *
  * @since 2.3.0
  */
 void
@@ -461,6 +499,7 @@
  * Closes a connection due to an SSL error; this is basically a shortcut to
  * turning the #PurpleSslErrorType into a #PurpleConnectionError and a
  * human-readable string and then calling purple_connection_error_reason().
+ *
  * @since 2.3.0
  */
 void
@@ -484,6 +523,7 @@
  *
  * @return @c TRUE if the account should not be automatically reconnected, and
  *         @c FALSE otherwise.
+ *
  * @since 2.3.0
  */
 gboolean
--- a/libpurple/conversation.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/conversation.c	Sat Mar 07 01:59:40 2009 +0000
@@ -665,7 +665,7 @@
 			text = purple_buddy_get_contact_alias(b);
 	} else if(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		if(account && ((chat = purple_blist_find_chat(account, name)) != NULL))
-			text = chat->alias;
+			text = purple_chat_get_name(chat);
 	}
 
 
@@ -912,7 +912,7 @@
 
 				if (purple_account_get_alias(account) != NULL)
 					alias = account->alias;
-				else if (b != NULL && strcmp(b->name, purple_buddy_get_contact_alias(b)))
+				else if (b != NULL && !purple_strequal(purple_buddy_get_name(b), purple_buddy_get_contact_alias(b)))
 					alias = purple_buddy_get_contact_alias(b);
 				else if (purple_connection_get_display_name(gc) != NULL)
 					alias = purple_connection_get_display_name(gc);
@@ -1480,7 +1480,7 @@
 
 		str = g_strdup(purple_normalize(account, who));
 
-		if (!strcmp(str, purple_normalize(account, chat->nick))) {
+		if (purple_strequal(str, purple_normalize(account, chat->nick))) {
 			flags |= PURPLE_MESSAGE_SEND;
 		} else {
 			flags |= PURPLE_MESSAGE_RECV;
@@ -1601,7 +1601,7 @@
 		const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
 
 		if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
-			if (!strcmp(chat->nick, purple_normalize(conv->account, user))) {
+			if (purple_strequal(chat->nick, purple_normalize(conv->account, user))) {
 				const char *alias2 = purple_account_get_alias(conv->account);
 				if (alias2 != NULL)
 					alias = alias2;
@@ -1692,7 +1692,7 @@
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
 	g_return_if_fail(prpl_info != NULL);
 
-	if (!strcmp(chat->nick, purple_normalize(conv->account, old_user))) {
+	if (purple_strequal(chat->nick, purple_normalize(conv->account, old_user))) {
 		const char *alias;
 
 		/* Note this for later. */
--- a/libpurple/conversation.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/conversation.h	Sat Mar 07 01:59:40 2009 +0000
@@ -1350,7 +1350,7 @@
  * Retrieves the extended menu items for the conversation.
  *
  * @param conv The conversation.
- * 
+ *
  * @return  A list of PurpleMenuAction items, harvested by the
  *          chat-extended-menu signal. The list and the menuaction
  *          items should be freed by the caller.
--- a/libpurple/core.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/core.c	Sat Mar 07 01:59:40 2009 +0000
@@ -46,9 +46,11 @@
 #include "signals.h"
 #include "smiley.h"
 #include "sound.h"
+#include "sound-theme-loader.h"
 #include "sslconn.h"
 #include "status.h"
 #include "stun.h"
+#include "theme-manager.h"
 #include "util.h"
 
 #ifdef HAVE_DBUS
@@ -143,6 +145,8 @@
 
 	purple_plugins_probe(G_MODULE_SUFFIX);
 
+	purple_theme_manager_init();
+
 	/* The buddy icon code uses the imgstore, so init it early. */
 	purple_imgstore_init();
 
@@ -171,7 +175,7 @@
 	purple_xfers_init();
 	purple_idle_init();
 	purple_smileys_init();
-
+	purple_theme_manager_init();
 	/*
 	 * Call this early on to try to auto-detect our IP address and
 	 * hopefully save some time later.
@@ -181,6 +185,9 @@
 	if (ops != NULL && ops->ui_init != NULL)
 		ops->ui_init();
 
+	/* The UI may have registered some theme types, so refresh them */
+	purple_theme_manager_refresh();
+
 	return TRUE;
 }
 
@@ -233,6 +240,7 @@
 	purple_savedstatuses_uninit();
 	purple_status_uninit();
 	purple_sound_uninit();
+	purple_theme_manager_uninit();
 	purple_xfers_uninit();
 	purple_proxy_uninit();
 	purple_dnsquery_uninit();
@@ -350,15 +358,7 @@
 			const char *user_dir = purple_user_dir();
 			char *dbus_owner_user_dir = purple_dbus_owner_user_dir();
 
-			if (NULL == user_dir && NULL != dbus_owner_user_dir)
-				is_single_instance = TRUE;
-			else if (NULL != user_dir && NULL == dbus_owner_user_dir)
-				is_single_instance = TRUE;
-			else if (NULL == user_dir && NULL == dbus_owner_user_dir)
-				is_single_instance = FALSE;
-			else
-				is_single_instance = strcmp(dbus_owner_user_dir, user_dir);
-
+			is_single_instance = !purple_strequal(dbus_owner_user_dir, user_dir);
 			g_free(dbus_owner_user_dir);
 		}
 	}
@@ -489,7 +489,7 @@
 		if (g_file_test(name, G_FILE_TEST_IS_SYMLINK))
 		{
 			/* We're only going to duplicate a logs symlink. */
-			if (!strcmp(entry, "logs"))
+			if (purple_strequal(entry, "logs"))
 			{
 				char *link;
 #if GLIB_CHECK_VERSION(2,4,0)
@@ -532,7 +532,8 @@
 
 				logs_dir = g_build_filename(user_dir, "logs", NULL);
 
-				if (!strcmp(link, "../.purple/logs") || !strcmp(link, logs_dir))
+				if (purple_strequal(link, "../.purple/logs") ||
+				    purple_strequal(link, logs_dir))
 				{
 					/* If the symlink points to the new directory, we're
 					 * likely just trying again after a failed migration,
@@ -577,7 +578,7 @@
 		/* Deal with directories... */
 		if (g_file_test(name, G_FILE_TEST_IS_DIR))
 		{
-			if (!strcmp(entry, "icons"))
+			if (purple_strequal(entry, "icons"))
 			{
 				/* This is a special case for the Album plugin, which
 				 * stores data in the icons folder.  We're not copying
@@ -646,7 +647,7 @@
 
 				g_dir_close(icons_dir);
 			}
-			else if (!strcmp(entry, "plugins"))
+			else if (purple_strequal(entry, "plugins"))
 			{
 				/* Do nothing, because we broke plugin compatibility.
 				 * This means that the plugins directory gets left behind. */
--- a/libpurple/core.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/core.h	Sat Mar 07 01:59:40 2009 +0000
@@ -90,17 +90,17 @@
 
 /**
  * <p>
- * 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 
+ * 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 instead of purple_core_quit();
- * for an immediate exit, use a timeout value of 0: 
+ * for an immediate exit, use a timeout value of 0:
  * </p>
  *
  * <code>purple_timeout_add(0, purple_core_quitcb, NULL);</code>
  *
  * <p>
- * This is ensures that code from your plugin is not being 
+ * This is ensures that code from your plugin is not being
  * 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
--- a/libpurple/dbus-bindings.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/dbus-bindings.h	Sat Mar 07 01:59:40 2009 +0000
@@ -84,7 +84,7 @@
 					int              first_arg_type,
 					va_list          var_args);
 
-dbus_int32_t* purple_dbusify_GList(GList *list, gboolean free_memory, 
+dbus_int32_t* purple_dbusify_GList(GList *list, gboolean free_memory,
 				 dbus_int32_t *len);
 dbus_int32_t* purple_dbusify_GSList(GSList *list, gboolean free_memory,
 				  dbus_int32_t *len);
--- a/libpurple/dbus-server.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/dbus-server.h	Sat Mar 07 01:59:40 2009 +0000
@@ -173,7 +173,7 @@
 
 /**
  * Determines whether this instance owns the DBus service name
- * 
+ *
  * @since 2.1.0
  */
 gboolean purple_dbus_is_owner(void);
--- a/libpurple/desktopitem.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/desktopitem.c	Sat Mar 07 01:59:40 2009 +0000
@@ -41,12 +41,12 @@
  * modify it under the terms of the GNU Library General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
- * 
+ *
  * The Gnome Library 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
  * Library General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Library General Public
  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
  * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
@@ -67,7 +67,7 @@
 	GList *languages;
 
 	PurpleDesktopItemType type;
-	
+
 	/* `modified' means that the ditem has been
 	 * modified since the last save. */
 	gboolean modified;
@@ -108,30 +108,30 @@
 
 	switch (type [0]) {
 	case 'A':
-		if (!strcmp (type, "Application"))
+		if (purple_strequal (type, "Application"))
 			return PURPLE_DESKTOP_ITEM_TYPE_APPLICATION;
 		break;
 	case 'L':
-		if (!strcmp (type, "Link"))
+		if (purple_strequal (type, "Link"))
 			return PURPLE_DESKTOP_ITEM_TYPE_LINK;
 		break;
 	case 'F':
-		if (!strcmp (type, "FSDevice"))
+		if (purple_strequal (type, "FSDevice"))
 			return PURPLE_DESKTOP_ITEM_TYPE_FSDEVICE;
 		break;
 	case 'M':
-		if (!strcmp (type, "MimeType"))
+		if (purple_strequal (type, "MimeType"))
 			return PURPLE_DESKTOP_ITEM_TYPE_MIME_TYPE;
 		break;
 	case 'D':
-		if (!strcmp (type, "Directory"))
+		if (purple_strequal (type, "Directory"))
 			return PURPLE_DESKTOP_ITEM_TYPE_DIRECTORY;
 		break;
 	case 'S':
-		if (!strcmp (type, "Service"))
+		if (purple_strequal (type, "Service"))
 			return PURPLE_DESKTOP_ITEM_TYPE_SERVICE;
 
-		else if (!strcmp (type, "ServiceType"))
+		else if (purple_strequal (type, "ServiceType"))
 			return PURPLE_DESKTOP_ITEM_TYPE_SERVICE_TYPE;
 		break;
 	default:
@@ -149,12 +149,12 @@
 
 	if (section == NULL)
 		return NULL;
-	if (strcmp (section, "Desktop Entry") == 0)
+	if (purple_strequal (section, "Desktop Entry"))
 		return NULL;
 
 	for (li = item->sections; li != NULL; li = li->next) {
 		sec = li->data;
-		if (strcmp (sec->name, section) == 0)
+		if (purple_strequal (sec->name, section))
 			return sec;
 	}
 
@@ -235,7 +235,7 @@
 				item->keys = g_list_append (item->keys,
 							    g_strdup (key));
 
-			g_hash_table_replace (item->main_hash, 
+			g_hash_table_replace (item->main_hash,
 					      g_strdup (key),
 					      g_strdup (value));
 		} else {
@@ -264,7 +264,7 @@
 
 	set (item, attr, value);
 
-	if (strcmp (attr, PURPLE_DESKTOP_ITEM_TYPE) == 0)
+	if (purple_strequal (attr, PURPLE_DESKTOP_ITEM_TYPE))
 		item->type = type_from_string (value);
 }
 
@@ -280,7 +280,7 @@
 	retval->main_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
 						   (GDestroyNotify) g_free,
 						   (GDestroyNotify) g_free);
-	
+
 	/* These are guaranteed to be set */
 	_purple_desktop_item_set_string (retval,
 				       PURPLE_DESKTOP_ITEM_NAME,
@@ -354,16 +354,16 @@
 			p++;
 			if (*p == ' ')
 				p++;
-			if (strcmp (p, "UTF-8") == 0) {
+			if (purple_strequal (p, "UTF-8")) {
 				return ENCODING_UTF8;
-			} else if (strcmp (p, "Legacy-Mixed") == 0) {
+			} else if (purple_strequal (p, "Legacy-Mixed")) {
 				return ENCODING_LEGACY_MIXED;
 			} else {
 				/* According to the spec we're not supposed
 				 * to read a file like this */
 				return ENCODING_UNKNOWN;
 			}
-		} else if (strcmp ("[KDE Desktop Entry]", buf) == 0) {
+		} else if (purple_strequal ("[KDE Desktop Entry]", buf)) {
 			old_kde = TRUE;
 			/* don't break yet, we still want to support
 			 * Encoding even here */
@@ -557,7 +557,7 @@
 		char *utf8_string;
 		if (char_encoding == NULL)
 			return NULL;
-		if (strcmp (char_encoding, "ASCII") == 0) {
+		if (purple_strequal (char_encoding, "ASCII")) {
 			return decode_string_and_dup (value);
 		}
 		utf8_string = g_convert (value, -1, "UTF-8", char_encoding,
@@ -673,7 +673,7 @@
 	char *val;
 	/* we always store everything in UTF-8 */
 	if (cur_section == NULL &&
-	    strcmp (key, PURPLE_DESKTOP_ITEM_ENCODING) == 0) {
+	    purple_strequal (key, PURPLE_DESKTOP_ITEM_ENCODING)) {
 		k = g_strdup (key);
 		val = g_strdup ("UTF-8");
 	} else {
@@ -690,14 +690,14 @@
 			g_free (locale);
 			return;
 		}
-		
+
 		g_strchomp (val);
 
 		/* For old KDE entries, we can also split by a comma
 		 * on sort order, so convert to semicolons */
 		if (old_kde &&
 		    cur_section == NULL &&
-		    strcmp (key, PURPLE_DESKTOP_ITEM_SORT_ORDER) == 0 &&
+		    purple_strequal (key, PURPLE_DESKTOP_ITEM_SORT_ORDER) &&
 		    strchr (val, ';') == NULL) {
 			int i;
 			for (i = 0; val[i] != '\0'; i++) {
@@ -720,7 +720,7 @@
 
 		/* Take care of the language part */
 		if (locale != NULL &&
-		    strcmp (locale, "C") == 0) {
+		    purple_strequal (locale, "C")) {
 			char *p;
 			/* Whack C locale */
 			p = strchr (k, '[');
@@ -791,11 +791,10 @@
 						PURPLE_DESKTOP_ITEM_TYPE);
 	if (type == NULL && uri != NULL) {
 		char *base = g_path_get_basename (uri);
-		if (base != NULL &&
-		    strcmp (base, ".directory") == 0) {
+		if (purple_strequal(base, ".directory")) {
 			/* This gotta be a directory */
 			g_hash_table_replace (item->main_hash,
-					      g_strdup (PURPLE_DESKTOP_ITEM_TYPE), 
+					      g_strdup (PURPLE_DESKTOP_ITEM_TYPE),
 					      g_strdup ("Directory"));
 			item->keys = g_list_prepend
 				(item->keys, g_strdup (PURPLE_DESKTOP_ITEM_TYPE));
@@ -813,7 +812,7 @@
 lookup_locale (const PurpleDesktopItem *item, const char *key, const char *locale)
 {
 	if (locale == NULL ||
-	    strcmp (locale, "C") == 0) {
+	    purple_strequal (locale, "C")) {
 		return lookup (item, key);
 	} else {
 		const char *ret;
@@ -857,7 +856,7 @@
 	type = lookup (item, PURPLE_DESKTOP_ITEM_TYPE);
 
 	/* understand old gnome style url exec thingies */
-	if (type != NULL && strcmp (type, "URL") == 0) {
+	if (purple_strequal(type, "URL")) {
 		const char *exec = lookup (item, PURPLE_DESKTOP_ITEM_EXEC);
 		set (item, PURPLE_DESKTOP_ITEM_TYPE, "Link");
 		if (exec != NULL) {
@@ -877,7 +876,7 @@
 		if (name == NULL)
 			name = g_strdup (_("No name"));
 		g_hash_table_replace (item->main_hash,
-				      g_strdup (PURPLE_DESKTOP_ITEM_NAME), 
+				      g_strdup (PURPLE_DESKTOP_ITEM_NAME),
 				      name);
 		item->keys = g_list_prepend
 			(item->keys, g_strdup (PURPLE_DESKTOP_ITEM_NAME));
@@ -885,7 +884,7 @@
 	if (lookup (item, PURPLE_DESKTOP_ITEM_ENCODING) == NULL) {
 		/* We store everything in UTF-8 so write that down */
 		g_hash_table_replace (item->main_hash,
-				      g_strdup (PURPLE_DESKTOP_ITEM_ENCODING), 
+				      g_strdup (PURPLE_DESKTOP_ITEM_ENCODING),
 				      g_strdup ("UTF-8"));
 		item->keys = g_list_prepend
 			(item->keys, g_strdup (PURPLE_DESKTOP_ITEM_ENCODING));
@@ -893,7 +892,7 @@
 	if (lookup (item, PURPLE_DESKTOP_ITEM_VERSION) == NULL) {
 		/* this is the version that we follow, so write it down */
 		g_hash_table_replace (item->main_hash,
-				      g_strdup (PURPLE_DESKTOP_ITEM_VERSION), 
+				      g_strdup (PURPLE_DESKTOP_ITEM_VERSION),
 				      g_strdup ("1.0"));
 		item->keys = g_list_prepend
 			(item->keys, g_strdup (PURPLE_DESKTOP_ITEM_VERSION));
@@ -954,7 +953,7 @@
 	while ((c = getc (df)) != EOF) {
 		if (c == '\r')		/* Ignore Carriage Return */
 			continue;
-		
+
 		switch (state) {
 
 		case OnSecHeader:
@@ -968,13 +967,11 @@
 					cur_section->keys = g_list_reverse
 						(cur_section->keys);
 				}
-				if (strcmp (CharBuffer,
-					    "KDE Desktop Entry") == 0) {
+				if (purple_strequal (CharBuffer, "KDE Desktop Entry")) {
 					/* Main section */
 					cur_section = NULL;
 					old_kde = TRUE;
-				} else if (strcmp (CharBuffer,
-						   "Desktop Entry") == 0) {
+				} else if (purple_strequal(CharBuffer, "Desktop Entry")) {
 					/* Main section */
 					cur_section = NULL;
 				} else {
@@ -1025,16 +1022,16 @@
 			/* On first pass, don't allow dangling keys */
 			if (state == FirstBrace)
 				break;
-	    
+
 			if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
 				break;
-	    
+
 			if (c == '\n' || PURPLE_DESKTOP_ITEM_OVERFLOW) { /* Abort Definition */
 				next = CharBuffer;
 				state = KeyDef;
 				break;
 			}
-	    
+
 			if (c == '=' || PURPLE_DESKTOP_ITEM_OVERFLOW){
 				*next = '\0';
 
@@ -1067,7 +1064,7 @@
 			break;
 
 		} /* switch */
-	
+
 	} /* while ((c = getc_unlocked (f)) != EOF) */
 	if (c == EOF && state == KeyValue) {
 		*next = '\0';
@@ -1158,7 +1155,7 @@
 		printf ("Can't open %s: %s", filename, g_strerror(errno));
 		return NULL;
 	}
-	
+
 	retval = ditem_load(dfile, FALSE, filename);
 
 	return retval;
@@ -1203,7 +1200,7 @@
 	/* Languages */
 	retval->languages = g_list_copy (item->languages);
 	for (li = retval->languages; li != NULL; li = li->next)
-		li->data = g_strdup (li->data);	
+		li->data = g_strdup (li->data);
 
 	/* Keys */
 	retval->keys = g_list_copy (item->keys);
--- a/libpurple/desktopitem.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/desktopitem.h	Sat Mar 07 01:59:40 2009 +0000
@@ -41,12 +41,12 @@
  * modify it under the terms of the GNU Library General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
- * 
+ *
  * The Gnome Library 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
  * Library General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Library General Public
  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
  * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
@@ -155,7 +155,7 @@
  *
  * @param item The item to be copied
  *
- * @return The new copy 
+ * @return The new copy
  */
 PurpleDesktopItem *purple_desktop_item_copy (const PurpleDesktopItem *item);
 
--- a/libpurple/eventloop.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/eventloop.h	Sat Mar 07 01:59:40 2009 +0000
@@ -119,8 +119,8 @@
 	 * @see purple_input_remove
 	 */
 	gboolean (*input_remove)(guint handle);
-	
-	
+
+
 	/**
 	 * If implemented, should get the current error status for an input.
 	 *
@@ -161,7 +161,7 @@
 /*@{*/
 /**
  * Creates a callback timer.
- * 
+ *
  * The timer will repeat until the function returns @c FALSE. The
  * first call will be at the end of the first interval.
  *
@@ -185,12 +185,12 @@
  *
  * This function allows UIs to group timers for better power efficiency.  For
  * this reason, @a interval may be rounded by up to a second.
- * 
+ *
  * @param interval	The time between calls of the function, in
  *                      seconds.
  * @param function	The function to call.
  * @param data		data to pass to @a function.
- * @return A handle to the timer which can be passed to 
+ * @return A handle to the timer which can be passed to
  *         purple_timeout_remove() to remove the timer.
  *
  * @since 2.1.0
--- a/libpurple/idle.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/idle.c	Sat Mar 07 01:59:40 2009 +0000
@@ -126,14 +126,14 @@
 	idle_reporting = purple_prefs_get_string("/purple/away/idle_reporting");
 	auto_away = purple_prefs_get_bool("/purple/away/away_when_idle");
 
-	if (!strcmp(idle_reporting, "system") &&
+	if (purple_strequal(idle_reporting, "system") &&
 		(idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL))
 	{
 		/* Use system idle time (mouse or keyboard movement, etc.) */
 		time_idle = idle_ui_ops->get_time_idle();
 		idle_recheck_interval = 1;
 	}
-	else if (!strcmp(idle_reporting, "purple"))
+	else if (purple_strequal(idle_reporting, "purple"))
 	{
 		/* Use 'Purple idle' */
 		time_idle = time(NULL) - last_active_time;
@@ -214,7 +214,7 @@
 
 
 /*
- * Check idle and set the timer to fire at the next idle-worth event 
+ * Check idle and set the timer to fire at the next idle-worth event
  */
 static gboolean
 check_idleness_timer(void)
--- a/libpurple/internal.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/internal.h	Sat Mar 07 01:59:40 2009 +0000
@@ -102,7 +102,7 @@
 #include <gmodule.h>
 
 #ifdef PURPLE_PLUGINS
-# ifdef HAVE_DLFCN_H 
+# ifdef HAVE_DLFCN_H
 #  include <dlfcn.h>
 # endif
 #endif
--- a/libpurple/log.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/log.c	Sat Mar 07 01:59:40 2009 +0000
@@ -34,6 +34,7 @@
 #include "util.h"
 #include "stringref.h"
 #include "imgstore.h"
+#include "time.h"
 
 static GSList *loggers = NULL;
 
@@ -46,6 +47,7 @@
 	PurpleAccount *account;
 };
 static GHashTable *logsize_users = NULL;
+static GHashTable *logsize_users_decayed = NULL;
 
 static void log_get_log_sets_common(GHashTable *sets);
 
@@ -161,14 +163,27 @@
 	lu->account = log->account;
 
 	if(g_hash_table_lookup_extended(logsize_users, lu, NULL, &ptrsize)) {
+		char *tmp = lu->name;
+
 		total = GPOINTER_TO_INT(ptrsize);
 		total += written;
 		g_hash_table_replace(logsize_users, lu, GINT_TO_POINTER(total));
+
+		/* The hash table takes ownership of lu, so create a new one
+		 * for the logsize_users_decayed check below. */
+		lu = g_new(struct _purple_logsize_user, 1);
+		lu->name = g_strdup(tmp);
+		lu->account = log->account;
+	}
+
+	if(g_hash_table_lookup_extended(logsize_users_decayed, lu, NULL, &ptrsize)) {
+		total = GPOINTER_TO_INT(ptrsize);
+		total += written;
+		g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(total));
 	} else {
 		g_free(lu->name);
 		g_free(lu);
 	}
-
 }
 
 char *purple_log_read(PurpleLog *log, PurpleLogReadFlags *flags)
@@ -200,7 +215,7 @@
 static guint _purple_logsize_user_equal(struct _purple_logsize_user *lu1,
 		struct _purple_logsize_user *lu2)
 {
-	return (lu1->account == lu2->account && (!strcmp(lu1->name, lu2->name)));
+	return (lu1->account == lu2->account && purple_strequal(lu1->name, lu2->name));
 }
 
 static void _purple_logsize_user_free_key(struct _purple_logsize_user *lu)
@@ -250,6 +265,49 @@
 	return size;
 }
 
+gint purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleAccount *account)
+{
+	gpointer ptrscore;
+	int score;
+	GSList *n;
+	struct _purple_logsize_user *lu;
+	time_t now;
+	time(&now);
+
+	lu = g_new(struct _purple_logsize_user, 1);
+	lu->name = g_strdup(purple_normalize(account, name));
+	lu->account = account;
+
+	if(g_hash_table_lookup_extended(logsize_users_decayed, lu, NULL, &ptrscore)) {
+		score = GPOINTER_TO_INT(ptrscore);
+		g_free(lu->name);
+		g_free(lu);
+	} else {
+		double score_double = 0.0;
+		for (n = loggers; n; n = n->next) {
+			PurpleLogLogger *logger = n->data;
+
+			if(logger->list) {
+				GList *logs = (logger->list)(type, name, account);
+
+				while (logs) {
+					PurpleLog *log = (PurpleLog*)(logs->data);
+					/* Activity score counts bytes in the log, exponentially
+					   decayed with a half-life of 14 days. */
+					score_double += purple_log_get_size(log) *
+						pow(0.5, difftime(now, log->time)/1209600.0);
+					purple_log_free(log);
+					logs = g_list_delete_link(logs, logs);
+				}
+			}
+		}
+
+		score = (gint)score_double;
+		g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(score));
+	}
+	return score;
+}
+
 gboolean purple_log_is_deletable(PurpleLog *log)
 {
 	g_return_val_if_fail(log != NULL, FALSE);
@@ -324,7 +382,7 @@
 	GSList *l = loggers;
 	while (l) {
 		logger = l->data;
-		if (!strcmp(logger->id, value)) {
+		if (purple_strequal(logger->id, value)) {
 			purple_log_logger_set(logger);
 			return;
 		}
@@ -406,7 +464,7 @@
 	if (g_slist_find(loggers, logger))
 		return;
 	loggers = g_slist_append(loggers, logger);
-	if (strcmp(purple_prefs_get_string("/purple/logging/format"), logger->id) == 0) {
+	if (purple_strequal(purple_prefs_get_string("/purple/logging/format"), logger->id)) {
 		purple_prefs_trigger_callback("/purple/logging/format");
 	}
 }
@@ -588,11 +646,11 @@
 	void *handle = purple_log_get_handle();
 
 	purple_prefs_add_none("/purple/logging");
-	purple_prefs_add_bool("/purple/logging/log_ims", FALSE);
-	purple_prefs_add_bool("/purple/logging/log_chats", FALSE);
+	purple_prefs_add_bool("/purple/logging/log_ims", TRUE);
+	purple_prefs_add_bool("/purple/logging/log_chats", TRUE);
 	purple_prefs_add_bool("/purple/logging/log_system", FALSE);
 
-	purple_prefs_add_string("/purple/logging/format", "txt");
+	purple_prefs_add_string("/purple/logging/format", "html");
 
 	html_logger = purple_log_logger_new("html", _("HTML"), 11,
 									  NULL,
@@ -661,6 +719,9 @@
 	logsize_users = g_hash_table_new_full((GHashFunc)_purple_logsize_user_hash,
 			(GEqualFunc)_purple_logsize_user_equal,
 			(GDestroyNotify)_purple_logsize_user_free_key, NULL);
+	logsize_users_decayed = g_hash_table_new_full((GHashFunc)_purple_logsize_user_hash,
+				(GEqualFunc)_purple_logsize_user_equal,
+				(GDestroyNotify)_purple_logsize_user_free_key, NULL);
 }
 
 void
@@ -679,6 +740,9 @@
 	purple_log_logger_remove(old_logger);
 	purple_log_logger_free(old_logger);
 	old_logger = NULL;
+
+	g_hash_table_destroy(logsize_users);
+	g_hash_table_destroy(logsize_users_decayed);
 }
 
 /****************************************************************************
@@ -1019,7 +1083,7 @@
 				continue;
 			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-			if (!strcmp(protocol_unescaped, prpl_info->list_icon((PurpleAccount *)account_iter->data, NULL)))
+			if (purple_strequal(protocol_unescaped, prpl_info->list_icon((PurpleAccount *)account_iter->data, NULL)))
 				accounts = g_list_prepend(accounts, account_iter->data);
 		}
 		g_free(protocol_unescaped);
@@ -1039,7 +1103,7 @@
 			/* Find the account for username in the list of accounts for protocol. */
 			username_unescaped = purple_unescape_filename(username);
 			for (account_iter = g_list_first(accounts) ; account_iter != NULL ; account_iter = account_iter->next) {
-				if (!strcmp(((PurpleAccount *)account_iter->data)->username, username_unescaped)) {
+				if (purple_strequal(((PurpleAccount *)account_iter->data)->username, username_unescaped)) {
 					account = account_iter->data;
 					break;
 				}
@@ -1068,14 +1132,14 @@
 				/* Chat for .chat or .system at the end of the name to determine the type. */
 				if (len >= 7) {
 					gchar *tmp = &name[len - 7];
-					if (!strcmp(tmp, ".system")) {
+					if (purple_strequal(tmp, ".system")) {
 						set->type = PURPLE_LOG_SYSTEM;
 						*tmp = '\0';
 					}
 				}
 				if (len > 5) {
 					gchar *tmp = &name[len - 5];
-					if (!strcmp(tmp, ".chat")) {
+					if (purple_strequal(tmp, ".chat")) {
 						set->type = PURPLE_LOG_CHAT;
 						*tmp = '\0';
 					}
@@ -1773,29 +1837,29 @@
 			sscanf(convostart, "%*s %s %d %d:%d:%d %d",
 			       month, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tm.tm_year);
 			/* Ugly hack, in case current locale is not English */
-			if (strcmp(month, "Jan") == 0) {
+			if (purple_strequal(month, "Jan")) {
 				tm.tm_mon= 0;
-			} else if (strcmp(month, "Feb") == 0) {
+			} else if (purple_strequal(month, "Feb")) {
 				tm.tm_mon = 1;
-			} else if (strcmp(month, "Mar") == 0) {
+			} else if (purple_strequal(month, "Mar")) {
 				tm.tm_mon = 2;
-			} else if (strcmp(month, "Apr") == 0) {
+			} else if (purple_strequal(month, "Apr")) {
 				tm.tm_mon = 3;
-			} else if (strcmp(month, "May") == 0) {
+			} else if (purple_strequal(month, "May")) {
 				tm.tm_mon = 4;
-			} else if (strcmp(month, "Jun") == 0) {
+			} else if (purple_strequal(month, "Jun")) {
 				tm.tm_mon = 5;
-			} else if (strcmp(month, "Jul") == 0) {
+			} else if (purple_strequal(month, "Jul")) {
 				tm.tm_mon = 6;
-			} else if (strcmp(month, "Aug") == 0) {
+			} else if (purple_strequal(month, "Aug")) {
 				tm.tm_mon = 7;
-			} else if (strcmp(month, "Sep") == 0) {
+			} else if (purple_strequal(month, "Sep")) {
 				tm.tm_mon = 8;
-			} else if (strcmp(month, "Oct") == 0) {
+			} else if (purple_strequal(month, "Oct")) {
 				tm.tm_mon = 9;
-			} else if (strcmp(month, "Nov") == 0) {
+			} else if (purple_strequal(month, "Nov")) {
 				tm.tm_mon = 10;
-			} else if (strcmp(month, "Dec") == 0) {
+			} else if (purple_strequal(month, "Dec")) {
 				tm.tm_mon = 11;
 			}
 			tm.tm_year -= 1900;
@@ -1930,7 +1994,7 @@
 
 		/* Make sure we're dealing with a log file. */
 		ext = &name[len - 4];
-		if (strcmp(ext, ".log")) {
+		if (!purple_strequal(ext, ".log")) {
 			g_free(name);
 			continue;
 		}
@@ -1943,7 +2007,7 @@
 		set->type = PURPLE_LOG_IM;
 		if (len > 9) {
 			char *tmp = &name[len - 9];
-			if (!strcmp(tmp, ".chat")) {
+			if (purple_strequal(tmp, ".chat")) {
 				set->type = PURPLE_LOG_CHAT;
 				*tmp = '\0';
 			}
@@ -1952,22 +2016,28 @@
 		set->name = set->normalized_name = name;
 
 		/* Search the buddy list to find the account and to determine if this is a buddy. */
-		for (gnode = purple_get_blist()->root; !found && gnode != NULL; gnode = gnode->next)
+		for (gnode = purple_blist_get_root();
+		     !found && gnode != NULL;
+			 gnode = purple_blist_node_get_sibling_next(gnode))
 		{
 			if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 				continue;
 
-			for (cnode = gnode->child; !found && cnode != NULL; cnode = cnode->next)
+			for (cnode = purple_blist_node_get_first_child(gnode);
+			     !found && cnode != NULL;
+				 cnode = purple_blist_node_get_sibling_next(cnode))
 			{
 				if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 					continue;
 
-				for (bnode = cnode->child; !found && bnode != NULL; bnode = bnode->next)
+				for (bnode = purple_blist_node_get_first_child(cnode);
+				     !found && bnode != NULL;
+					 bnode = purple_blist_node_get_sibling_next(bnode))
 				{
 					PurpleBuddy *buddy = (PurpleBuddy *)bnode;
 
-					if (!strcmp(buddy->name, name)) {
-						set->account = buddy->account;
+					if (purple_strequal(purple_buddy_get_name(buddy), name)) {
+						set->account = purple_buddy_get_account(buddy);
 						set->buddy = TRUE;
 						found = TRUE;
 					}
--- a/libpurple/log.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/log.h	Sat Mar 07 01:59:40 2009 +0000
@@ -194,7 +194,7 @@
  * Creates a new log
  *
  * @param type        The type of log this is.
- * @param name        The name of this conversation (screenname, chat name,
+ * @param name        The name of this conversation (buddy name, chat name,
  *                    etc.)
  * @param account     The account the conversation is occurring on
  * @param conv        The conversation being logged
@@ -294,6 +294,19 @@
 int purple_log_get_total_size(PurpleLogType type, const char *name, PurpleAccount *account);
 
 /**
+ * Returns the activity score of a log, based on total size in bytes,
+ * which is then decayed based on age
+ *
+ * @param type                The type of the log
+ * @param name                The name of the log
+ * @param account             The account
+ * @return                    The activity score
+ *
+ * @since 2.6.0
+ */
+int purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleAccount *account);
+
+/**
  * Tests whether a log is deletable
  *
  * A return value of @c FALSE indicates that purple_log_delete() will fail on this
--- a/libpurple/nat-pmp.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/nat-pmp.c	Sat Mar 07 01:59:40 2009 +0000
@@ -126,7 +126,7 @@
 
 	for (i = 0; i < RTAX_MAX; i++)
 	{
-		if (bitmask & (1 << i)) 
+		if (bitmask & (1 << i))
 		{
 			addrs[i] = sa;
 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
@@ -139,7 +139,7 @@
 				sa = (struct sockaddr*)(sizeof(struct sockaddr_in6) + (char *)sa);
 #endif
 #endif
-		} 
+		}
 		else
 		{
 			addrs[i] = NULL;
@@ -192,7 +192,7 @@
     mib[5] = 0;
 
 	/* Determine the buffer side needed to get the full routing table */
-    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 
+    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
 	{
 		purple_debug_warning("nat-pmp", "sysctl: net.route.0.0.dump estimate\n");
 		return NULL;
@@ -205,7 +205,7 @@
     }
 
 	/* Read the routing table into buf */
-    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 
+    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
 	{
 		purple_debug_warning("nat-pmp", "sysctl: net.route.0.0.dump\n");
 		return NULL;
@@ -213,12 +213,12 @@
 
     lim = buf + needed;
 
-    for (next = buf; next < lim; next += rtm->rtm_msglen) 
+    for (next = buf; next < lim; next += rtm->rtm_msglen)
 	{
 		rtm = (struct rt_msghdr *)next;
 		sa = (struct sockaddr *)(rtm + 1);
-		
-		if (sa->sa_family == AF_INET) 
+
+		if (sa->sa_family == AF_INET)
 		{
 			sin = (struct sockaddr_in*) sa;
 
@@ -240,7 +240,7 @@
 					memcpy(&mask, rti_info[RTAX_NETMASK], sizeof(mask));
 
 				if (rtm->rtm_addrs & RTA_GATEWAY &&
-					is_default_route(&addr, &mask)) 
+					is_default_route(&addr, &mask))
 				{
 					if (rti_info[RTAX_GATEWAY]) {
 						struct sockaddr_in *rti_sin = (struct sockaddr_in *)rti_info[RTAX_GATEWAY];
@@ -263,7 +263,7 @@
 }
 
 /*!
- *	purple_pmp_get_public_ip() will return the publicly facing IP address of the 
+ *	purple_pmp_get_public_ip() will return the publicly facing IP address of the
  *	default NAT gateway. The function will return NULL if:
  *		- The gateway doesn't support NAT-PMP
  *		- The gateway errors in some other spectacular fashion
@@ -278,10 +278,10 @@
 	PurplePmpIpRequest req;
 	PurplePmpIpResponse resp;
 	int sendfd;
-	
+
 	if (pmp_info.status == PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER)
 		return NULL;
-	
+
 	if ((pmp_info.status == PURPLE_PMP_STATUS_DISCOVERED) && (pmp_info.publicip != NULL))
 	{
 #ifdef PMP_DEBUG
@@ -318,7 +318,7 @@
 	/* The NAT-PMP spec says we should attempt to contact the gateway 9 times, doubling the time we wait each time.
 	 * Even starting with a timeout of 0.1 seconds, that means that we have a total waiting of 204.6 seconds.
 	 * With the recommended timeout of 0.25 seconds, we're talking 511.5 seconds (8.5 minutes).
-	 * 
+	 *
 	 * This seems really silly... if this were nonblocking, a couple retries might be in order, but it's not at present.
 	 */
 #ifdef PMP_DEBUG
@@ -327,7 +327,7 @@
 #endif
 
 	/* TODO: Non-blocking! */
-	
+
 	if (sendto(sendfd, &req, sizeof(req), 0, (struct sockaddr *)(gateway), sizeof(struct sockaddr)) < 0)
 	{
 		purple_debug_info("nat-pmp", "There was an error sending the NAT-PMP public IP request! (%s)\n", g_strerror(errno));
@@ -370,7 +370,7 @@
 
 	if (!publicsockaddr) {
 		g_free(gateway);
-		
+
 		pmp_info.status = PURPLE_PMP_STATUS_UNABLE_TO_DISCOVER;
 		return NULL;
 	}
@@ -437,7 +437,7 @@
 	/* The NAT-PMP spec says we should attempt to contact the gateway 9 times, doubling the time we wait each time.
 	 * Even starting with a timeout of 0.1 seconds, that means that we have a total waiting of 204.6 seconds.
 	 * With the recommended timeout of 0.25 seconds, we're talking 511.5 seconds (8.5 minutes).
-	 * 
+	 *
 	 * This seems really silly... if this were nonblocking, a couple retries might be in order, but it's not at present.
 	 * XXX Make this nonblocking.
 	 * XXX This code looks like the pmp_get_public_ip() code. Can it be consolidated?
--- a/libpurple/network.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/network.c	Sat Mar 07 01:59:40 2009 +0000
@@ -96,6 +96,10 @@
 static NMState nm_get_network_state(void);
 #endif
 
+#if defined(HAVE_NETWORKMANAGER) || defined(_WIN32)
+static gboolean force_online;
+#endif
+
 const unsigned char *
 purple_network_ip_atoi(const char *ip)
 {
@@ -671,6 +675,9 @@
 purple_network_is_available(void)
 {
 #ifdef HAVE_NETWORKMANAGER
+	if (force_online)
+		return TRUE;
+
 	if (!have_nm_state)
 	{
 		have_nm_state = TRUE;
@@ -685,12 +692,20 @@
 	return FALSE;
 
 #elif defined _WIN32
-	return (current_network_count > 0);
+	return (current_network_count > 0 || force_online);
 #else
 	return TRUE;
 #endif
 }
 
+void
+purple_network_force_online()
+{
+#if defined(HAVE_NETWORKMANAGER) || defined(_WIN32)
+	force_online = TRUE;
+#endif
+}
+
 #ifdef HAVE_NETWORKMANAGER
 static void
 nm_update_state(NMState state)
--- a/libpurple/network.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/network.h	Sat Mar 07 01:59:40 2009 +0000
@@ -208,6 +208,17 @@
 gboolean purple_network_is_available(void);
 
 /**
+ * Makes purple_network_is_available() always return @c TRUE.
+ *
+ * This is what backs the --force-online command line argument in Pidgin,
+ * for example.  This is useful for offline testing, especially when
+ * combined with nullprpl.
+ *
+ * @since 2.6.0
+ */
+void purple_network_force_online(void);
+
+/**
  * Get the handle for the network system
  *
  * @return the handle to the network system
--- a/libpurple/notify.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/notify.c	Sat Mar 07 01:59:40 2009 +0000
@@ -444,7 +444,7 @@
 purple_notify_user_info_entry_new(const char *label, const char *value)
 {
 	PurpleNotifyUserInfoEntry *user_info_entry;
-	
+
 	user_info_entry = g_new0(PurpleNotifyUserInfoEntry, 1);
 	PURPLE_DBUS_REGISTER_POINTER(user_info_entry, PurpleNotifyUserInfoEntry);
 	user_info_entry->label = g_strdup(label);
@@ -458,7 +458,7 @@
 purple_notify_user_info_entry_destroy(PurpleNotifyUserInfoEntry *user_info_entry)
 {
 	g_return_if_fail(user_info_entry != NULL);
-	
+
 	g_free(user_info_entry->label);
 	g_free(user_info_entry->value);
 	PURPLE_DBUS_UNREGISTER_POINTER(user_info_entry);
@@ -469,11 +469,11 @@
 purple_notify_user_info_new()
 {
 	PurpleNotifyUserInfo *user_info;
-	
+
 	user_info = g_new0(PurpleNotifyUserInfo, 1);
 	PURPLE_DBUS_REGISTER_POINTER(user_info, PurpleNotifyUserInfo);
 	user_info->user_info_entries = NULL;
-	
+
 	return user_info;
 }
 
@@ -484,10 +484,10 @@
 
 	for (l = user_info->user_info_entries; l != NULL; l = l->next) {
 		PurpleNotifyUserInfoEntry *user_info_entry = l->data;
-		
+
 		purple_notify_user_info_entry_destroy(user_info_entry);
 	}
-	
+
 	g_list_free(user_info->user_info_entries);
 	PURPLE_DBUS_UNREGISTER_POINTER(user_info);
 	g_free(user_info);
@@ -506,7 +506,7 @@
 {
 	GList *l;
 	GString *text;
-	
+
 	text = g_string_new("");
 
 	for (l = user_info->user_info_entries; l != NULL; l = l->next) {
@@ -532,7 +532,7 @@
 		if ((user_info_entry->type != PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK) &&
 			(l->next && ((((PurpleNotifyUserInfoEntry *)(l->next->data))->type != PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK))))
 			g_string_append(text, newline);
-		
+
 		/* Add an extra newline after a section header */
 		if (user_info_entry->type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER)
 			g_string_append(text, newline);
@@ -563,7 +563,7 @@
 purple_notify_user_info_entry_get_value(PurpleNotifyUserInfoEntry *user_info_entry)
 {
 	g_return_val_if_fail(user_info_entry != NULL, NULL);
-	
+
 	return user_info_entry->value;
 }
 
@@ -596,7 +596,7 @@
 purple_notify_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
 {
 	PurpleNotifyUserInfoEntry *entry;
-	
+
 	entry = purple_notify_user_info_entry_new(label, value);
 	user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
 }
@@ -623,7 +623,7 @@
 purple_notify_user_info_add_section_header(PurpleNotifyUserInfo *user_info, const char *label)
 {
 	PurpleNotifyUserInfoEntry *entry;
-	
+
 	entry = purple_notify_user_info_entry_new(label, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER;
 
@@ -634,10 +634,10 @@
 purple_notify_user_info_prepend_section_header(PurpleNotifyUserInfo *user_info, const char *label)
 {
 	PurpleNotifyUserInfoEntry *entry;
-	
+
 	entry = purple_notify_user_info_entry_new(label, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER;
-	
+
 	user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
 }
 
@@ -645,7 +645,7 @@
 purple_notify_user_info_add_section_break(PurpleNotifyUserInfo *user_info)
 {
 	PurpleNotifyUserInfoEntry *entry;
-	
+
 	entry = purple_notify_user_info_entry_new(NULL, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK;
 
@@ -656,10 +656,10 @@
 purple_notify_user_info_prepend_section_break(PurpleNotifyUserInfo *user_info)
 {
 	PurpleNotifyUserInfoEntry *entry;
-	
+
 	entry = purple_notify_user_info_entry_new(NULL, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK;
-	
+
 	user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
 }
 
--- a/libpurple/notify.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/notify.h	Sat Mar 07 01:59:40 2009 +0000
@@ -291,6 +291,7 @@
  */
 void purple_notify_searchresults_row_add(PurpleNotifySearchResults *results,
 									   GList *row);
+
 #if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
 /**
  * Returns a number of the rows in the search results object.
@@ -558,14 +559,21 @@
  */
 void purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
 /**
  * Remove a PurpleNotifyUserInfoEntry from a PurpleNotifyUserInfo object
  * without freeing the entry.
  *
  * @param user_info        The PurpleNotifyUserInfo
  * @param user_info_entry  The PurpleNotifyUserInfoEntry
+ *
+ * @deprecated Nothing is using this function and it should be removed
+ *             in 3.0.0.  Or, if we decide we want to keep it in 3.0.0
+ *             then we should make purple_notify_user_info_entry_destroy
+ *             public so that entries can be free'd after they're removed.
  */
 void purple_notify_user_info_remove_entry(PurpleNotifyUserInfo *user_info, PurpleNotifyUserInfoEntry *user_info_entry);
+#endif
 
 /**
  * Create a new PurpleNotifyUserInfoEntry
--- a/libpurple/ntlm.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/ntlm.h	Sat Mar 07 01:59:40 2009 +0000
@@ -46,7 +46,7 @@
  *
  * @param type2 String containing the base64 encoded type2 message
  * @param flags If not @c NULL, this will store the flags for the message
- * 
+ *
  * @return The nonce for use in message type3.  This is a statically
  *         allocated 8 byte binary string.
  */
--- a/libpurple/plugin.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugin.c	Sat Mar 07 01:59:40 2009 +0000
@@ -218,7 +218,7 @@
 	g_free(basename);
 	if (plugin != NULL)
 	{
-		if (!strcmp(filename, plugin->path))
+		if (purple_strequal(filename, plugin->path))
 			return plugin;
 		else if (!purple_plugin_is_unloadable(plugin))
 		{
@@ -357,7 +357,7 @@
 		return NULL;
 	}
 	else if (plugin->info->ui_requirement &&
-			strcmp(plugin->info->ui_requirement, purple_core_get_ui()))
+			!purple_strequal(plugin->info->ui_requirement, purple_core_get_ui()))
 	{
 		plugin->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
 					purple_core_get_ui(), plugin->info->ui_requirement);
@@ -1538,7 +1538,7 @@
 	for (l = plugins; l != NULL; l = l->next) {
 		plugin = l->data;
 
-		if (!strcmp(plugin->info->name, name))
+		if (purple_strequal(plugin->info->name, name))
 			return plugin;
 	}
 
@@ -1554,7 +1554,7 @@
 	for (l = plugins; l != NULL; l = l->next) {
 		plugin = l->data;
 
-		if (plugin->path != NULL && !strcmp(plugin->path, filename))
+		if (purple_strequal(plugin->path, filename))
 			return plugin;
 	}
 
@@ -1577,7 +1577,7 @@
 
 		if (plugin->path != NULL) {
 			tmp = purple_plugin_get_basename(plugin->path);
-			if (!strcmp(tmp, basename))
+			if (purple_strequal(tmp, basename))
 			{
 				g_free(tmp);
 				return plugin;
@@ -1603,7 +1603,7 @@
 	{
 		plugin = l->data;
 
-		if (plugin->info->id != NULL && !strcmp(plugin->info->id, id))
+		if (purple_strequal(plugin->info->id, id))
 			return plugin;
 	}
 
--- a/libpurple/plugin.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugin.h	Sat Mar 07 01:59:40 2009 +0000
@@ -188,7 +188,7 @@
 	/** NULL for plugin actions menu, set to the PurpleConnection for
 	    account actions menu */
 	gpointer context;
-	
+
 	gpointer user_data;
 };
 
@@ -363,7 +363,7 @@
  * Returns a plugin's name.
  *
  * @param plugin The plugin.
- * 
+ *
  * @return THe name of the plugin, or @c NULL.
  */
 const gchar *purple_plugin_get_name(const PurplePlugin *plugin);
--- a/libpurple/plugins/Makefile.am	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugins/Makefile.am	Sat Mar 07 01:59:40 2009 +0000
@@ -36,6 +36,7 @@
 newline_la_LDFLAGS          = -module -avoid-version
 notify_example_la_LDFLAGS   = -module -avoid-version
 offlinemsg_la_LDFLAGS       = -module -avoid-version
+one_time_password_la_LDFLAGS	= -module -avoid-version
 pluginpref_example_la_LDFLAGS = -module -avoid-version
 psychic_la_LDFLAGS          = -module -avoid-version
 signals_test_la_LDFLAGS		= -module -avoid-version
@@ -65,6 +66,7 @@
 	debug_example.la \
 	helloworld.la \
 	notify_example.la \
+	one_time_password.la \
 	pluginpref_example.la \
 	signals_test.la \
 	simple.la
@@ -81,6 +83,7 @@
 newline_la_SOURCES          = newline.c
 notify_example_la_SOURCES   = notify_example.c
 offlinemsg_la_SOURCES       = offlinemsg.c
+one_time_password_la_SOURCES	= one_time_password.c
 pluginpref_example_la_SOURCES = pluginpref_example.c
 psychic_la_SOURCES          = psychic.c
 signals_test_la_SOURCES		= signals-test.c
@@ -97,6 +100,7 @@
 newline_la_LIBADD           = $(GLIB_LIBS)
 notify_example_la_LIBADD    = $(GLIB_LIBS)
 offlinemsg_la_LIBADD        = $(GLIB_LIBS)
+one_time_password_la_LIBADD = $(GLIB_LIBS)
 pluginpref_example_la_LIBADD = $(GLIB_LIBS)
 psychic_la_LIBADD           = $(GLIB_LIBS)
 signals_test_la_LIBADD		= $(GLIB_LIBS)
--- a/libpurple/plugins/Makefile.mingw	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugins/Makefile.mingw	Sat Mar 07 01:59:40 2009 +0000
@@ -1,7 +1,7 @@
 #
 # Makefile.mingw
 #
-# Description: Makefile for win32 (mingw) version of LibPurple Plugins 
+# Description: Makefile for win32 (mingw) version of libpurple Plugins
 #
 
 PIDGIN_TREE_TOP := ../..
--- a/libpurple/plugins/autoaccept.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugins/autoaccept.c	Sat Mar 07 01:59:40 2009 +0000
@@ -95,7 +95,7 @@
 	char *dirname;
 
 	account = xfer->account;
-	node = (PurpleBlistNode *)purple_find_buddy(account, xfer->who);
+	node = PURPLE_BLIST_NODE(purple_find_buddy(account, xfer->who));
 
 	if (!node)
 	{
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/one_time_password.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,151 @@
+/*
+ * One Time Password support plugin for libpurple
+ *
+ * Copyright (C) 2009, Daniel Atallah <datallah@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02111-1301, USA.
+ */
+#include "internal.h"
+#include "debug.h"
+#include "plugin.h"
+#include "version.h"
+#include "account.h"
+#include "accountopt.h"
+
+#define PLUGIN_ID "core-one_time_password"
+#define PREF_NAME PLUGIN_ID "_enabled"
+
+static void
+signed_on_cb(PurpleConnection *conn, void *data)
+{
+	PurpleAccount *account = purple_connection_get_account(conn);
+
+	if (purple_account_get_bool(account, PREF_NAME, FALSE)) {
+		if(purple_account_get_remember_password(account))
+			purple_debug_error("One Time Password",
+					   "Unable to enforce one time password for account %s (%s).\n"
+					   "Account is set to remember the password.\n",
+					   purple_account_get_username(account),
+					   purple_account_get_protocol_name(account));
+		else {
+
+			purple_debug_info("One Time Password", "Clearing password for account %s (%s).\n",
+					  purple_account_get_username(account),
+					  purple_account_get_protocol_name(account));
+
+			purple_account_set_password(account, NULL);
+			/* TODO: Do we need to somehow clear conn->password ? */
+		}
+	}
+}
+
+static gboolean
+plugin_load(PurplePlugin *plugin)
+{
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleAccountOption *option;
+	GList *l;
+
+	/* Register protocol preference. */
+	for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
+		prpl = (PurplePlugin *)l->data;
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+		if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) {
+			option = purple_account_option_bool_new(_("One Time Password"),
+								PREF_NAME, FALSE);
+			prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+		}
+	}
+
+	/* Register callback. */
+	purple_signal_connect(purple_connections_get_handle(), "signed-on",
+			      plugin, PURPLE_CALLBACK(signed_on_cb), NULL);
+
+	return TRUE;
+}
+
+static gboolean
+plugin_unload(PurplePlugin *plugin)
+{
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleAccountOption *option;
+	GList *l, *options;
+
+	/* Remove protocol preference. */
+	for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
+		prpl = (PurplePlugin *)l->data;
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+		if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) {
+			options = prpl_info->protocol_options;
+			while (options != NULL) {
+				option = (PurpleAccountOption *) options->data;
+				if (strcmp(PREF_NAME, purple_account_option_get_setting(option)) == 0) {
+					prpl_info->protocol_options = g_list_delete_link(prpl_info->protocol_options, options);
+					purple_account_option_destroy(option);
+					break;
+				}
+				options = options->next;
+			}
+		}
+	}
+
+	/* Callback will be automagically unregistered */
+
+	return TRUE;
+}
+
+static PurplePluginInfo info =
+{
+	PURPLE_PLUGIN_MAGIC,
+	PURPLE_MAJOR_VERSION,
+	PURPLE_MINOR_VERSION,
+	PURPLE_PLUGIN_STANDARD,				/**< type           */
+	NULL,						/**< ui_requirement */
+	0,						/**< flags          */
+	NULL,						/**< dependencies   */
+	PURPLE_PRIORITY_DEFAULT,			/**< priority       */
+	PLUGIN_ID,					/**< id             */
+	N_("One Time Password Support"),		/**< name           */
+	DISPLAY_VERSION,				/**< version        */
+							/**  summary        */
+	N_("Enforce that passwords are used only once."),
+							/**  description    */
+	N_("Allows you to enforce on a per-account basis that passwords not "
+	   "being saved are only used in a single successful connection.\n"
+	   "Note: The account password must not be saved for this to work."),
+	"Daniel Atallah <datallah@pidgin.im>",		/**< author         */
+	PURPLE_WEBSITE,					/**< homepage       */
+	plugin_load,					/**< load           */
+	plugin_unload,					/**< unload         */
+	NULL,						/**< destroy        */
+	NULL,						/**< ui_info        */
+	NULL,						/**< extra_info     */
+	NULL,						/**< prefs_info     */
+	NULL,						/**< actions        */
+	NULL,						/**< reserved 1     */
+	NULL,						/**< reserved 2     */
+	NULL,						/**< reserved 3     */
+	NULL						/**< reserved 4     */
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(one_time_password, init_plugin, info)
--- a/libpurple/plugins/perl/common/BuddyList.xs	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugins/perl/common/BuddyList.xs	Sat Mar 07 01:59:40 2009 +0000
@@ -363,9 +363,9 @@
 PROTOTYPES: ENABLE
 
 Purple::BuddyList::Buddy
-purple_buddy_new(account, screenname, alias)
+purple_buddy_new(account, name, alias)
 	Purple::Account account
-	const char *screenname
+	const char *name
 	const char *alias
 
 const char *
--- a/libpurple/plugins/perl/common/Request.xs	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugins/perl/common/Request.xs	Sat Mar 07 01:59:40 2009 +0000
@@ -211,10 +211,11 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_account_new(id, text, account = NULL)
+purple_request_field_account_new(class, id, text, account = NULL)
 	const char *id
 	const char *text
 	Purple::Account account
+	C_ARGS: id, text, account
 
 Purple::Account
 purple_request_field_account_get_default_value(field)
@@ -255,10 +256,11 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_bool_new(id, text, default_value = TRUE)
+purple_request_field_bool_new(class, id, text, default_value = TRUE)
 	const char *id
 	const char *text
 	gboolean default_value
+	C_ARGS: id, text, default_value
 
 gboolean
 purple_request_field_bool_get_default_value(field)
@@ -282,10 +284,11 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_choice_new(id, text, default_value = 0)
+purple_request_field_choice_new(class, id, text, default_value = 0)
 	const char *id
 	const char *text
 	int default_value
+	C_ARGS: id, text, default_value
 
 void
 purple_request_field_choice_add(field, label)
@@ -324,10 +327,11 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_int_new(id, text, default_value = 0)
+purple_request_field_int_new(clas, id, text, default_value = 0)
 	const char *id
 	const char *text
 	int default_value
+	C_ARGS: id, text, default_value
 
 int
 purple_request_field_int_get_default_value(field)
@@ -355,17 +359,19 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_label_new(id, text)
+purple_request_field_label_new(class, id, text)
 	const char *id
 	const char *text
+	C_ARGS: id, text
 
 MODULE = Purple::Request  PACKAGE = Purple::Request::Field  PREFIX = purple_request_field_
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_list_new(id, text)
+purple_request_field_list_new(class, id, text)
 	const char *id
 	const char *text
+	C_ARGS: id, text
 
 void
 purple_request_field_list_add(field, item, data)
@@ -425,10 +431,11 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_new(id, text, type)
+purple_request_field_new(class, id, text, type)
 	const char *id
 	const char *text
 	Purple::RequestFieldType type
+	C_ARGS: id, text, type
 
 void
 purple_request_field_set_label(field, label)
@@ -454,11 +461,12 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Field
-purple_request_field_string_new(id, text, default_value, multiline)
+purple_request_field_string_new(class, id, text, default_value, multiline)
 	const char *id
 	const char *text
 	const char *default_value
 	gboolean multiline
+	C_ARGS: id, text, default_value, multiline
 
 const char *
 purple_request_field_string_get_default_value(field)
@@ -527,8 +535,9 @@
 	Purple::Request::Field::Group group
 
 Purple::Request::Field::Group
-purple_request_field_group_new(title)
+purple_request_field_group_new(class, title)
 	const char *title
+	C_ARGS: title
 
 MODULE = Purple::Request  PACKAGE = Purple::Request::Field  PREFIX = purple_request_field_
 PROTOTYPES: ENABLE
@@ -561,7 +570,8 @@
 PROTOTYPES: ENABLE
 
 Purple::Request::Fields
-purple_request_fields_new()
+purple_request_fields_new(class)
+	C_ARGS: /* void */
 
 void
 purple_request_fields_add_group(fields, group)
--- a/libpurple/plugins/statenotify.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugins/statenotify.c	Sat Mar 07 01:59:40 2009 +0000
@@ -33,7 +33,7 @@
 	g_return_if_fail(conv->type == PURPLE_CONV_TYPE_IM);
 
 	/* Prevent duplicate notifications for buddies in multiple groups */
-	if (buddy != purple_find_buddy(buddy->account, buddy->name))
+	if (buddy != purple_find_buddy(account, buddy_name))
 		return;
 
 	who = purple_buddy_get_alias(buddy);
--- a/libpurple/plugins/tcl/tcl_cmds.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/plugins/tcl/tcl_cmds.c	Sat Mar 07 01:59:40 2009 +0000
@@ -401,9 +401,9 @@
 		return NULL;
 
 	if (!strcmp(type, "buddy")) {
-		node = (PurpleBlistNode *)purple_find_buddy(account, name);
+		node = PURPLE_BLIST_NODE(purple_find_buddy(account, name));
 	} else if (!strcmp(type, "group")) {
-		node = (PurpleBlistNode *)purple_blist_find_chat(account, name);
+		node = PURPLE_BLIST_NODE(purple_blist_find_chat(account, name));
 	}
 
 	return node;
@@ -1495,9 +1495,12 @@
 	enum { CMD_STATUS_ATTR, CMD_STATUS_TYPE } cmd;
 	PurpleStatus *status;
 	PurpleStatusType *status_type;
+	int error;
+#if !(defined PURPLE_DISABLE_DEPRECATED)
 	PurpleValue *value;
 	const char *attr;
-	int error, v;
+	int v;
+#endif
 
 	if (objc < 2) {
 		Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?");
@@ -1509,6 +1512,7 @@
 
 	switch (cmd) {
 	case CMD_STATUS_ATTR:
+#if !(defined PURPLE_DISABLE_DEPRECATED)
 		if (objc != 4 && objc != 5) {
 			Tcl_WrongNumArgs(interp, 2, objv, "status attr_id ?value?");
 			return TCL_ERROR;
@@ -1554,6 +1558,7 @@
                                          Tcl_NewStringObj("attribute has unknown type", -1));
 			return TCL_ERROR;
 		}
+#endif
 		break;
 	case CMD_STATUS_TYPE:
 		if (objc != 3) {
@@ -1727,6 +1732,7 @@
 						  (purple_status_type_get_primitive(status_type)), -1));
 		break;
 	case CMD_STATUS_TYPE_PRIMARY_ATTR:
+#if !(defined PURPLE_DISABLE_DEPRECATED)
 		if (objc != 3) {
 			Tcl_WrongNumArgs(interp, 2, objv, "statustype");
 			return TCL_ERROR;
@@ -1735,6 +1741,7 @@
 			return TCL_ERROR;
 		Tcl_SetObjResult(interp,
 				 Tcl_NewStringObj(purple_status_type_get_primary_attr(status_type), -1));
+#endif
 		break;
 	case CMD_STATUS_TYPE_SAVEABLE:
 		if (objc != 3) {
--- a/libpurple/pounce.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/pounce.c	Sat Mar 07 01:59:40 2009 +0000
@@ -326,7 +326,7 @@
 		data->buffer = NULL;
 	}
 
-	if (!strcmp(element_name, "pounce")) {
+	if (purple_strequal(element_name, "pounce")) {
 		const char *ui = g_hash_table_lookup(atts, "ui");
 
 		if (ui == NULL) {
@@ -338,7 +338,7 @@
 
 		data->events = 0;
 	}
-	else if (!strcmp(element_name, "account")) {
+	else if (purple_strequal(element_name, "account")) {
 		const char *protocol_id = g_hash_table_lookup(atts, "protocol");
 
 		if (protocol_id == NULL) {
@@ -348,7 +348,7 @@
 		else
 			data->protocol_id = g_strdup(protocol_id);
 	}
-	else if (!strcmp(element_name, "option")) {
+	else if (purple_strequal(element_name, "option")) {
 		const char *type = g_hash_table_lookup(atts, "type");
 
 		if (type == NULL) {
@@ -358,7 +358,7 @@
 		else
 			data->option_type = g_strdup(type);
 	}
-	else if (!strcmp(element_name, "event")) {
+	else if (purple_strequal(element_name, "event")) {
 		const char *type = g_hash_table_lookup(atts, "type");
 
 		if (type == NULL) {
@@ -368,7 +368,7 @@
 		else
 			data->event_type = g_strdup(type);
 	}
-	else if (!strcmp(element_name, "action")) {
+	else if (purple_strequal(element_name, "action")) {
 		const char *type = g_hash_table_lookup(atts, "type");
 
 		if (type == NULL) {
@@ -378,7 +378,7 @@
 		else
 			data->action_name = g_strdup(type);
 	}
-	else if (!strcmp(element_name, "param")) {
+	else if (purple_strequal(element_name, "param")) {
 		const char *param_name = g_hash_table_lookup(atts, "name");
 
 		if (param_name == NULL) {
@@ -404,7 +404,7 @@
 		data->buffer = NULL;
 	}
 
-	if (!strcmp(element_name, "account")) {
+	if (purple_strequal(element_name, "account")) {
 		char *tmp;
 		g_free(data->account_name);
 		data->account_name = g_strdup(buffer);
@@ -412,43 +412,43 @@
 		data->protocol_id = g_strdup(_purple_oscar_convert(buffer, tmp));
 		g_free(tmp);
 	}
-	else if (!strcmp(element_name, "pouncee")) {
+	else if (purple_strequal(element_name, "pouncee")) {
 		g_free(data->pouncee);
 		data->pouncee = g_strdup(buffer);
 	}
-	else if (!strcmp(element_name, "option")) {
-		if (!strcmp(data->option_type, "on-away"))
+	else if (purple_strequal(element_name, "option")) {
+		if (purple_strequal(data->option_type, "on-away"))
 			data->options |= PURPLE_POUNCE_OPTION_AWAY;
 
 		g_free(data->option_type);
 		data->option_type = NULL;
 	}
-	else if (!strcmp(element_name, "event")) {
-		if (!strcmp(data->event_type, "sign-on"))
+	else if (purple_strequal(element_name, "event")) {
+		if (purple_strequal(data->event_type, "sign-on"))
 			data->events |= PURPLE_POUNCE_SIGNON;
-		else if (!strcmp(data->event_type, "sign-off"))
+		else if (purple_strequal(data->event_type, "sign-off"))
 			data->events |= PURPLE_POUNCE_SIGNOFF;
-		else if (!strcmp(data->event_type, "away"))
+		else if (purple_strequal(data->event_type, "away"))
 			data->events |= PURPLE_POUNCE_AWAY;
-		else if (!strcmp(data->event_type, "return-from-away"))
+		else if (purple_strequal(data->event_type, "return-from-away"))
 			data->events |= PURPLE_POUNCE_AWAY_RETURN;
-		else if (!strcmp(data->event_type, "idle"))
+		else if (purple_strequal(data->event_type, "idle"))
 			data->events |= PURPLE_POUNCE_IDLE;
-		else if (!strcmp(data->event_type, "return-from-idle"))
+		else if (purple_strequal(data->event_type, "return-from-idle"))
 			data->events |= PURPLE_POUNCE_IDLE_RETURN;
-		else if (!strcmp(data->event_type, "start-typing"))
+		else if (purple_strequal(data->event_type, "start-typing"))
 			data->events |= PURPLE_POUNCE_TYPING;
-		else if (!strcmp(data->event_type, "typed"))
+		else if (purple_strequal(data->event_type, "typed"))
 			data->events |= PURPLE_POUNCE_TYPED;
-		else if (!strcmp(data->event_type, "stop-typing"))
+		else if (purple_strequal(data->event_type, "stop-typing"))
 			data->events |= PURPLE_POUNCE_TYPING_STOPPED;
-		else if (!strcmp(data->event_type, "message-received"))
+		else if (purple_strequal(data->event_type, "message-received"))
 			data->events |= PURPLE_POUNCE_MESSAGE_RECEIVED;
 
 		g_free(data->event_type);
 		data->event_type = NULL;
 	}
-	else if (!strcmp(element_name, "action")) {
+	else if (purple_strequal(element_name, "action")) {
 		if (data->pounce != NULL) {
 			purple_pounce_action_register(data->pounce, data->action_name);
 			purple_pounce_action_set_enabled(data->pounce, data->action_name, TRUE);
@@ -457,7 +457,7 @@
 		g_free(data->action_name);
 		data->action_name = NULL;
 	}
-	else if (!strcmp(element_name, "param")) {
+	else if (purple_strequal(element_name, "param")) {
 		if (data->pounce != NULL) {
 			purple_pounce_action_set_attribute(data->pounce, data->action_name,
 											 data->param_name, buffer);
@@ -466,7 +466,7 @@
 		g_free(data->param_name);
 		data->param_name = NULL;
 	}
-	else if (!strcmp(element_name, "events")) {
+	else if (purple_strequal(element_name, "events")) {
 		PurpleAccount *account;
 
 		account = purple_accounts_find(data->account_name, data->protocol_id);
@@ -499,11 +499,11 @@
 		g_free(data->pouncee);
 		data->pouncee = NULL;
 	}
-	else if (!strcmp(element_name, "save")) {
+	else if (purple_strequal(element_name, "save")) {
 		if (data->pounce != NULL)
 			purple_pounce_set_save(data->pounce, TRUE);
 	}
-	else if (!strcmp(element_name, "pounce")) {
+	else if (purple_strequal(element_name, "pounce")) {
 		data->pounce  = NULL;
 		data->events  = 0;
 		data->options = 0;
@@ -1023,7 +1023,7 @@
 
 	for (iter = pounces; iter; iter = iter->next) {
 		PurplePounce *pounce = iter->data;
-		if (pounce->ui_type && strcmp(pounce->ui_type, ui) == 0)
+		if (purple_strequal(pounce->ui_type, ui))
 			list = g_list_prepend(list, pounce);
 	}
 	list = g_list_reverse(list);
@@ -1042,35 +1042,39 @@
 static void
 buddy_state_cb(PurpleBuddy *buddy, PurplePounceEvent event)
 {
-	purple_pounce_execute(buddy->account, buddy->name, event);
+	PurpleAccount *account = purple_buddy_get_account(buddy);
+	const gchar *name = purple_buddy_get_name(buddy);
+
+	purple_pounce_execute(account, name, event);
 }
 
 static void
 buddy_status_changed_cb(PurpleBuddy *buddy, PurpleStatus *old_status,
                         PurpleStatus *status)
 {
+	PurpleAccount *account = purple_buddy_get_account(buddy);
+	const gchar *name = purple_buddy_get_name(buddy);
 	gboolean old_available, available;
 
 	available = purple_status_is_available(status);
 	old_available = purple_status_is_available(old_status);
 
 	if (available && !old_available)
-		purple_pounce_execute(buddy->account, buddy->name,
-		                    PURPLE_POUNCE_AWAY_RETURN);
+		purple_pounce_execute(account, name, PURPLE_POUNCE_AWAY_RETURN);
 	else if (!available && old_available)
-		purple_pounce_execute(buddy->account, buddy->name,
-		                    PURPLE_POUNCE_AWAY);
+		purple_pounce_execute(account, name, PURPLE_POUNCE_AWAY);
 }
 
 static void
 buddy_idle_changed_cb(PurpleBuddy *buddy, gboolean old_idle, gboolean idle)
 {
+	PurpleAccount *account = purple_buddy_get_account(buddy);
+	const gchar *name = purple_buddy_get_name(buddy);
+
 	if (idle && !old_idle)
-		purple_pounce_execute(buddy->account, buddy->name,
-		                    PURPLE_POUNCE_IDLE);
+		purple_pounce_execute(account, name, PURPLE_POUNCE_IDLE);
 	else if (!idle && old_idle)
-		purple_pounce_execute(buddy->account, buddy->name,
-		                    PURPLE_POUNCE_IDLE_RETURN);
+		purple_pounce_execute(account, name, PURPLE_POUNCE_IDLE_RETURN);
 }
 
 static void
--- a/libpurple/prefs.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/prefs.c	Sat Mar 07 01:59:40 2009 +0000
@@ -250,33 +250,34 @@
 	GString *pref_name_full;
 	GList *tmp;
 
-	if(strcmp(element_name, "pref") && strcmp(element_name, "item"))
+	if(!purple_strequal(element_name, "pref") &&
+	   !purple_strequal(element_name, "item"))
 		return;
 
 	for(i = 0; attribute_names[i]; i++) {
-		if(!strcmp(attribute_names[i], "name")) {
+		if(purple_strequal(attribute_names[i], "name")) {
 			pref_name = attribute_values[i];
-		} else if(!strcmp(attribute_names[i], "type")) {
-			if(!strcmp(attribute_values[i], "bool"))
+		} else if(purple_strequal(attribute_names[i], "type")) {
+			if(purple_strequal(attribute_values[i], "bool"))
 				pref_type = PURPLE_PREF_BOOLEAN;
-			else if(!strcmp(attribute_values[i], "int"))
+			else if(purple_strequal(attribute_values[i], "int"))
 				pref_type = PURPLE_PREF_INT;
-			else if(!strcmp(attribute_values[i], "string"))
+			else if(purple_strequal(attribute_values[i], "string"))
 				pref_type = PURPLE_PREF_STRING;
-			else if(!strcmp(attribute_values[i], "stringlist"))
+			else if(purple_strequal(attribute_values[i], "stringlist"))
 				pref_type = PURPLE_PREF_STRING_LIST;
-			else if(!strcmp(attribute_values[i], "path"))
+			else if(purple_strequal(attribute_values[i], "path"))
 				pref_type = PURPLE_PREF_PATH;
-			else if(!strcmp(attribute_values[i], "pathlist"))
+			else if(purple_strequal(attribute_values[i], "pathlist"))
 				pref_type = PURPLE_PREF_PATH_LIST;
 			else
 				return;
-		} else if(!strcmp(attribute_names[i], "value")) {
+		} else if(purple_strequal(attribute_names[i], "value")) {
 			pref_value = attribute_values[i];
 		}
 	}
 
-	if(!strcmp(element_name, "item")) {
+	if(purple_strequal(element_name, "item")) {
 		struct purple_pref *pref;
 
 		pref_name_full = g_string_new("");
@@ -301,7 +302,7 @@
 	} else {
 		char *decoded;
 
-		if(!pref_name || !strcmp(pref_name, "/"))
+		if(!pref_name || purple_strequal(pref_name, "/"))
 			return;
 
 		pref_name_full = g_string_new(pref_name);
@@ -352,7 +353,7 @@
 						  const gchar *element_name,
 						  gpointer user_data, GError **error)
 {
-	if(prefs_stack && !strcmp(element_name, "pref")) {
+	if(prefs_stack && purple_strequal(element_name, "pref")) {
 		g_free(prefs_stack->data);
 		prefs_stack = g_list_delete_link(prefs_stack, prefs_stack);
 	}
@@ -521,7 +522,7 @@
 	char *parent_name = get_path_dirname(name);
 	struct purple_pref *ret = &prefs;
 
-	if(strcmp(parent_name, "/")) {
+	if(!purple_strequal(parent_name, "/")) {
 		ret = find_pref(parent_name);
 	}
 
@@ -571,7 +572,7 @@
 	my_name = get_path_basename(name);
 
 	for(sibling = parent->first_child; sibling; sibling = sibling->sibling) {
-		if(!strcmp(sibling->name, my_name)) {
+		if(purple_strequal(sibling->name, my_name)) {
 			g_free(my_name);
 			return NULL;
 		}
@@ -849,10 +850,7 @@
 			return;
 		}
 
-		if((value && !pref->value.string) ||
-				(!value && pref->value.string) ||
-				(value && pref->value.string &&
-				 strcmp(pref->value.string, value))) {
+		if (!purple_strequal(pref->value.string, value)) {
 			g_free(pref->value.string);
 			pref->value.string = g_strdup(value);
 			do_callbacks(name, pref);
@@ -909,10 +907,7 @@
 			return;
 		}
 
-		if((value && !pref->value.string) ||
-				(!value && pref->value.string) ||
-				(value && pref->value.string &&
-				 strcmp(pref->value.string, value))) {
+		if (!purple_strequal(pref->value.string, value)) {
 			g_free(pref->value.string);
 			pref->value.string = g_strdup(value);
 			do_callbacks(name, pref);
@@ -1106,7 +1101,7 @@
 		next = child->sibling;
 		for(newchild = newpref->first_child; newchild != NULL; newchild = newchild->sibling)
 		{
-			if(!strcmp(child->name, newchild->name))
+			if(purple_strequal(child->name, newchild->name))
 			{
 				purple_prefs_rename_node(child, newchild);
 				break;
--- a/libpurple/prefs.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/prefs.h	Sat Mar 07 01:59:40 2009 +0000
@@ -67,8 +67,8 @@
 #endif
 
 /**************************************************************************/
-/** @name Prefs API                                                       
-    Preferences are named according to a directory-like structure.        
+/** @name Prefs API
+    Preferences are named according to a directory-like structure.
     Example: "/plugins/core/potato/is_from_idaho" (probably a boolean)    */
 /**************************************************************************/
 /*@{*/
--- a/libpurple/privacy.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/privacy.c	Sat Mar 07 01:59:40 2009 +0000
@@ -232,8 +232,10 @@
 	while (list != NULL)
 	{
 		PurpleBuddy *buddy = list->data;
-		if (!g_slist_find_custom(account->permit, buddy->name, (GCompareFunc)g_utf8_collate))
-			purple_privacy_permit_add(account, buddy->name, local);
+		const gchar *name = purple_buddy_get_name(buddy);
+
+		if (!g_slist_find_custom(account->permit, name, (GCompareFunc)g_utf8_collate))
+			purple_privacy_permit_add(account, name, local);
 		list = g_slist_delete_link(list, list);
 	}
 }
@@ -267,7 +269,7 @@
 				for (list = account->permit; list != NULL;) {
 					char *person = list->data;
 					list = list->next;
-					if (strcmp(norm, person) != 0)
+					if (!purple_strequal(norm, person))
 						purple_privacy_permit_remove(account, person, local);
 				}
 			}
@@ -311,7 +313,7 @@
 				for (list = account->deny; list != NULL; ) {
 					char *person = list->data;
 					list = list->next;
-					if (strcmp(norm, person) != 0)
+					if (!purple_strequal(norm, person))
 						purple_privacy_deny_remove(account, person, local);
 				}
 			}
--- a/libpurple/privacy.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/privacy.h	Sat Mar 07 01:59:40 2009 +0000
@@ -125,7 +125,7 @@
  *									changed to PURPLE_PRIVACY_ALLOW_USERS, all the
  *									buddies are added to the allow-list, and the
  *									user is also added to the allow-list.
- * 
+ *
  * @param account	The account.
  * @param who		The name of the user.
  * @param local		Whether the change is local-only.
--- a/libpurple/protocols/Makefile.mingw	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/Makefile.mingw	Sat Mar 07 01:59:40 2009 +0000
@@ -2,7 +2,7 @@
 #
 # Author: hermanator12002@yahoo.com
 # Date 9/11/02
-# Description: Protocols Makefile for win32 (mingw) port of LibPurple
+# Description: Protocols Makefile for win32 (mingw) port of libpurple
 #
 
 PIDGIN_TREE_TOP := ../..
--- a/libpurple/protocols/bonjour/bonjour.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Sat Mar 07 01:59:40 2009 +0000
@@ -261,9 +261,10 @@
 
 
 static void bonjour_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
-	if (buddy->proto_data) {
-		bonjour_buddy_delete(buddy->proto_data);
-		buddy->proto_data = NULL;
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy);
+	if (bb) {
+		bonjour_buddy_delete(bb);
+		purple_buddy_set_protocol_data(buddy, NULL);
 	}
 }
 
@@ -303,7 +304,7 @@
 	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
 	BonjourBuddy *bb;
 
-	if (buddy == NULL || buddy->proto_data == NULL)
+	if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
 	{
 		/*
 		 * This buddy is not in our buddy list, and therefore does not really
@@ -312,7 +313,6 @@
 		return;
 	}
 
-	bb = buddy->proto_data;
 	bonjour_jabber_close_conversation(bb->conversation);
 	bb->conversation = NULL;
 }
@@ -351,7 +351,7 @@
 {
 	PurplePresence *presence;
 	PurpleStatus *status;
-	BonjourBuddy *bb = buddy->proto_data;
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(buddy);
 	const char *status_description;
 	const char *message;
 
@@ -417,8 +417,7 @@
 {
 	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
 
-	return (buddy != NULL && buddy->proto_data != NULL);
-
+	return (buddy != NULL && purple_buddy_get_protocol_data(buddy) != NULL);
 }
 
 static gboolean
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Sat Mar 07 01:59:40 2009 +0000
@@ -386,10 +386,9 @@
 
 	buddy = purple_find_buddy(xfer->account, xfer->who);
 	/* this buddy is offline. */
-	if (buddy == NULL || buddy->proto_data == NULL)
+	if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
 		return;
 
-	bb = (BonjourBuddy *)buddy->proto_data;
 	/* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
 	if (bb->ips)
 		xf->buddy_ip = g_strdup(bb->ips->data);
@@ -410,6 +409,7 @@
 	const char *type, *id;
 	BonjourData *bd;
 	PurpleXfer *xfer;
+	const gchar *name = NULL;
 
 	g_return_if_fail(pc != NULL);
 	g_return_if_fail(packet != NULL);
@@ -421,6 +421,8 @@
 
 	purple_debug_info("bonjour", "xep-si-parse.\n");
 
+	name = purple_buddy_get_name(pb);
+
 	type = xmlnode_get_attrib(packet, "type");
 	id = xmlnode_get_attrib(packet, "id");
 	if(type) {
@@ -448,31 +450,34 @@
 
 				/* TODO: Make sure that it is advertising a bytestreams transfer */
 
-				bonjour_xfer_receive(pc, id, sid, pb->name, filesize, filename, XEP_BYTESTREAMS);
+				bonjour_xfer_receive(pc, id, sid, name, filesize, filename, XEP_BYTESTREAMS);
 
 				parsed_receive = TRUE;
 			}
 
 			if (!parsed_receive) {
+				BonjourData *bd = purple_connection_get_protocol_data(pc);
+
 				purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
-				xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel");
+				xep_ft_si_reject(bd, id, name, "403", "cancel");
 				/*TODO: Send Cancel (501) */
 			}
 		} else if(!strcmp(type, "result")) {
 			purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
 
-			xfer = bonjour_si_xfer_find(bd, id, pb->name);
+			xfer = bonjour_si_xfer_find(bd, id, name);
 
 			if(xfer == NULL) {
+				BonjourData *bd = purple_connection_get_protocol_data(pc);
 				purple_debug_info("bonjour", "xfer find fail.\n");
-				xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel");
+				xep_ft_si_reject(bd, id, name, "403", "cancel");
 			} else
 				bonjour_bytestreams_init(xfer);
 
 		} else if(!strcmp(type, "error")) {
 			purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
 
-			xfer = bonjour_si_xfer_find(bd, id, pb->name);
+			xfer = bonjour_si_xfer_find(bd, id, name);
 
 			if(xfer == NULL)
 				purple_debug_info("bonjour", "xfer find fail.\n");
@@ -501,7 +506,7 @@
 	purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
 
 	type = xmlnode_get_attrib(packet, "type");
-	from = pb->name;
+	from = purple_buddy_get_name(pb);
 	query = xmlnode_get_child(packet,"query");
 	if(type) {
 		if(!strcmp(type, "set")) {
@@ -841,8 +846,10 @@
 static void
 bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb)
 {
+	PurpleAccount *account = NULL;
 	XepXfer *xf;
 	char dstaddr[41];
+	const gchar *name = NULL;
 	unsigned char hashval[20];
 	char *p;
 	int i;
@@ -856,7 +863,10 @@
 	if(!xf)
 		return;
 
-	p = g_strdup_printf("%s%s%s", xf->sid, pb->name, purple_account_get_username(pb->account));
+	name = purple_buddy_get_name(pb);
+	account = purple_buddy_get_account(pb);
+
+	p = g_strdup_printf("%s%s%s", xf->sid, name, purple_account_get_username(account));
 	purple_cipher_digest_region("sha1", (guchar *)p, strlen(p),
 				    sizeof(hashval), hashval, NULL);
 	g_free(p);
--- a/libpurple/protocols/bonjour/buddy.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Sat Mar 07 01:59:40 2009 +0000
@@ -157,8 +157,8 @@
 		purple_blist_add_buddy(buddy, NULL, group, NULL);
 	}
 
-	buddy->proto_data = bonjour_buddy;
 	name = purple_buddy_get_name(buddy);
+	purple_buddy_set_protocol_data(buddy, bonjour_buddy);
 
 	/* Create the alias for the buddy using the first and the last name */
 	if (bonjour_buddy->nick && *bonjour_buddy->nick)
@@ -210,8 +210,8 @@
 	if (PURPLE_BLIST_NODE_SHOULD_SAVE(pb)) {
 		purple_prpl_got_user_status(purple_buddy_get_account(pb),
 					    purple_buddy_get_name(pb), "offline", NULL);
-		bonjour_buddy_delete(pb->proto_data);
-		pb->proto_data = NULL;
+		bonjour_buddy_delete(purple_buddy_get_protocol_data(pb));
+		purple_buddy_set_protocol_data(pb, NULL);
 	} else {
 		purple_account_remove_buddy(purple_buddy_get_account(pb), pb, NULL);
 		purple_blist_remove_buddy(pb);
--- a/libpurple/protocols/bonjour/jabber.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Sat Mar 07 01:59:40 2009 +0000
@@ -240,17 +240,21 @@
 _match_buddies_by_address(gpointer key, gpointer value, gpointer data)
 {
 	PurpleBuddy *pb = value;
+	PurpleAccount *account = NULL;
+	BonjourBuddy *bb = NULL;
 	struct _match_buddies_by_address_t *mbba = data;
 
+	account = purple_buddy_get_account(pb);
+	bb = purple_buddy_get_protocol_data(pb);
+
 	/*
 	 * If the current PurpleBuddy's data is not null and the PurpleBuddy's account
 	 * is the same as the account requesting the check then continue to determine
 	 * whether one of the buddies IPs matches the target IP.
 	 */
-	if (mbba->jdata->account == pb->account && pb->proto_data != NULL)
+	if (mbba->jdata->account == account && bb != NULL)
 	{
 		const char *ip;
-		BonjourBuddy *bb = pb->proto_data;
 		GSList *tmp = bb->ips;
 
 		while(tmp) {
@@ -268,7 +272,7 @@
 _send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	PurpleBuddy *pb = data;
-	BonjourBuddy *bb = pb->proto_data;
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
 	BonjourJabberConversation *bconv = bb->conversation;
 	int ret, writelen;
 
@@ -285,13 +289,16 @@
 	if (ret < 0 && errno == EAGAIN)
 		return;
 	else if (ret <= 0) {
-		PurpleConversation *conv;
+		PurpleConversation *conv = NULL;
+		PurpleAccount *account = NULL;
 		const char *error = g_strerror(errno);
 
 		purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n",
 				   purple_buddy_get_name(pb), error ? error : "(null)");
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+		account = purple_buddy_get_account(pb);
+
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
 		if (conv != NULL)
 			purple_conversation_write(conv, NULL,
 				  _("Unable to send message."),
@@ -310,7 +317,7 @@
 {
 	gint ret;
 	int len = strlen(message);
-	BonjourBuddy *bb = pb->proto_data;
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
 	BonjourJabberConversation *bconv = bb->conversation;
 
 	/* If we're not ready to actually send, append it to the buffer */
@@ -329,12 +336,15 @@
 		ret = 0;
 	else if (ret <= 0) {
 		PurpleConversation *conv;
+		PurpleAccount *account;
 		const char *error = g_strerror(errno);
 
 		purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n",
 				   purple_buddy_get_name(pb), error ? error : "(null)");
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+		account = purple_buddy_get_account(pb);
+
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
 		if (conv != NULL)
 			purple_conversation_write(conv, NULL,
 				  _("Unable to send message."),
@@ -373,11 +383,12 @@
 
 	/* Inform the user that the conversation has been closed */
 	BonjourBuddy *bb = NULL;
+	const gchar *name = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
 
-	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)");
+	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", name);
 
 	if(bconv->pb != NULL)
-		bb = bconv->pb->proto_data;
+		bb = purple_buddy_get_protocol_data(bconv->pb);
 #if 0
 	if(bconv->pb != NULL) {
 		PurpleConversation *conv;
@@ -411,9 +422,11 @@
 			purple_debug_warning("bonjour", "receive error: %s\n", err ? err : "(null)");
 
 			bonjour_jabber_close_conversation(bconv);
-			if (bconv->pb != NULL && bconv->pb->proto_data != NULL) {
-				BonjourBuddy *bb = bconv->pb->proto_data;
-				bb->conversation = NULL;
+			if (bconv->pb != NULL) {
+				BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
+				
+				if(bb != NULL)
+					bb->conversation = NULL;
 			}
 
 			/* I guess we really don't need to notify the user.
@@ -421,7 +434,8 @@
 		}
 		return;
 	} else if (len == 0) { /* The other end has closed the socket */
-		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (bconv->pb && bconv->pb->name) ? bconv->pb->name : "(unknown)");
+		const gchar *name = purple_buddy_get_name(bconv->pb);
+		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name) ? name : "(unknown)");
 		bonjour_jabber_stream_ended(bconv);
 		return;
 	} else {
@@ -465,7 +479,7 @@
 		BonjourBuddy *bb = NULL;
 
 		if(bconv->pb) {
-			bb = bconv->pb->proto_data;
+			bb = purple_buddy_get_protocol_data(bconv->pb);
 			bname = purple_buddy_get_name(bconv->pb);
 		}
 
@@ -644,7 +658,7 @@
 	mbba = g_new0(struct _match_buddies_by_address_t, 1);
 	mbba->address = address_text;
 	mbba->jdata = jdata;
-	g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
+	g_hash_table_foreach(purple_blist_get_buddies(), _match_buddies_by_address, mbba);
 
 	if (mbba->matched_buddies == NULL) {
 		purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
@@ -729,17 +743,20 @@
 _connected_to_buddy(gpointer data, gint source, const gchar *error)
 {
 	PurpleBuddy *pb = data;
-	BonjourBuddy *bb = pb->proto_data;
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
 
 	bb->conversation->connect_data = NULL;
 
 	if (source < 0) {
-		PurpleConversation *conv;
+		PurpleConversation *conv = NULL;
+		PurpleAccount *account = NULL;
 
 		purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
 				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error ? error : "(null)");
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+		account = purple_buddy_get_account(pb);
+
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
 		if (conv != NULL)
 			purple_conversation_write(conv, NULL,
 				  _("Unable to send the message, the conversation couldn't be started."),
@@ -752,12 +769,15 @@
 
 	if (!bonjour_jabber_send_stream_init(bb->conversation, source)) {
 		const char *err = g_strerror(errno);
-		PurpleConversation *conv;
+		PurpleConversation *conv = NULL;
+		PurpleAccount *account = NULL;
 
 		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
 				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, err ? err : "(null)");
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
+		account = purple_buddy_get_account(pb);
+
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
 		if (conv != NULL)
 			purple_conversation_write(conv, NULL,
 				  _("Unable to send the message, the conversation couldn't be started."),
@@ -777,14 +797,14 @@
 
 void
 bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
-	PurpleBuddy *pb;
+	PurpleBuddy *pb = NULL;
+	BonjourBuddy *bb = NULL;
 
 	g_return_if_fail(bconv->ip != NULL);
 	g_return_if_fail(bconv->pb == NULL);
 
 	pb = purple_find_buddy(bconv->account, bconv->buddy_name);
-	if (pb && pb->proto_data) {
-		BonjourBuddy *bb = pb->proto_data;
+	if (pb && (bb = purple_buddy_get_protocol_data(pb))) {
 		const char *ip;
 		GSList *tmp = bb->ips;
 
@@ -834,7 +854,7 @@
 	mbba = g_new0(struct _match_buddies_by_address_t, 1);
 	mbba->address = bconv->ip;
 	mbba->jdata = jdata;
-	g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
+	g_hash_table_foreach(purple_blist_get_buddies(), _match_buddies_by_address, mbba);
 
 	/* If there is exactly one match, use it */
 	if(mbba->matched_buddies != NULL) {
@@ -842,7 +862,7 @@
 			purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
 		else {
 			PurpleBuddy *pb = mbba->matched_buddies->data;
-			BonjourBuddy *bb = pb->proto_data;
+			BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
 
 			purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
 				purple_buddy_get_name(pb), bconv->ip);
@@ -882,12 +902,10 @@
 	g_return_val_if_fail(to != NULL, NULL);
 
 	pb = purple_find_buddy(jdata->account, to);
-	if (pb == NULL || pb->proto_data == NULL)
+	if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL)
 		/* You can not send a message to an offline buddy */
 		return NULL;
 
-	bb = (BonjourBuddy *) pb->proto_data;
-
 	/* Check if there is a previously open conversation */
 	if (bb->conversation == NULL)
 	{
@@ -934,7 +952,7 @@
 	int ret;
 
 	pb = _find_or_start_conversation(jdata, to);
-	if (pb == NULL || pb->proto_data == NULL) {
+	if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) {
 		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
 		/* You can not send a message to an offline buddy */
 		return -10000;
@@ -942,8 +960,6 @@
 
 	purple_markup_html_to_xhtml(body, &xhtml, &message);
 
-	bb = pb->proto_data;
-
 	message_node = xmlnode_new("message");
 	xmlnode_set_attrib(message_node, "to", bb->name);
 	xmlnode_set_attrib(message_node, "from", purple_account_get_username(jdata->account));
@@ -993,7 +1009,7 @@
 
 	/* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
 	if(bconv->pb != NULL) {
-		BonjourBuddy *bb = bconv->pb->proto_data;
+		BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
 		if (bb->conversation == bconv)
 			bb->conversation = NULL;
 	}
@@ -1022,7 +1038,7 @@
 				tmp_next = xfers->next;
 				/* We only need to cancel this if it hasn't actually started transferring. */
 				/* This will change if we ever support IBB transfers. */
-				if (strcmp(xfer->who, bconv->pb->name) == 0
+				if (strcmp(xfer->who, purple_buddy_get_name(bconv->pb)) == 0
 						&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
 							|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
 					purple_xfer_cancel_remote(xfer);
@@ -1081,7 +1097,7 @@
 
 		buddies = purple_find_buddies(jdata->account, NULL);
 		for (l = buddies; l; l = l->next) {
-			BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data;
+			BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
 			if (bb != NULL) {
 				bonjour_jabber_close_conversation(bb->conversation);
 				bb->conversation = NULL;
@@ -1144,15 +1160,20 @@
 check_if_blocked(PurpleBuddy *pb)
 {
 	gboolean blocked = FALSE;
-	GSList *l;
+	GSList *l = NULL;
 	PurpleAccount *acc = purple_buddy_get_account(pb);
 
 	if(acc == NULL)
 		return FALSE;
 
+	acc = purple_buddy_get_account(pb);
+
 	for(l = acc->deny; l != NULL; l = l->next) {
-		if(!purple_utf8_strcasecmp(pb->name, (char *)l->data)) {
-			purple_debug_info("bonjour", "%s has been blocked by %s.\n", pb->name, acc->username);
+		const gchar *name = purple_buddy_get_name(pb);
+		const gchar *username = purple_account_get_username(acc);
+
+		if(!purple_utf8_strcasecmp(name, (char *)l->data)) {
+			purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username);
 			blocked = TRUE;
 			break;
 		}
@@ -1164,16 +1185,19 @@
 xep_iq_parse(xmlnode *packet, PurpleBuddy *pb)
 {
 	xmlnode *child;
+	PurpleAccount *account;
+	PurpleConnection *gc;
 
 	if(check_if_blocked(pb))
 		return;
 
+		account = purple_buddy_get_account(pb);
+		gc = purple_account_get_connection(account);
+
 	if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "error")))
-		xep_si_parse(purple_account_get_connection(pb->account),
-			packet, pb);
+		xep_si_parse(gc, packet, pb);
 	else
-		xep_bytestreams_parse(purple_account_get_connection(pb->account),
-			packet, pb);
+		xep_bytestreams_parse(gc, packet, pb);
 }
 
 int
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Sat Mar 07 01:59:40 2009 +0000
@@ -124,7 +124,7 @@
 	g_return_if_fail(r != NULL);
 
 	pb = purple_find_buddy(account, name);
-	bb = (pb != NULL) ? pb->proto_data : NULL;
+	bb = (pb != NULL) ? purple_buddy_get_protocol_data(pb) : NULL;
 
 	switch (event) {
 		case AVAHI_RESOLVER_FAILURE:
@@ -252,7 +252,7 @@
 			purple_debug_info("bonjour", "_browser_callback - Remove service\n");
 			pb = purple_find_buddy(account, name);
 			if (pb != NULL) {
-				BonjourBuddy *bb = pb->proto_data;
+				BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
 				AvahiBuddyImplData *b_impl;
 				GSList *l;
 				AvahiSvcResolverData *rd_search;
--- a/libpurple/protocols/gg/buddylist.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/gg/buddylist.c	Sat Mar 07 01:59:40 2009 +0000
@@ -41,37 +41,46 @@
 	GGPInfo *info = gc->proto_data;
 	PurpleAccount *account = purple_connection_get_account(gc);
 
-	PurpleBuddyList *blist;
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleBuddy *buddy;
 	uin_t *userlist = NULL;
 	gchar *types = NULL;
 	int size = 0, ret = 0;
 
-	if ((blist = purple_get_blist()) == NULL)
-	    return;
-
-	for (gnode = blist->root; gnode != NULL; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root();
+	     gnode != NULL;
+	     gnode = purple_blist_node_get_sibling_next(gnode))
+	{
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
 
-		for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) {
+		for (cnode = purple_blist_node_get_first_child(gnode);
+		     cnode != NULL;
+		     cnode = purple_blist_node_get_sibling_next(cnode))
+		{
 			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
 
-			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) {
+			for (bnode = purple_blist_node_get_first_child(cnode);
+			     bnode != NULL;
+			     bnode = purple_blist_node_get_sibling_next(bnode))
+			{
+				const gchar *name = NULL;
+
 				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 
 				buddy = (PurpleBuddy *)bnode;
 
-				if (buddy->account != account)
+				if (purple_buddy_get_account(buddy) != account)
 					continue;
 
+				name = purple_buddy_get_name(buddy);
+
 				size++;
 				userlist = (uin_t *) g_renew(uin_t, userlist, size);
 				types    = (gchar *) g_renew(gchar, types, size);
-				userlist[size - 1] = ggp_str_to_uin(buddy->name);
+				userlist[size - 1] = ggp_str_to_uin(name);
 				types[size - 1]    = GG_USER_NORMAL;
 				purple_debug_info("gg", "ggp_buddylist_send: adding %d\n",
 						userlist[size - 1]);
@@ -173,36 +182,45 @@
 void ggp_buddylist_offline(PurpleConnection *gc)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
-	PurpleBuddyList *blist;
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleBuddy *buddy;
 
-	if ((blist = purple_get_blist()) == NULL)
-		return;
-
-	for (gnode = blist->root; gnode != NULL; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root();
+	     gnode != NULL;
+	     gnode = purple_blist_node_get_sibling_next(gnode))
+	{
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
 
-		for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) {
+		for (cnode = purple_blist_node_get_first_child(gnode);
+		     cnode != NULL;
+		     cnode = purple_blist_node_get_sibling_next(cnode))
+		{
 			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
 
-			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) {
+			for (bnode = purple_blist_node_get_first_child(cnode);
+			     bnode != NULL;
+			     bnode = purple_blist_node_get_sibling_next(bnode))
+			{
+				const gchar *name = NULL;
+
 				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 
 				buddy = (PurpleBuddy *)bnode;
+				
+				name = purple_buddy_get_name(buddy);
 
-				if (buddy->account != account)
+				if (purple_buddy_get_account(buddy) != account)
 					continue;
 
 				purple_prpl_got_user_status(
-					account, buddy->name, "offline", NULL);
+					account, name, "offline", NULL);
 
 				purple_debug_info("gg",
 					"ggp_buddylist_offline: gone: %s\n",
-					buddy->name);
+					name);
 			}
 		}
 	}
@@ -212,41 +230,46 @@
 /* char *ggp_buddylist_dump(PurpleAccount *account) {{{ */
 char *ggp_buddylist_dump(PurpleAccount *account)
 {
-	PurpleBuddyList *blist;
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleGroup *group;
 	PurpleBuddy *buddy;
-	GString *buddylist;
+	GString *buddylist = g_string_sized_new(1024);
 	char *ptr;
 
-	if ((blist = purple_get_blist()) == NULL)
-		return NULL;
-
-	buddylist = g_string_sized_new(1024);
-
-	for (gnode = blist->root; gnode != NULL; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root();
+	     gnode != NULL;
+	     gnode = purple_blist_node_get_sibling_next(gnode))
+	{
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
 
 		group = (PurpleGroup *)gnode;
 
-		for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) {
+		for (cnode = purple_blist_node_get_first_child(gnode);
+		     cnode != NULL;
+		     cnode = purple_blist_node_get_sibling_next(cnode))
+		{
 			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
 
-			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) {
-				gchar *name, *alias, *gname;
+			for (bnode = purple_blist_node_get_first_child(cnode);
+			     bnode != NULL;
+			     bnode = purple_blist_node_get_sibling_next(bnode))
+			{
+				const gchar *name, *alias, *gname;
 
 				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 
 				buddy = (PurpleBuddy *)bnode;
-				if (buddy->account != account)
+				if (purple_buddy_get_account(buddy) != account)
 					continue;
 
-				name = buddy->name;
-				alias = buddy->alias ? buddy->alias : buddy->name;
-				gname = group->name;
+				name = purple_buddy_get_name(buddy);
+				alias = purple_buddy_get_alias(buddy);
+				if(alias == NULL)
+					alias = name;
+				gname = purple_group_get_name(group);
 
 				g_string_append_printf(buddylist,
 						"%s;%s;%s;%s;%s;%s;%s;%s%s\r\n",
--- a/libpurple/protocols/gg/gg.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/gg/gg.c	Sat Mar 07 01:59:40 2009 +0000
@@ -2052,11 +2052,12 @@
 {
 	PurpleAccount *account;
 	GGPInfo *info = gc->proto_data;
+	const gchar *name = purple_buddy_get_name(buddy);
 
-	gg_add_notify(info->session, ggp_str_to_uin(buddy->name));
+	gg_add_notify(info->session, ggp_str_to_uin(name));
 
 	account = purple_connection_get_account(gc);
-	if (strcmp(purple_account_get_username(account), buddy->name) == 0) {
+	if (strcmp(purple_account_get_username(account), name) == 0) {
 		ggp_status_fake_to_self(account);
 	}
 }
@@ -2066,7 +2067,7 @@
 {
 	GGPInfo *info = gc->proto_data;
 
-	gg_remove_notify(info->session, ggp_str_to_uin(buddy->name));
+	gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy)));
 }
 
 static void ggp_join_chat(PurpleConnection *gc, GHashTable *data)
--- a/libpurple/protocols/irc/irc.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/irc/irc.c	Sat Mar 07 01:59:40 2009 +0000
@@ -564,7 +564,7 @@
 {
 	struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
 	struct irc_buddy *ib = g_new0(struct irc_buddy, 1);
-	ib->name = g_strdup(buddy->name);
+	ib->name = g_strdup(purple_buddy_get_name(buddy));
 	g_hash_table_insert(irc->buddies, ib->name, ib);
 
 	/* if the timer isn't set, this is during signon, so we don't want to flood
@@ -577,7 +577,7 @@
 static void irc_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
-	g_hash_table_remove(irc->buddies, buddy->name);
+	g_hash_table_remove(irc->buddies, purple_buddy_get_name(buddy));
 }
 
 static void read_input(struct irc_conn *irc, int len)
--- a/libpurple/protocols/irc/msgs.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/irc/msgs.c	Sat Mar 07 01:59:40 2009 +0000
@@ -79,6 +79,7 @@
 	PurpleConnection *gc;
 	PurpleStatus *status;
 	PurpleBlistNode *gnode, *cnode, *bnode;
+	PurpleAccount *account;
 
 	if ((gc = purple_account_get_connection(irc->account)) == NULL
 	    || PURPLE_CONNECTION_IS_CONNECTED(gc))
@@ -86,6 +87,7 @@
 
 	purple_connection_set_display_name(gc, nick);
 	purple_connection_set_state(gc, PURPLE_CONNECTED);
+	account = purple_connection_get_account(gc);
 
 	/* If we're away then set our away message */
 	status = purple_account_get_active_status(irc->account);
@@ -95,20 +97,29 @@
 	}
 
 	/* this used to be in the core, but it's not now */
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root();
+	     gnode;
+	     gnode = purple_blist_node_get_sibling_next(gnode))
+	{
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
+		for(cnode = purple_blist_node_get_first_child(gnode);
+		    cnode;
+		    cnode = purple_blist_node_get_sibling_next(cnode))
+		{
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for(bnode = cnode->child; bnode; bnode = bnode->next) {
+			for(bnode = purple_blist_node_get_first_child(cnode);
+			    bnode;
+			    bnode = purple_blist_node_get_sibling_next(bnode))
+			{
 				PurpleBuddy *b;
 				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 				b = (PurpleBuddy *)bnode;
-				if(b->account == gc->account) {
+				if(purple_buddy_get_account(b) == account) {
 					struct irc_buddy *ib = g_new0(struct irc_buddy, 1);
-					ib->name = g_strdup(b->name);
+					ib->name = g_strdup(purple_buddy_get_name(b));
 					g_hash_table_insert(irc->buddies, ib->name, ib);
 				}
 			}
--- a/libpurple/protocols/jabber/Makefile.am	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/Makefile.am	Sat Mar 07 01:59:40 2009 +0000
@@ -19,6 +19,8 @@
 			  disco.h \
 			  google.c \
 			  google.h \
+			  ibb.c \
+			  ibb.h \
 			  iq.c \
 			  iq.h \
 			  jabber.c \
--- a/libpurple/protocols/jabber/Makefile.mingw	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/Makefile.mingw	Sat Mar 07 01:59:40 2009 +0000
@@ -52,6 +52,7 @@
 			data.c \
 			disco.c \
 			google.c \
+			ibb.c \
 			iq.c \
 			jabber.c \
 			jutil.c \
--- a/libpurple/protocols/jabber/adhoccommands.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.c	Sat Mar 07 01:59:40 2009 +0000
@@ -47,26 +47,26 @@
 	JabberID *jabberid;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr = NULL;
-	
+
 	if(strcmp(type, "result"))
 		return;
-	
+
 	query = xmlnode_get_child_with_namespace(packet,"query","http://jabber.org/protocol/disco#items");
 	if(!query)
 		return;
 	node = xmlnode_get_attrib(query,"node");
 	if(!node || strcmp(node, "http://jabber.org/protocol/commands"))
 		return;
-	
+
 	if((jabberid = jabber_id_new(from))) {
 		if(jabberid->resource && (jb = jabber_buddy_find(js, from, TRUE)))
 			jbr = jabber_buddy_find_resource(jb, jabberid->resource);
 		jabber_id_free(jabberid);
 	}
-	
+
 	if(!jbr)
 		return;
-	
+
 	if(jbr->commands) {
 		/* since the list we just received is complete, wipe the old one */
 		while(jbr->commands) {
@@ -78,7 +78,7 @@
 			jbr->commands = g_list_delete_link(jbr->commands, jbr->commands);
 		}
 	}
-	
+
 	for(item = query->child; item; item = item->next) {
 		JabberAdHocCommands *cmd;
 		if(item->type != XMLNODE_TYPE_TAG)
@@ -86,11 +86,11 @@
 		if(strcmp(item->name, "item"))
 			continue;
 		cmd = g_new0(JabberAdHocCommands, 1);
-		
+
 		cmd->jid = g_strdup(xmlnode_get_attrib(item,"jid"));
 		cmd->node = g_strdup(xmlnode_get_attrib(item,"node"));
 		cmd->name = g_strdup(xmlnode_get_attrib(item,"name"));
-		
+
 		jbr->commands = g_list_append(jbr->commands,cmd);
 	}
 }
@@ -103,13 +103,13 @@
 	JabberAdHocActionInfo *actionInfo = user_data;
 	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
 	jabber_iq_set_callback(iq, jabber_adhoc_parse, NULL);
-	
+
 	xmlnode_set_attrib(iq->node, "to", actionInfo->who);
 	command = xmlnode_new_child(iq->node,"command");
 	xmlnode_set_namespace(command,"http://jabber.org/protocol/commands");
 	xmlnode_set_attrib(command,"sessionid",actionInfo->sessionid);
 	xmlnode_set_attrib(command,"node",actionInfo->node);
-	
+
 	/* cancel is handled differently on ad-hoc commands than regular forms */
 	if(!strcmp(xmlnode_get_namespace(result),"jabber:x:data") && !strcmp(xmlnode_get_attrib(result, "type"),"cancel")) {
 		xmlnode_set_attrib(command,"action","cancel");
@@ -118,7 +118,7 @@
 			xmlnode_set_attrib(command,"action",actionhandle);
 		xmlnode_insert_child(command,result);
 	}
-	
+
 	for(action = actionInfo->actionslist; action; action = g_list_next(action)) {
 		char *handle = action->data;
 		g_free(handle);
@@ -127,7 +127,7 @@
 	g_free(actionInfo->sessionid);
 	g_free(actionInfo->who);
 	g_free(actionInfo->node);
-	
+
 	jabber_iq_send(iq);
 }
 
@@ -136,12 +136,12 @@
 	const char *status = xmlnode_get_attrib(command,"status");
 	xmlnode *xdata = xmlnode_get_child_with_namespace(command,"x","jabber:x:data");
 	const char *type = xmlnode_get_attrib(packet,"type");
-	
+
 	if(type && !strcmp(type,"error")) {
 		char *msg = jabber_parse_error(js, packet, NULL);
 		if(!msg)
 			msg = g_strdup(_("Unknown Error"));
-		
+
 		purple_notify_error(NULL, _("Ad-Hoc Command Failed"),
 							_("Ad-Hoc Command Failed"), msg);
 		g_free(msg);
@@ -149,20 +149,20 @@
 	}
 	if(!type || strcmp(type,"result"))
 		return;
-	
+
 	if(!status)
 		return;
-	
+
 	if(!strcmp(status,"completed")) {
 		/* display result */
 		xmlnode *note = xmlnode_get_child(command,"note");
-		
+
 		if(note) {
 			char *data = xmlnode_get_data(note);
 			purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), data, NULL);
 			g_free(data);
 		}
-		
+
 		if(xdata)
 			jabber_x_data_request(js, xdata, (jabber_x_data_cb)do_adhoc_ignoreme, NULL);
 		return;
@@ -175,7 +175,7 @@
 		JabberAdHocActionInfo *actionInfo;
 		if(!xdata)
 			return; /* shouldn't happen */
-		
+
 		actions = xmlnode_get_child(command,"actions");
 		if(!actions) {
 			JabberXDataAction *defaultaction = g_new0(JabberXDataAction, 1);
@@ -196,13 +196,13 @@
 				}
 			}
 		}
-		
+
 		actionInfo = g_new0(JabberAdHocActionInfo, 1);
 		actionInfo->sessionid = g_strdup(xmlnode_get_attrib(command,"sessionid"));
 		actionInfo->who = g_strdup(xmlnode_get_attrib(packet,"from"));
 		actionInfo->node = g_strdup(xmlnode_get_attrib(command,"node"));
 		actionInfo->actionslist = actionslist;
-		
+
 		jabber_x_data_request_with_actions(js,xdata,actionslist,actionindex,do_adhoc_action_cb,actionInfo);
 	}
 }
@@ -211,8 +211,9 @@
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		JabberAdHocCommands *cmd = data;
 		PurpleBuddy *buddy = (PurpleBuddy *) node;
-		JabberStream *js = purple_account_get_connection(buddy->account)->proto_data;
-		
+		PurpleAccount *account = purple_buddy_get_account(buddy);
+		JabberStream *js = purple_account_get_connection(account)->proto_data;
+
 		jabber_adhoc_execute(js, cmd);
 	}
 }
@@ -220,7 +221,7 @@
 static void jabber_adhoc_server_got_list_cb(JabberStream *js, xmlnode *packet, gpointer data) {
 	xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#items");
 	xmlnode *item;
-	
+
 	if(!query)
 		return;
 
@@ -233,7 +234,7 @@
 		g_free(cmd);
 		js->commands = g_list_delete_link(js->commands, js->commands);
 	}
-	
+
 	/* re-fill list */
 	for(item = query->child; item; item = item->next) {
 		JabberAdHocCommands *cmd;
@@ -245,7 +246,7 @@
 		cmd->jid = g_strdup(xmlnode_get_attrib(item,"jid"));
 		cmd->node = g_strdup(xmlnode_get_attrib(item,"node"));
 		cmd->name = g_strdup(xmlnode_get_attrib(item,"name"));
-		
+
 		js->commands = g_list_append(js->commands,cmd);
 	}
 }
@@ -253,10 +254,10 @@
 void jabber_adhoc_server_get_list(JabberStream *js) {
 	JabberIq *iq = jabber_iq_new_query(js,JABBER_IQ_GET,"http://jabber.org/protocol/disco#items");
 	xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
-	
+
 	xmlnode_set_attrib(iq->node,"to",js->user->domain);
 	xmlnode_set_attrib(query,"node","http://jabber.org/protocol/commands");
-	
+
 	jabber_iq_set_callback(iq,jabber_adhoc_server_got_list_cb,NULL);
 	jabber_iq_send(iq);
 }
@@ -268,9 +269,9 @@
 	xmlnode_set_namespace(command,"http://jabber.org/protocol/commands");
 	xmlnode_set_attrib(command,"node",cmd->node);
 	xmlnode_set_attrib(command,"action","execute");
-	
+
 	jabber_iq_set_callback(iq,jabber_adhoc_parse,NULL);
-	
+
 	jabber_iq_send(iq);
 }
 
@@ -279,7 +280,7 @@
 	if(cmd) {
 		PurpleConnection *gc = (PurpleConnection *) action->context;
 		JabberStream *js = gc->proto_data;
-		
+
 		jabber_adhoc_execute(js, cmd);
 	}
 }
@@ -287,7 +288,7 @@
 void jabber_adhoc_init_server_commands(JabberStream *js, GList **m) {
 	GList *cmdlst;
 	JabberBuddy *jb;
-	
+
 	/* also add commands for other clients connected to the same account on another resource */
 	char *accountname = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
 	if((jb = jabber_buddy_find(js, accountname, TRUE))) {
@@ -306,7 +307,7 @@
 		}
 	}
 	g_free(accountname);
-	
+
 	/* now add server commands */
 	for(cmdlst = js->commands; cmdlst; cmdlst = g_list_next(cmdlst)) {
 		JabberAdHocCommands *cmd = cmdlst->data;
--- a/libpurple/protocols/jabber/auth.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/auth.c	Sat Mar 07 01:59:40 2009 +0000
@@ -384,7 +384,7 @@
 					}
 					/* Remove space which separated this mech from the next */
 					if (strlen(js->sasl_mechs->str) > 0 && ((js->sasl_mechs->str)[0] == ' ')) {
-						g_string_erase(js->sasl_mechs, 0, 1);	
+						g_string_erase(js->sasl_mechs, 0, 1);
 					}
 					again = TRUE;
 				}
@@ -397,7 +397,7 @@
 		auth = xmlnode_new("auth");
 		xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl");
 		xmlnode_set_attrib(auth, "mechanism", js->current_mech);
-		
+
 		xmlnode_set_attrib(auth, "xmlns:ga", "http://www.google.com/talk/protocol/auth");
 		xmlnode_set_attrib(auth, "ga:client-uses-full-bind-result", "true");
 
@@ -1057,13 +1057,13 @@
 			}
 			/* Remove space which separated this mech from the next */
 			if (strlen(js->sasl_mechs->str) > 0 && ((js->sasl_mechs->str)[0] == ' ')) {
-				g_string_erase(js->sasl_mechs, 0, 1);	
-			}			
+				g_string_erase(js->sasl_mechs, 0, 1);
+			}
 		}
 		if (strlen(js->sasl_mechs->str)) {
 			/* If we have remaining mechs to try, do so */
 			sasl_dispose(&js->sasl);
-			
+
 			jabber_auth_start_cyrus(js);
 			return;
 		}
--- a/libpurple/protocols/jabber/buddy.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sat Mar 07 01:59:40 2009 +0000
@@ -170,7 +170,7 @@
 	g_return_if_fail(jbr != NULL);
 
 	jbr->jb->resources = g_list_remove(jbr->jb->resources, jbr);
-	
+
 	while(jbr->commands) {
 		JabberAdHocCommands *cmd = jbr->commands->data;
 		g_free(cmd->jid);
@@ -424,7 +424,7 @@
 {
 	PurpleStoredImage *img;
 	JabberIq *iq;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	xmlnode *vc_node;
 	const struct tag_attr *tag_attr;
 
@@ -495,7 +495,10 @@
 
 void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
-	if(((JabberStream*)gc->proto_data)->pep) {
+	PurplePresence *gpresence;
+	PurpleStatus *status;
+
+	if(((JabberStream*)purple_connection_get_protocol_data(gc))->pep) {
 		/* XEP-0084: User Avatars */
 		if(img) {
 			/*
@@ -538,37 +541,37 @@
 				guint32 height = ntohl(png->ihdr.height);
 				xmlnode *publish, *item, *data, *metadata, *info;
 				char *lengthstring, *widthstring, *heightstring;
-				
+
 				/* compute the sha1 hash */
 				char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
 				char *base64avatar;
-				
+
 				publish = xmlnode_new("publish");
 				xmlnode_set_attrib(publish,"node",AVATARNAMESPACEDATA);
-				
+
 				item = xmlnode_new_child(publish, "item");
 				xmlnode_set_attrib(item, "id", hash);
-				
+
 				data = xmlnode_new_child(item, "data");
 				xmlnode_set_namespace(data,AVATARNAMESPACEDATA);
-				
+
 				base64avatar = purple_base64_encode(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
 				xmlnode_insert_data(data,base64avatar,-1);
 				g_free(base64avatar);
-				
+
 				/* publish the avatar itself */
-				jabber_pep_publish((JabberStream*)gc->proto_data, publish);
-				
+				jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish);
+
 				/* next step: publish the metadata */
 				publish = xmlnode_new("publish");
 				xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA);
-				
+
 				item = xmlnode_new_child(publish, "item");
 				xmlnode_set_attrib(item, "id", hash);
-				
+
 				metadata = xmlnode_new_child(item, "metadata");
 				xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA);
-				
+
 				info = xmlnode_new_child(metadata, "info");
 				xmlnode_set_attrib(info, "id", hash);
 				xmlnode_set_attrib(info, "type", "image/png");
@@ -581,10 +584,10 @@
 				heightstring = g_strdup_printf("%u", height);
 				xmlnode_set_attrib(info, "height", heightstring);
 				g_free(heightstring);
-				
+
 				/* publish the metadata */
-				jabber_pep_publish((JabberStream*)gc->proto_data, publish);
-				
+				jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish);
+
 				g_free(hash);
 			} else {
 				purple_debug_error("jabber", "jabber_set_buddy_icon received non-png data");
@@ -793,18 +796,18 @@
 								  (jbr->client.version ? jbr->client.version : ""));
 			purple_notify_user_info_add_pair(user_info, _("Client"), tmp);
 			g_free(tmp);
-			
+
 			if(jbr->client.os) {
 				purple_notify_user_info_prepend_pair(user_info, _("Operating System"), jbr->client.os);
 			}
-		}		
+		}
 		if(jbir) {
 			if(jbir->idle_seconds > 0) {
 				char *idle = purple_str_seconds_to_string(jbir->idle_seconds);
 				purple_notify_user_info_prepend_pair(user_info, _("Idle"), idle);
 				g_free(idle);
 			}
-		}		
+		}
 		if(jbr) {
 			char *purdy = NULL;
 			const char *status_name = jabber_buddy_state_get_name(jbr->state);
@@ -822,7 +825,7 @@
 		} else {
 			purple_notify_user_info_prepend_pair(user_info, _("Status"), _("Unknown"));
 		}
-#if 0 
+#if 0
 		/* #if 0 this for now; I think this would be far more useful if we limited this to a particular set of features
  		 * of particular interest (-vv jumps out as one). As it is now, I don't picture people getting all excited: "Oh sweet crap!
  		 * So-and-so supports 'jabber:x:data' AND 'Collaborative Data Objects'!"
@@ -833,7 +836,7 @@
 			GList *iter;
 			for(iter = jbr->caps->features; iter; iter = g_list_next(iter)) {
 				const char *feature = iter->data;
-				
+
 				if(!strcmp(feature, "jabber:iq:last"))
 					feature = _("Last Activity");
 				else if(!strcmp(feature, "http://jabber.org/protocol/disco#info"))
@@ -942,7 +945,7 @@
 
 			if(strlen(tmp->str) > 0)
 				purple_notify_user_info_prepend_pair(user_info, _("Capabilities"), tmp->str);
-			
+
 			g_string_free(tmp, TRUE);
 		}
 #endif
@@ -962,7 +965,7 @@
 				purple_notify_user_info_prepend_pair(user_info,
 												 _("Client"), tmp);
 				g_free(tmp);
-				
+
 				if(jbr->client.os) {
 					purple_notify_user_info_prepend_pair(user_info, _("Operating System"), jbr->client.os);
 				}
@@ -981,14 +984,14 @@
 				purdy = purple_strdup_withhtml(jbr->status);
 			if(status_name && purdy && !strcmp(status_name, purdy))
 				status_name = NULL;
-			
+
 			tmp = g_strdup_printf("%s%s%s", (status_name ? status_name : ""),
 								  ((status_name && purdy) ? ": " : ""),
 								  (purdy ? purdy : ""));
 			purple_notify_user_info_prepend_pair(user_info, _("Status"), tmp);
 			g_free(tmp);
 			g_free(purdy);
-			
+
 			if(multiple_resources) {
 				tmp = g_strdup_printf("%d", jbr->priority);
 				purple_notify_user_info_prepend_pair(user_info, _("Priority"), tmp);
@@ -1003,7 +1006,7 @@
 				GList *iter;
 				for(iter = jbr->caps->features; iter; iter = g_list_next(iter)) {
 					const char *feature = iter->data;
-					
+
 					if(!strcmp(feature, "jabber:iq:last"))
 						feature = _("Last Activity");
 					else if(!strcmp(feature, "http://jabber.org/protocol/disco#info"))
@@ -1106,13 +1109,13 @@
 						feature = _("Hop Check");
 					else if(g_str_has_suffix(feature, "+notify"))
 						feature = NULL;
-					
+
 					if(feature)
 						g_string_append_printf(tmp, "%s\n", feature);
 				}
 				if(strlen(tmp->str) > 0)
 					purple_notify_user_info_prepend_pair(user_info, _("Capabilities"), tmp->str);
-				
+
 				g_string_free(tmp, TRUE);
 			}
 #endif
@@ -1180,7 +1183,7 @@
 void jabber_vcard_fetch_mine(JabberStream *js)
 {
 	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);
@@ -1254,13 +1257,13 @@
 					}
 					g_free(text2);
 				}
-			} else if(text && !strcmp(child->name, "NICKNAME")) {				
+			} else if(text && !strcmp(child->name, "NICKNAME")) {
 				/* Prefer the Nickcname to the Full Name as the serverside alias if it's not just part of the jid.
 				 * Ignore it if it's part of the jid. */
 				if (strstr(bare_jid, text) == NULL) {
 					g_free(serverside_alias);
 					serverside_alias = g_strdup(text);
-					
+
 					purple_notify_user_info_add_pair(user_info, _("Nickname"), text);
 				}
 			} else if(text && !strcmp(child->name, "BDAY")) {
@@ -1305,7 +1308,7 @@
 					}
 					g_free(text2);
 				}
-				
+
 				if (address_line_added)
 					purple_notify_user_info_add_section_break(user_info);
 
@@ -1347,8 +1350,8 @@
 					escaped = g_markup_escape_text(userid, -1);
 					mailto = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", escaped, escaped);
 					purple_notify_user_info_add_pair(user_info, _("Email"), mailto);
-					
-					g_free(mailto);					
+
+					g_free(mailto);
 					g_free(escaped);
 					g_free(userid);
 				}
@@ -1415,7 +1418,7 @@
 		if (b) {
 			purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", serverside_alias);
 		}
-		
+
 		g_free(serverside_alias);
 	}
 
@@ -1437,7 +1440,7 @@
 					 "do_buddy_avatar_update_fromurl got error \"%s\"", error_message);
 		return;
 	}
-	
+
 	purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, (void*)url_text, len, info->id);
 	g_free(info->from);
 	g_free(info->id);
@@ -1452,29 +1455,29 @@
 	size_t size;
 	if(!items)
 		return;
-	
+
 	item = xmlnode_get_child(items, "item");
 	if(!item)
 		return;
-	
+
 	data = xmlnode_get_child_with_namespace(item,"data",AVATARNAMESPACEDATA);
 	if(!data)
 		return;
-	
+
 	checksum = xmlnode_get_attrib(item,"id");
 	if(!checksum)
 		return;
-	
+
 	b64data = xmlnode_get_data(data);
 	if(!b64data)
 		return;
-	
+
 	img = purple_base64_decode(b64data, &size);
 	if(!img) {
 		g_free(b64data);
 		return;
 	}
-	
+
 	purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum);
 	g_free(b64data);
 }
@@ -1485,7 +1488,7 @@
 	xmlnode *item, *metadata;
 	if(!buddy)
 		return;
-	
+
 	checksum = purple_buddy_icons_get_checksum_for_user(buddy);
 	item = xmlnode_get_child(items,"item");
 	metadata = xmlnode_get_child_with_namespace(item, "metadata", AVATARNAMESPACEMETA);
@@ -1505,7 +1508,7 @@
 			if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) {
 				const char *type = xmlnode_get_attrib(info,"type");
 				const char *id = xmlnode_get_attrib(info,"id");
-				
+
 				if(checksum && id && !strcmp(id, checksum)) {
 					/* we already have that avatar, so we don't have to do anything */
 					goodinfo = NULL;
@@ -1521,7 +1524,7 @@
 		} else if(goodinfo) {
 			const char *url = xmlnode_get_attrib(goodinfo, "url");
 			const char *id = xmlnode_get_attrib(goodinfo,"id");
-			
+
 			/* the avatar might either be stored in a pep node, or on a HTTP/HTTPS URL */
 			if(!url)
 				jabber_pep_request_item(js, from, AVATARNAMESPACEDATA, id, do_buddy_avatar_update_data);
@@ -1777,7 +1780,7 @@
 
 void jabber_buddy_get_info(PurpleConnection *gc, const char *who)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberID *jid = jabber_id_new(who);
 
 	if (!jid)
@@ -1837,10 +1840,10 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	js = gc->proto_data;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	js = purple_connection_get_protocol_data(gc);
 
-	jabber_buddy_set_invisibility(js, buddy->name, TRUE);
+	jabber_buddy_set_invisibility(js, purple_buddy_get_name(buddy), TRUE);
 }
 
 static void jabber_buddy_make_visible(PurpleBlistNode *node, gpointer data)
@@ -1852,10 +1855,10 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	js = gc->proto_data;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	js = purple_connection_get_protocol_data(gc);
 
-	jabber_buddy_set_invisibility(js, buddy->name, FALSE);
+	jabber_buddy_set_invisibility(js, purple_buddy_get_name(buddy), FALSE);
 }
 
 static void jabber_buddy_cancel_presence_notification(PurpleBlistNode *node,
@@ -1868,11 +1871,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	js = gc->proto_data;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	js = purple_connection_get_protocol_data(gc);
 
 	/* I wonder if we should prompt the user before doing this */
-	jabber_presence_subscription_set(js, buddy->name, "unsubscribed");
+	jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "unsubscribed");
 }
 
 static void jabber_buddy_rerequest_auth(PurpleBlistNode *node, gpointer data)
@@ -1884,10 +1887,10 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	js = gc->proto_data;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	js = purple_connection_get_protocol_data(gc);
 
-	jabber_presence_subscription_set(js, buddy->name, "subscribe");
+	jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "subscribe");
 }
 
 
@@ -1900,18 +1903,18 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	js = gc->proto_data;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	js = purple_connection_get_protocol_data(gc);
 
-	jabber_presence_subscription_set(js, buddy->name, "unsubscribe");
+	jabber_presence_subscription_set(js, purple_buddy_get_name(buddy), "unsubscribe");
 }
 
 static void jabber_buddy_login(PurpleBlistNode *node, gpointer data) {
 	if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		/* simply create a directed presence of the current status */
 		PurpleBuddy *buddy = (PurpleBuddy *) node;
-		PurpleConnection *gc = purple_account_get_connection(buddy->account);
-		JabberStream *js = gc->proto_data;
+		PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+		JabberStream *js = purple_connection_get_protocol_data(gc);
 		PurpleAccount *account = purple_connection_get_account(gc);
 		PurplePresence *gpresence = purple_account_get_presence(account);
 		PurpleStatus *status = purple_presence_get_active_status(gpresence);
@@ -1919,14 +1922,14 @@
 		JabberBuddyState state;
 		char *msg;
 		int priority;
-		
+
 		purple_status_to_jabber(status, &state, &msg, &priority);
 		presence = jabber_presence_create_js(js, state, msg, priority);
-		
+
 		g_free(msg);
-		
-		xmlnode_set_attrib(presence, "to", buddy->name);
-		
+
+		xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy));
+
 		jabber_send(js, presence);
 		xmlnode_free(presence);
 	}
@@ -1936,13 +1939,14 @@
 	if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		/* simply create a directed unavailable presence */
 		PurpleBuddy *buddy = (PurpleBuddy *) node;
-		JabberStream *js = purple_account_get_connection(buddy->account)->proto_data;
+		PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+		JabberStream *js = purple_connection_get_protocol_data(gc);
 		xmlnode *presence;
-		
+
 		presence = jabber_presence_create_js(js, JABBER_BUDDY_STATE_UNAVAILABLE, NULL, 0);
-		
-		xmlnode_set_attrib(presence, "to", buddy->name);
-		
+
+		xmlnode_set_attrib(presence, "to", purple_buddy_get_name(buddy));
+
 		jabber_send(js, presence);
 		xmlnode_free(presence);
 	}
@@ -1950,9 +1954,10 @@
 
 static GList *jabber_buddy_menu(PurpleBuddy *buddy)
 {
-	PurpleConnection *gc = purple_account_get_connection(buddy->account);
-	JabberStream *js = gc->proto_data;
-	JabberBuddy *jb = jabber_buddy_find(js, buddy->name, TRUE);
+	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	JabberStream *js = purple_connection_get_protocol_data(gc);
+	const char *name = purple_buddy_get_name(buddy);
+	JabberBuddy *jb = jabber_buddy_find(js, name, TRUE);
 	GList *jbrs;
 
 	GList *m = NULL;
@@ -1998,7 +2003,7 @@
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
-	
+
 	/*
 	 * This if-condition implements parts of XEP-0100: Gateway Interaction
 	 *
@@ -2007,7 +2012,7 @@
 	 * that gateways on the roster can be identified by having no '@' in their jid. This is a faily safe assumption, since
 	 * people don't tend to have a server or other service there.
 	 */
-	if (g_utf8_strchr(buddy->name, -1, '@') == NULL) {
+	if (g_utf8_strchr(name, -1, '@') == NULL) {
 		act = purple_menu_action_new(_("Log In"),
 									 PURPLE_CALLBACK(jabber_buddy_login),
 									 NULL, NULL);
@@ -2017,7 +2022,7 @@
 									 NULL, NULL);
 		m = g_list_append(m, act);
 	}
-	
+
 	/* add all ad hoc commands to the action menu */
 	for(jbrs = jb->resources; jbrs; jbrs = g_list_next(jbrs)) {
 		JabberBuddyResource *jbr = jbrs->data;
@@ -2475,7 +2480,7 @@
 void jabber_user_search_begin(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	purple_request_input(gc, _("Enter a User Directory"), _("Enter a User Directory"),
 			_("Select a user directory to search"),
--- a/libpurple/protocols/jabber/chat.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/chat.c	Sat Mar 07 01:59:40 2009 +0000
@@ -353,7 +353,7 @@
 	jcm = g_hash_table_lookup(chat->members, who);
 	if (jcm != NULL && jcm->jid)
 		return g_strdup(jcm->jid);
-	
+
 
 	return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who);
 }
@@ -685,7 +685,7 @@
 		xmlnode_insert_data(status, msg, -1);
 	}
 	jabber_send(chat->js, presence);
-	
+
 	xmlnode_free(presence);
 	g_free(room_jid);
 }
@@ -811,7 +811,7 @@
 			FALSE, FALSE, NULL,
 			_("Find Rooms"), PURPLE_CALLBACK(roomlist_ok_cb),
 			_("Cancel"), PURPLE_CALLBACK(roomlist_cancel_cb),
-			purple_connection_get_account(gc), NULL, NULL,			 
+			purple_connection_get_account(gc), NULL, NULL,
 			js);
 
 	return js->roomlist;
@@ -1033,7 +1033,7 @@
 	iq = jabber_iq_new_query(chat->js, JABBER_IQ_GET,
 			"http://jabber.org/protocol/disco#info");
 
-	xmlnode_set_attrib(iq->node, "to", room_jid); 
+	xmlnode_set_attrib(iq->node, "to", room_jid);
 
 	query = xmlnode_get_child(iq->node, "query");
 
--- a/libpurple/protocols/jabber/data.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/data.c	Sat Mar 07 01:59:40 2009 +0000
@@ -3,17 +3,17 @@
  * 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 Library 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 02110-1301,  USA
  */
- 
+
 #include <stdlib.h>
 #include <glib.h>
 #include <string.h>
@@ -71,7 +71,7 @@
 
 	data->cid = g_strdup(xmlnode_get_attrib(tag, "cid"));
 	data->type = g_strdup(xmlnode_get_attrib(tag, "type"));
-	
+
 	raw_data = xmlnode_get_data(tag);
 	data->data = purple_base64_decode(raw_data, &size);
 	data->size = size;
@@ -176,7 +176,7 @@
 jabber_data_find_remote_by_cid(const gchar *cid)
 {
 	purple_debug_info("jabber", "lookup remote smiley with cid = %s\n", cid);
-	
+
 	return g_hash_table_lookup(remote_data_by_cid, cid);
 }
 
@@ -186,7 +186,7 @@
 	purple_debug_info("jabber", "associating local smiley\n alt = %s, cid = %s\n",
 		alt, jabber_data_get_cid(data));
 	g_hash_table_insert(local_data_by_alt, g_strdup(alt), data);
-	g_hash_table_insert(local_data_by_cid, g_strdup(jabber_data_get_cid(data)), 
+	g_hash_table_insert(local_data_by_cid, g_strdup(jabber_data_get_cid(data)),
 		data);
 }
 
@@ -195,7 +195,7 @@
 {
 	purple_debug_info("jabber", "associating remote smiley, cid = %s\n",
 		jabber_data_get_cid(data));
-	g_hash_table_insert(remote_data_by_cid, g_strdup(jabber_data_get_cid(data)), 
+	g_hash_table_insert(remote_data_by_cid, g_strdup(jabber_data_get_cid(data)),
 		data);
 }
 
--- a/libpurple/protocols/jabber/data.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/data.h	Sat Mar 07 01:59:40 2009 +0000
@@ -3,17 +3,17 @@
  * 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 Library 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 02110-1301,  USA
  */
- 
+
 #ifndef JABBER_DATA_H
 #define JABBER_DATA_H
 
--- a/libpurple/protocols/jabber/disco.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/disco.c	Sat Mar 07 01:59:40 2009 +0000
@@ -88,6 +88,7 @@
 void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) {
 	const char *from = xmlnode_get_attrib(packet, "from");
 	const char *type = xmlnode_get_attrib(packet, "type");
+
 	if(!from || !type)
 		return;
 
@@ -118,6 +119,13 @@
 		if(node)
 			xmlnode_set_attrib(query, "node", node);
 
+		if(!node || !strcmp(node, CAPS0115_NODE "#" VERSION)) {
+			identity = xmlnode_new_child(query, "identity");
+			xmlnode_set_attrib(identity, "category", "client");
+			xmlnode_set_attrib(identity, "type", "pc"); /* XXX: bot, console,
+														 * handheld, pc, phone,
+														 * web */
+			xmlnode_set_attrib(identity, "name", PACKAGE);
 
 		if(!node || !strcmp(node, node_uri)) {
 			GList *features, *identities;
@@ -226,6 +234,10 @@
 				else if(!strcmp(var, "http://jabber.org/protocol/commands")) {
 					capabilities |= JABBER_CAP_ADHOC;
 				}
+				else if(!strcmp(var, "http://jabber.org/protocol/ibb")) {
+					purple_debug_info("jabber", "remote supports IBB\n");
+					capabilities |= JABBER_CAP_IBB;
+				}
 			}
 		}
 
@@ -269,7 +281,7 @@
 	if(type && !strcmp(type, "get")) {
 		JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT,
 				"http://jabber.org/protocol/disco#items");
-		
+
 		/* preserve node */
 		xmlnode *iq_query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
 		if(iq_query) {
--- a/libpurple/protocols/jabber/google.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Sat Mar 07 01:59:40 2009 +0000
@@ -283,6 +283,7 @@
 	xmlnode *group;
 	PurpleBuddy *b;
 	JabberBuddy *jb;
+	const char *balias;
 
 	js = (JabberStream*)(gc->proto_data);
 
@@ -309,13 +310,14 @@
 		g = purple_buddy_get_group(b);
 
 		group = xmlnode_new_child(item, "group");
-		xmlnode_insert_data(group, g->name, -1);
+		xmlnode_insert_data(group, purple_group_get_name(g), -1);
 
 		buddies = buddies->next;
 	}
 
+	balias = purple_buddy_get_local_buddy_alias(b);
 	xmlnode_set_attrib(item, "jid", who);
-	xmlnode_set_attrib(item, "name", b->alias ? b->alias : "");
+	xmlnode_set_attrib(item, "name", balias ? balias : "");
 	xmlnode_set_attrib(item, "gr:t", "B");
 	xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
 	xmlnode_set_attrib(query, "gr:ext", "2");
@@ -348,6 +350,7 @@
 	xmlnode *item;
 	xmlnode *group;
 	PurpleBuddy *b;
+	const char *balias;
 
 	g_return_if_fail(gc != NULL);
 	g_return_if_fail(who != NULL);
@@ -357,7 +360,7 @@
 	if (!js || !js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
 		return;
 
-	buddies = purple_find_buddies(js->gc->account, who);
+	buddies = purple_find_buddies(purple_connection_get_account(js->gc), who);
 	if(!buddies)
 		return;
 
@@ -375,13 +378,14 @@
 		g = purple_buddy_get_group(b);
 
 		group = xmlnode_new_child(item, "group");
-		xmlnode_insert_data(group, g->name, -1);
+		xmlnode_insert_data(group, purple_group_get_name(g), -1);
 
 		buddies = buddies->next;
 	}
 
+	balias = purple_buddy_get_local_buddy_alias(b);
 	xmlnode_set_attrib(item, "jid", who);
-	xmlnode_set_attrib(item, "name", b->alias ? b->alias : "");
+	xmlnode_set_attrib(item, "name", balias ? balias : "");
 	xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
 	xmlnode_set_attrib(query, "gr:ext", "2");
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/ibb.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,521 @@
+/*
+ * 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 Library 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 02110-1301,  USA
+ */
+
+#include "internal.h"
+#include "ibb.h"
+#include "debug.h"
+#include "xmlnode.h"
+
+#define JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE 4096
+
+static GHashTable *jabber_ibb_sessions = NULL;
+static GList *open_handlers = NULL;
+
+JabberIBBSession *
+jabber_ibb_session_create(JabberStream *js, const gchar *sid, const gchar *who,
+	gpointer user_data)
+{
+	JabberIBBSession *sess = g_new0(JabberIBBSession, 1);
+	sess->js = js;
+	if (sid) {
+		sess->sid = g_strdup(sid);
+	} else {
+		sess->sid = jabber_get_next_id(js);
+	}
+	sess->who = g_strdup(who);
+	sess->block_size = JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE;
+	sess->state = JABBER_IBB_SESSION_NOT_OPENED;
+	sess->user_data = user_data;
+
+	g_hash_table_insert(jabber_ibb_sessions, sess->sid, sess);
+
+	return sess;
+}
+
+JabberIBBSession *
+jabber_ibb_session_create_from_xmlnode(JabberStream *js, xmlnode *packet,
+	gpointer user_data)
+{
+	JabberIBBSession *sess = NULL;
+	xmlnode *open = xmlnode_get_child_with_namespace(packet, "open",
+		XEP_0047_NAMESPACE);
+	const gchar *sid = xmlnode_get_attrib(open, "sid");
+	const gchar *block_size = xmlnode_get_attrib(open, "block-size");
+
+	if (!open) {
+		return NULL;
+	}
+
+	if (!sid || !block_size) {
+		purple_debug_error("jabber",
+			"IBB session open tag requires sid and block-size attributes\n");
+		g_free(sess);
+		return NULL;
+	}
+
+	sess = jabber_ibb_session_create(js, sid,
+			xmlnode_get_attrib(packet, "from"), user_data);
+	sess->id = g_strdup(xmlnode_get_attrib(packet, "id"));
+	sess->block_size = atoi(block_size);
+	/* if we create a session from an incoming <open/> request, it means the
+	  session is immediatly open... */
+	sess->state = JABBER_IBB_SESSION_OPENED;
+
+	return sess;
+}
+
+void
+jabber_ibb_session_destroy(JabberIBBSession *sess)
+{
+	purple_debug_info("jabber", "IBB: destroying session %p %s\n", sess,
+		sess->sid);
+
+	if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) {
+		jabber_ibb_session_close(sess);
+	}
+
+	if (sess->last_iq_id) {
+		purple_debug_info("jabber", "IBB: removing callback for <iq/> %s\n",
+			sess->last_iq_id);
+		jabber_iq_remove_callback_by_id(jabber_ibb_session_get_js(sess),
+			sess->last_iq_id);
+		g_free(sess->last_iq_id);
+		sess->last_iq_id = NULL;
+	}
+
+	g_hash_table_remove(jabber_ibb_sessions, sess->sid);
+	g_free(sess->id);
+	g_free(sess->sid);
+	g_free(sess->who);
+	g_free(sess);
+}
+
+const gchar *
+jabber_ibb_session_get_sid(const JabberIBBSession *sess)
+{
+	return sess->sid;
+}
+
+JabberStream *
+jabber_ibb_session_get_js(JabberIBBSession *sess)
+{
+	return sess->js;
+}
+
+const gchar *
+jabber_ibb_session_get_who(const JabberIBBSession *sess)
+{
+	return sess->who;
+}
+
+guint16
+jabber_ibb_session_get_send_seq(const JabberIBBSession *sess)
+{
+	return sess->send_seq;
+}
+
+guint16
+jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess)
+{
+	return sess->recv_seq;
+}
+
+JabberIBBSessionState
+jabber_ibb_session_get_state(const JabberIBBSession *sess)
+{
+	return sess->state;
+}
+
+gsize
+jabber_ibb_session_get_block_size(const JabberIBBSession *sess)
+{
+	return sess->block_size;
+}
+
+void
+jabber_ibb_session_set_block_size(JabberIBBSession *sess, gsize size)
+{
+	if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_NOT_OPENED) {
+		sess->block_size = size;
+	} else {
+		purple_debug_error("jabber",
+			"Can't set block size on an open IBB session\n");
+	}
+}
+
+gpointer
+jabber_ibb_session_get_user_data(JabberIBBSession *sess)
+{
+	return sess->user_data;
+}
+
+void
+jabber_ibb_session_set_opened_callback(JabberIBBSession *sess,
+	JabberIBBOpenedCallback *cb)
+{
+	sess->opened_cb = cb;
+}
+
+void
+jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess,
+	JabberIBBSentCallback *cb)
+{
+	sess->data_sent_cb = cb;
+}
+
+void
+jabber_ibb_session_set_closed_callback(JabberIBBSession *sess,
+	JabberIBBClosedCallback *cb)
+{
+	sess->closed_cb = cb;
+}
+
+void
+jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess,
+	JabberIBBDataCallback *cb)
+{
+	sess->data_received_cb = cb;
+}
+
+void
+jabber_ibb_session_set_error_callback(JabberIBBSession *sess,
+	JabberIBBErrorCallback *cb)
+{
+	sess->error_cb = cb;
+}
+
+static void
+jabber_ibb_session_opened_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	JabberIBBSession *sess = (JabberIBBSession *) data;
+
+	if (strcmp(xmlnode_get_attrib(packet, "type"), "error") == 0) {
+		sess->state = JABBER_IBB_SESSION_ERROR;
+	} else {
+		sess->state = JABBER_IBB_SESSION_OPENED;
+	}
+
+	if (sess->opened_cb) {
+		sess->opened_cb(sess);
+	}
+}
+
+void
+jabber_ibb_session_open(JabberIBBSession *sess)
+{
+	if (jabber_ibb_session_get_state(sess) != JABBER_IBB_SESSION_NOT_OPENED) {
+		purple_debug_error("jabber",
+			"jabber_ibb_session called on an already open stream\n");
+	} else {
+		JabberIq *set = jabber_iq_new(sess->js, JABBER_IQ_SET);
+		xmlnode *open = xmlnode_new("open");
+		gchar block_size[10];
+
+		xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+		xmlnode_set_namespace(open, XEP_0047_NAMESPACE);
+		xmlnode_set_attrib(open, "sid", jabber_ibb_session_get_sid(sess));
+		g_snprintf(block_size, sizeof(block_size), "%" G_GSIZE_FORMAT,
+			jabber_ibb_session_get_block_size(sess));
+		xmlnode_set_attrib(open, "block-size", block_size);
+		xmlnode_insert_child(set->node, open);
+
+		jabber_iq_set_callback(set, jabber_ibb_session_opened_cb, sess);
+
+		jabber_iq_send(set);
+	}
+}
+
+void
+jabber_ibb_session_close(JabberIBBSession *sess)
+{
+	JabberIBBSessionState state = jabber_ibb_session_get_state(sess);
+
+	if (state != JABBER_IBB_SESSION_OPENED && state != JABBER_IBB_SESSION_ERROR) {
+		purple_debug_error("jabber",
+			"jabber_ibb_session_close called on a session that has not been"
+			"opened\n");
+	} else {
+		JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
+			JABBER_IQ_SET);
+		xmlnode *close = xmlnode_new("close");
+
+		xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+		xmlnode_set_namespace(close, XEP_0047_NAMESPACE);
+		xmlnode_set_attrib(close, "sid", jabber_ibb_session_get_sid(sess));
+		xmlnode_insert_child(set->node, close);
+		jabber_iq_send(set);
+		sess->state = JABBER_IBB_SESSION_CLOSED;
+	}
+}
+
+void
+jabber_ibb_session_accept(JabberIBBSession *sess)
+{
+	JabberIq *result = jabber_iq_new(jabber_ibb_session_get_js(sess),
+		JABBER_IQ_RESULT);
+
+	xmlnode_set_attrib(result->node, "to", jabber_ibb_session_get_who(sess));
+	jabber_iq_set_id(result, sess->id);
+	jabber_iq_send(result);
+	sess->state = JABBER_IBB_SESSION_OPENED;
+}
+
+static void
+jabber_ibb_session_send_acknowledge_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	JabberIBBSession *sess = (JabberIBBSession *) data;
+	xmlnode *error = xmlnode_get_child(packet, "error");
+
+	if (sess) {
+		/* reset callback */
+		if (sess->last_iq_id) {
+			g_free(sess->last_iq_id);
+			sess->last_iq_id = NULL;
+		}
+
+		if (error) {
+			jabber_ibb_session_close(sess);
+			sess->state = JABBER_IBB_SESSION_ERROR;
+
+			if (sess->error_cb) {
+				sess->error_cb(sess);
+			}
+		} else {
+			if (sess->data_sent_cb) {
+				sess->data_sent_cb(sess);
+			}
+		}
+	} else {
+		/* the session has gone away, it was probably cancelled */
+		purple_debug_info("jabber",
+			"got response from send data, but IBB session is no longer active\n");
+	}
+}
+
+void
+jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data,
+                             gsize size)
+{
+	JabberIBBSessionState state = jabber_ibb_session_get_state(sess);
+
+	purple_debug_info("jabber", "sending data block of %" G_GSIZE_FORMAT " bytes on IBB stream\n",
+		size);
+
+	if (state != JABBER_IBB_SESSION_OPENED) {
+		purple_debug_error("jabber",
+			"trying to send data on a non-open IBB session\n");
+	} else if (size > jabber_ibb_session_get_block_size(sess)) {
+		purple_debug_error("jabber",
+			"trying to send a too large packet in the IBB session\n");
+	} else {
+		JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
+			JABBER_IQ_SET);
+		xmlnode *data_element = xmlnode_new("data");
+		char *base64 = purple_base64_encode(data, size);
+		char seq[10];
+		g_snprintf(seq, sizeof(seq), "%u", jabber_ibb_session_get_send_seq(sess));
+
+		xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+		xmlnode_set_namespace(data_element, XEP_0047_NAMESPACE);
+		xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess));
+		xmlnode_set_attrib(data_element, "seq", seq);
+		xmlnode_insert_data(data_element, base64, -1);
+
+		xmlnode_insert_child(set->node, data_element);
+
+		purple_debug_info("jabber",
+			"IBB: setting send <iq/> callback for session %p %s\n", sess,
+			sess->sid);
+		jabber_iq_set_callback(set, jabber_ibb_session_send_acknowledge_cb, sess);
+		sess->last_iq_id = g_strdup(xmlnode_get_attrib(set->node, "id"));
+		purple_debug_info("jabber", "IBB: set sess->last_iq_id: %s\n",
+			sess->last_iq_id);
+		jabber_iq_send(set);
+
+		g_free(base64);
+		(sess->send_seq)++;
+	}
+}
+
+static void
+jabber_ibb_send_error_response(JabberStream *js, xmlnode *packet)
+{
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_ERROR);
+	xmlnode *error = xmlnode_new("error");
+	xmlnode *item_not_found = xmlnode_new("item-not-found");
+
+	xmlnode_set_namespace(item_not_found,
+		"urn:ietf:params:xml:ns:xmpp-stanzas");
+	xmlnode_set_attrib(error, "code", "440");
+	xmlnode_set_attrib(error, "type", "cancel");
+	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+	xmlnode_set_attrib(result->node, "to",
+		xmlnode_get_attrib(packet, "from"));
+	xmlnode_insert_child(error, item_not_found);
+	xmlnode_insert_child(result->node, error);
+
+	jabber_iq_send(result);
+}
+
+void
+jabber_ibb_parse(JabberStream *js, xmlnode *packet)
+{
+	xmlnode *data = xmlnode_get_child_with_namespace(packet, "data",
+		XEP_0047_NAMESPACE);
+	xmlnode *close = xmlnode_get_child_with_namespace(packet, "close",
+		XEP_0047_NAMESPACE);
+	xmlnode *open = xmlnode_get_child_with_namespace(packet, "open",
+		XEP_0047_NAMESPACE);
+	const gchar *sid =
+		data ? xmlnode_get_attrib(data, "sid") :
+			close ? xmlnode_get_attrib(close, "sid") : NULL;
+	JabberIBBSession *sess =
+		sid ? g_hash_table_lookup(jabber_ibb_sessions, sid) : NULL;
+	const gchar *who = xmlnode_get_attrib(packet, "from");
+
+	if (sess) {
+
+		if (strcmp(who, jabber_ibb_session_get_who(sess)) != 0) {
+			/* the iq comes from a different JID than the remote JID of the
+			  session, ignore it */
+			purple_debug_error("jabber",
+				"Got IBB iq from wrong JID, ignoring\n");
+		} else if (data) {
+			const gchar *seq_attr = xmlnode_get_attrib(data, "seq");
+			guint16 seq = (seq_attr ? atoi(seq_attr) : 0);
+
+			/* reject the data, and set the session in error if we get an
+			  out-of-order packet */
+			if (seq_attr && seq == jabber_ibb_session_get_recv_seq(sess)) {
+				/* sequence # is the expected... */
+				JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+
+				jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+				xmlnode_set_attrib(result->node, "to",
+					xmlnode_get_attrib(packet, "from"));
+
+				if (sess->data_received_cb) {
+					gchar *base64 = xmlnode_get_data(data);
+					gsize size;
+					gpointer rawdata = purple_base64_decode(base64, &size);
+
+					g_free(base64);
+
+					if (rawdata) {
+						purple_debug_info("jabber",
+							"got %" G_GSIZE_FORMAT " bytes of data on IBB stream\n",
+							size);
+						if (size > jabber_ibb_session_get_block_size(sess)) {
+							purple_debug_error("jabber",
+								"IBB: received a too large packet\n");
+							if (sess->error_cb)
+								sess->error_cb(sess);
+							g_free(rawdata);
+							return;
+						} else {
+							purple_debug_info("jabber",
+								"calling IBB callback for received data\n");
+							sess->data_received_cb(sess, rawdata, size);
+						}
+						g_free(rawdata);
+					} else {
+						purple_debug_error("jabber",
+							"IBB: invalid BASE64 data received\n");
+						if (sess->error_cb)
+							sess->error_cb(sess);
+						return;
+
+					}
+				}
+
+				(sess->recv_seq)++;
+				jabber_iq_send(result);
+
+			} else {
+				purple_debug_error("jabber",
+					"Received an out-of-order/invalid IBB packet\n");
+				sess->state = JABBER_IBB_SESSION_ERROR;
+
+				if (sess->error_cb) {
+					sess->error_cb(sess);
+				}
+			}
+		} else if (close) {
+			sess->state = JABBER_IBB_SESSION_CLOSED;
+			purple_debug_info("jabber", "IBB: received close\n");
+
+			if (sess->closed_cb) {
+				purple_debug_info("jabber", "IBB: calling closed handler\n");
+				sess->closed_cb(sess);
+			}
+
+		} else {
+			/* this should never happen */
+			purple_debug_error("jabber", "Received bogus iq for IBB session\n");
+		}
+	} else if (open) {
+		JabberIq *result;
+		const GList *iterator;
+
+		/* run all open handlers registered until one returns true */
+		for (iterator = open_handlers ; iterator ;
+			 iterator = g_list_next(iterator)) {
+			JabberIBBOpenHandler *handler = iterator->data;
+
+			if (handler(js, packet)) {
+				result = jabber_iq_new(js, JABBER_IQ_RESULT);
+				xmlnode_set_attrib(result->node, "to",
+					xmlnode_get_attrib(packet, "from"));
+				jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+				jabber_iq_send(result);
+				return;
+			}
+		}
+		/* no open callback returned success, reject */
+		jabber_ibb_send_error_response(js, packet);
+	} else {
+		/* send error reply */
+		jabber_ibb_send_error_response(js, packet);
+	}
+}
+
+void
+jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb)
+{
+	open_handlers = g_list_append(open_handlers, cb);
+}
+
+void
+jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb)
+{
+	open_handlers = g_list_remove(open_handlers, cb);
+}
+
+void
+jabber_ibb_init(void)
+{
+	jabber_ibb_sessions = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+void
+jabber_ibb_uninit(void)
+{
+	g_hash_table_destroy(jabber_ibb_sessions);
+	g_list_free(open_handlers);
+	jabber_ibb_sessions = NULL;
+	open_handlers = NULL;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/ibb.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,119 @@
+/*
+ * 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 Library 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 02110-1301,  USA
+ */
+
+#ifndef _PURPLE_JABBER_IBB_H_
+#define _PURPLE_JABBER_IBB_H_
+
+#include "jabber.h"
+#include "iq.h"
+
+#define XEP_0047_NAMESPACE "http://jabber.org/protocol/ibb"
+
+typedef struct _JabberIBBSession JabberIBBSession;
+
+typedef void
+(JabberIBBDataCallback)(JabberIBBSession *, const gpointer data, gsize size);
+
+typedef void (JabberIBBOpenedCallback)(JabberIBBSession *);
+typedef void (JabberIBBClosedCallback)(JabberIBBSession *);
+typedef void (JabberIBBErrorCallback)(JabberIBBSession *);
+typedef void (JabberIBBSentCallback)(JabberIBBSession *);
+
+typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, xmlnode *packet);
+
+typedef enum {
+	JABBER_IBB_SESSION_NOT_OPENED,
+	JABBER_IBB_SESSION_OPENED,
+	JABBER_IBB_SESSION_CLOSED,
+	JABBER_IBB_SESSION_ERROR
+} JabberIBBSessionState;
+
+struct _JabberIBBSession {
+	JabberStream *js;
+	gchar *who;
+	gchar *sid;
+	gchar *id;
+	guint16 send_seq;
+	guint16 recv_seq;
+	gsize block_size;
+
+	/* session state */
+	JabberIBBSessionState state;
+
+	/* user data (f.ex. a handle to a PurpleXfer) */
+	gpointer user_data;
+
+	/* callbacks */
+	JabberIBBOpenedCallback *opened_cb;
+	JabberIBBSentCallback *data_sent_cb;
+	JabberIBBClosedCallback *closed_cb;
+	/* callback for receiving data */
+	JabberIBBDataCallback *data_received_cb;
+	JabberIBBErrorCallback *error_cb;
+
+	/* store the last sent IQ (to permit cancel of callback) */
+	gchar *last_iq_id;
+};
+
+JabberIBBSession *jabber_ibb_session_create(JabberStream *js, const gchar *sid,
+	const gchar *who, gpointer user_data);
+JabberIBBSession *jabber_ibb_session_create_from_xmlnode(JabberStream *js,
+	xmlnode *packet, gpointer user_data);
+
+void jabber_ibb_session_destroy(JabberIBBSession *sess);
+
+void jabber_ibb_session_set_opened_callback(JabberIBBSession *sess,
+	JabberIBBOpenedCallback *cb);
+void jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess,
+	JabberIBBSentCallback *cb);
+void jabber_ibb_session_set_closed_callback(JabberIBBSession *sess,
+	JabberIBBClosedCallback *cb);
+void jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess,
+	JabberIBBDataCallback *cb);
+void jabber_ibb_session_set_error_callback(JabberIBBSession *sess,
+	JabberIBBErrorCallback *cb);
+
+void jabber_ibb_session_open(JabberIBBSession *sess);
+void jabber_ibb_session_close(JabberIBBSession *sess);
+void jabber_ibb_session_accept(JabberIBBSession *sess);
+void jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data,
+	gsize size);
+
+const gchar *jabber_ibb_session_get_sid(const JabberIBBSession *sess);
+JabberStream *jabber_ibb_session_get_js(JabberIBBSession *sess);
+const gchar *jabber_ibb_session_get_who(const JabberIBBSession *sess);
+
+guint16 jabber_ibb_session_get_send_seq(const JabberIBBSession *sess);
+guint16 jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess);
+
+JabberIBBSessionState jabber_ibb_session_get_state(const JabberIBBSession *sess);
+
+gsize jabber_ibb_session_get_block_size(const JabberIBBSession *sess);
+void jabber_ibb_session_set_block_size(JabberIBBSession *sess, gsize size);
+
+gpointer jabber_ibb_session_get_user_data(JabberIBBSession *sess);
+
+/* handle incoming packet */
+void jabber_ibb_parse(JabberStream *js, xmlnode *packet);
+
+/* add a handler for open session */
+void jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb);
+void jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb);
+
+void jabber_ibb_init(void);
+void jabber_ibb_uninit(void);
+
+#endif /* _PURPLE_JABBER_IBB_H_ */
--- a/libpurple/protocols/jabber/iq.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/iq.c	Sat Mar 07 01:59:40 2009 +0000
@@ -34,6 +34,7 @@
 #include "ping.h"
 #include "adhoccommands.h"
 #include "data.h"
+#include "ibb.h"
 
 #ifdef _WIN32
 #include "utsname.h"
@@ -393,6 +394,13 @@
 		return;
 	}
 
+	if (xmlnode_get_child_with_namespace(packet, "data", XEP_0047_NAMESPACE)
+		|| xmlnode_get_child_with_namespace(packet, "close", XEP_0047_NAMESPACE)
+		|| xmlnode_get_child_with_namespace(packet, "open", XEP_0047_NAMESPACE)) {
+		jabber_ibb_parse(js, packet);
+		return;
+	}
+
 	/* If we get here, send the default error reply mandated by XMPP-CORE */
 	if(!strcmp(type, "set") || !strcmp(type, "get")) {
 		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
--- a/libpurple/protocols/jabber/jabber.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Mar 07 01:59:40 2009 +0000
@@ -40,9 +40,9 @@
 #include "version.h"
 #include "xmlnode.h"
 
-#include "caps.h"
 #include "auth.h"
 #include "buddy.h"
+#include "caps.h"
 #include "chat.h"
 #include "data.h"
 #include "disco.h"
@@ -64,7 +64,6 @@
 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5)
 
 static PurplePlugin *my_protocol = NULL;
-
 GList *jabber_features = NULL;
 GList *jabber_identities = NULL;
 
@@ -187,7 +186,7 @@
 {
 	if(xmlnode_get_child(packet, "starttls")) {
 		if(jabber_process_starttls(js, packet))
-	
+
 			return;
 	} else if(purple_account_get_bool(js->gc->account, "require_tls", FALSE) && !js->gsc) {
 		purple_connection_error_reason (js->gc,
@@ -480,10 +479,10 @@
 
 	if (js->keepalive_timeout == -1) {
 		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(120, (GSourceFunc)(jabber_pong_timeout), gc);
 		jabber_iq_set_callback(iq, jabber_pong_cb, NULL);
 		jabber_iq_send(iq);
@@ -591,7 +590,7 @@
 		jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
 	jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
 	purple_ssl_input_add(gsc, jabber_recv_cb_ssl, gc);
-	
+
 	/* Tell the app that we're doing encryption */
 	jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
 }
@@ -785,14 +784,14 @@
 			_("Invalid XMPP ID"));
 		return;
 	}
-	
+
 	if (!js->user->domain || *(js->user->domain) == '\0') {
 		purple_connection_error_reason (gc,
 			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
 			_("Invalid XMPP ID. Domain must be set."));
 		return;
 	}
-	
+
 	if((my_jb = jabber_buddy_find(js, purple_account_get_username(account), TRUE)))
 		my_jb->subscription |= JABBER_SUB_BOTH;
 
@@ -837,7 +836,7 @@
 
 	/* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll
 	 * invoke the magic of SRV lookups, to figure out host and port */
-	if(connect_server[0]) { 
+	if(connect_server[0]) {
 		jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE);
 	} else {
 		js->srv_query_data = purple_srv_resolve("xmpp-client",
@@ -923,10 +922,10 @@
 		g_free(buf);
 	} else {
 		char *msg = jabber_parse_error(js, packet, NULL);
-		
+
 		if(!msg)
 			msg = g_strdup(_("Unknown Error"));
-		
+
 		purple_notify_error(NULL, _("Unregistration Failed"),
 							_("Unregistration Failed"), msg);
 		g_free(msg);
@@ -969,9 +968,9 @@
 					if (cbdata->who)
 						xmlnode_set_attrib(iq->node,"to",cbdata->who);
 					xmlnode_new_child(query, "remove");
-					
+
 					jabber_iq_set_callback(iq, jabber_unregistration_result_cb, cbdata->who);
-					
+
 					jabber_iq_send(iq);
 					g_free(cbdata);
 					return;
@@ -1101,7 +1100,7 @@
 			return;
 		}
 	}
-	
+
 	if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
 		jabber_x_data_request(js, x, jabber_register_x_data_cb, g_strdup(from));
 		return;
@@ -1252,7 +1251,7 @@
 
 void jabber_register_gateway(JabberStream *js, const char *gateway) {
 	JabberIq *iq;
-	
+
 	iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:register");
 	xmlnode_set_attrib(iq->node, "to", gateway);
 	jabber_iq_send(iq);
@@ -1327,7 +1326,7 @@
 	const char *type = xmlnode_get_attrib(packet,"type");
 	if(!strcmp(type,"error")) {
 		char *msg = jabber_parse_error(js, packet, NULL);
-		
+
 		purple_notify_error(js->gc, _("Error unregistering account"),
 							_("Error unregistering account"), msg);
 		g_free(msg);
@@ -1361,7 +1360,7 @@
 void jabber_unregister_account(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data) {
 	PurpleConnection *gc = purple_account_get_connection(account);
 	JabberStream *js;
-	
+
 	if(gc->state != PURPLE_CONNECTED) {
 		if(gc->state != PURPLE_CONNECTING)
 			jabber_login(account);
@@ -1371,7 +1370,7 @@
 		js->unregistration_user_data = user_data;
 		return;
 	}
-	
+
 	js = gc->proto_data;
 
 	if (js->unregistration) {
@@ -1677,10 +1676,10 @@
 	feat = g_new0(JabberFeature,1);
 	feat->namespace = g_strdup(namespace);
 	feat->is_enabled = cb;
-	
+
 	/* try to remove just in case it already exists in the list */
 	jabber_remove_feature(namespace);
-	
+
 	jabber_features = g_list_append(jabber_features, feat);
 }
 
@@ -1753,13 +1752,14 @@
 {
 	JabberStream *js;
 	JabberBuddy *jb = NULL;
-
-	if(!b->account->gc)
+	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(b));
+
+	if(!gc)
 		return NULL;
 
-	js = b->account->gc->proto_data;
+	js = gc->proto_data;
 	if(js)
-		jb = jabber_buddy_find(js, b->name, FALSE);
+		jb = jabber_buddy_find(js, purple_buddy_get_name(b), FALSE);
 
 	if(!PURPLE_BUDDY_IS_ONLINE(b)) {
 		if(jb && (jb->subscription & JABBER_SUB_PENDING ||
@@ -1773,9 +1773,11 @@
 {
 	char *ret = NULL;
 	JabberBuddy *jb = NULL;
-	
-	if (b->account->gc && b->account->gc->proto_data)
-		jb = jabber_buddy_find(b->account->gc->proto_data, b->name, FALSE);
+	PurpleAccount *account = purple_buddy_get_account(b);
+	PurpleConnection *gc = purple_account_get_connection(account);
+
+	if (gc && gc->proto_data)
+		jb = jabber_buddy_find(gc->proto_data, purple_buddy_get_name(b), FALSE);
 
 	if(jb && !PURPLE_BUDDY_IS_ONLINE(b) && (jb->subscription & JABBER_SUB_PENDING || !(jb->subscription & JABBER_SUB_TO))) {
 		ret = g_strdup(_("Not Authorized"));
@@ -1804,14 +1806,19 @@
 void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	JabberBuddy *jb;
+	PurpleAccount *account;
+	PurpleConnection *gc;
 
 	g_return_if_fail(b != NULL);
-	g_return_if_fail(b->account != NULL);
-	g_return_if_fail(b->account->gc != NULL);
-	g_return_if_fail(b->account->gc->proto_data != NULL);
-
-	jb = jabber_buddy_find(b->account->gc->proto_data, b->name,
-			FALSE);
+
+	account = purple_buddy_get_account(b);
+	g_return_if_fail(account != NULL);
+
+	gc = purple_account_get_connection(account);
+	g_return_if_fail(gc != NULL);
+	g_return_if_fail(gc->proto_data != NULL);
+
+	jb = jabber_buddy_find(gc->proto_data, purple_buddy_get_name(b), FALSE);
 
 	if(jb) {
 		JabberBuddyResource *jbr = NULL;
@@ -1919,9 +1926,12 @@
 	PurpleStatusType *type;
 	GList *types = NULL;
 	PurpleValue *priority_value;
+	PurpleValue *buzz_enabled;
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 1);
+	buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
+	purple_value_set_boolean(buzz_enabled, TRUE);
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
 			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE),
 			NULL, TRUE, TRUE, FALSE,
@@ -1930,12 +1940,14 @@
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
-			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
+			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
 	types = g_list_append(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 1);
+	buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
+	purple_value_set_boolean(buzz_enabled, TRUE);
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
 			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_CHAT),
 			_("Chatty"), TRUE, TRUE, FALSE,
@@ -1944,12 +1956,14 @@
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
-			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
+			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
 	types = g_list_append(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
+	buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
+	purple_value_set_boolean(buzz_enabled, TRUE);
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
 			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY),
 			NULL, TRUE, TRUE, FALSE,
@@ -1958,12 +1972,14 @@
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
-			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
+			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
 	types = g_list_append(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
+	buzz_enabled = purple_value_new(PURPLE_TYPE_BOOLEAN);
+	purple_value_set_boolean(buzz_enabled, TRUE);
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY,
 			jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_XA),
 			NULL, TRUE, TRUE, FALSE,
@@ -1972,7 +1988,7 @@
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
-			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
+			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
 	types = g_list_append(types, type);
 
@@ -1986,7 +2002,6 @@
 			"mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING),
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
-			"buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN),
 			NULL);
 	types = g_list_append(types, type);
 
@@ -2132,7 +2147,7 @@
 
 	if(js->pep)
 		jabber_pep_init_actions(&m);
-	
+
 	if(js->commands)
 		jabber_adhoc_init_server_commands(js, &m);
 
@@ -2147,19 +2162,24 @@
 	if(!(jid = jabber_id_new(name)))
 		return NULL;
 
-	for(gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
+	for(gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
+		for(cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			PurpleChat *chat = (PurpleChat*)cnode;
 			const char *room, *server;
+			GHashTable *components;
 			if(!PURPLE_BLIST_NODE_IS_CHAT(cnode))
 				continue;
 
-			if(chat->account != account)
+			if (purple_chat_get_account(chat) != account)
 				continue;
 
-			if(!(room = g_hash_table_lookup(chat->components, "room")))
+			components = purple_chat_get_components(chat);
+			if(!(room = g_hash_table_lookup(components, "room")))
 				continue;
-			if(!(server = g_hash_table_lookup(chat->components, "server")))
+			if(!(server = g_hash_table_lookup(components, "server")))
 				continue;
 
 			if(jid->node && jid->domain &&
@@ -2179,7 +2199,7 @@
 	JabberID *jid;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
-	
+
 	if(!(jid = jabber_id_new(who)))
 		return;
 
@@ -2440,7 +2460,7 @@
 	if (!chat || !args || !args[0] || !args[1])
 		return PURPLE_CMD_RET_FAILED;
 
-	if (strcmp(args[1], "owner") != 0 && 
+	if (strcmp(args[1], "owner") != 0 &&
 	    strcmp(args[1], "admin") != 0 &&
 	    strcmp(args[1], "member") != 0 &&
 	    strcmp(args[1], "outcast") != 0 &&
@@ -2569,29 +2589,30 @@
 
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
+	PurpleConnection *gc = js->gc;
+	PurpleBuddy *buddy =
+		purple_find_buddy(purple_connection_get_account(gc), username);
+	const gchar *alias =
+		buddy ? purple_buddy_get_contact_alias(buddy) : username;
 
 	if(!username)
 		return FALSE;
 
 	jb = jabber_buddy_find(js, username, FALSE);
 	if(!jb) {
-		*error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), username);
+		*error = g_strdup_printf(_("Unable to buzz, because there is nothing "
+			"known about %s."), alias);
 		return FALSE;
 	}
 
 	jbr = jabber_buddy_find_resource(jb, NULL);
-	if(!jbr) {
-		*error = g_strdup_printf(_("Unable to buzz, because user %s might be offline."), username);
+	if (!jbr) {
+		*error = g_strdup_printf(_("Unable to buzz, because %s might be offline."),
+			alias);
 		return FALSE;
 	}
 
-	/* Is this message sufficiently useful to not just fold it in with the tail error condition below? */
-	if(!jbr->caps.info) {
-		*error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), username);
-		return FALSE;
-	}
-
-	if (jabber_resource_has_capability(jbr, "http://www.xmpp.org/extensions/xep-0224.html#ns")) {
+	if (jabber_resource_has_capability(jbr, XEP_0224_NAMESPACE)) {
 		xmlnode *buzz, *msg = xmlnode_new("message");
 		gchar *to;
 
@@ -2603,27 +2624,57 @@
 		xmlnode_set_attrib(msg, "type", "headline");
 
 		buzz = xmlnode_new_child(msg, "attention");
-		xmlnode_set_namespace(buzz, "http://www.xmpp.org/extensions/xep-0224.html#ns");
+		xmlnode_set_namespace(buzz, XEP_0224_NAMESPACE);
 
 		jabber_send(js, msg);
 		xmlnode_free(msg);
 
 		return TRUE;
+	} else {
+		*error = g_strdup_printf(_("Unable to buzz, because %s does "
+			"not support it or do not wish to receive buzzes now."), alias);
+		return FALSE;
 	}
-
-	*error = g_strdup_printf(_("Unable to buzz, because the user %s does not support it."), username);
-	return FALSE;
 }
 
 static PurpleCmdRet jabber_cmd_buzz(PurpleConversation *conv,
 		const char *cmd, char **args, char **error, void *data)
 {
 	JabberStream *js = conv->account->gc->proto_data;
-
-	if(!args || !args[0])
+	const gchar *who;
+
+	if (!args || !args[0]) {
+		/* use the buddy from conversation, if it's a one-to-one conversation */
+		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+			who = purple_conversation_get_name(conv);
+		} else {
+			return PURPLE_CMD_RET_FAILED;
+		}
+	} else {
+		who = args[0];
+	}
+
+	if (_jabber_send_buzz(js, who, error)) {
+		const gchar *alias;
+		gchar *str;
+		PurpleBuddy *buddy =
+			purple_find_buddy(purple_connection_get_account(conv->account->gc),
+				who);
+
+		if (buddy != NULL)
+			alias = purple_buddy_get_contact_alias(buddy);
+		else
+			alias = who;
+
+		str = g_strdup_printf(_("Buzzing %s..."), alias);
+		purple_conversation_write(conv, NULL, str,
+			PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
+		g_free(str);
+
+		return PURPLE_CMD_RET_OK;
+	} else {
 		return PURPLE_CMD_RET_FAILED;
-
-	return _jabber_send_buzz(js, args[0], error)  ? PURPLE_CMD_RET_OK : PURPLE_CMD_RET_FAILED;
+	}
 }
 
 GList *jabber_attention_types(PurpleAccount *account)
@@ -2736,8 +2787,9 @@
 					  "prpl-jabber", jabber_cmd_ping,
 					  _("ping &lt;jid&gt;:	Ping a user/component/server."),
 					  NULL);
-	purple_cmd_register("buzz", "s", PURPLE_CMD_P_PRPL,
-					  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
+	purple_cmd_register("buzz", "w", PURPLE_CMD_P_PRPL,
+					  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY |
+					  PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
 					  "prpl-jabber", jabber_cmd_buzz,
 					  _("buzz: Buzz a user to get their attention"), NULL);
 }
--- a/libpurple/protocols/jabber/jabber.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Sat Mar 07 01:59:40 2009 +0000
@@ -204,7 +204,7 @@
 	gboolean unregistration;
 	PurpleAccountUnregistrationCb unregistration_cb;
 	void *unregistration_user_data;
-	
+
 	gboolean vcard_fetched;
 
 	/* Entity Capabilities hash */
@@ -215,16 +215,16 @@
 
 	/* Is Buzz enabled? */
 	gboolean allowBuzz;
-	
+
 	/* A list of JabberAdHocCommands supported by the server */
 	GList *commands;
-	
+
 	/* last presence update to check for differences */
 	JabberBuddyState old_state;
 	char *old_msg;
 	int old_priority;
 	char *old_avatarhash;
-	
+
 	/* same for user tune */
 	char *old_artist;
 	char *old_title;
@@ -232,9 +232,9 @@
 	char *old_uri;
 	int old_length;
 	char *old_track;
-	
+
 	char *certificate_CN;
-	
+
 	/* A purple timeout tag for the keepalive */
 	int keepalive_timeout;
 	
--- a/libpurple/protocols/jabber/libxmpp.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Sat Mar 07 01:59:40 2009 +0000
@@ -44,6 +44,7 @@
 #include "usertune.h"
 #include "caps.h"
 #include "data.h"
+#include "ibb.h"
 
 static PurplePluginProtocolInfo prpl_info =
 {
@@ -137,7 +138,7 @@
 			     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
 			     purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
 			     purple_value_new_outgoing(PURPLE_TYPE_STRING));
-	
+
 	return TRUE;
 }
 
@@ -146,12 +147,14 @@
 	purple_signal_unregister(plugin, "jabber-receiving-xmlnode");
 
 	purple_signal_unregister(plugin, "jabber-sending-xmlnode");
-	
+
 	purple_signal_unregister(plugin, "jabber-sending-text");
 
 	/* reverse order of init_plugin */
 	jabber_bosh_uninit();
 	jabber_data_uninit();
+	jabber_si_uninit();
+	jabber_ibb_uninit();
 	/* PEP things should be uninit via jabber_pep_uninit, not here */
 	jabber_pep_uninit();
 	jabber_caps_uninit();
@@ -212,24 +215,19 @@
 #endif
 	PurpleAccountUserSplit *split;
 	PurpleAccountOption *option;
-	
 	/* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */
 	split = purple_account_user_split_new(_("Domain"), NULL, '@');
 	purple_account_user_split_set_reverse(split, FALSE);
 	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
-	
 	split = purple_account_user_split_new(_("Resource"), NULL, '/');
 	purple_account_user_split_set_reverse(split, FALSE);
 	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
-	
 	option = purple_account_option_bool_new(_("Require SSL/TLS"), "require_tls", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
-	
 	option = purple_account_option_bool_new(_("Force old (port 5223) SSL"), "old_ssl", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
-	
 	option = purple_account_option_bool_new(
 						_("Allow plaintext auth over unencrypted streams"),
 						"auth_plain_in_clear", FALSE);
@@ -292,11 +290,15 @@
 
 	#warning implement adding and retrieving own features via IPC API
 
+	jabber_ibb_init();
+	jabber_si_init();
+
 	jabber_add_feature(AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb);
 	jabber_add_feature(AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb);
 	jabber_add_feature("http://www.xmpp.org/extensions/xep-0224.html#ns",
 					   jabber_buzz_isenabled);
 	jabber_add_feature(XEP_0231_NAMESPACE, jabber_custom_smileys_isenabled);
+	jabber_add_feature(XEP_0047_NAMESPACE, NULL);
 
 	jabber_pep_register_handler(AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata);
 }
--- a/libpurple/protocols/jabber/message.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/message.c	Sat Mar 07 01:59:40 2009 +0000
@@ -118,7 +118,7 @@
 				}
 			}
 			serv_got_typing_stopped(jm->js->gc, from);
-			
+
 		} else {
 			serv_got_typing_stopped(jm->js->gc, from);
 		}
@@ -138,7 +138,7 @@
 				g_free(jbr->thread_id);
 			jbr->thread_id = g_strdup(jbr->thread_id);
 		}
-		
+
 		if (jm->js->googletalk && jm->xhtml == NULL) {
 			char *tmp = jm->body;
 			jm->body = jabber_google_format_to_html(jm->body);
@@ -289,8 +289,6 @@
 static void handle_buzz(JabberMessage *jm) {
 	PurpleBuddy *buddy;
 	PurpleAccount *account;
-	PurpleConversation *c;
-	char *username;
 
 	/* Delayed buzz MUST NOT be accepted */
 	if(jm->delayed)
@@ -305,15 +303,8 @@
 	if ((buddy = purple_find_buddy(account, jm->from)) == NULL)
 		return; /* Do not accept buzzes from unknown people */
 
-	c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account);
-	if (c == NULL)
-		c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, jm->from);
-
-	username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
 	/* xmpp only has 1 attention type, so index is 0 */
-	purple_prpl_got_attention(jm->js->gc, username, 0);
-
-	g_free(username);
+	purple_prpl_got_attention(jm->js->gc, jm->from, 0);
 }
 
 /* used internally by the functions below */
@@ -328,14 +319,14 @@
 	GHashTable *table)
 {
 	xmlnode *child;
-	
+
 	for (child = xmlnode_get_child(message, "img") ; child ;
 		 child = xmlnode_get_next_twin(child)) {
 		const gchar *src = xmlnode_get_attrib(child, "src");
-		
+
 		if (g_str_has_prefix(src, "cid:")) {
 			const gchar *cid = src + 4;
-			
+
 			/* if we haven't "fetched" this yet... */
 			if (!g_hash_table_lookup(table, cid)) {
 				/* take a copy of the cid and let the SmileyRef own it... */
@@ -343,14 +334,14 @@
 				JabberSmileyRef *ref = g_new0(JabberSmileyRef, 1);
 				const gchar *alt = xmlnode_get_attrib(child, "alt");
 				ref->cid = temp_cid;
-				/* if there is no "alt" string, use the cid... 
+				/* if there is no "alt" string, use the cid...
 				 include the entire src, eg. "cid:.." to avoid linkification */
 				if (alt && alt[0] != '\0') {
 					/* workaround for when "alt" is set to the value of the
 					 CID (which Jabbim seems to do), to avoid it showing up
 						 as an mailto: link */
 					if (purple_email_is_valid(alt)) {
-						ref->alt = g_strdup_printf("smiley:%s", alt); 
+						ref->alt = g_strdup_printf("smiley:%s", alt);
 					} else {
 						ref->alt = g_strdup(alt);
 					}
@@ -361,7 +352,7 @@
 			}
 		}
 	}
-		
+
 	for (child = message->child ; child ; child = child->next) {
 		jabber_message_get_refs_from_xmlnode_internal(child, table);
 	}
@@ -372,9 +363,9 @@
 {
 	GList **refs = (GList **) user_data;
 	JabberSmileyRef *ref = (JabberSmileyRef *) value;
-	
+
 	*refs = g_list_append(*refs, ref);
-	
+
 	return TRUE;
 }
 
@@ -383,9 +374,9 @@
 {
 	GList *refs = NULL;
 	GHashTable *unique_refs = g_hash_table_new(g_str_hash, g_str_equal);
-	
+
 	jabber_message_get_refs_from_xmlnode_internal(message, unique_refs);
-	(void) g_hash_table_foreach_steal(unique_refs, 
+	(void) g_hash_table_foreach_steal(unique_refs,
 		jabber_message_get_refs_steal, (gpointer) &refs);
 	g_hash_table_destroy(unique_refs);
 	return refs;
@@ -518,7 +509,7 @@
 
 static void
 jabber_message_send_data_request(JabberStream *js, PurpleConversation *conv,
-								 const gchar *cid, const gchar *who, 
+								 const gchar *cid, const gchar *who,
 								 const gchar *alt)
 {
 	JabberIq *request = jabber_iq_new(js, JABBER_IQ_GET);
@@ -580,7 +571,7 @@
 			char *text = xmlnode_get_data(child);
 			if (!text) {
 				xmlnode *enclosed_text_node;
-				
+
 				if ((enclosed_text_node = xmlnode_get_child(child, "text")))
 					text = xmlnode_get_data(enclosed_text_node);
 			}
@@ -632,7 +623,7 @@
 					smiley_refs = jabber_message_get_refs_from_xmlnode(child);
 					purple_debug_info("jabber", "found %d smileys\n",
 						g_list_length(smiley_refs));
-					
+
 					if (jm->type == JABBER_MESSAGE_GROUPCHAT) {
 						JabberID *jid = jabber_id_new(jm->from);
 						JabberChat *chat = NULL;
@@ -676,7 +667,7 @@
 					const gchar *cid = ref->cid;
 					const gchar *alt = ref->alt;
 
-					purple_debug_info("jabber", 
+					purple_debug_info("jabber",
 						"about to add custom smiley %s to the conv\n", alt);
 					if (purple_conv_custom_smiley_add(conv, alt, "cid", cid,
 						    TRUE)) {
@@ -684,8 +675,8 @@
 								jabber_data_find_remote_by_cid(cid);
 						/* if data is already known, we add write it immediatly */
 						if (data) {
-							purple_debug_info("jabber", 
-								"data is already known\n"); 
+							purple_debug_info("jabber",
+								"data is already known\n");
 							purple_conv_custom_smiley_write(conv, alt,
 								jabber_data_get_data(data),
 								jabber_data_get_size(data));
@@ -731,7 +722,7 @@
 			jm->type = JABBER_MESSAGE_EVENT;
 			for(items = xmlnode_get_child(child,"items"); items; items = items->next)
 				jm->eventitems = g_list_append(jm->eventitems, items);
-		} else if(!strcmp(child->name, "attention") && !strcmp(xmlns,"http://www.xmpp.org/extensions/xep-0224.html#ns")) {
+		} else if(!strcmp(child->name, "attention") && !strcmp(xmlns, XEP_0224_NAMESPACE)) {
 			jm->hasBuzz = TRUE;
 		} else if(!strcmp(child->name, "delay") && !strcmp(xmlns,"urn:xmpp:delay")) {
 			const char *timestamp = xmlnode_get_attrib(child, "stamp");
@@ -910,9 +901,9 @@
 {
 	JabberStream *js = (JabberStream *) gc->proto_data;
 	JabberBuddy *jb;
-	
+
 	if (!js) {
-		purple_debug_error("jabber", 
+		purple_debug_error("jabber",
 			"jabber_conv_support_custom_smileys: could not find stream\n");
 		return FALSE;
 	}
@@ -965,7 +956,7 @@
 
 	if(type)
 		xmlnode_set_attrib(message, "type", type);
- 
+
 	if (jm->id)
 		xmlnode_set_attrib(message, "id", jm->id);
 
@@ -1022,7 +1013,7 @@
 		PurpleConversation *conv =
 			purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, jm->to,
 				account);
- 
+
 		if (jabber_conv_support_custom_smileys(jm->js->gc, conv, jm->to)) {
 			GList *found_smileys = jabber_message_xhtml_find_smileys(jm->xhtml);
 
@@ -1037,19 +1028,19 @@
 					const gchar *shortcut = purple_smiley_get_shortcut(smiley);
 					const JabberData *data =
 						jabber_data_find_local_by_alt(shortcut);
-							
+
 					/* the object has not been sent before */
 					if (!data) {
 						PurpleStoredImage *image =
 								purple_smiley_get_stored_image(smiley);
 						const gchar *ext = purple_imgstore_get_extension(image);
 						JabberStream *js = jm->js;
-						
+
 						JabberData *new_data =
 							jabber_data_create_from_data(purple_imgstore_get_data(image),
 								purple_imgstore_get_size(image),
 								jabber_message_get_mimetype_from_ext(ext), js);
-						purple_debug_info("jabber", 
+						purple_debug_info("jabber",
 							"cache local smiley alt = %s, cid = %s\n",
 							shortcut, jabber_data_get_cid(new_data));
 						jabber_data_associate_local(new_data, shortcut);
@@ -1124,7 +1115,7 @@
 	}
 
 	buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg);
-	
+
 	purple_markup_html_to_xhtml(buf, &xhtml, &jm->body);
 	g_free(buf);
 
--- a/libpurple/protocols/jabber/message.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/message.h	Sat Mar 07 01:59:40 2009 +0000
@@ -26,6 +26,8 @@
 #include "jabber.h"
 #include "xmlnode.h"
 
+#define XEP_0224_NAMESPACE "urn:xmpp:attention:0"
+
 typedef struct _JabberMessage {
 	JabberStream *js;
 	enum {
--- a/libpurple/protocols/jabber/pep.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/pep.c	Sat Mar 07 01:59:40 2009 +0000
@@ -33,7 +33,7 @@
 void jabber_pep_init(void) {
 	if(!pep_handlers) {
 		pep_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-		
+
 		/* register PEP handlers */
 		jabber_mood_init();
 		jabber_tune_init();
@@ -65,31 +65,31 @@
 	xmlnode *pubsub = xmlnode_get_child_with_namespace(packet,"pubsub","http://jabber.org/protocol/pubsub");
 	xmlnode *items = NULL;
 	JabberPEPHandler *cb = data;
-	
+
 	if(pubsub)
 		items = xmlnode_get_child(pubsub, "items");
-	
+
 	cb(js, from, items);
 }
 
 void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb) {
 	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
 	xmlnode *pubsub, *items, *item;
-	
+
 	xmlnode_set_attrib(iq->node,"to",to);
 	pubsub = xmlnode_new_child(iq->node,"pubsub");
-	
+
 	xmlnode_set_namespace(pubsub,"http://jabber.org/protocol/pubsub");
-	
+
 	items = xmlnode_new_child(pubsub, "items");
 	xmlnode_set_attrib(items,"node",node);
-	
+
 	item = xmlnode_new_child(items, "item");
 	if(id)
 		xmlnode_set_attrib(item, "id", id);
-	
+
 	jabber_iq_set_callback(iq,do_pep_iq_request_item_callback,(gpointer)cb);
-	
+
 	jabber_iq_send(iq);
 }
 
@@ -102,15 +102,15 @@
 	JabberPEPHandler *jph;
 	GList *itemslist;
 	char *jid = jabber_get_bare_jid(jm->from);
-	
+
 	for(itemslist = jm->eventitems; itemslist; itemslist = itemslist->next) {
 		xmlnode *items = (xmlnode*)itemslist->data;
 		const char *nodename = xmlnode_get_attrib(items,"node");
-		
+
 		if(nodename && (jph = g_hash_table_lookup(pep_handlers, nodename)))
 			jph(jm->js, jid, items);
 	}
-	
+
 	/* discard items we don't have a handler for */
 	g_free(jid);
 }
--- a/libpurple/protocols/jabber/presence.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/presence.c	Sat Mar 07 01:59:40 2009 +0000
@@ -111,7 +111,7 @@
 	}
 
 	gc = purple_account_get_connection(account);
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 	jabber_presence_send(js, FALSE);
 }
 
@@ -139,7 +139,7 @@
 	}
 
 	purple_status_to_jabber(status, &state, &stripped, &priority);
-	
+
 	/* check for buzz support */
 	allowBuzz = purple_status_get_attr_boolean(status,"buzz");
 	/* changing the buzz state has to trigger a re-broadcasting of the presence for caps */
@@ -148,7 +148,7 @@
 	if (js->googletalk && !stripped && purple_status_is_active(tune)) {
 		stripped = jabber_google_presence_outgoing(tune);
 	}
-	
+
 #define CHANGED(a,b) ((!a && b) || (a && a[0] == '\0' && b && b[0] != '\0') || \
 					  (a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b)))
 	/* check if there are any differences to the <presence> and send them in that case */
@@ -171,9 +171,9 @@
 
 		g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
 		xmlnode_free(presence);
-		
+
 		/* update old values */
-		
+
 		if(js->old_msg)
 			g_free(js->old_msg);
 		if(js->old_avatarhash)
@@ -195,7 +195,7 @@
 		length = (!purple_status_get_attr_value(tune, PURPLE_TUNE_TIME)) ? -1 :
 				purple_status_get_attr_int(tune, PURPLE_TUNE_TIME);
 	}
-	
+
 	if(CHANGED(artist, js->old_artist) || CHANGED(title, js->old_title) || CHANGED(source, js->old_source) ||
 	   CHANGED(uri, js->old_uri) || CHANGED(track, js->old_track) || (length != js->old_length)) {
 		PurpleJabberTuneInfo tuneinfo = {
@@ -207,7 +207,7 @@
 			(char*)uri
 		};
 		jabber_tune_set(js->gc, &tuneinfo);
-		
+
 		/* update old values */
 		g_free(js->old_artist);
 		g_free(js->old_title);
@@ -279,21 +279,21 @@
 		char extlist[1024];
 		unsigned remaining = 1023; /* one less for the \0 */
 		GList *feature;
-		
+
 		extlist[0] = '\0';
 		for(feature = jabber_features; feature && remaining > 0; feature = feature->next) {
 			JabberFeature *feat = (JabberFeature*)feature->data;
 			unsigned featlen;
-			
-			if(feat->is_enabled != NULL && feat->is_enabled(js, feat->namespace) == FALSE)
+
+			if(feat->is_enabled != NULL && feat->is_enabled(js, feat->shortname, feat->namespace) == FALSE)
 				continue; /* skip this feature */
-			
+
 			featlen = strlen(feat->shortname);
-			
+
 			/* cut off when we don't have any more space left in our buffer (too bad) */
 			if(featlen > remaining)
 				break;
-			
+
 			strncat(extlist,feat->shortname,remaining);
 			remaining -= featlen;
 			if(feature->next) { /* no space at the end */
--- a/libpurple/protocols/jabber/roster.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/roster.c	Sat Mar 07 01:59:40 2009 +0000
@@ -80,15 +80,16 @@
 
 		buddies = g_slist_remove(buddies, b);
 
-		if((l = g_slist_find_custom(g2, g->name, (GCompareFunc)strcmp))) {
-			const char *servernick;
+		if((l = g_slist_find_custom(g2, purple_group_get_name(g), (GCompareFunc)strcmp))) {
+			const char *servernick, *balias;
 
 			/* Previously stored serverside / buddy-supplied alias */
 			if((servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick")))
 				serv_got_alias(js->gc, jid, servernick);
 
 			/* Alias from our roster retrieval */
-			if(alias && (!b->alias || strcmp(b->alias, alias)))
+			balias = purple_buddy_get_local_buddy_alias(b);
+			if(alias && (!balias || strcmp(balias, alias)))
 				purple_serv_got_private_alias(js->gc, jid, alias);
 			g_free(l->data);
 			g2 = g_slist_delete_link(g2, l);
@@ -104,7 +105,7 @@
 		if (pool) {
 			b = pool->data;
 			pool = g_list_delete_link(pool, pool);
-		} else {			
+		} else {
 			b = purple_buddy_new(js->gc->account, jid, alias);
 		}
 
@@ -119,11 +120,13 @@
 		/* If we just learned about ourself, then fake our status,
 		 * because we won't be receiving a normal presence message
 		 * about ourself. */
-		if(!strcmp(b->name, my_bare_jid)) {
+		if(!strcmp(purple_buddy_get_name(b), my_bare_jid)) {
 			PurplePresence *gpresence;
 			PurpleStatus *status;
+			PurpleAccount *account;
 
-			gpresence = purple_account_get_presence(js->gc->account);
+			account = purple_connection_get_account(js->gc);
+			gpresence = purple_account_get_presence(account);
 			status = purple_presence_get_active_status(gpresence);
 			jabber_presence_fake_to_self(js, status);
 		}
@@ -274,6 +277,7 @@
 	GSList *groups = NULL, *l;
 	JabberIq *iq;
 	xmlnode *query, *item, *group;
+	const char *balias;
 
 	if (js->currently_parsing_roster_push)
 		return;
@@ -290,7 +294,7 @@
 		while(buddies) {
 			b = buddies->data;
 			g = purple_buddy_get_group(b);
-			groups = g_slist_append(groups, g->name);
+			groups = g_slist_append(groups, (char *)purple_group_get_name(g));
 			buddies = g_slist_remove(buddies, b);
 		}
 	}
@@ -302,7 +306,8 @@
 
 	xmlnode_set_attrib(item, "jid", name);
 
-	xmlnode_set_attrib(item, "name", b->alias ? b->alias : "");
+	balias = purple_buddy_get_local_buddy_alias(b);
+	xmlnode_set_attrib(item, "name", balias ? balias : "");
 
 	for(l = groups; l; l = l->next) {
 		group = xmlnode_new_child(item, "group");
@@ -311,7 +316,7 @@
 
 	if(!grps)
 		g_slist_free(groups);
-	
+
 	if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER) {
 		jabber_google_roster_outgoing(js, query, item);
 		xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
@@ -328,14 +333,16 @@
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
 	char *my_bare_jid;
+	const char *name;
 
 	if(!js->roster_parsed)
 		return;
 
-	if(!(who = jabber_get_bare_jid(buddy->name)))
+	name = purple_buddy_get_name(buddy);
+	if(!(who = jabber_get_bare_jid(name)))
 		return;
 
-	jb = jabber_buddy_find(js, buddy->name, FALSE);
+	jb = jabber_buddy_find(js, name, FALSE);
 
 	jabber_roster_update(js, who, NULL);
 
@@ -376,6 +383,7 @@
 	GSList *buddies, *groups = NULL;
 	PurpleBuddy *b;
 	PurpleGroup *g;
+	const char *gname;
 
 	if(!old_group || !new_group || !strcmp(old_group, new_group))
 		return;
@@ -384,10 +392,11 @@
 	while(buddies) {
 		b = buddies->data;
 		g = purple_buddy_get_group(b);
-		if(!strcmp(g->name, old_group))
+		gname = purple_group_get_name(g);
+		if(!strcmp(gname, old_group))
 			groups = g_slist_append(groups, (char*)new_group); /* ick */
 		else
-			groups = g_slist_append(groups, g->name);
+			groups = g_slist_append(groups, (char*)gname);
 		buddies = g_slist_remove(buddies, b);
 	}
 	jabber_roster_update(gc->proto_data, name, groups);
@@ -398,15 +407,17 @@
 		PurpleGroup *group, GList *moved_buddies)
 {
 	GList *l;
+	const char *gname = purple_group_get_name(group);
 	for(l = moved_buddies; l; l = l->next) {
 		PurpleBuddy *buddy = l->data;
-		jabber_roster_group_change(gc, buddy->name, old_name, group->name);
+		jabber_roster_group_change(gc, purple_buddy_get_name(buddy), old_name, gname);
 	}
 }
 
 void jabber_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 		PurpleGroup *group) {
-	GSList *buddies = purple_find_buddies(gc->account, buddy->name);
+	const char *name = purple_buddy_get_name(buddy);
+	GSList *buddies = purple_find_buddies(purple_connection_get_account(gc), name);
 
 	buddies = g_slist_remove(buddies, buddy);
 	if(buddies != NULL) {
@@ -417,11 +428,11 @@
 		while(buddies) {
 			tmpbuddy = buddies->data;
 			tmpgroup = purple_buddy_get_group(tmpbuddy);
-			groups = g_slist_append(groups, tmpgroup->name);
+			groups = g_slist_append(groups, (char *)purple_group_get_name(tmpgroup));
 			buddies = g_slist_remove(buddies, tmpbuddy);
 		}
 
-		jabber_roster_update(gc->proto_data, buddy->name, groups);
+		jabber_roster_update(gc->proto_data, name, groups);
 		g_slist_free(groups);
 	} else {
 		JabberIq *iq = jabber_iq_new_query(gc->proto_data, JABBER_IQ_SET,
@@ -429,7 +440,7 @@
 		xmlnode *query = xmlnode_get_child(iq->node, "query");
 		xmlnode *item = xmlnode_new_child(query, "item");
 
-		xmlnode_set_attrib(item, "jid", buddy->name);
+		xmlnode_set_attrib(item, "jid", name);
 		xmlnode_set_attrib(item, "subscription", "remove");
 
 		jabber_iq_send(iq);
--- a/libpurple/protocols/jabber/si.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Sat Mar 07 01:59:40 2009 +0000
@@ -32,6 +32,7 @@
 #include "buddy.h"
 #include "disco.h"
 #include "jabber.h"
+#include "ibb.h"
 #include "iq.h"
 #include "si.h"
 
@@ -63,8 +64,15 @@
 	size_t rxlen;
 	gsize rxmaxlen;
 	int local_streamhost_fd;
+
+	JabberIBBSession *ibb_session;
+	guint ibb_timeout_handle;
+	FILE *fp;
 } JabberSIXfer;
 
+/* some forward declarations */
+static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer);
+
 static PurpleXfer*
 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
 {
@@ -178,6 +186,32 @@
 	return FALSE;
 }
 
+static void
+jabber_si_bytestreams_ibb_timeout_remove(JabberSIXfer *jsx)
+{
+	if (jsx->ibb_timeout_handle) {
+		purple_timeout_remove(jsx->ibb_timeout_handle);
+		jsx->ibb_timeout_handle = 0;
+	}
+}
+
+static gboolean
+jabber_si_bytestreams_ibb_timeout_cb(gpointer data)
+{
+	PurpleXfer *xfer = (PurpleXfer *) data;
+	JabberSIXfer *jsx = xfer->data;
+
+	if (jsx && !jsx->ibb_session) {
+		purple_debug_info("jabber",
+			"jabber_si_bytestreams_ibb_timeout called and IBB session not set "
+			" up yet, cancel transfer");
+		jabber_si_bytestreams_ibb_timeout_remove(jsx);
+		purple_xfer_cancel_local(xfer);
+	}
+
+	return FALSE;
+}
+
 static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
 {
 	JabberSIXfer *jsx = xfer->data;
@@ -200,7 +234,28 @@
 
 		jabber_iq_send(iq);
 
-		purple_xfer_cancel_local(xfer);
+		/* if IBB is available, revert to that before giving up... */
+		if (jsx->stream_method & STREAM_METHOD_IBB) {
+			/* if we are the initializer, init IBB */
+			purple_debug_info("jabber",
+				"jabber_si_bytestreams_attempt_connect: "
+				"no streamhosts found, trying IBB\n");
+			/* if we are the sender, open an IBB session, but not if we already
+			  did it, since we could have received the error <iq/> from the
+			  receiver already... */
+			if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+				&& !jsx->ibb_session) {
+				jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+			} else {
+				/* setup a timeout to cancel waiting for IBB open */
+				jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
+					jabber_si_bytestreams_ibb_timeout_cb, xfer);
+			}
+			/* if we are the receiver, just wait for IBB open, callback is
+			  already set up... */
+		} else {
+			purple_xfer_cancel_local(xfer);
+		}
 
 		return;
 	}
@@ -654,8 +709,32 @@
 	jsx = xfer->data;
 
 	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
-		if (type && !strcmp(type, "error"))
-			purple_xfer_cancel_remote(xfer);
+	  purple_debug_info("jabber",
+			    "jabber_si_xfer_connect_proxy_cb: type = %s\n",
+			    type);
+		if (type && !strcmp(type, "error")) {
+			/* if IBB is available, open IBB session */
+			purple_debug_info("jabber",
+				"jabber_si_xfer_connect_proxy_cb: got error, method: %d\n",
+				jsx->stream_method);
+			if (jsx->stream_method & STREAM_METHOD_IBB) {
+				purple_debug_info("jabber", "IBB is possible, try it\n");
+				/* if we are the sender and haven't already opened an IBB
+				  session, do so now (we might already have failed to open
+				  the bytestream proxy ourselves when receiving this <iq/> */
+				if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+					&& !jsx->ibb_session) {
+					jabber_si_xfer_ibb_send_init(js, xfer);
+				} else {
+					jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
+						jabber_si_bytestreams_ibb_timeout_cb, xfer);
+				}
+				/* if we are receiver, just wait for IBB open stanza, callback
+				  is already set up */
+			} else {
+				purple_xfer_cancel_remote(xfer);
+			}
+		}
 		return;
 	}
 
@@ -682,8 +761,22 @@
 			purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
 			purple_xfer_start(xfer, xfer->fd, NULL, -1);
 		} else {
-			purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n");
-			purple_xfer_cancel_local(xfer);
+			/* if available, try to revert to IBB... */
+			if (jsx->stream_method & STREAM_METHOD_IBB) {
+				purple_debug_info("jabber",
+					"jabber_si_connect_proxy_cb: trying to revert to IBB\n");
+				if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+					jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+				} else {
+					jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
+						jabber_si_bytestreams_ibb_timeout_cb, xfer);
+				}
+				/* if we are the receiver, we are already set up...*/
+			} else {
+				purple_debug_info("jabber",
+					"streamhost-used does not match any proxy that was offered to target\n");
+				purple_xfer_cancel_local(xfer);
+			}
 		}
 		g_free(my_jid);
 		return;
@@ -810,8 +903,26 @@
 	/* We have no way of transferring, cancel the transfer */
 	if (streamhost_count == 0) {
 		jabber_iq_free(iq);
-		/* We should probably notify the target, but this really shouldn't ever happen */
-		purple_xfer_cancel_local(xfer);
+
+		/* if available, revert to IBB */
+		if (jsx->stream_method & STREAM_METHOD_IBB) {
+			purple_debug_info("jabber",
+				"jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
+			if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+				/* if we are the sender, init the IBB session... */
+				jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+			} else {
+				jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
+					jabber_si_bytestreams_ibb_timeout_cb, xfer);
+			}
+			/* if we are the receiver, we should just wait... the IBB open
+			  handler has already been set up... */
+		} else {
+			/* We should probably notify the target,
+			  but this really shouldn't ever happen */
+			purple_xfer_cancel_local(xfer);
+		}
+
 		return;
 	}
 
@@ -841,11 +952,242 @@
 
 }
 
+static void
+jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberStream *js = jabber_ibb_session_get_js(sess);
+	PurpleConnection *gc = js->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
+
+	purple_debug_error("jabber", "an error occured during IBB file transfer\n");
+	purple_xfer_error(purple_xfer_get_type(xfer), account,
+		jabber_ibb_session_get_who(sess),
+			_("An error occured on the in-band bytestream transfer\n"));
+	purple_xfer_cancel_remote(xfer);
+}
+
+static void
+jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberStream *js = jabber_ibb_session_get_js(sess);
+	PurpleConnection *gc = js->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
+
+	purple_debug_info("jabber", "the remote user closed the transfer\n");
+	if (purple_xfer_get_bytes_remaining(xfer) > 0) {
+		purple_xfer_error(purple_xfer_get_type(xfer), account,
+			jabber_ibb_session_get_who(sess), _("Transfer was closed."));
+		purple_xfer_cancel_remote(xfer);
+	} else {
+		purple_xfer_set_completed(xfer, TRUE);
+		purple_xfer_end(xfer);
+	}
+}
+
+static void
+jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
+	gsize size)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+	if (size <= purple_xfer_get_bytes_remaining(xfer)) {
+		purple_debug_info("jabber", "about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n",
+			size);
+		if(!fwrite(data, size, 1, jsx->fp)) {
+			purple_debug_error("jabber", "error writing to file\n");
+			purple_xfer_cancel_remote(xfer);
+			return;
+		}
+		purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + size);
+		purple_xfer_update_progress(xfer);
+
+		if (purple_xfer_get_bytes_remaining(xfer) == 0) {
+			purple_xfer_set_completed(xfer, TRUE);
+			purple_xfer_end(xfer);
+		}
+	} else {
+		/* trying to write past size of file transfers negotiated size,
+		  reject transfer to protect against malicious behaviour */
+		purple_debug_error("jabber",
+			"IBB file transfer send more data than expected\n");
+		purple_xfer_cancel_remote(xfer);
+	}
+
+}
+
+static gboolean
+jabber_si_xfer_ibb_open_cb(JabberStream *js, xmlnode *packet)
+{
+	const gchar *who = xmlnode_get_attrib(packet, "from");
+	xmlnode *open = xmlnode_get_child(packet, "open");
+	const gchar *sid = xmlnode_get_attrib(open, "sid");
+	PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
+	if (xfer) {
+		JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+		JabberIBBSession *sess =
+			jabber_ibb_session_create_from_xmlnode(js, packet, xfer);
+		const char *filename;
+
+		jabber_si_bytestreams_ibb_timeout_remove(jsx);
+
+		if (sess) {
+			/* open the file to write to */
+			filename = purple_xfer_get_local_filename(xfer);
+			jsx->fp = g_fopen(filename, "wb");
+			if (jsx->fp == NULL) {
+				purple_debug_error("jabber", "failed to open file %s for writing: %s\n",
+					filename, g_strerror(errno));
+				purple_xfer_cancel_remote(xfer);
+				return FALSE;
+			}
+
+			/* setup callbacks here...*/
+			jabber_ibb_session_set_data_received_callback(sess,
+				jabber_si_xfer_ibb_recv_data_cb);
+			jabber_ibb_session_set_closed_callback(sess,
+				jabber_si_xfer_ibb_closed_cb);
+			jabber_ibb_session_set_error_callback(sess,
+				jabber_si_xfer_ibb_error_cb);
+
+			jsx->ibb_session = sess;
+
+			/* start the transfer */
+			purple_xfer_start(xfer, 0, NULL, 0);
+			return TRUE;
+		} else {
+			/* failed to create IBB session */
+			purple_debug_error("jabber", "failed to create IBB session\n");
+			purple_xfer_cancel_remote(xfer);
+			return FALSE;
+		}
+	} else {
+		/* we got an IBB <open/> for an unknown file transfer, pass along... */
+		purple_debug_info("jabber",
+			"IBB open did not match any SI file transfer\n");
+		return FALSE;
+	}
+}
+
+static void
+jabber_si_xfer_ibb_send_data(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+	gsize packet_size = remaining < jabber_ibb_session_get_block_size(sess) ?
+		remaining : jabber_ibb_session_get_block_size(sess);
+	gpointer data = g_malloc(packet_size);
+	int res;
+
+	purple_debug_info("jabber", "IBB: about to read %" G_GSIZE_FORMAT " bytes from file %p\n",
+		packet_size, jsx->fp);
+	res = fread(data, packet_size, 1, jsx->fp);
+
+	if (res == 1) {
+		jabber_ibb_session_send_data(sess, data, packet_size);
+		purple_xfer_set_bytes_sent(xfer,
+			purple_xfer_get_bytes_sent(xfer) + packet_size);
+		purple_xfer_update_progress(xfer);
+	} else {
+		purple_debug_error("jabber",
+			"jabber_si_xfer_ibb_send_data: error reading from file\n");
+		purple_xfer_cancel_local(xfer);
+	}
+}
+
+static void
+jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+
+	if (remaining == 0) {
+		/* close the session */
+		jabber_ibb_session_close(sess);
+		purple_xfer_set_completed(xfer, TRUE);
+		purple_xfer_end(xfer);
+	} else {
+		/* send more... */
+		jabber_si_xfer_ibb_send_data(sess);
+	}
+}
+
+static void
+jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	JabberStream *js = jabber_ibb_session_get_js(sess);
+	PurpleConnection *gc = js->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
+
+	if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) {
+		const char *filename = purple_xfer_get_local_filename(xfer);
+		jsx->fp = g_fopen(filename, "rb");
+		if (jsx->fp == NULL) {
+			purple_debug_error("jabber", "Failed to open file %s for reading: %s\n",
+				filename, g_strerror(errno));
+			purple_xfer_error(purple_xfer_get_type(xfer), account,
+				jabber_ibb_session_get_who(sess),
+				_("Failed to open the file"));
+			purple_xfer_cancel_local(xfer);
+			return;
+		}
+
+		purple_xfer_start(xfer, 0, NULL, 0);
+		purple_xfer_set_bytes_sent(xfer, 0);
+		purple_xfer_update_progress(xfer);
+		jabber_si_xfer_ibb_send_data(sess);
+	} else {
+		/* error */
+		purple_xfer_error(purple_xfer_get_type(xfer), account,
+			jabber_ibb_session_get_who(sess),
+			_("Failed to open in-band bytestream"));
+		purple_xfer_end(xfer);
+	}
+}
+
+static void
+jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
+{
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+	purple_xfer_ref(xfer);
+
+	jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
+		purple_xfer_get_remote_user(xfer), xfer);
+
+	if (jsx->ibb_session) {
+		/* should set callbacks here... */
+		jabber_ibb_session_set_opened_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_opened_cb);
+		jabber_ibb_session_set_data_sent_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_sent_cb);
+		jabber_ibb_session_set_closed_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_closed_cb);
+		jabber_ibb_session_set_error_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_error_cb);
+
+		/* open the IBB session */
+		jabber_ibb_session_open(jsx->ibb_session);
+
+	} else {
+		/* failed to create IBB session */
+		purple_debug_error("jabber",
+			"failed to initiate IBB session for file transfer\n");
+		purple_xfer_cancel_local(xfer);
+	}
+}
+
 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet,
 		gpointer data)
 {
 	PurpleXfer *xfer = data;
 	xmlnode *si, *feature, *x, *field, *value;
+	gboolean found_method = FALSE;
 
 	if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
 		purple_xfer_cancel_remote(xfer);
@@ -864,20 +1206,33 @@
 
 	for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
 		const char *var = xmlnode_get_attrib(field, "var");
+		JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
 
 		if(var && !strcmp(var, "stream-method")) {
 			if((value = xmlnode_get_child(field, "value"))) {
 				char *val = xmlnode_get_data(value);
 				if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) {
 					jabber_si_xfer_bytestreams_send_init(xfer);
-					g_free(val);
-					return;
+					jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
+					found_method = TRUE;
+				} else if (val && !strcmp(val, XEP_0047_NAMESPACE)) {
+					jsx->stream_method |= STREAM_METHOD_IBB;
+					if (!found_method) {
+						/* we haven't tried to init a bytestream session, yet
+						  start IBB right away... */
+						jabber_si_xfer_ibb_send_init(js, xfer);
+						found_method = TRUE;
+					}
 				}
 				g_free(val);
 			}
 		}
 	}
-	purple_xfer_cancel_remote(xfer);
+
+	if (!found_method) {
+		purple_xfer_cancel_remote(xfer);
+	}
+
 }
 
 static void jabber_si_xfer_send_request(PurpleXfer *xfer)
@@ -914,14 +1269,14 @@
 	field = xmlnode_new_child(x, "field");
 	xmlnode_set_attrib(field, "var", "stream-method");
 	xmlnode_set_attrib(field, "type", "list-single");
+	/* maybe we should add an option to always skip bytestreams for people
+		behind troublesome firewalls */
 	option = xmlnode_new_child(field, "option");
 	value = xmlnode_new_child(option, "value");
 	xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
-	/*
 	option = xmlnode_new_child(field, "option");
 	value = xmlnode_new_child(option, "value");
 	xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
-	*/
 
 	jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
 
@@ -935,38 +1290,66 @@
 static void jabber_si_xfer_free(PurpleXfer *xfer)
 {
 	JabberSIXfer *jsx = xfer->data;
-	JabberStream *js = jsx->js;
+
+	if (jsx) {
+		JabberStream *js = jsx->js;
+
+		js->file_transfers = g_list_remove(js->file_transfers, xfer);
 
-	js->file_transfers = g_list_remove(js->file_transfers, xfer);
+		if (jsx->connect_data != NULL)
+			purple_proxy_connect_cancel(jsx->connect_data);
+		if (jsx->listen_data != NULL)
+			purple_network_listen_cancel(jsx->listen_data);
+		if (jsx->iq_id != NULL)
+			jabber_iq_remove_callback_by_id(js, jsx->iq_id);
+		if (jsx->local_streamhost_fd >= 0)
+			close(jsx->local_streamhost_fd);
+		if (jsx->connect_timeout > 0)
+			purple_timeout_remove(jsx->connect_timeout);
+		if (jsx->ibb_timeout_handle > 0)
+			purple_timeout_remove(jsx->ibb_timeout_handle);
 
-	if (jsx->connect_data != NULL)
-		purple_proxy_connect_cancel(jsx->connect_data);
-	if (jsx->listen_data != NULL)
-		purple_network_listen_cancel(jsx->listen_data);
-	if (jsx->iq_id != NULL)
-		jabber_iq_remove_callback_by_id(js, jsx->iq_id);
-	if (jsx->local_streamhost_fd >= 0)
-		close(jsx->local_streamhost_fd);
-	if (jsx->connect_timeout > 0)
-		purple_timeout_remove(jsx->connect_timeout);
+		if (jsx->streamhosts) {
+			g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
+			g_list_free(jsx->streamhosts);
+		}
+
+		if (jsx->ibb_session) {
+			purple_debug_info("jabber",
+				"jabber_si_xfer_free: destroying IBB session\n");
+			jabber_ibb_session_destroy(jsx->ibb_session);
+		}
 
-	if (jsx->streamhosts) {
-		g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
-		g_list_free(jsx->streamhosts);
-	}
+		if (jsx->fp) {
+			purple_debug_info("jabber",
+				"jabber_si_xfer_free: closing file for IBB transfer\n");
+			fclose(jsx->fp);
+		}
 
-	g_free(jsx->stream_id);
-	g_free(jsx->iq_id);
-	/* XXX: free other stuff */
-	g_free(jsx->rxqueue);
-	g_free(jsx);
-	xfer->data = NULL;
+		g_free(jsx->stream_id);
+		g_free(jsx->iq_id);
+		/* XXX: free other stuff */
+		g_free(jsx->rxqueue);
+		g_free(jsx);
+		xfer->data = NULL;
 
-	purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p", jsx);
+		purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx);
+	}
 }
 
+/*
+ * These four functions should only be called from the PurpleXfer functions
+ * (typically purple_xfer_cancel_(remote|local), purple_xfer_end, or
+ * purple_xfer_request_denied.
+ */
 static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
 {
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+
+	/* if there is an IBB session active, send close on that */
+	if (jsx->ibb_session) {
+		jabber_ibb_session_close(jsx->ibb_session);
+	}
 	jabber_si_xfer_free(xfer);
 	purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n");
 }
@@ -981,6 +1364,11 @@
 
 static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
 {
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	/* if there is an IBB session active, send close */
+	if (jsx->ibb_session) {
+		jabber_ibb_session_close(jsx->ibb_session);
+	}
 	jabber_si_xfer_free(xfer);
 	purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n");
 }
@@ -995,9 +1383,16 @@
 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
 		JabberCapabilities capabilities, gpointer data)
 {
-	PurpleXfer *xfer = data;
+	PurpleXfer *xfer = (PurpleXfer *) data;
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
 
-	if(capabilities & JABBER_CAP_SI_FILE_XFER) {
+	if (capabilities & JABBER_CAP_IBB) {
+		purple_debug_info("jabber",
+			"jabber_si_xfer_send_disco_cb: remote JID supports IBB\n");
+		jsx->stream_method |= STREAM_METHOD_IBB;
+	}
+
+	if (capabilities & JABBER_CAP_SI_FILE_XFER) {
 		jabber_si_xfer_send_request(xfer);
 	} else {
 		char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
@@ -1124,17 +1519,21 @@
 		x = xmlnode_new_child(feature, "x");
 		xmlnode_set_namespace(x, "jabber:x:data");
 		xmlnode_set_attrib(x, "type", "submit");
-
 		field = xmlnode_new_child(x, "field");
 		xmlnode_set_attrib(field, "var", "stream-method");
 
-		value = xmlnode_new_child(field, "value");
-		if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS)
+		/* we should maybe "remember" if bytestreams has failed before (in the
+			same session) with this JID, and only present IBB as an option to
+			avoid unnessesary timeout */
+		/* maybe we should have an account option to always just try IBB
+			for people who know their firewalls are very restrictive */
+		if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
+			value = xmlnode_new_child(field, "value");
 			xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
-		/*
-		else if(jsx->stream_method & STREAM_METHOD_IBB)
-		xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
-		*/
+		} else if(jsx->stream_method & STREAM_METHOD_IBB) {
+			value = xmlnode_new_child(field, "value");
+			xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
+		}
 
 		jabber_iq_send(iq);
 	}
@@ -1156,6 +1555,9 @@
 		jsx->js = js;
 		jsx->local_streamhost_fd = -1;
 
+		jsx->ibb_session = NULL;
+		jsx->fp = NULL;
+
 		purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
 		purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
 		purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
@@ -1227,6 +1629,8 @@
 	jsx = g_new0(JabberSIXfer, 1);
 	jsx->local_streamhost_fd = -1;
 
+	jsx->ibb_session = NULL;
+
 	for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
 		const char *var = xmlnode_get_attrib(field, "var");
 		if(var && !strcmp(var, "stream-method")) {
@@ -1237,10 +1641,8 @@
 					if((val = xmlnode_get_data(value))) {
 						if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) {
 							jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
-							/*
 						} else if(!strcmp(val, "http://jabber.org/protocol/ibb")) {
 							jsx->stream_method |= STREAM_METHOD_IBB;
-							*/
 						}
 						g_free(val);
 					}
@@ -1278,4 +1680,15 @@
 	}
 }
 
+void
+jabber_si_init(void)
+{
+	jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
+}
 
+void
+jabber_si_uninit(void)
+{
+	jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb);
+}
+
--- a/libpurple/protocols/jabber/si.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/si.h	Sat Mar 07 01:59:40 2009 +0000
@@ -30,5 +30,7 @@
 void jabber_si_parse(JabberStream *js, xmlnode *packet);
 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who);
 void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);
+void jabber_si_init(void);
+void jabber_si_uninit(void);
 
 #endif /* _PURPLE_JABBER_SI_H_ */
--- a/libpurple/protocols/jabber/usermood.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Sat Mar 07 01:59:40 2009 +0000
@@ -103,7 +103,7 @@
 	/* ignore the mood of people not on our buddy list */
 	if (!buddy || !item)
 		return;
-	
+
 	mood = xmlnode_get_child_with_namespace(item, "mood", "http://jabber.org/protocol/mood");
 	if (!mood)
 		return;
@@ -179,10 +179,10 @@
 
 	field = purple_request_field_choice_new("mood",
 											_("Mood"), 0);
-	
+
 	for(i = 0; moodstrings[i]; ++i)
 		purple_request_field_choice_add(field, _(moodstrings[i]));
-	
+
 	purple_request_field_set_required(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
@@ -190,7 +190,7 @@
 											_("Description"), NULL,
 											FALSE);
 	purple_request_field_group_add_field(group, field);
-	
+
 	purple_request_fields(gc, _("Edit User Mood"),
 						  _("Edit User Mood"),
 						  _("Please select your mood from the list."),
@@ -199,7 +199,7 @@
 						  _("Cancel"), NULL,
 						  purple_connection_get_account(gc), NULL, NULL,
 						  gc);
-	
+
 }
 
 void jabber_mood_init_action(GList **m) {
@@ -222,7 +222,7 @@
 		xmlnode *textnode = xmlnode_new_child(moodnode, "text");
 		xmlnode_insert_data(textnode, text, -1);
 	}
-	
+
 	jabber_pep_publish(js, publish);
 	/* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free
 	   (yay for well-defined memory management rules) */
--- a/libpurple/protocols/jabber/usernick.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/usernick.c	Sat Mar 07 01:59:40 2009 +0000
@@ -34,11 +34,11 @@
 	JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
 	xmlnode *nick;
 	char *nickname = NULL;
-	
+
 	/* ignore the tune of people not on our buddy list */
 	if (!buddy || !item)
 		return;
-	
+
 	nick = xmlnode_get_child_with_namespace(item, "nick", "http://jabber.org/protocol/nick");
 	if (!nick)
 		return;
@@ -49,15 +49,15 @@
 
 static void do_nick_set(JabberStream *js, const char *nick) {
 	xmlnode *publish, *nicknode;
-	
+
 	publish = xmlnode_new("publish");
 	xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/nick");
 	nicknode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "nick");
 	xmlnode_set_namespace(nicknode, "http://jabber.org/protocol/nick");
-	
+
 	if(nick && nick[0] != '\0')
 		xmlnode_insert_data(nicknode, nick, -1);
-	
+
 	jabber_pep_publish(js, publish);
 	/* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free
 		(yay for well-defined memory management rules) */
@@ -66,13 +66,13 @@
 static void do_nick_got_own_nick_cb(JabberStream *js, const char *from, xmlnode *items) {
 	char *oldnickname = NULL;
 	xmlnode *item = xmlnode_get_child(items,"item");
-	
+
 	if(item) {
 		xmlnode *nick = xmlnode_get_child_with_namespace(item,"nick","http://jabber.org/protocol/nick");
 		if(nick)
 			oldnickname = xmlnode_get_data(nick);
 	}
-	
+
 	purple_request_input(js->gc, _("Set User Nickname"), _("Please specify a new nickname for you."),
 		_("This information is visible to all contacts on your contact list, so choose something appropriate."),
 		oldnickname, FALSE, FALSE, NULL, _("Set"), PURPLE_CALLBACK(do_nick_set), _("Cancel"), NULL,
@@ -84,7 +84,7 @@
 	PurpleConnection *gc = (PurpleConnection *) action->context;
 	JabberStream *js = gc->proto_data;
 	char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
-	
+
 	/* since the nickname might have been changed by another resource of this account, we always have to request the old one
 		from the server to present as the default for the new one */
 	jabber_pep_request_item(js, jid, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb);
--- a/libpurple/protocols/jabber/usertune.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/usertune.c	Sat Mar 07 01:59:40 2009 +0000
@@ -116,12 +116,12 @@
 void jabber_tune_set(PurpleConnection *gc, const PurpleJabberTuneInfo *tuneinfo) {
 	xmlnode *publish, *tunenode;
 	JabberStream *js = gc->proto_data;
-	
+
 	publish = xmlnode_new("publish");
 	xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/tune");
 	tunenode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "tune");
 	xmlnode_set_namespace(tunenode, "http://jabber.org/protocol/tune");
-	
+
 	if(tuneinfo) {
 		if(tuneinfo->artist && tuneinfo->artist[0] != '\0')
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "artist"),tuneinfo->artist,-1);
@@ -139,7 +139,7 @@
 		if(tuneinfo->track && tuneinfo->track[0] != '\0')
 			xmlnode_insert_data(xmlnode_new_child(tunenode, "track"),tuneinfo->track,-1);
 	}
-	
+
 	jabber_pep_publish(js, publish);
 	/* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free
 	   (yay for well-defined memory management rules) */
--- a/libpurple/protocols/jabber/xdata.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/jabber/xdata.c	Sat Mar 07 01:59:40 2009 +0000
@@ -372,7 +372,7 @@
 		if(field && xmlnode_get_child(fn, "required"))
 			purple_request_field_set_required(field,TRUE);
 	}
-	
+
 	if(actions != NULL) {
 		PurpleRequestField *actionfield;
 		GList *action;
@@ -382,7 +382,7 @@
 
 		for(action = actions; action; action = g_list_next(action)) {
 			JabberXDataAction *a = action->data;
-			
+
 			purple_request_field_choice_add(actionfield, a->name);
 			data->actions = g_list_append(data->actions, g_strdup(a->handle));
 		}
--- a/libpurple/protocols/msn/directconn.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/msn/directconn.c	Sat Mar 07 01:59:40 2009 +0000
@@ -201,24 +201,6 @@
 
 	g_free(buffer);
 
-#if 0
-	/* Let's write the length of the data. */
-	ret = write(directconn->fd, &len, sizeof(len));
-
-	/* Let's write the data. */
-	ret = write(directconn->fd, data, len);
-
-	char *str;
-	str = g_strdup_printf("/home/revo/msntest/w%.4d.bin", directconn->c);
-
-	FILE *tf = g_fopen(str, "w");
-	fwrite(&len, 1, sizeof(len), tf);
-	fwrite(data, 1, len, tf);
-	fclose(tf);
-
-	g_free(str);
-#endif
-
 	directconn->c++;
 
 	return ret;
@@ -341,7 +323,7 @@
 		MsnMessage *msg;
 
 #ifdef DEBUG_DC
-		str = g_strdup_printf("/home/revo/msntest/r%.4d.bin", directconn->c);
+		str = g_strdup_printf("%s/msntest/r%.4d.bin", g_get_home_dir(), directconn->c);
 
 		FILE *tf = g_fopen(str, "w");
 		fwrite(body, 1, len, tf);
--- a/libpurple/protocols/msn/msn.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/msn/msn.c	Sat Mar 07 01:59:40 2009 +0000
@@ -457,23 +457,27 @@
 	PurpleConnection *gc;
 	MsnSession *session;
 	MsnMobileData *data;
+	PurpleAccount *account;
+	const char *name;
 
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
+	name = purple_buddy_get_name(buddy);
 
 	session = gc->proto_data;
 
 	data = g_new0(MsnMobileData, 1);
 	data->gc = gc;
-	data->passport = buddy->name;
+	data->passport = name;
 
 	purple_request_input(gc, NULL, _("Send a mobile message."), NULL,
 					   NULL, TRUE, FALSE, NULL,
 					   _("Page"), G_CALLBACK(send_to_mobile_cb),
 					   _("Close"), G_CALLBACK(close_mobile_page_cb),
-					   purple_connection_get_account(gc), purple_buddy_get_name(buddy), NULL,
+					   account, name, NULL,
 					   data);
 }
 
@@ -505,6 +509,7 @@
 {
 	PurpleBuddy *buddy;
 	PurpleConnection *gc;
+	PurpleAccount *account;
 
 	MsnSession *session;
 	MsnSwitchBoard *swboard;
@@ -514,13 +519,14 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
 
 	session = gc->proto_data;
 
 	swboard = msn_switchboard_new(session);
 	msn_switchboard_request(swboard);
-	msn_switchboard_request_add_user(swboard, buddy->name);
+	msn_switchboard_request_add_user(swboard, purple_buddy_get_name(buddy));
 
 	/* TODO: This might move somewhere else, after USR might be */
 	swboard->chat_id = msn_switchboard_get_chat_id();
@@ -528,9 +534,9 @@
 	swboard->flag = MSN_SB_FLAG_IM;
 
 	/* Local alias > Display name > Username */
-	if ((alias = purple_account_get_alias(buddy->account)) == NULL)
+	if ((alias = purple_account_get_alias(account)) == NULL)
 		if ((alias = purple_connection_get_display_name(gc)) == NULL)
-			alias = purple_account_get_username(buddy->account);
+			alias = purple_account_get_username(account);
 
 	purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
 	                          alias, NULL, PURPLE_CBFLAGS_NONE, TRUE);
@@ -613,7 +619,7 @@
 static const char *
 msn_list_emblems(PurpleBuddy *b)
 {
-	MsnUser *user = b->proto_data;
+	MsnUser *user = purple_buddy_get_protocol_data(b);
 
 	if (user != NULL) {
 		if (user->clientid & MSN_CLIENT_CAP_BOT)
@@ -694,7 +700,7 @@
 	PurplePresence *presence = purple_buddy_get_presence(buddy);
 	PurpleStatus *status = purple_presence_get_active_status(presence);
 
-	user = buddy->proto_data;
+	user = purple_buddy_get_protocol_data(buddy);
 
 	if (purple_presence_is_online(presence))
 	{
@@ -937,7 +943,7 @@
 
 	g_return_val_if_fail(buddy != NULL, NULL);
 
-	user = buddy->proto_data;
+	user = purple_buddy_get_protocol_data(buddy);
 
 	if (user != NULL)
 	{
@@ -950,8 +956,8 @@
 		}
 	}
 
-	if (g_ascii_strcasecmp(buddy->name,
-	                       purple_account_get_username(buddy->account)))
+	if (g_ascii_strcasecmp(purple_buddy_get_name(buddy),
+				purple_account_get_username(purple_buddy_get_account(buddy))))
 	{
 		act = purple_menu_action_new(_("Initiate _Chat"),
 		                           PURPLE_CALLBACK(initiate_chat_cb),
@@ -1430,14 +1436,15 @@
 {
 	MsnSession *session;
 	MsnUserList *userlist;
-	const char *who;
+	const char *who, *gname;
 	MsnUser *user;
 
 	session = gc->proto_data;
 	userlist = session->userlist;
-	who = msn_normalize(gc->account, buddy->name);
-
-	purple_debug_info("msn", "Add user:%s to group:%s\n", who, (group && group->name) ? group->name : "(null)");
+	who = msn_normalize(purple_connection_get_account(gc), purple_buddy_get_name(buddy));
+
+	gname = group ? purple_group_get_name(group) : NULL;
+	purple_debug_info("msn", "Add user:%s to group:%s\n", who, gname ? gname : "(null)");
 	if (!session->logged_in)
 	{
 #if 0
@@ -1464,12 +1471,12 @@
 	if ((user != NULL) && (user->networkid != MSN_NETWORK_UNKNOWN)) {
 		/* We already know this buddy and their network. This function knows
 		   what to do with users already in the list and stuff... */
-		msn_userlist_add_buddy(userlist, who, group ? group->name : NULL);
+		msn_userlist_add_buddy(userlist, who, gname);
 	} else {
 		char **tokens;
 		char *fqy;
 		/* We need to check the network for this buddy first */
-		msn_userlist_save_pending_buddy(userlist, who, group ? group->name : NULL);
+		msn_userlist_save_pending_buddy(userlist, who, gname);
 		tokens = g_strsplit(who, "@", 2);
 		fqy = g_strdup_printf("<ml><d n=\"%s\"><c n=\"%s\"/></d></ml>",
 		                      tokens[1],
@@ -1494,7 +1501,7 @@
 		return;
 
 	/* XXX - Does buddy->name need to be msn_normalize'd here?  --KingAnt */
-	msn_userlist_rem_buddy(userlist, buddy->name);
+	msn_userlist_rem_buddy(userlist, purple_buddy_get_name(buddy));
 }
 
 static void
@@ -1747,20 +1754,22 @@
 				 PurpleGroup *group, GList *moved_buddies)
 {
 	MsnSession *session;
+	const char *gname;
 
 	session = gc->proto_data;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->userlist != NULL);
 
+	gname = purple_group_get_name(group);
 	if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL)
 	{
-		msn_contact_rename_group(session, old_name, group->name);
+		msn_contact_rename_group(session, old_name, gname);
 	}
 	else
 	{
 		/* not found */
-		msn_add_group(session, NULL, group->name);
+		msn_add_group(session, NULL, gname);
 	}
 }
 
@@ -1820,20 +1829,22 @@
 {
 	MsnSession *session;
 	MsnCmdProc *cmdproc;
+	const char *gname;
 
 	session = gc->proto_data;
 	cmdproc = session->notification->cmdproc;
-
-	purple_debug_info("msn", "Remove group %s\n", group->name);
+	gname = purple_group_get_name(group);
+
+	purple_debug_info("msn", "Remove group %s\n", gname);
 	/*we can't delete the default group*/
-	if(!strcmp(group->name, MSN_INDIVIDUALS_GROUP_NAME)||
-		!strcmp(group->name, MSN_NON_IM_GROUP_NAME))
+	if(!strcmp(gname, MSN_INDIVIDUALS_GROUP_NAME)||
+		!strcmp(gname, MSN_NON_IM_GROUP_NAME))
 	{
 		purple_debug_info("msn", "This group can't be removed, returning.\n");
 		return ;
 	}
 
-	msn_del_group(session, group->name);
+	msn_del_group(session, gname);
 }
 
 /**
@@ -1850,17 +1861,19 @@
 	if (b)
 	{
 		char *tmp;
-
-		if (b->alias && b->alias[0])
+		const char *alias;
+
+		alias = purple_buddy_get_local_buddy_alias(b);
+		if (alias && alias[0])
 		{
-			char *aliastext = g_markup_escape_text(b->alias, -1);
+			char *aliastext = g_markup_escape_text(alias, -1);
 			purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext);
 			g_free(aliastext);
 		}
 
-		if (b->server_alias)
+		if ((alias = purple_buddy_get_server_alias(b)) != NULL)
 		{
-			char *nicktext = g_markup_escape_text(b->server_alias, -1);
+			char *nicktext = g_markup_escape_text(alias, -1);
 			tmp = g_strdup_printf("<font sml=\"msn\">%s</font>", nicktext);
 			purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
 			g_free(tmp);
--- a/libpurple/protocols/msn/notification.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Mar 07 01:59:40 2009 +0000
@@ -665,13 +665,14 @@
 		if (user->passport && !strcmp(user->passport, "messenger@microsoft.com"))
 			continue;
 
-		if ((user->list_op & MSN_LIST_OP_MASK) == (MSN_LIST_AL_OP | MSN_LIST_BL_OP)) {
+		if ((user->list_op & MSN_LIST_OP_MASK & ~MSN_LIST_FL_OP)
+		 == (MSN_LIST_AL_OP | MSN_LIST_BL_OP)) {
 			/* The server will complain if we send it a user on both the
 			   Allow and Block lists. So assume they're on the Block list
 			   and remove them from the Allow list in the membership lists to
 			   stop this from happening again. */
 			purple_debug_warning("msn",
-			                     "User %s is on both Allow and Block list,"
+			                     "User %s is on both Allow and Block list; "
 			                     "removing from Allow list.\n",
 			                     user->passport);
 			msn_userlist_rem_buddy_from_list(session->userlist, user->passport, MSN_LIST_AL);
--- a/libpurple/protocols/msn/session.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/msn/session.c	Sat Mar 07 01:59:40 2009 +0000
@@ -271,16 +271,21 @@
 	 * being logged in. This no longer happens, so we manually iterate
 	 * over the whole buddy list to identify sync issues.
 	 */
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
 		PurpleGroup *group = (PurpleGroup *)gnode;
 		const char *group_name;
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		group_name = group->name;
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
+		group_name = purple_group_get_name(group);
+		for(cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for(bnode = cnode->child; bnode; bnode = bnode->next) {
+			for(bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode)) {
 				PurpleBuddy *b;
 				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
--- a/libpurple/protocols/msn/user.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/msn/user.c	Sat Mar 07 01:59:40 2009 +0000
@@ -294,9 +294,8 @@
 		b = purple_buddy_new(account, passport, NULL);
 		purple_blist_add_buddy(b, NULL, g, NULL);
 	}
-	b->proto_data = user;
+	purple_buddy_set_protocol_data(b, user);
 	/*Update the blist Node info*/
-//	purple_blist_node_set_string(&(b->node), "", "");
 }
 
 /*check if the msn user is online*/
--- a/libpurple/protocols/msn/userlist.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sat Mar 07 01:59:40 2009 +0000
@@ -931,31 +931,37 @@
 msn_userlist_load(MsnSession *session)
 {
 	PurpleBlistNode *gnode, *cnode, *bnode;
-	PurpleConnection *gc = purple_account_get_connection(session->account);
+	PurpleAccount *account = session->account;
+	PurpleConnection *gc = purple_account_get_connection(account);
 	GSList *l;
 	MsnUser * user;
 
 	g_return_if_fail(gc != NULL);
 
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next)
+	for (gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode))
 	{
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for (cnode = gnode->child; cnode; cnode = cnode->next)
+		for (cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode))
 		{
 			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for (bnode = cnode->child; bnode; bnode = bnode->next)
+			for (bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode))
 			{
 				PurpleBuddy *b;
 				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 				b = (PurpleBuddy *)bnode;
-				if (b->account == gc->account)
+				if (purple_buddy_get_account(b) == account)
 				{
 					user = msn_userlist_find_add_user(session->userlist,
-						b->name,NULL);
-					b->proto_data = user;
+						purple_buddy_get_name(b), NULL);
+					purple_buddy_set_protocol_data(b, user);
 					msn_user_set_op(user, MSN_LIST_FL_OP);
 				}
 			}
--- a/libpurple/protocols/myspace/README	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/myspace/README	Sat Mar 07 01:59:40 2009 +0000
@@ -1,9 +1,8 @@
-MySpaceIM Protocol Plugin for Libpurple     by Jeff Connelly 20070807
-
+MySpaceIM Protocol Plugin for libpurple by Jeff Connelly 2007-08-07
 
 Greetings. This package contains a plugin for libpurple (as used in
-Pidgin, formerly Gaim) to connect to the new MySpaceIM instant messaging 
-network and send/receive messages. Functionality is only basic as of yet, 
+Pidgin, formerly Gaim) to connect to the new MySpaceIM instant messaging
+network and send/receive messages. Functionality is only basic as of yet,
 and this code should be considered alpha quality.
 
 This code was initially developed under Google Summer of Code 2007.
@@ -15,10 +14,10 @@
 Login using your _email address_ you use to login to myspace.com. You can't
 login using your numeric ID or alias.
 
-To test it out, send a message to yourself (by your username or numeric 
-uid (email not yet supported)) or tom (6221). In either case you should 
-get a reply. You should also be able to talk to other MySpaceIM users if 
-you desire. Replies will always be shown as coming from a user's username, 
+To test it out, send a message to yourself (by your username or numeric
+uid (email not yet supported)) or tom (6221). In either case you should
+get a reply. You should also be able to talk to other MySpaceIM users if
+you desire. Replies will always be shown as coming from a user's username,
 even if you IM by email or userid.
 
 Feedback welcome. You can IM my test account at "msimprpl" if you feel like it.
@@ -26,4 +25,3 @@
 Enjoy,
 -Jeff Connelly
 msimprpl@xyzzy.cjb.net
-
--- a/libpurple/protocols/myspace/myspace.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Sat Mar 07 01:59:40 2009 +0000
@@ -202,7 +202,7 @@
 		/* Next, see if on buddy list and know uid. */
 		buddy = purple_find_buddy(session->account, username);
 		if (buddy) {
-			uid = purple_blist_node_get_int(&buddy->node, "UserID");
+			uid = purple_blist_node_get_int(PURPLE_BLIST_NODE(buddy), "UserID");
 		} else {
 			uid = 0;
 		}
@@ -311,7 +311,7 @@
 		/* See finch/gnthistory.c */
 		buddy = cur->data;
 
-		uid = purple_blist_node_get_int(&buddy->node, "UserID");
+		uid = purple_blist_node_get_int(PURPLE_BLIST_NODE(buddy), "UserID");
 		name = purple_buddy_get_name(buddy);
 
 		if (uid == wanted_uid)
@@ -383,12 +383,17 @@
 	MsimSession *session;
 	MsimUser *user;
 	const gchar *display_name, *headline;
+	PurpleAccount *account;
+	PurpleConnection *gc;
 
 	g_return_val_if_fail(buddy != NULL, NULL);
 
 	user = msim_get_user_from_buddy(buddy);
 
-	session = (MsimSession *)buddy->account->gc->proto_data;
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
+	session = (MsimSession *)gc->proto_data;
+
 	g_return_val_if_fail(MSIM_SESSION_VALID(session), NULL);
 
 	display_name = headline = NULL;
@@ -435,8 +440,10 @@
 
 	if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
 		MsimSession *session;
-
-		session = (MsimSession *)buddy->account->gc->proto_data;
+		PurpleAccount *account = purple_buddy_get_account(buddy);
+		PurpleConnection *gc = purple_account_get_connection(account);
+ 
+		session = (MsimSession *)gc->proto_data;
 
 		g_return_if_fail(MSIM_SESSION_VALID(session));
 
@@ -1036,11 +1043,11 @@
 		 * of numbers in the buddy list.
 		 */
 		if (display_name != NULL) {
-			purple_blist_node_set_string(&buddy->node, "DisplayName", display_name);
+			purple_blist_node_set_string(PURPLE_BLIST_NODE(buddy), "DisplayName", display_name);
 			serv_got_alias(session->gc, username, display_name);
 		} else {
 			serv_got_alias(session->gc, username,
-					purple_blist_node_get_string(&buddy->node, "DisplayName"));
+					purple_blist_node_get_string(PURPLE_BLIST_NODE(buddy), "DisplayName"));
 		}
 	}
 	g_free(display_name);
@@ -1050,7 +1057,7 @@
 
 	user->id = uid;
 	/* Keep track of the user ID across sessions */
-	purple_blist_node_set_int(&buddy->node, "UserID", uid);
+	purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "UserID", uid);
 
 	/* Stores a few fields in the MsimUser, relevant to the buddy itself.
 	 * AvatarURL, Headline, ContactID. */
@@ -1374,7 +1381,7 @@
 		user->id = msim_msg_get_integer(msg, "f");
 
 		/* Keep track of the user ID across sessions */
-		purple_blist_node_set_int(&buddy->node, "UserID", user->id);
+		purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy), "UserID", user->id);
 
 		msim_store_user_info(session, msg, NULL);
 	} else {
@@ -2628,10 +2635,14 @@
 	MsimMessage *msg;
 	MsimMessage *msg_persist;
 	MsimMessage *body;
+	const char *name, *gname;
 
 	session = (MsimSession *)gc->proto_data;
+	name = purple_buddy_get_name(buddy);
+	gname = group ? purple_group_get_name(group) : NULL;
+
 	purple_debug_info("msim", "msim_add_buddy: want to add %s to %s\n",
-			buddy->name, (group && group->name) ? group->name : "(no group)");
+			name, gname ? gname : "(no group)");
 
 	msg = msim_msg_new(
 			"addbuddy", MSIM_TYPE_BOOLEAN, TRUE,
@@ -2640,7 +2651,7 @@
 			"reason", MSIM_TYPE_STRING, g_strdup(""),
 			NULL);
 
-	if (!msim_postprocess_outgoing(session, msg, buddy->name, "newprofileid", "reason")) {
+	if (!msim_postprocess_outgoing(session, msg, name, "newprofileid", "reason")) {
 		purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("'addbuddy' command failed."));
 		msim_msg_free(msg);
 		return;
@@ -2652,7 +2663,7 @@
 
 	body = msim_msg_new(
 			"ContactID", MSIM_TYPE_STRING, g_strdup("<uid>"),
-			"GroupName", MSIM_TYPE_STRING, g_strdup(group->name),
+			"GroupName", MSIM_TYPE_STRING, g_strdup(gname),
 			"Position", MSIM_TYPE_INTEGER, 1000,
 			"Visibility", MSIM_TYPE_INTEGER, 1,
 			"NickName", MSIM_TYPE_STRING, g_strdup(""),
@@ -2673,7 +2684,7 @@
 		"body", MSIM_TYPE_DICTIONARY, body,
 		NULL);
 
-	if (!msim_postprocess_outgoing(session, msg_persist, buddy->name, "body", NULL))
+	if (!msim_postprocess_outgoing(session, msg_persist, name, "body", NULL))
 	{
 		purple_notify_error(NULL, NULL, _("Failed to add buddy"), _("persist command failed"));
 		msim_msg_free(msg_persist);
@@ -2682,7 +2693,14 @@
 	msim_msg_free(msg_persist);
 
 	/* Add to allow list, remove from block list */
-	msim_update_blocklist_for_buddy(session, buddy->name, TRUE, FALSE);
+	msim_update_blocklist_for_buddy(session, name, TRUE, FALSE);
+}
+
+static void
+msim_buddy_free(PurpleBuddy *buddy)
+{
+	msim_user_free(purple_buddy_get_protocol_data(buddy));
+	purple_buddy_set_protocol_data(buddy, NULL);
 }
 
 /**
@@ -2694,8 +2712,10 @@
 	MsimSession *session;
 	MsimMessage *delbuddy_msg;
 	MsimMessage *persist_msg;
+	const char *name;
 
 	session = (MsimSession *)gc->proto_data;
+	name = purple_buddy_get_name(buddy);
 
 	delbuddy_msg = msim_msg_new(
 				"delbuddy", MSIM_TYPE_BOOLEAN, TRUE,
@@ -2703,7 +2723,7 @@
 				/* 'delprofileid' with uid will be inserted here. */
 				NULL);
 
-	if (!msim_postprocess_outgoing(session, delbuddy_msg, buddy->name, "delprofileid", NULL)) {
+	if (!msim_postprocess_outgoing(session, delbuddy_msg, name, "delprofileid", NULL)) {
 		purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("'delbuddy' command failed"));
 		msim_msg_free(delbuddy_msg);
 		return;
@@ -2722,7 +2742,7 @@
 			"body", MSIM_TYPE_STRING, g_strdup("ContactID=<uid>"),
 			NULL);
 
-	if (!msim_postprocess_outgoing(session, persist_msg, buddy->name, "body", NULL)) {
+	if (!msim_postprocess_outgoing(session, persist_msg, name, "body", NULL)) {
 		purple_notify_error(NULL, NULL, _("Failed to remove buddy"), _("persist command failed"));
 		msim_msg_free(persist_msg);
 		return;
@@ -2734,15 +2754,12 @@
 	 * doesn't seem like it would be necessary, but the official client
 	 * does it)
 	 */
-	if (!msim_update_blocklist_for_buddy(session, buddy->name, FALSE, FALSE)) {
+	if (!msim_update_blocklist_for_buddy(session, name, FALSE, FALSE)) {
 		purple_notify_error(NULL, NULL,
 				_("Failed to remove buddy"), _("blocklist command failed"));
 		return;
 	}
-	if (buddy->proto_data) {
-		msim_user_free(buddy->proto_data);
-		buddy->proto_data = NULL;
-	}
+	msim_buddy_free(buddy);
 }
 
 /**
@@ -2832,13 +2849,6 @@
 	msim_update_blocklist_for_buddy(session, name, FALSE, FALSE);
 }
 
-static void
-msim_buddy_free(PurpleBuddy *buddy)
-{
-	msim_user_free(buddy->proto_data);
-	buddy->proto_data = NULL;
-}
-
 /**
  * Returns a string of a username in canonical form. Basically removes all the
  * spaces, lowercases the string, and looks up user IDs to usernames.
--- a/libpurple/protocols/myspace/user.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/myspace/user.c	Sat Mar 07 01:59:40 2009 +0000
@@ -52,17 +52,14 @@
 		return NULL;
 	}
 
-	if (!buddy->proto_data) {
+	if (!(user = purple_buddy_get_protocol_data(buddy))) {
 		/* No MsimUser for this buddy; make one. */
 
 		user = g_new0(MsimUser, 1);
 		user->buddy = buddy;
-		user->id = purple_blist_node_get_int((PurpleBlistNode*)buddy, "UserID");
-		buddy->proto_data = (gpointer)user;
+		purple_buddy_set_protocol_data(buddy, user);
 	}
 
-	user = (MsimUser *)(buddy->proto_data);
-
 	return user;
 }
 
@@ -111,6 +108,7 @@
 {
 	PurplePresence *presence;
 	gchar *str;
+	guint uid;
 	guint cv;
 
 	/* Useful to identify the account the tooltip refers to.
@@ -119,6 +117,8 @@
 		purple_notify_user_info_add_pair(user_info, _("User"), user->username);
 	}
 
+	uid = purple_blist_node_get_int((PurpleBlistNode *)user->buddy, "UserID");
+
 	/* a/s/l...the vitals */
 	if (user->age) {
 		char age[16];
@@ -209,9 +209,9 @@
 		gsize len,
 		const gchar *error_message)
 {
-	MsimUser *user;
-
-	user = (MsimUser *)user_data;
+	MsimUser *user = (MsimUser *)user_data;
+	const char *name = purple_buddy_get_name(user->buddy);
+	PurpleAccount *account;
 
 	purple_debug_info("msim_downloaded_buddy_icon",
 			"Downloaded %" G_GSIZE_FORMAT " bytes\n", len);
@@ -219,12 +219,12 @@
 	if (!url_text) {
 		purple_debug_info("msim_downloaded_buddy_icon",
 				"failed to download icon for %s",
-				user->buddy->name);
+				name);
 		return;
 	}
 
-	purple_buddy_icons_set_for_user(user->buddy->account,
-			user->buddy->name,
+	account = purple_buddy_get_account(user->buddy);
+	purple_buddy_icons_set_for_user(account, name,
 			g_memdup((gchar *)url_text, len), len,
 			/* Use URL itself as buddy icon "checksum" (TODO: ETag) */
 			user->image_url);		/* checksum */
@@ -247,7 +247,9 @@
 static void msim_set_artist_or_title(MsimUser *user, const char *new_artist, const char *new_title)
 {
 	PurplePresence *presence;
+	PurpleAccount *account;
 	const char *prev_artist, *prev_title;
+	const char *name;
 
 	if (user->buddy == NULL)
 		/* User not on buddy list so nothing to do */
@@ -261,8 +263,11 @@
 	if (new_title && !*new_title)
 		new_title = NULL;
 
+	account = purple_buddy_get_account(user->buddy);
+	name = purple_buddy_get_name(user->buddy);
+
 	if (!new_artist && !new_title) {
-		purple_prpl_got_user_status_deactive(user->buddy->account, user->buddy->name, "tune");
+		purple_prpl_got_user_status_deactive(account, name, "tune");
 		return;
 	}
 
@@ -282,7 +287,7 @@
 	if (!new_title)
 		new_title = prev_title;
 
-	purple_prpl_got_user_status(user->buddy->account, user->buddy->name, "tune",
+	purple_prpl_got_user_status(account, name, "tune",
 			PURPLE_TUNE_TITLE, new_title,
 			PURPLE_TUNE_ARTIST, new_artist,
 			NULL);
@@ -299,14 +304,16 @@
 static void
 msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user)
 {
+	const char *name = user->buddy ? purple_buddy_get_name(user->buddy) : NULL;
+
 	if (g_str_equal(key_str, "UserID") || g_str_equal(key_str, "ContactID")) {
 		/* Save to buddy list, if it exists, for quick cached uid lookup with msim_uid2username_from_blist(). */
 		user->id = atol(value_str);
 		g_free(value_str);
 		if (user->buddy)
 		{
-			purple_debug_info("msim", "associating uid %s with username %s\n", key_str, user->buddy->name);
-			purple_blist_node_set_int(&user->buddy->node, "UserID", user->id);
+			purple_debug_info("msim", "associating uid %s with username %s\n", key_str, name);
+			purple_blist_node_set_int(PURPLE_BLIST_NODE(user->buddy), "UserID", user->id);
 		}
 		/* Need to store in MsimUser, too? What if not on blist? */
 	} else if (g_str_equal(key_str, "Age")) {
@@ -359,9 +366,8 @@
 		/* Instead of showing 'no photo' picture, show nothing. */
 		if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif"))
 		{
-			purple_buddy_icons_set_for_user(user->buddy->account,
-				user->buddy->name,
-				NULL, 0, NULL);
+			purple_buddy_icons_set_for_user(purple_buddy_get_account(user->buddy),
+				name, NULL, 0, NULL);
 			return;
 		}
 
--- a/libpurple/protocols/myspace/zap.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/myspace/zap.c	Sat Mar 07 01:59:40 2009 +0000
@@ -173,13 +173,13 @@
 	buddy = (PurpleBuddy *)node;
 
 	/* Find the session */
-	account = buddy->account;
+	account = purple_buddy_get_account(buddy);
 	gc = purple_account_get_connection(account);
 	session = (MsimSession *)gc->proto_data;
 
 	zap = GPOINTER_TO_INT(zap_num_ptr);
 
-	purple_prpl_send_attention(session->gc, buddy->name, zap);
+	purple_prpl_send_attention(session->gc, purple_buddy_get_name(buddy), zap);
 }
 
 /** Return menu, if any, for a buddy list node. */
--- a/libpurple/protocols/novell/novell.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/novell/novell.c	Sat Mar 07 01:59:40 2009 +0000
@@ -293,7 +293,7 @@
 								nm_user_record_get_display_id(user_record));
 
 		alias = purple_buddy_get_alias(buddy);
-		if (alias == NULL || *alias == '\0' || (strcmp(alias, buddy->name) == 0)) {
+		if (alias == NULL || *alias == '\0' || (strcmp(alias, purple_buddy_get_name(buddy)) == 0)) {
 			purple_blist_alias_buddy(buddy,
 								   nm_user_record_get_full_name(user_record));
 
@@ -1175,10 +1175,12 @@
 	const char *status_id;
 	const char *text = NULL;
 	const char *dn;
+	const char *name;
 	int idle = 0;
 	gboolean loggedin = TRUE;
 
-	account = buddy->account;
+	account = purple_buddy_get_account(buddy);
+	name = purple_buddy_get_name(buddy);
 
 	switch (novellstatus) {
 		case NM_STATUS_AVAILABLE:
@@ -1205,7 +1207,7 @@
 	}
 
 	/* Get status text for the user */
-	dn = nm_lookup_dn(user, buddy->name);
+	dn = nm_lookup_dn(user, name);
 	if (dn) {
 		NMUserRecord *user_record = nm_find_user_record(user, dn);
 		if (user_record) {
@@ -1213,9 +1215,9 @@
 		}
 	}
 
-	purple_prpl_got_user_status(account, buddy->name, status_id,
+	purple_prpl_got_user_status(account, name, status_id,
 							  "message", text, NULL);
-	purple_prpl_got_user_idle(account, buddy->name,
+	purple_prpl_got_user_idle(account, name,
 							(novellstatus == NM_STATUS_AWAY_IDLE), idle);
 }
 
@@ -1230,44 +1232,46 @@
 	PurpleBlistNode *bnode;
 	PurpleGroup *group;
 	PurpleBuddy *buddy;
-	PurpleBuddyList *blist;
 	GSList *rem_list = NULL;
 	GSList *l;
 	NMFolder *folder = NULL;
 	const char *gname = NULL;
 
-	if ((blist = purple_get_blist())) {
-		for (gnode = blist->root; gnode; gnode = gnode->next) {
-			if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+	for (gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
+		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+			continue;
+		group = (PurpleGroup *) gnode;
+		gname = purple_group_get_name(group);
+		for (cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
+			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			group = (PurpleGroup *) gnode;
-			for (cnode = gnode->child; cnode; cnode = cnode->next) {
-				if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+			for (bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode)) {
+				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
-				for (bnode = cnode->child; bnode; bnode = bnode->next) {
-					if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
-						continue;
-					buddy = (PurpleBuddy *) bnode;
-					if (buddy->account == user->client_data) {
-						gname = group->name;
-						if (strcmp(group->name, NM_ROOT_FOLDER_NAME) == 0)
-							gname = "";
-						folder = nm_find_folder(user, gname);
-						if (folder == NULL ||
-							!nm_folder_find_contact_by_display_id(folder, buddy->name)) {
-							rem_list = g_slist_append(rem_list, buddy);
-						}
+				buddy = (PurpleBuddy *) bnode;
+				if (purple_buddy_get_account(buddy) == user->client_data) {
+					if (strcmp(gname, NM_ROOT_FOLDER_NAME) == 0)
+						gname = "";
+					folder = nm_find_folder(user, gname);
+					if (folder == NULL ||
+							!nm_folder_find_contact_by_display_id(folder, purple_buddy_get_name(buddy))) {
+						rem_list = g_slist_append(rem_list, buddy);
 					}
 				}
 			}
 		}
-
-		if (rem_list) {
-			for (l = rem_list; l; l = l->next) {
-				purple_blist_remove_buddy(l->data);
-			}
-			g_slist_free(rem_list);
+	}
+
+	if (rem_list) {
+		for (l = rem_list; l; l = l->next) {
+			purple_blist_remove_buddy(l->data);
 		}
+		g_slist_free(rem_list);
 	}
 }
 
@@ -1613,14 +1617,14 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 
 	user = gc->proto_data;
 	if (user == NULL)
 		return;
 
 	/* We should already have a userrecord for the buddy */
-	user_record = nm_find_user_record(user, buddy->name);
+	user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
 	if (user_record == NULL)
 		return;
 
@@ -2538,7 +2542,7 @@
 	NMContact *contact;
 	NMUser *user;
 	NMERR_T rc = NM_OK;
-	const char *alias, *gname;
+	const char *alias, *gname, *bname;
 
 	if (gc == NULL || buddy == NULL || group == NULL)
 		return;
@@ -2554,22 +2558,22 @@
 		return;
 
 	contact = nm_create_contact();
-	nm_contact_set_dn(contact, buddy->name);
+	nm_contact_set_dn(contact, purple_buddy_get_name(buddy));
 
 	/* Remove the PurpleBuddy (we will add it back after adding it
 	 * to the server side list). Save the alias if there is one.
 	 */
 	alias = purple_buddy_get_alias(buddy);
-	if (alias && strcmp(alias, buddy->name))
+	bname = purple_buddy_get_name(buddy);
+	if (alias && strcmp(alias, bname))
 		nm_contact_set_display_name(contact, alias);
 
 	purple_blist_remove_buddy(buddy);
 	buddy = NULL;
 
-	if (strcmp(group->name, NM_ROOT_FOLDER_NAME) == 0) {
+	gname = purple_group_get_name(group);
+	if (strcmp(gname, NM_ROOT_FOLDER_NAME) == 0) {
 		gname = "";
-	} else {
-		gname = group->name;
 	}
 
 	folder = nm_find_folder(user, gname);
@@ -2603,11 +2607,10 @@
 		return;
 
 	user = (NMUser *) gc->proto_data;
-	if (user && (dn = nm_lookup_dn(user, buddy->name))) {
-		if (strcmp(group->name, NM_ROOT_FOLDER_NAME) == 0) {
+	if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) {
+		gname = purple_group_get_name(group);
+		if (strcmp(gname, NM_ROOT_FOLDER_NAME) == 0) {
 			gname = "";
-		} else {
-			gname = group->name;
 		}
 		folder = nm_find_folder(user, gname);
 		if (folder) {
@@ -2637,7 +2640,7 @@
 
 	user = (NMUser *) gc->proto_data;
 	if (user) {
-		NMFolder *folder = nm_find_folder(user, group->name);
+		NMFolder *folder = nm_find_folder(user, purple_group_get_name(group));
 
 		if (folder) {
 			rc = nm_send_remove_folder(user, folder,
@@ -2684,9 +2687,11 @@
 				}
 
 				if (group) {
+					const char *balias;
 					buddy = purple_find_buddy_in_group(user->client_data,
 													 name, group);
-					if (buddy && strcmp(buddy->alias, alias))
+					balias = buddy ? purple_buddy_get_local_buddy_alias(buddy) : NULL;
+					if (balias && strcmp(balias, alias))
 						purple_blist_alias_buddy(buddy, alias);
 				}
 
@@ -2777,8 +2782,9 @@
 
 	user = gc->proto_data;
 	if (user) {
+		const char *gname = purple_group_get_name(group);
 		/* Does new folder exist already? */
-		if (nm_find_folder(user, group->name)) {
+		if (nm_find_folder(user, gname)) {
 			/* purple_blist_rename_group() adds the buddies
 			 * to the new group and removes the old group...
 			 * so there is nothing more to do here.
@@ -2793,7 +2799,7 @@
 
 		folder = nm_find_folder(user, old_name);
 		if (folder) {
-			rc = nm_send_rename_folder(user, folder, group->name,
+			rc = nm_send_rename_folder(user, folder, gname,
 									   _rename_folder_resp_cb, NULL);
 			_check_for_disconnect(user, rc);
 		}
@@ -2819,12 +2825,12 @@
 	if (buddy == NULL)
 		return;
 
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 	if (gc == NULL || (user = gc->proto_data) == NULL)
 		return;
 
 	if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
-		user_record = nm_find_user_record(user, buddy->name);
+		user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
 		if (user_record) {
 			status = nm_user_record_get_status(user_record);
 			text = nm_user_record_get_status_text(user_record);
@@ -2923,14 +2929,16 @@
 {
 	const char *text = NULL;
 	const char *dn = NULL;
-
-	if (buddy && buddy->account) {
-		PurpleConnection *gc = purple_account_get_connection(buddy->account);
+	PurpleAccount *account;
+
+	account = buddy ? purple_buddy_get_account(buddy) : NULL;
+	if (buddy && account) {
+		PurpleConnection *gc = purple_account_get_connection(account);
 
 		if (gc && gc->proto_data) {
 			NMUser *user = gc->proto_data;
 
-			dn = nm_lookup_dn(user, buddy->name);
+			dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
 			if (dn) {
 				NMUserRecord *user_record = nm_find_user_record(user, dn);
 
--- a/libpurple/protocols/null/README	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/null/README	Sat Mar 07 01:59:40 2009 +0000
@@ -28,11 +28,10 @@
 -----------------------
 
 To build, just run ./configure as usual in the root directory of the pidgin
-source distribution. Then cd libpurple/protocols/null and type make. To
-install, copy libnull.la and .libs/libnull.so into your ~/.purple/plugins
-directory. Then run Pidgin.
+source distribution. Then cd libpurple/protocols/null and then make.  To
+install, run make install.  Then run Pidgin.
 
-To build nullprpl on Windows (with Cygwin/MinGW), use Makefile.mingw.
+To build nullprpl on Windows (with Cygwin/MinGW), use: make -f Makefile.mingw
 
 -----
 USAGE
--- a/libpurple/protocols/null/nullprpl.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Sat Mar 07 01:59:40 2009 +0000
@@ -220,25 +220,7 @@
  */
 static const char *nullprpl_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
 {
-  /* shamelessly steal (er, borrow) the meanwhile protocol icon. it's cute! */
-  return "meanwhile";
-}
-
-static const char *nullprpl_list_emblem(PurpleBuddy *buddy)
-{
-  const char* emblem;
-
-  if (get_nullprpl_gc(buddy->name)) {
-    PurplePresence *presence = purple_buddy_get_presence(buddy);
-    PurpleStatus *status = purple_presence_get_active_status(presence);
-    emblem = purple_status_get_name(status);
-  } else {
-    emblem = "offline";
-  }
-
-  purple_debug_info("nullprpl", "using emblem %s for %s's buddy %s\n",
-                    emblem, buddy->account->username, buddy->name);
-  return emblem;
+  return "null";
 }
 
 static char *nullprpl_status_text(PurpleBuddy *buddy) {
@@ -304,22 +286,22 @@
                     acct->username,
                     NULL_STATUS_ONLINE, NULL_STATUS_AWAY, NULL_STATUS_OFFLINE);
 
-  type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, NULL_STATUS_ONLINE,
-                                NULL_STATUS_ONLINE, TRUE);
-  purple_status_type_add_attr(type, "message", _("Online"),
-                              purple_value_new(PURPLE_TYPE_STRING));
+  type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+      NULL_STATUS_ONLINE, NULL, TRUE, TRUE, FALSE,
+      "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+      NULL);
   types = g_list_prepend(types, type);
 
-  type = purple_status_type_new(PURPLE_STATUS_AWAY, NULL_STATUS_AWAY,
-                                NULL_STATUS_AWAY, TRUE);
-  purple_status_type_add_attr(type, "message", _("Away"),
-                              purple_value_new(PURPLE_TYPE_STRING));
+  type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
+      NULL_STATUS_AWAY, NULL, TRUE, TRUE, FALSE,
+      "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+      NULL);
   types = g_list_prepend(types, type);
-  
-  type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL_STATUS_OFFLINE,
-                                NULL_STATUS_OFFLINE, TRUE);
-  purple_status_type_add_attr(type, "message", _("Offline"),
-                              purple_value_new(PURPLE_TYPE_STRING));
+
+  type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
+      NULL_STATUS_OFFLINE, NULL, TRUE, TRUE, FALSE,
+      "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+      NULL);
   types = g_list_prepend(types, type);
 
   return g_list_reverse(types);
@@ -1073,7 +1055,7 @@
       PURPLE_ICON_SCALE_DISPLAY,       /* scale_rules */
   },
   nullprpl_list_icon,                  /* list_icon */
-  nullprpl_list_emblem,                /* list_emblem */
+  NULL,                                /* list_emblem */
   nullprpl_status_text,                /* status_text */
   nullprpl_tooltip_text,               /* tooltip_text */
   nullprpl_status_types,               /* status_types */
@@ -1130,24 +1112,24 @@
   NULL,                                /* whiteboard_prpl_ops */
   NULL,                                /* send_raw */
   NULL,                                /* roomlist_room_serialize */
-  NULL,                                /* padding... */
-  NULL,
-  NULL,
-	sizeof(PurplePluginProtocolInfo),    /* struct_size */
-  NULL
+  NULL,	                               /* unregister_user */
+  NULL,                                /* send_attention */
+  NULL,                                /* attention_types */
+  sizeof(PurplePluginProtocolInfo),    /* struct_size */
+  NULL,                                /* get_account_text_table */
 };
 
 static void nullprpl_init(PurplePlugin *plugin)
 {
   /* see accountopt.h for information about user splits and protocol options */
   PurpleAccountUserSplit *split = purple_account_user_split_new(
-    _("Example user split (unused)"),  /* text shown to user */
-    "default",                         /* default value */
-    '@');                              /* field separator */
+    _("Example user split"),  /* text shown to user */
+    "default",                /* default value */
+    '@');                     /* field separator */
   PurpleAccountOption *option = purple_account_option_string_new(
-    _("Example option (unused)"),      /* text shown to user */
-    "example",                         /* pref name */
-    "default");                        /* default value */
+    _("Example option"),      /* text shown to user */
+    "example",                /* pref name */
+    "default");               /* default value */
 
   purple_debug_info("nullprpl", "starting up\n");
 
@@ -1156,13 +1138,13 @@
 
   /* register whisper chat command, /msg */
   purple_cmd_register("msg",
-                    "ws",                /* args: recipient and message */
+                    "ws",                  /* args: recipient and message */
                     PURPLE_CMD_P_DEFAULT,  /* priority */
                     PURPLE_CMD_FLAG_CHAT,
                     "prpl-null",
                     send_whisper,
                     "msg &lt;username&gt; &lt;message&gt;: send a private message, aka a whisper",
-                    NULL);               /* userdata */
+                    NULL);                 /* userdata */
 
   /* get ready to store offline messages */
   goffline_messages = g_hash_table_new_full(g_str_hash,  /* hash fn */
@@ -1189,12 +1171,12 @@
   NULL,                                                    /* dependencies */
   PURPLE_PRIORITY_DEFAULT,                                 /* priority */
   NULLPRPL_ID,                                             /* id */
-  "Nullprpl",                                              /* name */
-  "0.3",                                                   /* version */
-  "Null Protocol Plugin",                                  /* summary */
-  "Null Protocol Plugin",                                  /* description */
-  "Ryan Barrett <nullprpl@ryanb.org>",                     /* author */
-  "http://snarfed.org/space/pidgin+null+protocol+plugin",  /* homepage */
+  "Null - Testing Plugin",                                 /* name */
+  DISPLAY_VERSION,                                         /* version */
+  N_("Null Protocol Plugin"),                              /* summary */
+  N_("Null Protocol Plugin"),                              /* description */
+  NULL,                                                    /* author */
+  PURPLE_WEBSITE,                                          /* homepage */
   NULL,                                                    /* load */
   NULL,                                                    /* unload */
   nullprpl_destroy,                                        /* destroy */
--- a/libpurple/protocols/oscar/bstream.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/bstream.c	Sat Mar 07 01:59:40 2009 +0000
@@ -311,3 +311,37 @@
 
 	return byte_stream_putle32(bs, atoi(purple_account_get_username(account)));
 }
+
+void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data)
+{
+	byte_stream_put16(bs, type);
+
+	if (data != NULL && data->len > 0) {
+		/* Flags. 0x04 means "this asset has data attached to it" */
+		byte_stream_put8(bs, 0x04); /* Flags */
+		byte_stream_put8(bs, data->len); /* Length */
+		byte_stream_rewind(data);
+		byte_stream_putbs(bs, data, data->len); /* Data */
+	} else {
+		byte_stream_put8(bs, 0x00); /* No flags */
+		byte_stream_put8(bs, 0x00); /* Length */
+		/* No data */
+	}
+}
+
+void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr)
+{
+	ByteStream data;
+	size_t len = datastr != NULL ? strlen(datastr) : 0;
+
+	if (len > 0) {
+		byte_stream_new(&data, 2 + len + 2);
+		byte_stream_put16(&data, len); /* Length */
+		byte_stream_putstr(&data, datastr); /* String */
+		byte_stream_put16(&data, 0x0000); /* Unknown */
+		byte_stream_put_bart_asset(bs, type, &data);
+		byte_stream_destroy(&data);
+	} else {
+		byte_stream_put_bart_asset(bs, type, NULL);
+	}
+}
--- a/libpurple/protocols/oscar/family_admin.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_admin.c	Sat Mar 07 01:59:40 2009 +0000
@@ -21,9 +21,8 @@
 /*
  * Family 0x0007 - Account Administration.
  *
- * Used for stuff like changing the formating of your screen name, changing your
+ * Used for stuff like changing the formating of your username, changing your
  * email address, requesting an account confirmation email, getting account info,
- *
  */
 
 #include "oscar.h"
@@ -32,7 +31,7 @@
  * Subtype 0x0002 - Request a bit of account info.
  *
  * Info should be one of the following:
- * 0x0001 - Screen name formatting
+ * 0x0001 - Username formatting
  * 0x0011 - Email address
  * 0x0013 - Unknown
  */
@@ -111,7 +110,7 @@
 }
 
 /**
- * Subtype 0x0004 - Set screenname formatting.
+ * Subtype 0x0004 - Set the formatting of username (change spaces and capitalization).
  */
 void
 aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick)
--- a/libpurple/protocols/oscar/family_alert.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_alert.c	Sat Mar 07 01:59:40 2009 +0000
@@ -22,10 +22,10 @@
  * Family 0x0018 - Email notification
  *
  * Used for being alerted when the email address(es) associated with
- * your screen name get new electronic-m.  For normal AIM accounts, you
- * get the email address screenname@netscape.net.  AOL accounts have
- * screenname@aol.com, and can also activate a netscape.net account.
- *
+ * your username get new electronic-m.  For normal AIM accounts, you
+ * get the email address username@netscape.net.  AOL accounts have
+ * username@aol.com, and can also activate a netscape.net account.
+ * Note: This information might be out of date.
  */
 
 #include "oscar.h"
@@ -88,7 +88,7 @@
  * but this is coded so it will handle that, and handle it well.
  * This tells you if you have unread mail or not, the URL you
  * should use to access that mail, and the domain name for the
- * email account (screenname@domainname.com).  If this is the
+ * email account (username@domainname.com).  If this is the
  * first 0x0007 SNAC you've received since you signed on, or if
  * this is just a periodic status update, this will also contain
  * the number of unread emails that you have.
--- a/libpurple/protocols/oscar/family_auth.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Sat Mar 07 01:59:40 2009 +0000
@@ -229,7 +229,7 @@
 
 	/* Truncate ICQ and AOL passwords, if necessary */
 	password_len = strlen(password);
-	if (aim_snvalid_icq(sn) && (password_len > MAXICQPASSLEN))
+	if (oscar_util_valid_name_icq(sn) && (password_len > MAXICQPASSLEN))
 		password_len = MAXICQPASSLEN;
 	else if (truncate_pass && password_len > 8)
 		password_len = 8;
@@ -293,11 +293,11 @@
 	tlvlist = aim_tlvlist_read(bs);
 
 	/*
-	 * No matter what, we should have a screen name.
+	 * No matter what, we should have a username.
 	 */
 	if (aim_tlv_gettlv(tlvlist, 0x0001, 1)) {
-		info->sn = aim_tlv_getstr(tlvlist, 0x0001, 1);
-		purple_connection_set_display_name(od->gc, info->sn);
+		info->bn = aim_tlv_getstr(tlvlist, 0x0001, 1);
+		purple_connection_set_display_name(od->gc, info->bn);
 	}
 
 	/*
@@ -394,7 +394,7 @@
 
 #if 0
 	/*
-	 * Unknown.  Seen on an @mac.com screen name with value of 0x003f
+	 * Unknown.  Seen on an @mac.com username with value of 0x003f
 	 */
 	if (aim_tlv_gettlv(tlvlist, 0x0055, 1)) {
 		/* Unhandled */
@@ -421,7 +421,7 @@
  *   - connect
  *   - server sends flap version
  *   - client sends flap version
- *   - client sends screen name (17/6)
+ *   - client sends username (17/6)
  *   - server sends hash key (17/7)
  *   - client sends auth request (17/2 -- aim_send_login)
  *   - server yells
@@ -460,7 +460,7 @@
  * Subtype 0x0006
  *
  * In AIM 3.5 protocol, the first stage of login is to request login from the
- * Authorizer, passing it the screen name for verification.  If the name is
+ * Authorizer, passing it the username for verification.  If the name is
  * invalid, a 0017/0003 is spit back, with the standard error contents.  If
  * valid, a 0017/0007 comes back, which is the signal to send it the main
  * login command (0017/0002).
@@ -527,7 +527,7 @@
 	/*
 	 * If the truncate_pass TLV exists then we should truncate the
 	 * user's password to 8 characters.  This flag is sent to us
-	 * when logging in with an AOL user's screen name.
+	 * when logging in with an AOL user's username.
 	 */
 	truncate_pass = aim_tlv_gettlv(tlvlist, 0x0026, 1) != NULL;
 
@@ -597,7 +597,7 @@
 {
 	if (od->authinfo != NULL)
 	{
-		g_free(od->authinfo->sn);
+		g_free(od->authinfo->bn);
 		g_free(od->authinfo->bosip);
 		g_free(od->authinfo->errorurl);
 		g_free(od->authinfo->email);
--- a/libpurple/protocols/oscar/family_bart.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_bart.c	Sat Mar 07 01:59:40 2009 +0000
@@ -90,26 +90,26 @@
  * Subtype 0x0004 - Request someone's icon.
  *
  * @param od The oscar session.
- * @param sn The screen name of the person who's icon you are requesting.
+ * @param bn The name of the buddy whose icon you are requesting.
  * @param iconcsum The MD5 checksum of the icon you are requesting.
  * @param iconcsumlen Length of the MD5 checksum given above.  Should be 10 bytes.
  * @return Return 0 if no errors, otherwise return the error number.
  */
 int
-aim_bart_request(OscarData *od, const char *sn, guint8 iconcsumtype, const guint8 *iconcsum, guint16 iconcsumlen)
+aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconcsum, guint16 iconcsumlen)
 {
 	FlapConnection *conn;
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !bn || !strlen(bn) || !iconcsum || !iconcsumlen)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 1+strlen(sn) + 4 + 1+iconcsumlen);
+	byte_stream_new(&bs, 1+strlen(bn) + 4 + 1+iconcsumlen);
 
-	/* Screen name */
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	/* Buddy name */
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	/* Some numbers.  You like numbers, right? */
 	byte_stream_put8(&bs, 0x01);
@@ -138,11 +138,11 @@
 {
 	int ret = 0;
 	aim_rxcallback_t userfunc;
-	char *sn;
+	char *bn;
 	guint16 flags, iconlen;
 	guint8 iconcsumtype, iconcsumlen, *iconcsum, *icon;
 
-	sn = byte_stream_getstr(bs, byte_stream_get8(bs));
+	bn = byte_stream_getstr(bs, byte_stream_get8(bs));
 	flags = byte_stream_get16(bs);
 	iconcsumtype = byte_stream_get8(bs);
 	iconcsumlen = byte_stream_get8(bs);
@@ -151,9 +151,9 @@
 	icon = byte_stream_getraw(bs, iconlen);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, sn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen);
+		ret = userfunc(od, conn, frame, bn, iconcsumtype, iconcsum, iconcsumlen, icon, iconlen);
 
-	g_free(sn);
+	g_free(bn);
 	g_free(iconcsum);
 	g_free(icon);
 
--- a/libpurple/protocols/oscar/family_bos.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_bos.c	Sat Mar 07 01:59:40 2009 +0000
@@ -94,8 +94,7 @@
  *  AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
  *  AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
  *
- * list should be a list of
- * screen names in the form "Screen Name One&ScreenNameTwo&" etc.
+ * list should be a list of "Buddy Name One&BuddyNameTwo&" etc.
  *
  * Equivelents to options in WinAIM:
  *   - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
--- a/libpurple/protocols/oscar/family_buddy.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_buddy.c	Sat Mar 07 01:59:40 2009 +0000
@@ -121,7 +121,7 @@
  *
  * This just builds the "set buddy list" command then queues it.
  *
- * buddy_list = "Screen Name One&ScreenNameTwo&";
+ * buddy_list = "Buddy Name One&BuddyNameTwo&";
  *
  * XXX Clean this up.
  *
@@ -222,7 +222,7 @@
 		ret = userfunc(od, conn, frame, &userinfo);
 
 	if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && userinfo.flags & AIM_FLAG_AWAY)
-		aim_locate_autofetch_away_message(od, userinfo.sn);
+		aim_locate_autofetch_away_message(od, userinfo.bn);
 
 	aim_info_free(&userinfo);
 
--- a/libpurple/protocols/oscar/family_feedbag.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_feedbag.c	Sat Mar 07 01:59:40 2009 +0000
@@ -243,7 +243,7 @@
 	if (!cur1->name && cur2->name)
 		return 6;
 
-	if (cur1->name && cur2->name && aim_sncmp(cur1->name, cur2->name))
+	if (cur1->name && cur2->name && oscar_util_name_compare(cur1->name, cur2->name))
 		return 7;
 
 	if (cur1->gid != cur2->gid)
@@ -285,8 +285,8 @@
 }
 
 /**
- * Locally find an item given a group name, screen name, and type.  If group name
- * and screen name are null, then just return the first item of the given type.
+ * Locally find an item given a group name, buddy name, and type.  If group name
+ * and buddy name are null, then just return the first item of the given type.
  *
  * @param list A pointer to the current list of items.
  * @param gn The group name of the desired item.
@@ -294,31 +294,31 @@
  * @param type The type of the desired item.
  * @return Return a pointer to the item if found, else return NULL.
  */
-struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, guint16 type)
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type)
 {
 	struct aim_ssi_item *cur;
 	if (!list)
 		return NULL;
 
-	if (gn && sn) { /* For finding buddies in groups */
+	if (gn && bn) { /* For finding buddies in groups */
 		for (cur=list; cur; cur=cur->next)
-			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
+			if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
 				struct aim_ssi_item *curg;
 				for (curg=list; curg; curg=curg->next)
-					if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
+					if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(oscar_util_name_compare(curg->name, gn)))
 						return cur;
 			}
 
 	} else if (gn) { /* For finding groups */
 		for (cur=list; cur; cur=cur->next) {
-			if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(aim_sncmp(cur->name, gn))) {
+			if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(oscar_util_name_compare(cur->name, gn))) {
 				return cur;
 			}
 		}
 
-	} else if (sn) { /* For finding permits, denies, and ignores */
+	} else if (bn) { /* For finding permits, denies, and ignores */
 		for (cur=list; cur; cur=cur->next) {
-			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
+			if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
 				return cur;
 			}
 		}
@@ -336,14 +336,14 @@
  * Check if the given buddy exists in any group in the buddy list.
  *
  * @param list A pointer to the current list of items.
- * @param sn The group name of the desired item.
+ * @param bn The group name of the desired item.
  * @return Return a pointer to the name of the item if found, else return NULL;
  */
-struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn)
+struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn)
 {
-	if (!sn)
+	if (!bn)
 		return NULL;
-	return aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY);
+	return aim_ssi_itemlist_finditem(list, NULL, bn, AIM_SSI_TYPE_BUDDY);
 }
 
 /**
@@ -353,12 +353,12 @@
  * @param bn The buddy name of the desired item.
  * @return Return a pointer to the name of the item if found, else return NULL;
  */
-char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn)
+char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn)
 {
 	struct aim_ssi_item *cur, *curg;
-	if (!list || !sn)
+	if (!list || !bn)
 		return NULL;
-	if (!(cur = aim_ssi_itemlist_exists(list, sn)))
+	if (!(cur = aim_ssi_itemlist_exists(list, bn)))
 		return NULL;
 	if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
 		return NULL;
@@ -406,14 +406,14 @@
  *
  * @param list A pointer to the current list of items.
  * @param gn The group of the buddy.
- * @param sn The name of the buddy.
+ * @param bn The name of the buddy.
  * @return A pointer to a NULL terminated string that is the buddy's
  *         alias, or NULL if the buddy has no alias.  You should free
  *         this returned value!
  */
-char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn)
+char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn)
 {
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
 	if (cur) {
 		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1);
 		if (tlv && tlv->length)
@@ -427,14 +427,14 @@
  *
  * @param list A pointer to the current list of items.
  * @param gn The group of the buddy.
- * @param sn The name of the buddy.
+ * @param bn The name of the buddy.
  * @return A pointer to a NULL terminated string that is the buddy's
  *         comment, or NULL if the buddy has no comment.  You should free
  *         this returned value!
  */
-char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn)
+char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn)
 {
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
 	if (cur) {
 		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1);
 		if (tlv && tlv->length) {
@@ -449,12 +449,12 @@
  *
  * @param list A pointer to the current list of items.
  * @param gn The group of the buddy.
- * @param sn The name of the buddy.
+ * @param bn The name of the buddy.
  * @return 1 if you are waiting for authorization; 0 if you are not
  */
-gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn)
+gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn)
 {
-	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
 	if (cur) {
 		if (aim_tlv_gettlv(cur->data, 0x0066, 1))
 			return TRUE;
@@ -678,7 +678,7 @@
 			cur2 = cur->next;
 			while (cur2) {
 				next2 = cur2->next;
-				if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!aim_sncmp(cur->name, cur2->name))) {
+				if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!oscar_util_name_compare(cur->name, cur2->name))) {
 					aim_ssi_itemlist_del(&od->ssi.local, cur2);
 				}
 				cur2 = next2;
@@ -916,16 +916,16 @@
  * @param od The oscar odion.
  * @param oldgn The group that the buddy is currently in.
  * @param newgn The group that the buddy should be moved in to.
- * @param sn The name of the buddy to be moved.
+ * @param bn The name of the buddy to be moved.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *sn)
+int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn)
 {
 	struct aim_ssi_item *buddy;
 	GSList *data;
 
 	/* Find the buddy */
-	buddy = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, sn, AIM_SSI_TYPE_BUDDY);
+	buddy = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY);
 	if (buddy == NULL)
 		return -EINVAL;
 
@@ -933,10 +933,10 @@
 	data = aim_tlvlist_copy(buddy->data);
 
 	/* Delete the old item */
-	aim_ssi_delbuddy(od, sn, oldgn);
+	aim_ssi_delbuddy(od, bn, oldgn);
 
 	/* Add the new item using the EXACT SAME TLV list */
-	aim_ssi_addbuddy(od, sn, newgn, data, NULL, NULL, NULL, FALSE);
+	aim_ssi_addbuddy(od, bn, newgn, data, NULL, NULL, NULL, FALSE);
 
 	return 0;
 }
@@ -946,19 +946,19 @@
  *
  * @param od The oscar odion.
  * @param gn The group that the buddy is currently in.
- * @param sn The screen name of the buddy.
+ * @param bn The name of the buddy.
  * @param alias The new alias for the buddy, or NULL if you want to remove
  *        a buddy's comment.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *sn, const char *alias)
+int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias)
 {
 	struct aim_ssi_item *tmp;
 
-	if (!od || !gn || !sn)
+	if (!od || !gn || !bn)
 		return -EINVAL;
 
-	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
+	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
 		return -EINVAL;
 
 	/* Either add or remove the 0x0131 TLV from the TLV chain */
@@ -976,19 +976,19 @@
  *
  * @param od The oscar odion.
  * @param gn The group that the buddy is currently in.
- * @param sn The screen name of the buddy.
+ * @param bn The name of the buddy.
  * @param alias The new comment for the buddy, or NULL if you want to remove
  *        a buddy's comment.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-int aim_ssi_editcomment(OscarData *od, const char *gn, const char *sn, const char *comment)
+int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *comment)
 {
 	struct aim_ssi_item *tmp;
 
-	if (!od || !gn || !sn)
+	if (!od || !gn || !bn)
 		return -EINVAL;
 
-	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
+	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
 		return -EINVAL;
 
 	/* Either add or remove the 0x0131 TLV from the TLV chain */
@@ -1681,20 +1681,20 @@
  * Authorizes a contact so they can add you to their contact list.
  *
  */
-int aim_ssi_sendauth(OscarData *od, char *sn, char *msg)
+int aim_ssi_sendauth(OscarData *od, char *bn, char *msg)
 {
 	FlapConnection *conn;
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 1+strlen(sn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
+	byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
 
-	/* Screen name */
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	/* Username */
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	/* Message (null terminated) */
 	byte_stream_put16(&bs, msg ? strlen(msg) : 0);
@@ -1722,13 +1722,13 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	guint16 tmp;
-	char *sn, *msg;
+	char *bn, *msg;
 
-	/* Read screen name */
+	/* Read buddy name */
 	if ((tmp = byte_stream_get8(bs)))
-		sn = byte_stream_getstr(bs, tmp);
+		bn = byte_stream_getstr(bs, tmp);
 	else
-		sn = NULL;
+		bn = NULL;
 
 	/* Read message (null terminated) */
 	if ((tmp = byte_stream_get16(bs)))
@@ -1740,9 +1740,9 @@
 	tmp = byte_stream_get16(bs);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, sn, msg);
+		ret = userfunc(od, conn, frame, bn, msg);
 
-	g_free(sn);
+	g_free(bn);
 	g_free(msg);
 
 	return ret;
@@ -1755,20 +1755,20 @@
  * granted, denied, or dropped.
  *
  */
-int aim_ssi_sendauthrequest(OscarData *od, char *sn, const char *msg)
+int aim_ssi_sendauthrequest(OscarData *od, char *bn, const char *msg)
 {
 	FlapConnection *conn;
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 1+strlen(sn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
+	byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
 
-	/* Screen name */
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	/* Username */
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	/* Message (null terminated) */
 	byte_stream_put16(&bs, msg ? strlen(msg) : 0);
@@ -1796,13 +1796,13 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	guint16 tmp;
-	char *sn, *msg;
+	char *bn, *msg;
 
-	/* Read screen name */
+	/* Read buddy name */
 	if ((tmp = byte_stream_get8(bs)))
-		sn = byte_stream_getstr(bs, tmp);
+		bn = byte_stream_getstr(bs, tmp);
 	else
-		sn = NULL;
+		bn = NULL;
 
 	/* Read message (null terminated) */
 	if ((tmp = byte_stream_get16(bs)))
@@ -1814,9 +1814,9 @@
 	tmp = byte_stream_get16(bs);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, sn, msg);
+		ret = userfunc(od, conn, frame, bn, msg);
 
-	g_free(sn);
+	g_free(bn);
 	g_free(msg);
 
 	return ret;
@@ -1832,20 +1832,20 @@
  * if reply=0x01 then grant
  *
  */
-int aim_ssi_sendauthreply(OscarData *od, char *sn, guint8 reply, const char *msg)
+int aim_ssi_sendauthreply(OscarData *od, char *bn, guint8 reply, const char *msg)
 {
 	FlapConnection *conn;
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 1+strlen(sn) + 1 + 2+(msg ? (strlen(msg)+1) : 0) + 2);
+	byte_stream_new(&bs, 1+strlen(bn) + 1 + 2+(msg ? (strlen(msg)+1) : 0) + 2);
 
-	/* Screen name */
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	/* Username */
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	/* Grant or deny */
 	byte_stream_put8(&bs, reply);
@@ -1880,13 +1880,13 @@
 	aim_rxcallback_t userfunc;
 	guint16 tmp;
 	guint8 reply;
-	char *sn, *msg;
+	char *bn, *msg;
 
-	/* Read screen name */
+	/* Read buddy name */
 	if ((tmp = byte_stream_get8(bs)))
-		sn = byte_stream_getstr(bs, tmp);
+		bn = byte_stream_getstr(bs, tmp);
 	else
-		sn = NULL;
+		bn = NULL;
 
 	/* Read reply */
 	reply = byte_stream_get8(bs);
@@ -1901,9 +1901,9 @@
 	tmp = byte_stream_get16(bs);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, sn, reply, msg);
+		ret = userfunc(od, conn, frame, bn, reply, msg);
 
-	g_free(sn);
+	g_free(bn);
 	g_free(msg);
 
 	return ret;
@@ -1917,18 +1917,18 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	guint16 tmp;
-	char *sn;
+	char *bn;
 
-	/* Read screen name */
+	/* Read buddy name */
 	if ((tmp = byte_stream_get8(bs)))
-		sn = byte_stream_getstr(bs, tmp);
+		bn = byte_stream_getstr(bs, tmp);
 	else
-		sn = NULL;
+		bn = NULL;
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, sn);
+		ret = userfunc(od, conn, frame, bn);
 
-	g_free(sn);
+	g_free(bn);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_icbm.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Sat Mar 07 01:59:40 2009 +0000
@@ -61,16 +61,16 @@
  * @param bs The bstream to write the ICBM header to.
  * @param c c is for cookie, and cookie is for me.
  * @param channel The ICBM channel (1 through 4).
- * @param sn Null-terminated scrizeen nizame.
+ * @param bn Null-terminated scrizeen nizame.
  * @return The number of bytes written.  It's really not useful.
  */
-static int aim_im_puticbm(ByteStream *bs, const guchar *c, guint16 channel, const char *sn)
+static int aim_im_puticbm(ByteStream *bs, const guchar *c, guint16 channel, const char *bn)
 {
 	byte_stream_putraw(bs, c, 8);
 	byte_stream_put16(bs, channel);
-	byte_stream_put8(bs, strlen(sn));
-	byte_stream_putstr(bs, sn);
-	return 8+2+1+strlen(sn);
+	byte_stream_put8(bs, strlen(bn));
+	byte_stream_putstr(bs, bn);
+	return 8+2+1+strlen(bn);
 }
 
 /**
@@ -324,7 +324,7 @@
 	aim_icbm_makecookie(cookie);
 
 	/* ICBM header */
-	aim_im_puticbm(&data, cookie, 0x0001, args->destsn);
+	aim_im_puticbm(&data, cookie, 0x0001, args->destbn);
 
 	/* Message TLV (type 0x0002) */
 	byte_stream_put16(&data, 0x0002);
@@ -410,7 +410,7 @@
 	}
 
 	/* XXX - should be optional */
-	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destbn, strlen(args->destbn)+1);
 
 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &data);
 	byte_stream_destroy(&data);
@@ -431,11 +431,11 @@
  * that requires an explicit message length.  Use aim_im_sendch1_ext().
  *
  */
-int aim_im_sendch1(OscarData *od, const char *sn, guint16 flags, const char *msg)
+int aim_im_sendch1(OscarData *od, const char *bn, guint16 flags, const char *msg)
 {
 	struct aim_sendimext_args args;
 
-	args.destsn = sn;
+	args.destbn = bn;
 	args.flags = flags;
 	args.msg = msg;
 	args.msglen = strlen(msg);
@@ -451,7 +451,7 @@
 /*
  * Subtype 0x0006 - Send a chat invitation.
  */
-int aim_im_sendch2_chatinvite(OscarData *od, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
+int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -465,18 +465,18 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
-	if (!sn || !msg || !roomname)
+	if (!bn || !msg || !roomname)
 		return -EINVAL;
 
 	aim_icbm_makecookie(cookie);
 
-	byte_stream_new(&bs, 1142+strlen(sn)+strlen(roomname)+strlen(msg));
-
-	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, sn, strlen(sn)+1);
+	byte_stream_new(&bs, 1142+strlen(bn)+strlen(roomname)+strlen(msg));
+
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, bn, strlen(bn)+1);
 
 	/* XXX should be uncached by an unwritten 'invite accept' handler */
 	priv = g_malloc(sizeof(struct aim_invite_priv));
-	priv->sn = g_strdup(sn);
+	priv->bn = g_strdup(bn);
 	priv->roomname = g_strdup(roomname);
 	priv->exchange = exchange;
 	priv->instance = instance;
@@ -487,7 +487,7 @@
 		g_free(priv);
 
 	/* ICBM Header */
-	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 
 	/*
 	 * TLV t(0005)
@@ -532,7 +532,7 @@
  * This is also performance sensitive. (If you can believe it...)
  *
  */
-int aim_im_sendch2_icon(OscarData *od, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
+int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -542,17 +542,17 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
-	if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
+	if (!bn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
 		return -EINVAL;
 
 	aim_icbm_makecookie(cookie);
 
-	byte_stream_new(&bs, 8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2);
+	byte_stream_new(&bs, 8+2+1+strlen(bn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2);
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 
 	/*
 	 * TLV t(0005)
@@ -623,7 +623,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
-	if (!args || !args->destsn || !args->rtfmsg)
+	if (!args || !args->destbn || !args->rtfmsg)
 		return -EINVAL;
 
 	servdatalen = 2+2+16+2+4+1+2  +  2+2+4+4+4  +  2+4+2+strlen(args->rtfmsg)+1  +  4+4+4+strlen(rtfcap)+1;
@@ -635,7 +635,7 @@
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0002, args->destsn);
+	aim_im_puticbm(&bs, cookie, 0x0002, args->destbn);
 
 	/* TLV t(0005) - Encompasses everything below. */
 	byte_stream_put16(&bs, 0x0005);
@@ -708,12 +708,12 @@
 	if (conn == NULL)
 		return;
 
-	byte_stream_new(&bs, 118+strlen(peer_conn->sn));
+	byte_stream_new(&bs, 118+strlen(peer_conn->bn));
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->sn);
+	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
 
 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
@@ -757,12 +757,12 @@
 	if (conn == NULL)
 		return;
 
-	byte_stream_new(&bs, 11+strlen(peer_conn->sn) + 4+2+8+16);
+	byte_stream_new(&bs, 11+strlen(peer_conn->bn) + 4+2+8+16);
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->sn);
+	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
 
 	byte_stream_put16(&bs, 0x0005);
 	byte_stream_put16(&bs, 0x001a);
@@ -783,7 +783,7 @@
  * "I want to connect through a proxy server"
  */
 void
-aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 port, guint16 requestnumber)
+aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -795,12 +795,12 @@
 	if (conn == NULL)
 		return;
 
-	byte_stream_new(&bs, 246+strlen(sn));
+	byte_stream_new(&bs, 246+strlen(bn));
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 
 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
@@ -835,7 +835,7 @@
  * remote user to connect to us via a proxy server.
  */
 void
-aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 pin, guint16 requestnumber)
+aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -848,12 +848,12 @@
 	if (conn == NULL)
 		return;
 
-	byte_stream_new(&bs, 246+strlen(sn));
+	byte_stream_new(&bs, 246+strlen(bn));
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 
 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
@@ -898,7 +898,7 @@
  *
  */
 void
-aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
+aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -915,7 +915,7 @@
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 
 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
@@ -981,7 +981,7 @@
  * remote user to connect to us via a proxy server.
  */
 void
-aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
+aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -999,7 +999,7 @@
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 
 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
 
@@ -1073,29 +1073,29 @@
  * Subtype 0x0006 - Request the status message of the given ICQ user.
  *
  * @param od The oscar session.
- * @param sn The UIN of the user of whom you wish to request info.
+ * @param bn The UIN of the user of whom you wish to request info.
  * @param type The type of info you wish to request.  This should be the current
  *        state of the user, as one of the AIM_ICQ_STATE_* defines.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-int aim_im_sendch2_geticqaway(OscarData *od, const char *sn, int type)
+int aim_im_sendch2_geticqaway(OscarData *od, const char *bn, int type)
 {
 	FlapConnection *conn;
 	ByteStream bs;
 	aim_snacid_t snacid;
 	guchar cookie[8];
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)) || !bn)
 		return -EINVAL;
 
 	aim_icbm_makecookie(cookie);
 
-	byte_stream_new(&bs, 8+2+1+strlen(sn) + 4+0x5e + 4);
+	byte_stream_new(&bs, 8+2+1+strlen(bn) + 4+0x5e + 4);
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 
 	/* TLV t(0005) - Encompasses almost everything below. */
 	byte_stream_put16(&bs, 0x0005); /* T */
@@ -1176,12 +1176,12 @@
  * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people
  * were taking when they merged the two protocols.
  *
- * @param sn The destination screen name.
+ * @param bn The destination buddy name.
  * @param type The type of message.  0x0007 for authorization denied.  0x0008 for authorization granted.
  * @param message The message you want to send, it should be null terminated.
  * @return Return 0 if no errors, otherwise return the error number.
  */
-int aim_im_sendch4(OscarData *od, const char *sn, guint16 type, const char *message)
+int aim_im_sendch4(OscarData *od, const char *bn, guint16 type, const char *message)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -1191,17 +1191,17 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
 		return -EINVAL;
 
-	if (!sn || !type || !message)
+	if (!bn || !type || !message)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 8+3+strlen(sn)+12+strlen(message)+1+4);
+	byte_stream_new(&bs, 8+3+strlen(bn)+12+strlen(message)+1+4);
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	aim_icbm_makecookie(cookie);
 
 	/* ICBM header */
-	aim_im_puticbm(&bs, cookie, 0x0004, sn);
+	aim_im_puticbm(&bs, cookie, 0x0004, bn);
 
 	/*
 	 * TLV t(0005)
@@ -1246,8 +1246,8 @@
 	guchar cookie[8];
 	guint16 channel;
 	GSList *tlvlist;
-	char *sn;
-	int snlen;
+	char *bn;
+	int bnlen;
 	guint16 icbmflags = 0;
 	guint8 flag1 = 0, flag2 = 0;
 	gchar *msg = NULL;
@@ -1264,8 +1264,8 @@
 		return 0;
 	}
 
-	snlen = byte_stream_get8(bs);
-	sn = byte_stream_getstr(bs, snlen);
+	bnlen = byte_stream_get8(bs);
+	bn = byte_stream_getstr(bs, bnlen);
 
 	tlvlist = aim_tlvlist_read(bs);
 
@@ -1296,9 +1296,9 @@
 	}
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, channel, sn, msg, icbmflags, flag1, flag2);
-
-	g_free(sn);
+		ret = userfunc(od, conn, frame, channel, bn, msg, icbmflags, flag1, flag2);
+
+	g_free(bn);
 	g_free(msg);
 	aim_tlvlist_free(tlvlist);
 
@@ -1480,7 +1480,7 @@
 		msglen = byte_stream_get16(&mbs);
 		if (msglen > byte_stream_empty(&mbs))
 		{
-			purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+			purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
 			break;
 		}
 
@@ -1589,7 +1589,7 @@
 
 		if (length > byte_stream_empty(bs))
 		{
-			purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+			purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
 			break;
 		}
 
@@ -1625,14 +1625,14 @@
 
 			if (magic1 != 0x501)
 			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
 				break;
 			}
 
 			args.featureslen = byte_stream_get16(bs);
 			if (args.featureslen > byte_stream_empty(bs))
 			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
 				break;
 			}
 			if (args.featureslen == 0)
@@ -1654,7 +1654,7 @@
 			magic1 = byte_stream_get16(bs); /* 01 01 */
 			if (magic1 != 0x101) /* Bad, message comes before attributes */
 			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
 				break;
 			}
 			msglen = byte_stream_get16(bs);
@@ -1721,7 +1721,7 @@
 
 			if (length > byte_stream_empty(bs))
 			{
-				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
 				break;
 			}
 			g_free(args.extdata);
@@ -1796,7 +1796,7 @@
 			bnlen = byte_stream_get16(servdata);
 			bn = byte_stream_getstr(servdata, bnlen);
 
-			purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn);
+			purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->bn, gn, bn);
 
 			g_free(bn);
 		}
@@ -2285,7 +2285,7 @@
 }
 
 /*
- * Subtype 0x0008 - Send a warning to sn.
+ * Subtype 0x0008 - Send a warning to bn.
  *
  * Flags:
  *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
@@ -2293,21 +2293,21 @@
  * returns -1 on error (couldn't alloc packet), 0 on success.
  *
  */
-int aim_im_warn(OscarData *od, FlapConnection *conn, const char *sn, guint32 flags)
+int aim_im_warn(OscarData *od, FlapConnection *conn, const char *bn, guint32 flags)
 {
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !conn || !sn)
+	if (!od || !conn || !bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, strlen(sn)+3);
-
-	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0008, 0x0000, sn, strlen(sn)+1);
+	byte_stream_new(&bs, strlen(bn)+3);
+
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0008, 0x0000, bn, strlen(bn)+1);
 
 	byte_stream_put16(&bs, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000);
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0008, 0x0000, snacid, &bs);
 
@@ -2349,7 +2349,7 @@
  *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
  *
  */
-int aim_im_denytransfer(OscarData *od, const char *sn, const guchar *cookie, guint16 code)
+int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -2359,15 +2359,15 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
-	byte_stream_new(&bs, 8+2+1+strlen(sn)+6);
+	byte_stream_new(&bs, 8+2+1+strlen(bn)+6);
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
 
 	byte_stream_putraw(&bs, cookie, 8);
 
 	byte_stream_put16(&bs, 0x0002); /* channel */
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	aim_tlvlist_add_16(&tlvlist, 0x0003, code);
 	aim_tlvlist_write(&bs, &tlvlist);
@@ -2380,7 +2380,7 @@
 	return 0;
 }
 
-static void parse_status_note_text(OscarData *od, guchar *cookie, char *sn, ByteStream *bs)
+static void parse_status_note_text(OscarData *od, guchar *cookie, char *bn, ByteStream *bs)
 {
 	struct aim_icq_info *info;
 	struct aim_icq_info *prev_info;
@@ -2534,10 +2534,10 @@
 	g_free(status_note_text);
 	g_free(stripped_status_note_text);
 
-	buddy = purple_find_buddy(account, sn);
+	buddy = purple_find_buddy(account, bn);
 	if (buddy == NULL)
 	{
-		purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", sn);
+		purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", bn);
 		g_free(status_note);
 		return;
 	}
@@ -2548,7 +2548,7 @@
 	presence = purple_buddy_get_presence(buddy);
 	status = purple_presence_get_active_status(presence);
 
-	purple_prpl_got_user_status(account, sn,
+	purple_prpl_got_user_status(account, bn,
 			purple_status_get_id(status),
 			"message", status_note, NULL);
 
@@ -2565,26 +2565,26 @@
 	int ret = 0;
 	aim_rxcallback_t userfunc;
 	guint16 channel, reason;
-	char *sn;
+	char *bn;
 	guchar *cookie;
-	guint8 snlen;
+	guint8 bnlen;
 
 	cookie = byte_stream_getraw(bs, 8);
 	channel = byte_stream_get16(bs);
-	snlen = byte_stream_get8(bs);
-	sn = byte_stream_getstr(bs, snlen);
+	bnlen = byte_stream_get8(bs);
+	bn = byte_stream_getstr(bs, bnlen);
 	reason = byte_stream_get16(bs);
 
 	if (channel == 0x0002)
 	{
 		if (reason == 0x0003) /* channel-specific */
 			/* parse status note text */
-			parse_status_note_text(od, cookie, sn, bs);
+			parse_status_note_text(od, cookie, bn, bs);
 
 		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);
+			ret = userfunc(od, conn, frame, channel, bn, reason, cookie);
 
 	} else if (channel == 0x0004) { /* ICQ message */
 		switch (reason) {
@@ -2629,20 +2629,20 @@
 				msg = byte_stream_getraw(bs, len);
 
 				if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-					ret = userfunc(od, conn, frame, channel, sn, reason, state, msg);
+					ret = userfunc(od, conn, frame, channel, bn, reason, state, msg);
 
 				g_free(msg);
 			} break;
 
 			default: {
 				if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-					ret = userfunc(od, conn, frame, channel, sn, reason);
+					ret = userfunc(od, conn, frame, channel, bn, reason);
 			} break;
 		} /* end switch */
 	}
 
 	g_free(cookie);
-	g_free(sn);
+	g_free(bn);
 
 	return ret;
 }
@@ -2660,17 +2660,17 @@
 	aim_rxcallback_t userfunc;
 	guint16 ch;
 	guchar *cookie;
-	char *sn;
+	char *bn;
 	int ret = 0;
 
 	cookie = byte_stream_getraw(bs, 8);
 	ch = byte_stream_get16(bs);
-	sn = byte_stream_getstr(bs, byte_stream_get8(bs));
+	bn = byte_stream_getstr(bs, byte_stream_get8(bs));
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, ch, sn);
-
-	g_free(sn);
+		ret = userfunc(od, conn, frame, ch, bn);
+
+	g_free(bn);
 	g_free(cookie);
 
 	return ret;
@@ -2707,7 +2707,7 @@
  * and Purple 0.60 and newer.
  *
  */
-int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2)
+int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -2716,10 +2716,10 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
 		return -EINVAL;
 
-	if (!sn)
+	if (!bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 11+strlen(sn)+2);
+	byte_stream_new(&bs, 11+strlen(bn)+2);
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
 
@@ -2738,10 +2738,10 @@
 	byte_stream_put16(&bs, type1);
 
 	/*
-	 * Dest sn
+	 * Dest buddy name
 	 */
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	/*
 	 * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn)
@@ -2766,20 +2766,20 @@
 {
 	int ret = 0;
 	aim_rxcallback_t userfunc;
-	char *sn;
-	guint8 snlen;
+	char *bn;
+	guint8 bnlen;
 	guint16 type1, type2;
 
 	byte_stream_advance(bs, 8); /* Unknown - All 0's */
 	type1 = byte_stream_get16(bs);
-	snlen = byte_stream_get8(bs);
-	sn = byte_stream_getstr(bs, snlen);
+	bnlen = byte_stream_get8(bs);
+	bn = byte_stream_getstr(bs, bnlen);
 	type2 = byte_stream_get16(bs);
 
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-		ret = userfunc(od, conn, frame, type1, sn, type2);
-
-	g_free(sn);
+		ret = userfunc(od, conn, frame, type1, bn, type2);
+
+	g_free(bn);
 
 	return ret;
 }
--- a/libpurple/protocols/oscar/family_locate.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Sat Mar 07 01:59:40 2009 +0000
@@ -100,7 +100,7 @@
 	  0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}},
 
 	/* Supports "new status message features" (Who advertises this one?) */
-	/* OSCAR_CAPABILITY_HOST_STATUS_TEXT_AWARE */ 
+	/* OSCAR_CAPABILITY_HOST_STATUS_TEXT_AWARE */
 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
 	 {0x09, 0x46, 0x01, 0x0a, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
@@ -112,7 +112,7 @@
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
 	/* Client only asserts caps for services in which it is participating */
-	/* OSCAR_CAPABILITY_SMARTCAPS */ 
+	/* OSCAR_CAPABILITY_SMARTCAPS */
 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
 	 {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
@@ -253,11 +253,11 @@
 	FlapConnection *conn;
 	aim_rxcallback_t userfunc;
 
-	cur = aim_locate_finduserinfo(od, userinfo->sn);
+	cur = aim_locate_finduserinfo(od, userinfo->bn);
 
 	if (cur == NULL) {
 		cur = (aim_userinfo_t *)g_new0(aim_userinfo_t, 1);
-		cur->sn = g_strdup(userinfo->sn);
+		cur->bn = g_strdup(userinfo->bn);
 		cur->next = od->locate.userinfo;
 		od->locate.userinfo = cur;
 	}
@@ -366,35 +366,35 @@
 }
 
 /**
- * Remove this screen name from our queue.  If this info was requested
+ * Remove this buddy name from our queue.  If this info was requested
  * by our info request queue, then pop the next element off of the queue.
  *
  * @param od The aim session.
- * @param sn Screen name of the info we just received.
+ * @param bn Buddy name of the info we just received.
  * @return True if the request was explicit (client requested the info),
  *         false if the request was implicit (libfaim request the info).
  */
 static int
-aim_locate_gotuserinfo(OscarData *od, FlapConnection *conn, const char *sn)
+aim_locate_gotuserinfo(OscarData *od, FlapConnection *conn, const char *bn)
 {
 	struct userinfo_node *cur, *del;
 	int was_explicit = TRUE;
 
-	while ((od->locate.requested != NULL) && (aim_sncmp(sn, od->locate.requested->sn) == 0)) {
+	while ((od->locate.requested != NULL) && (oscar_util_name_compare(bn, od->locate.requested->bn) == 0)) {
 		del = od->locate.requested;
 		od->locate.requested = del->next;
 		was_explicit = FALSE;
-		g_free(del->sn);
+		g_free(del->bn);
 		g_free(del);
 	}
 
 	cur = od->locate.requested;
 	while ((cur != NULL) && (cur->next != NULL)) {
-		if (aim_sncmp(sn, cur->next->sn) == 0) {
+		if (oscar_util_name_compare(bn, cur->next->bn) == 0) {
 			del = cur->next;
 			cur->next = del->next;
 			was_explicit = FALSE;
-			g_free(del->sn);
+			g_free(del->bn);
 			g_free(del);
 		} else
 			cur = cur->next;
@@ -404,34 +404,34 @@
 }
 
 void
-aim_locate_autofetch_away_message(OscarData *od, const char *sn)
+aim_locate_autofetch_away_message(OscarData *od, const char *bn)
 {
 	struct userinfo_node *cur;
 
 	/* Make sure we haven't already made an info request for this buddy */
 	for (cur = od->locate.requested; cur != NULL; cur = cur->next)
-		if (aim_sncmp(sn, cur->sn) == 0)
+		if (oscar_util_name_compare(bn, cur->bn) == 0)
 			return;
 
 	/* Add a new node to our request queue */
 	cur = (struct userinfo_node *)g_malloc(sizeof(struct userinfo_node));
-	cur->sn = g_strdup(sn);
+	cur->bn = g_strdup(bn);
 	cur->next = od->locate.requested;
 	od->locate.requested = cur;
 
-	aim_locate_getinfoshort(od, cur->sn, 0x00000002);
+	aim_locate_getinfoshort(od, cur->bn, 0x00000002);
 }
 
-aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *sn) {
+aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn) {
 	aim_userinfo_t *cur = NULL;
 
-	if (sn == NULL)
+	if (bn == NULL)
 		return NULL;
 
 	cur = od->locate.userinfo;
 
 	while (cur != NULL) {
-		if (aim_sncmp(cur->sn, sn) == 0)
+		if (oscar_util_name_compare(cur->bn, bn) == 0)
 			return cur;
 		cur = cur->next;
 	}
@@ -552,7 +552,7 @@
 void
 aim_info_free(aim_userinfo_t *info)
 {
-	g_free(info->sn);
+	g_free(info->bn);
 	g_free(info->iconcsum);
 	g_free(info->info);
 	g_free(info->info_encoding);
@@ -572,7 +572,7 @@
 aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
 {
 	int curtlv, tlvcnt;
-	guint8 snlen;
+	guint8 bnlen;
 
 	if (!bs || !outinfo)
 		return -EINVAL;
@@ -581,11 +581,11 @@
 	memset(outinfo, 0x00, sizeof(aim_userinfo_t));
 
 	/*
-	 * Screen name.  Stored as an unterminated string prepended with a
+	 * Username.  Stored as an unterminated string prepended with a
 	 * byte containing its length.
 	 */
-	snlen = byte_stream_get8(bs);
-	outinfo->sn = byte_stream_getstr(bs, snlen);
+	bnlen = byte_stream_get8(bs);
+	outinfo->bn = byte_stream_getstr(bs, bnlen);
 
 	/*
 	 * Warning Level.  Stored as an unsigned short.
@@ -881,7 +881,7 @@
 			 */
 #ifdef LOG_UNKNOWN_TLV
 			purple_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n");
-			purple_debug_misc("oscar", "userinfo:   sn    =%s\n", outinfo->sn);
+			purple_debug_misc("oscar", "userinfo:   bn    =%s\n", outinfo->bn);
 			dumptlv(od, type, bs, length);
 #endif
 		}
@@ -906,8 +906,8 @@
 	if (!bs || !info)
 		return -EINVAL;
 
-	byte_stream_put8(bs, strlen(info->sn));
-	byte_stream_putstr(bs, info->sn);
+	byte_stream_put8(bs, strlen(info->bn));
+	byte_stream_putstr(bs, info->bn);
 
 	byte_stream_put16(bs, info->warnlevel);
 
@@ -922,7 +922,7 @@
 
 /* XXX - So, ICQ_OSCAR_SUPPORT is never defined anywhere... */
 #ifdef ICQ_OSCAR_SUPPORT
-	if (atoi(info->sn) != 0) {
+	if (atoi(info->bn) != 0) {
 		if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS)
 			aim_tlvlist_add_16(&tlvlist, 0x0006, info->icqinfo.status);
 		if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR)
@@ -953,35 +953,35 @@
 	aim_rxcallback_t userfunc;
 	aim_snac_t *snac2;
 	guint16 reason;
-	char *sn;
+	char *bn;
 	int was_explicit;
 
 	if (!(snac2 = aim_remsnac(od, snac->id))) {
-		purple_debug_misc("oscar", "faim: locate.c, error(): received response from unknown request!\n");
+		purple_debug_misc("oscar", "locate error: received response from unknown request!\n");
 		return 0;
 	}
 
 	if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) {
-		purple_debug_misc("oscar", "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family);
+		purple_debug_misc("oscar", "locate error: received response from invalid request! %d\n", snac2->family);
 		return 0;
 	}
 
-	if (!(sn = snac2->data)) {
-		purple_debug_misc("oscar", "faim: locate.c, error(): received response from request without a screen name!\n");
+	if (!(bn = snac2->data)) {
+		purple_debug_misc("oscar", "locate error: received response from request without a buddy name!\n");
 		return 0;
 	}
 
 	reason = byte_stream_get16(bs);
 
 	/*
-	 * Remove this screen name from our queue.  If the client requested
+	 * Remove this buddy name from our queue.  If the client requested
 	 * this buddy's info explicitly, then notify them that we do not have
 	 * info for this buddy.
 	 */
-	was_explicit = aim_locate_gotuserinfo(od, conn, sn);
+	was_explicit = aim_locate_gotuserinfo(od, conn, bn);
 	if (was_explicit == TRUE)
 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-			ret = userfunc(od, conn, frame, reason, sn);
+			ret = userfunc(od, conn, frame, reason, bn);
 
 	if (snac2)
 		g_free(snac2->data);
@@ -1157,29 +1157,29 @@
 /*
  * Subtype 0x0005 - Request info of another AIM user.
  *
- * @param sn The screenname whose info you wish to request.
+ * @param bn The buddy name whose info you wish to request.
  * @param infotype The type of info you wish to request.
  *        0x0001 - Info/profile
  *        0x0003 - Away message
  *        0x0004 - Capabilities
  */
 int
-aim_locate_getinfo(OscarData *od, const char *sn, guint16 infotype)
+aim_locate_getinfo(OscarData *od, const char *bn, guint16 infotype)
 {
 	FlapConnection *conn;
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 2+1+strlen(sn));
+	byte_stream_new(&bs, 2+1+strlen(bn));
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, NULL, 0);
 
 	byte_stream_put16(&bs, infotype);
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, snacid, &bs);
 
@@ -1229,18 +1229,18 @@
 	aim_tlvlist_free(tlvlist);
 
 	aim_locate_adduserinfo(od, userinfo);
-	userinfo2 = aim_locate_finduserinfo(od, userinfo->sn);
+	userinfo2 = aim_locate_finduserinfo(od, userinfo->bn);
 	aim_info_free(userinfo);
 	g_free(userinfo);
 
 	/*
-	 * Remove this screen name from our queue.  If the client requested
+	 * Remove this buddy name from our queue.  If the client requested
 	 * this buddy's info explicitly, then notify them that we have info
 	 * for this buddy.
 	 */
 	if (userinfo2 != NULL)
 	{
-		was_explicit = aim_locate_gotuserinfo(od, conn, userinfo2->sn);
+		was_explicit = aim_locate_gotuserinfo(od, conn, userinfo2->bn);
 		if (was_explicit == TRUE)
 			if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 				ret = userfunc(od, conn, frame, userinfo2);
@@ -1307,7 +1307,7 @@
 /*
  * Subtype 0x000b - Huh? What is this?
  */
-int aim_locate_000b(OscarData *od, const char *sn)
+int aim_locate_000b(OscarData *od, const char *bn)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -1315,15 +1315,15 @@
 
 		return -EINVAL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 1+strlen(sn));
+	byte_stream_new(&bs, 1+strlen(bn));
 
 	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, NULL, 0);
 
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
 	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, snacid, &bs);
 
@@ -1380,7 +1380,7 @@
  * Subtype 0x0015 - Request the info of a user using the short method.  This is
  * what iChat uses.  It normally is VERY leniently rate limited.
  *
- * @param sn The screen name whose info you wish to request.
+ * @param bn The buddy name whose info you wish to request.
  * @param flags The bitmask which specifies the type of info you wish to request.
  *        0x00000001 - Info/profile.
  *        0x00000002 - Away message.
@@ -1389,21 +1389,21 @@
  * @return Return 0 if no errors, otherwise return the error number.
  */
 int
-aim_locate_getinfoshort(OscarData *od, const char *sn, guint32 flags)
+aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags)
 {
 	FlapConnection *conn;
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
 		return -EINVAL;
 
-	byte_stream_new(&bs, 4 + 1 + strlen(sn));
+	byte_stream_new(&bs, 4 + 1 + strlen(bn));
 	byte_stream_put32(&bs, flags);
-	byte_stream_put8(&bs, strlen(sn));
-	byte_stream_putstr(&bs, sn);
+	byte_stream_put8(&bs, strlen(bn));
+	byte_stream_putstr(&bs, bn);
 
-	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, sn, strlen(sn)+1);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, bn, strlen(bn)+1);
 	flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, snacid, &bs, FALSE);
 
 	byte_stream_destroy(&bs);
--- a/libpurple/protocols/oscar/family_odir.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_odir.c	Sat Mar 07 01:59:40 2009 +0000
@@ -31,7 +31,7 @@
 /**
  * Subtype 0x0002 - Submit a User Search Request
  *
- * Search for an AIM screen name based on their email address.
+ * Search for an AIM buddy based on their email address.
  *
  * @param od The oscar session.
  * @param region Should be "us-ascii" unless you know what you're doing.
@@ -70,7 +70,7 @@
 /**
  * Subtype 0x0002 - Submit a User Search Request
  *
- * Search for an AIM screen name based on various info
+ * Search for an AIM buddy based on various info
  * about the person.
  *
  * @param od The oscar session.
@@ -202,7 +202,7 @@
 		new->country = aim_tlv_getstr(tlvlist, 0x0006, 1);
 		new->state = aim_tlv_getstr(tlvlist, 0x0007, 1);
 		new->city = aim_tlv_getstr(tlvlist, 0x0008, 1);
-		new->sn = aim_tlv_getstr(tlvlist, 0x0009, 1);
+		new->bn = aim_tlv_getstr(tlvlist, 0x0009, 1);
 		new->interest = aim_tlv_getstr(tlvlist, 0x000b, 1);
 		new->nick = aim_tlv_getstr(tlvlist, 0x000c, 1);
 		new->zip = aim_tlv_getstr(tlvlist, 0x000d, 1);
@@ -228,7 +228,7 @@
 		g_free(del->country);
 		g_free(del->state);
 		g_free(del->city);
-		g_free(del->sn);
+		g_free(del->bn);
 		g_free(del->interest);
 		g_free(del->nick);
 		g_free(del->zip);
--- a/libpurple/protocols/oscar/family_oservice.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Sat Mar 07 01:59:40 2009 +0000
@@ -825,7 +825,7 @@
 int
 aim_srv_setextrainfo(OscarData *od,
 		gboolean seticqstatus, guint32 icqstatus,
-		gboolean setavailmsg, const char *availmsg, const char *itmsurl)
+		gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -851,30 +851,17 @@
 	}
 #endif
 
-	if (setavailmsg)
+	if (setstatusmsg)
 	{
-		int availmsglen, itmsurllen;
+		size_t statusmsglen, itmsurllen;
 		ByteStream tmpbs;
 
-		availmsglen = (availmsg != NULL) ? strlen(availmsg) : 0;
+		statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0;
 		itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0;
 
-		byte_stream_new(&tmpbs, availmsglen + 8 + itmsurllen + 8);
-		byte_stream_put16(&tmpbs, 0x0002);
-		byte_stream_put8(&tmpbs, 0x04); /* Flags */
-		byte_stream_put8(&tmpbs, availmsglen + 4);
-		byte_stream_put16(&tmpbs, availmsglen);
-		if (availmsglen > 0)
-			byte_stream_putstr(&tmpbs, availmsg);
-		byte_stream_put16(&tmpbs, 0x0000);
-
-		byte_stream_put16(&tmpbs, 0x0009);
-		byte_stream_put8(&tmpbs, 0x04); /* Flags */
-		byte_stream_put8(&tmpbs, itmsurllen + 4);
-		byte_stream_put16(&tmpbs, itmsurllen);
-		if (itmsurllen > 0)
-			byte_stream_putstr(&tmpbs, itmsurl);
-		byte_stream_put16(&tmpbs, 0x0000);
+		byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8);
+		byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg);
+		byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl);
 
 		aim_tlvlist_add_raw(&tlvlist, 0x001d,
 				byte_stream_curpos(&tmpbs), tmpbs.data);
--- a/libpurple/protocols/oscar/odc.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/odc.c	Sat Mar 07 01:59:40 2009 +0000
@@ -62,7 +62,7 @@
 		PurpleConversation *conv;
 
 		account = purple_connection_get_account(conn->od->gc);
-		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn);
+		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
 		purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
 		g_free(tmp);
 	}
@@ -90,11 +90,11 @@
 
 	purple_debug_info("oscar", "Outgoing ODC frame to %s with "
 		"type=0x%04x, flags=0x%04x, payload length=%u\n",
-		conn->sn, frame->type, frame->flags, frame->payload.len);
+		conn->bn, frame->type, frame->flags, frame->payload.len);
 
 	account = purple_connection_get_account(conn->od->gc);
 	username = purple_account_get_username(account);
-	memcpy(frame->sn, username, strlen(username));
+	memcpy(frame->bn, username, strlen(username));
 	memcpy(frame->cookie, conn->cookie, 8);
 
 	length = 76;
@@ -116,7 +116,7 @@
 	byte_stream_put16(&bs, frame->flags);
 	byte_stream_put16(&bs, 0x0000);
 	byte_stream_put16(&bs, 0x0000);
-	byte_stream_putraw(&bs, frame->sn, 32);
+	byte_stream_putraw(&bs, frame->bn, 32);
 	byte_stream_putraw(&bs, frame->payload.data, frame->payload.len);
 
 	peer_connection_send(conn, &bs);
@@ -366,7 +366,7 @@
 		g_datalist_clear(&attributes);
 
 		/* Append the message up to the tag */
-		utf8 = purple_plugin_oscar_decode_im_part(account, conn->sn,
+		utf8 = purple_plugin_oscar_decode_im_part(account, conn->bn,
 				encoding, 0x0000, tmp, start - tmp);
 		if (utf8 != NULL) {
 			g_string_append(newmsg, utf8);
@@ -386,7 +386,7 @@
 	/* Append any remaining message data */
 	if (tmp <= msgend)
 	{
-		utf8 = purple_plugin_oscar_decode_im_part(account, conn->sn,
+		utf8 = purple_plugin_oscar_decode_im_part(account, conn->bn,
 				encoding, 0x0000, tmp, msgend - tmp);
 		if (utf8 != NULL) {
 			g_string_append(newmsg, utf8);
@@ -400,7 +400,7 @@
 		imflags |= PURPLE_MESSAGE_IMAGES;
 	if (autoreply)
 		imflags |= PURPLE_MESSAGE_AUTO_RESP;
-	serv_got_im(gc, conn->sn, newmsg->str, imflags, time(NULL));
+	serv_got_im(gc, conn->bn, newmsg->str, imflags, time(NULL));
 	g_string_free(newmsg, TRUE);
 
 	/* unref any images we allocated */
@@ -503,11 +503,11 @@
 	byte_stream_advance(bs, 4);
 	frame->flags = byte_stream_get16(bs);
 	byte_stream_advance(bs, 4);
-	byte_stream_getrawbuf(bs, frame->sn, 32);
+	byte_stream_getrawbuf(bs, frame->bn, 32);
 
 	purple_debug_info("oscar", "Incoming ODC frame from %s with "
 			"type=0x%04x, flags=0x%04x, payload length=%u\n",
-			frame->sn, frame->type, frame->flags, frame->payload.len);
+			frame->bn, frame->type, frame->flags, frame->payload.len);
 
 	if (!conn->ready)
 	{
@@ -558,7 +558,7 @@
 
 		/* Tell the local user that we are connected */
 		account = purple_connection_get_account(gc);
-		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn);
+		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
 		purple_conversation_write(conv, NULL, _("Direct IM established"),
 				PURPLE_MESSAGE_SYSTEM, time(NULL));
 	}
@@ -576,16 +576,16 @@
 		/* I had to leave this. It's just too funny. It reminds me of my sister. */
 		purple_debug_info("oscar", "ohmigod! %s has started typing "
 			"(DirectIM). He's going to send you a message! "
-			"*squeal*\n", conn->sn);
-		serv_got_typing(gc, conn->sn, 0, PURPLE_TYPING);
+			"*squeal*\n", conn->bn);
+		serv_got_typing(gc, conn->bn, 0, PURPLE_TYPING);
 	}
 	else if (frame->flags & 0x0004)
 	{
-		serv_got_typing(gc, conn->sn, 0, PURPLE_TYPED);
+		serv_got_typing(gc, conn->bn, 0, PURPLE_TYPED);
 	}
 	else
 	{
-		serv_got_typing_stopped(gc, conn->sn);
+		serv_got_typing_stopped(gc, conn->bn);
 	}
 
 	if (frame->payload.len > 0)
@@ -598,12 +598,12 @@
 
 			size1 = purple_str_size_to_units(frame->payload.len);
 			size2 = purple_str_size_to_units(DIRECTIM_MAX_FILESIZE);
-			tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM.  Try using file transfer instead.\n"), conn->sn, size1, size2);
+			tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM.  Try using file transfer instead.\n"), conn->bn, size1, size2);
 			g_free(size1);
 			g_free(size2);
 
 			account = purple_connection_get_account(conn->od->gc);
-			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn);
+			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
 			purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
 			g_free(tmp);
 
--- a/libpurple/protocols/oscar/oft.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/oft.c	Sat Mar 07 01:59:40 2009 +0000
@@ -544,7 +544,7 @@
 	frame.name = byte_stream_getraw(bs, frame.name_length);
 
 	purple_debug_info("oscar", "Incoming OFT frame from %s with "
-			"type=0x%04x\n", conn->sn, frame.type);
+			"type=0x%04x\n", conn->bn, frame.type);
 
 	/* TODOFT: peer_oft_dirconvert_fromstupid(frame->name); */
 
--- a/libpurple/protocols/oscar/oscar.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sat Mar 07 01:59:40 2009 +0000
@@ -204,7 +204,7 @@
 void oscar_set_info(PurpleConnection *gc, const char *info);
 static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status);
 static void oscar_set_extendedstatus(PurpleConnection *gc);
-static void oscar_format_screenname(PurpleConnection *gc, const char *nick);
+static void oscar_format_username(PurpleConnection *gc, const char *nick);
 static gboolean purple_ssi_rerequestdata(gpointer data);
 
 static void oscar_free_name_data(struct name_data *data) {
@@ -362,7 +362,7 @@
 	const char *charset = NULL;
 	char *ret = NULL;
 
-	if(aim_snvalid_icq(purple_account_get_username(account)))
+	if(oscar_util_valid_name_icq(purple_account_get_username(account)))
 		charset = purple_account_get_string(account, "encoding", NULL);
 
 	if(charset && *charset)
@@ -414,7 +414,7 @@
  * charsetstr1 is always set to what the correct encoding should be.
  */
 gchar *
-purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcesn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen)
+purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcebn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen)
 {
 	gchar *ret = NULL;
 	const gchar *charsetstr1, *charsetstr2;
@@ -428,7 +428,7 @@
 		charsetstr1 = "UTF-16BE";
 		charsetstr2 = "UTF-8";
 	} else if (charset == AIM_CHARSET_CUSTOM) {
-		if ((sourcesn != NULL) && aim_snvalid_icq(sourcesn))
+		if ((sourcebn != NULL) && oscar_util_valid_name_icq(sourcebn))
 			charsetstr1 = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
 		else
 			charsetstr1 = "ISO-8859-1";
@@ -458,7 +458,7 @@
 		str[datalen] = '\0';
 		salvage = purple_utf8_salvage(str);
 		tmp = g_strdup_printf(_("(There was an error receiving this message.  Either you and %s have different encodings selected, or %s has a buggy client.)"),
-					  sourcesn, sourcesn);
+					  sourcebn, sourcebn);
 		ret = g_strdup_printf("%s %s", salvage, tmp);
 		g_free(tmp);
 		g_free(str);
@@ -473,11 +473,11 @@
  */
 static void
 purple_plugin_oscar_convert_to_best_encoding(PurpleConnection *gc,
-				const char *destsn, const gchar *from,
+				const char *destbn, const gchar *from,
 				gchar **msg, int *msglen_int,
 				guint16 *charset, guint16 *charsubset)
 {
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	GError *err = NULL;
 	aim_userinfo_t *userinfo = NULL;
@@ -499,13 +499,13 @@
 	 * capability, and they are online, then attempt to send
 	 * as UTF-16BE.
 	 */
-	if ((destsn != NULL) && aim_snvalid_icq(destsn))
-		userinfo = aim_locate_finduserinfo(od, destsn);
+	if ((destbn != NULL) && oscar_util_valid_name_icq(destbn))
+		userinfo = aim_locate_finduserinfo(od, destbn);
 
 	if ((userinfo != NULL) && (userinfo->capabilities & OSCAR_CAPABILITY_UNICODE))
 	{
 		PurpleBuddy *b;
-		b = purple_find_buddy(account, destsn);
+		b = purple_find_buddy(account, destbn);
 		if ((b != NULL) && (PURPLE_BUDDY_IS_ONLINE(b)))
 		{
 			*msg = g_convert(from, -1, "UTF-16BE", "UTF-8", NULL, &msglen, &err);
@@ -529,7 +529,7 @@
 	 * ICQ then attempt to send as the user specified character encoding.
 	 */
 	charsetstr = "ISO-8859-1";
-	if ((destsn != NULL) && aim_snvalid_icq(destsn))
+	if ((destbn != NULL) && oscar_util_valid_name_icq(destbn))
 		charsetstr = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
 
 	/*
@@ -808,16 +808,16 @@
 	gchar *message = NULL, *itmsurl = NULL, *tmp;
 	gboolean is_away;
 
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 
 	if (userinfo == NULL)
-		userinfo = aim_locate_finduserinfo(od, b->name);
+		userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
 
 	if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
 		return;
 
 	if (b == NULL)
-		b = purple_find_buddy(purple_connection_get_account(gc), userinfo->sn);
+		b = purple_find_buddy(purple_connection_get_account(gc), userinfo->bn);
 
 	if (b) {
 		presence = purple_buddy_get_presence(b);
@@ -884,7 +884,7 @@
 
 	if (b) {
 		if (purple_presence_is_online(presence)) {
-			if (aim_snvalid_icq(b->name) || is_away || !message || !(*message)) {
+			if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) {
 				/* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
 				 * If the status name and the message are the same, only show one. */
 				const char *status_name = purple_status_get_name(status);
@@ -899,21 +899,20 @@
 				message = tmp;
 			}
 
+		} else if (aim_ssi_waitingforauth(od->ssi.local,
+			aim_ssi_itemlist_findparentname(od->ssi.local, purple_buddy_get_name(b)),
+			purple_buddy_get_name(b)))
+		{
+			/* Note if an offline buddy is not authorized */
+			tmp = g_strdup_printf("%s%s%s",
+					_("Not Authorized"),
+					(message && *message) ? ": " : "",
+					(message && *message) ? message : "");
+			g_free(message);
+			message = tmp;
 		} else {
-			if (aim_ssi_waitingforauth(od->ssi.local,
-									   aim_ssi_itemlist_findparentname(od->ssi.local, b->name),
-									   b->name)) {
-				/* Note if an offline buddy is not authorized */
-				tmp = g_strdup_printf("%s%s%s",
-									  _("Not Authorized"),
-									  (message && *message) ? ": " : "",
-									  (message && *message) ? message : "");
-				g_free(message);
-				message = tmp;
-			} else {
-				g_free(message);
-				message = g_strdup(_("Offline"));
-			}
+			g_free(message);
+			message = g_strdup(_("Offline"));
 		}
 
 	}
@@ -931,27 +930,30 @@
 	PurpleGroup *g = NULL;
 	struct buddyinfo *bi = NULL;
 	char *tmp;
-
-	od = gc->proto_data;
+	const char *bname, *gname = NULL;
+
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
 		return;
 
+	bname = purple_buddy_get_name(b);
 	if (userinfo == NULL)
-		userinfo = aim_locate_finduserinfo(od, b->name);
+		userinfo = aim_locate_finduserinfo(od, bname);
 
 	if (b == NULL)
-		b = purple_find_buddy(account, userinfo->sn);
+		b = purple_find_buddy(account, userinfo->bn);
 
 	if (b != NULL) {
 		g = purple_buddy_get_group(b);
+		gname = purple_group_get_name(g);
 		presence = purple_buddy_get_presence(b);
 		status = purple_presence_get_active_status(presence);
 	}
 
 	if (userinfo != NULL)
-		bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->sn));
+		bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
 
 	if ((bi != NULL) && (bi->ipaddr != 0)) {
 		tmp =  g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
@@ -969,8 +971,8 @@
 		g_free(tmp);
 	}
 
-	if ((b != NULL) && (b->name != NULL) && (g != NULL) && (g->name != NULL)) {
-		tmp = aim_ssi_getcomment(od->ssi.local, g->name, b->name);
+	if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
+		tmp = aim_ssi_getcomment(od->ssi.local, gname, bname);
 		if (tmp != NULL) {
 			char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
 			g_free(tmp);
@@ -1017,7 +1019,7 @@
 static struct chat_connection *
 find_oscar_chat(PurpleConnection *gc, int id)
 {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	GSList *cur;
 	struct chat_connection *cc;
 
@@ -1034,7 +1036,7 @@
 static struct chat_connection *
 find_oscar_chat_by_conn(PurpleConnection *gc, FlapConnection *conn)
 {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	GSList *cur;
 	struct chat_connection *cc;
 
@@ -1051,7 +1053,7 @@
 static struct chat_connection *
 find_oscar_chat_by_conv(PurpleConnection *gc, PurpleConversation *conv)
 {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	GSList *cur;
 	struct chat_connection *cc;
 
@@ -1076,7 +1078,7 @@
 static void
 oscar_chat_kill(PurpleConnection *gc, struct chat_connection *cc)
 {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	/* Notify the conversation window that we've left the chat */
 	serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(cc->conv)));
@@ -1292,10 +1294,10 @@
 		od->chpass = FALSE;
 	}
 	if (od->setnick) {
-		purple_debug_info("oscar", "formatting screen name\n");
-		aim_admin_setnick(od, conn, od->newsn);
-		g_free(od->newsn);
-		od->newsn = NULL;
+		purple_debug_info("oscar", "formatting username\n");
+		aim_admin_setnick(od, conn, od->newformatting);
+		g_free(od->newformatting);
+		od->newformatting = NULL;
 		od->setnick = FALSE;
 	}
 	if (od->conf) {
@@ -1391,7 +1393,7 @@
 	guint32 presence;
 
 	gc = data;
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	report_idle = strcmp((const char *)value, "none") != 0;
 	presence = aim_ssi_getpresence(od->ssi.local);
 
@@ -1414,7 +1416,7 @@
 	guint32 presence;
 
 	gc = data;
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	presence = aim_ssi_getpresence(od->ssi.local);
 
 	if (value)
@@ -1431,8 +1433,9 @@
 	FlapConnection *newconn;
 
 	gc = purple_account_get_connection(account);
-	od = gc->proto_data = oscar_data_new();
+	od = oscar_data_new();
 	od->gc = gc;
+	purple_connection_set_protocol_data(gc, od);
 
 	oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, purple_connerr, 0);
 	oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, flap_connection_established, 0);
@@ -1499,7 +1502,7 @@
 
 	purple_debug_misc("oscar", "oscar_login: gc = %p\n", gc);
 
-	if (!aim_snvalid(purple_account_get_username(account))) {
+	if (!oscar_util_valid_name(purple_account_get_username(account))) {
 		gchar *buf;
 		buf = g_strdup_printf(_("Unable to login: Could not sign on as %s because the username is invalid.  Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf);
@@ -1507,7 +1510,7 @@
 		return;
 	}
 
-	if (aim_snvalid_icq((purple_account_get_username(account)))) {
+	if (oscar_util_valid_name_icq((purple_account_get_username(account)))) {
 		od->icq = TRUE;
 	} else {
 		gc->flags |= PURPLE_CONNECTION_HTML;
@@ -1578,7 +1581,7 @@
 {
 	OscarData *od;
 
-	od = (OscarData *)gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 
 	while (od->oscar_chats)
 	{
@@ -1594,7 +1597,7 @@
 		g_free(cr);
 	}
 	oscar_data_destroy(od);
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 
 	purple_prefs_disconnect_by_handle(gc);
 
@@ -1605,7 +1608,7 @@
 purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
 {
 	PurpleConnection *gc = od->gc;
-	PurpleAccount *account = gc->account;
+	PurpleAccount *account = purple_connection_get_account(gc);
 	char *host; int port;
 	int i;
 	FlapConnection *newconn;
@@ -1619,13 +1622,13 @@
 	va_end(ap);
 
 	purple_debug_info("oscar",
-			   "inside auth_resp (Username: %s)\n", info->sn);
+			   "inside auth_resp (Username: %s)\n", info->bn);
 
 	if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
 		char buf[256];
 		switch (info->errorcode) {
 		case 0x01:
-			/* Unregistered screen name */
+			/* Unregistered username */
 			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Invalid username."));
 			break;
 		case 0x05:
@@ -1644,7 +1647,7 @@
 			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
 			break;
 		case 0x18:
-			/* screen name connecting too frequently */
+			/* username connecting too frequently */
 			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
 			break;
 		case 0x1c:
@@ -1722,7 +1725,7 @@
 purple_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg)
 {
 	PurpleConnection *gc = user_data;
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	aim_auth_securid_send(od, msg);
 }
@@ -1774,7 +1777,7 @@
 static void damn_you(gpointer data, gint source, PurpleInputCondition c)
 {
 	struct pieceofcrap *pos = data;
-	OscarData *od = pos->gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(pos->gc);
 	char in = '\0';
 	int x = 0;
 	unsigned char m[17];
@@ -1840,7 +1843,7 @@
 	pos->fd = source;
 
 	if (source < 0) {
-		GHashTable *ui_info = purple_core_get_ui_info();				
+		GHashTable *ui_info = purple_core_get_ui_info();
 		buf = g_strdup_printf(_("You may be disconnected shortly.  "
 				"Check %s for updates."),
 				((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE));
@@ -2099,7 +2102,7 @@
 	va_end(ap);
 
 	g_return_val_if_fail(info != NULL, 1);
-	g_return_val_if_fail(info->sn != NULL, 1);
+	g_return_val_if_fail(info->bn != NULL, 1);
 
 	if (info->present & AIM_USERINFO_PRESENT_FLAGS) {
 		if (info->flags & AIM_FLAG_AWAY)
@@ -2113,7 +2116,7 @@
 		}
 	}
 
-	if (aim_snvalid_icq(info->sn)) {
+	if (oscar_util_valid_name_icq(info->bn)) {
 		if (type & AIM_ICQ_STATE_CHAT)
 			status_id = OSCAR_STATUS_ID_FREE4CHAT;
 		else if (type & AIM_ICQ_STATE_DND)
@@ -2139,9 +2142,9 @@
 
 	if (info->flags & AIM_FLAG_WIRELESS)
 	{
-		purple_prpl_got_user_status(account, info->sn, OSCAR_STATUS_ID_MOBILE, NULL);
+		purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL);
 	} else {
-		purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE);
+		purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
 	}
 
 	if (info->status != NULL && info->status[0] != '\0')
@@ -2164,11 +2167,11 @@
 			 */
 			tmp2 = "";
 
-		purple_prpl_got_user_status(account, info->sn, status_id,
+		purple_prpl_got_user_status(account, info->bn, status_id,
 									"message", tmp2, "itmsurl", itmsurl, NULL);
 	}
 	else
-		purple_prpl_got_user_status(account, info->sn, status_id, "message", tmp2, NULL);
+		purple_prpl_got_user_status(account, info->bn, status_id, "message", tmp2, NULL);
 
 	g_free(tmp);
 
@@ -2180,7 +2183,7 @@
 		signon = info->onlinesince;
 	else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
 		signon = time(NULL) - info->sessionlen;
-	purple_prpl_got_user_login_time(account, info->sn, signon);
+	purple_prpl_got_user_login_time(account, info->bn, signon);
 
 	/* Idle time stuff */
 	/* info->idletime is the number of minutes that this user has been idle */
@@ -2188,15 +2191,15 @@
 		time_idle = time(NULL) - info->idletime * 60;
 
 	if (time_idle > 0)
-		purple_prpl_got_user_idle(account, info->sn, TRUE, time_idle);
+		purple_prpl_got_user_idle(account, info->bn, TRUE, time_idle);
 	else
-		purple_prpl_got_user_idle(account, info->sn, FALSE, 0);
+		purple_prpl_got_user_idle(account, info->bn, FALSE, 0);
 
 	/* Server stored icon stuff */
-	bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, info->sn));
+	bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, info->bn));
 	if (!bi) {
 		bi = g_new0(struct buddyinfo, 1);
-		g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, info->sn)), bi);
+		g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, info->bn)), bi);
 	}
 	bi->typingnot = FALSE;
 	bi->ico_informed = FALSE;
@@ -2208,20 +2211,20 @@
 		PurpleBuddy *b = NULL;
 
 		b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen);
-		b = purple_find_buddy(account, info->sn);
+		b = purple_find_buddy(account, info->bn);
 		if (b != NULL)
 			saved_b16 = purple_buddy_icons_get_checksum_for_user(b);
 
 		if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) {
 			/* Invalidate the old icon for this user */
-			purple_buddy_icons_set_for_user(account, info->sn, NULL, 0, NULL);
+			purple_buddy_icons_set_for_user(account, info->bn, NULL, 0, NULL);
 
 			/* Fetch the new icon (if we're not already doing so) */
-			if (g_slist_find_custom(od->requesticon, info->sn,
-					(GCompareFunc)aim_sncmp) == NULL)
+			if (g_slist_find_custom(od->requesticon, info->bn,
+					(GCompareFunc)oscar_util_name_compare) == NULL)
 			{
 				od->requesticon = g_slist_prepend(od->requesticon,
-						g_strdup(purple_normalize(account, info->sn)));
+						g_strdup(purple_normalize(account, info->bn)));
 				purple_icons_fetch(gc);
 			}
 		}
@@ -2248,9 +2251,9 @@
 	info = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
-	purple_prpl_got_user_status(account, info->sn, OSCAR_STATUS_ID_OFFLINE, NULL);
-	purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE);
-	g_hash_table_remove(od->buddyinfo, purple_normalize(gc->account, info->sn));
+	purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL);
+	purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
+	g_hash_table_remove(od->buddyinfo, purple_normalize(gc->account, info->bn));
 
 	return 1;
 }
@@ -2268,15 +2271,15 @@
 	GData *attribs;
 
 	purple_debug_misc("oscar", "Received IM from %s with %d parts\n",
-					userinfo->sn, args->mpmsg.numparts);
+					userinfo->bn, args->mpmsg.numparts);
 
 	if (args->mpmsg.numparts == 0)
 		return 1;
 
-	bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->sn));
+	bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
 	if (!bi) {
 		bi = g_new0(struct buddyinfo, 1);
-		g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, userinfo->sn)), bi);
+		g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, userinfo->bn)), bi);
 	}
 
 	if (args->icbmflags & AIM_IMFLAGS_AWAY)
@@ -2288,7 +2291,7 @@
 		bi->typingnot = FALSE;
 
 	if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) {
-		purple_debug_misc("oscar", "%s has an icon\n", userinfo->sn);
+		purple_debug_misc("oscar", "%s has an icon\n", userinfo->bn);
 		if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) {
 			bi->ico_need = TRUE;
 			bi->ico_len = args->iconlen;
@@ -2304,8 +2307,8 @@
 		size_t len = purple_imgstore_get_size(img);
 		purple_debug_info("oscar",
 				"Sending buddy icon to %s (%" G_GSIZE_FORMAT " bytes)\n",
-				userinfo->sn, len);
-		aim_im_sendch2_icon(od, userinfo->sn, data, len,
+				userinfo->bn, len);
+		aim_im_sendch2_icon(od, userinfo->bn, data, len,
 			purple_buddy_icons_get_account_icon_timestamp(account),
 			aimutil_iconsum(data, len));
 	}
@@ -2314,7 +2317,7 @@
 	message = g_string_new("");
 	curpart = args->mpmsg.parts;
 	while (curpart != NULL) {
-		tmp = purple_plugin_oscar_decode_im_part(account, userinfo->sn, curpart->charset,
+		tmp = purple_plugin_oscar_decode_im_part(account, userinfo->bn, curpart->charset,
 				curpart->charsubset, curpart->data, curpart->datalen);
 		if (tmp != NULL) {
 			g_string_append(message, tmp);
@@ -2334,7 +2337,7 @@
 	 * Note: There *may* be some clients which send messages as HTML formatted -
 	 *       they need to be special-cased somehow.
 	 */
-	if (aim_snvalid_icq(purple_account_get_username(account)) && aim_snvalid_icq(userinfo->sn)) {
+	if (oscar_util_valid_name_icq(purple_account_get_username(account)) && oscar_util_valid_name_icq(userinfo->bn)) {
 		/* being recevied by ICQ from ICQ - escape HTML so it is displayed as sent */
 		gchar *tmp2 = g_markup_escape_text(tmp, -1);
 		g_free(tmp);
@@ -2372,7 +2375,7 @@
 		g_datalist_clear(&attribs);
 	}
 
-	serv_got_im(gc, userinfo->sn, tmp, flags,
+	serv_got_im(gc, userinfo->bn, tmp, flags,
 			(args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
 	g_free(tmp);
 
@@ -2391,13 +2394,13 @@
 
 	gc = od->gc;
 	account = purple_connection_get_account(gc);
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 
 	if (args == NULL)
 		return 0;
 
 	purple_debug_misc("oscar", "Incoming rendezvous message of type %u, "
-			"user %s, status %hu\n", args->type, userinfo->sn, args->status);
+			"user %s, status %hu\n", args->type, userinfo->bn, args->status);
 
 	if (args->msg != NULL)
 	{
@@ -2443,7 +2446,7 @@
 				g_strdup_printf("%d", args->info.chat.roominfo.exchange));
 		serv_got_chat_invite(gc,
 				     utf8name,
-				     userinfo->sn,
+				     userinfo->bn,
 				     message,
 				     components);
 	}
@@ -2453,14 +2456,14 @@
 	{
 		if (args->status == AIM_RENDEZVOUS_PROPOSE)
 		{
-			peer_connection_got_proposition(od, userinfo->sn, message, args);
+			peer_connection_got_proposition(od, userinfo->bn, message, args);
 		}
 		else if (args->status == AIM_RENDEZVOUS_CANCEL)
 		{
 			/* The other user canceled a peer request */
 			PeerConnection *conn;
 
-			conn = peer_connection_find_by_cookie(od, userinfo->sn, args->cookie);
+			conn = peer_connection_find_by_cookie(od, userinfo->bn, args->cookie);
 			/*
 			 * If conn is NULL it means we haven't tried to create
 			 * a connection with that user.  They may be trying to
@@ -2491,7 +2494,7 @@
 
 	else if (args->type & OSCAR_CAPABILITY_BUDDYICON)
 	{
-		purple_buddy_icons_set_for_user(account, userinfo->sn,
+		purple_buddy_icons_set_for_user(account, userinfo->bn,
 									  g_memdup(args->info.icon.icon, args->info.icon.length),
 									  args->info.icon.length,
 									  NULL);
@@ -2528,9 +2531,10 @@
 	PurpleAccount *account;
 	PurpleBuddy *buddy;
 	PurpleGroup *group;
+	const char *bname, *gname;
 
 	gc = data->gc;
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 	buddy = purple_find_buddy(account, data->name);
 	if (buddy != NULL)
@@ -2540,15 +2544,17 @@
 
 	if (group != NULL)
 	{
+		bname = purple_buddy_get_name(buddy);
+		gname = purple_group_get_name(group);
 		purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
-				   buddy->name, group->name);
+				   bname, gname);
 		aim_ssi_sendauthrequest(od, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list."));
-		if (!aim_ssi_itemlist_finditem(od->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))
+		if (!aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY))
 		{
-			aim_ssi_addbuddy(od, buddy->name, group->name, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE);
+			aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE);
 
 			/* Mobile users should always be online */
-			if (buddy->name[0] == '+') {
+			if (bname[0] == '+') {
 				purple_prpl_got_user_status(account,
 						purple_buddy_get_name(buddy),
 						OSCAR_STATUS_ID_AVAILABLE, NULL);
@@ -2588,8 +2594,8 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	purple_auth_sendrequest(gc, buddy->name);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	purple_auth_sendrequest(gc, purple_buddy_get_name(buddy));
 }
 
 /* When other people ask you for authorization */
@@ -2598,7 +2604,7 @@
 {
 	struct name_data *data = cbdata;
 	PurpleConnection *gc = data->gc;
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	aim_ssi_sendauthreply(od, data->name, 0x01, NULL);
 
@@ -2610,7 +2616,7 @@
 purple_auth_dontgrant(struct name_data *data, char *msg)
 {
 	PurpleConnection *gc = data->gc;
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	aim_ssi_sendauthreply(od, data->name, 0x00, msg ? msg : _("No reason given."));
 }
@@ -2724,21 +2730,21 @@
 		case 0x06: { /* Someone requested authorization */
 			if (i >= 6) {
 				struct name_data *data = g_new(struct name_data, 1);
-				gchar *sn = g_strdup_printf("%u", args->uin);
+				gchar *bn = g_strdup_printf("%u", args->uin);
 				gchar *reason = NULL;
 
 				if (msg2[5] != NULL)
-					reason = purple_plugin_oscar_decode_im_part(account, sn, AIM_CHARSET_CUSTOM, 0x0000, msg2[5], strlen(msg2[5]));
+					reason = purple_plugin_oscar_decode_im_part(account, bn, AIM_CHARSET_CUSTOM, 0x0000, msg2[5], strlen(msg2[5]));
 
 				purple_debug_info("oscar",
 						   "Received an authorization request from UIN %u\n",
 						   args->uin);
 				data->gc = gc;
-				data->name = sn;
+				data->name = bn;
 				data->nick = NULL;
 
-				purple_account_request_authorization(account, sn, NULL, NULL,
-						reason, purple_find_buddy(account, sn) != NULL,
+				purple_account_request_authorization(account, bn, NULL, NULL,
+						reason, purple_find_buddy(account, bn) != NULL,
 						purple_auth_grant,
 						purple_auth_dontgrant_msgprompt, data);
 				g_free(reason);
@@ -2940,7 +2946,7 @@
 				   "You missed %hu messages from %s because they were invalid.",
 				   nummissed),
 				   nummissed,
-				   userinfo->sn);
+				   userinfo->bn);
 			break;
 		case 1: /* Message too large */
 			buf = g_strdup_printf(
@@ -2949,7 +2955,7 @@
 				   "You missed %hu messages from %s because they were too large.",
 				   nummissed),
 				   nummissed,
-				   userinfo->sn);
+				   userinfo->bn);
 			break;
 		case 2: /* Rate exceeded */
 			buf = g_strdup_printf(
@@ -2958,7 +2964,7 @@
 				   "You missed %hu messages from %s because the rate limit has been exceeded.",
 				   nummissed),
 				   nummissed,
-				   userinfo->sn);
+				   userinfo->bn);
 			break;
 		case 3: /* Evil Sender */
 			buf = g_strdup_printf(
@@ -2967,7 +2973,7 @@
 				   "You missed %hu messages from %s because his/her warning level is too high.",
 				   nummissed),
 				   nummissed,
-				   userinfo->sn);
+				   userinfo->bn);
 			break;
 		case 4: /* Evil Receiver */
 			buf = g_strdup_printf(
@@ -2976,7 +2982,7 @@
 				   "You missed %hu messages from %s because your warning level is too high.",
 				   nummissed),
 				   nummissed,
-				   userinfo->sn);
+				   userinfo->bn);
 			break;
 		default:
 			buf = g_strdup_printf(
@@ -2985,11 +2991,11 @@
 				   "You missed %hu messages from %s for an unknown reason.",
 				   nummissed),
 				   nummissed,
-				   userinfo->sn);
+				   userinfo->bn);
 			break;
 	}
 
-	if (!purple_conv_present_error(userinfo->sn, account, buf))
+	if (!purple_conv_present_error(userinfo->bn, account, buf))
 		purple_notify_error(od->gc, NULL, buf, NULL);
 	g_free(buf);
 
@@ -3107,7 +3113,7 @@
 static int purple_parse_msgerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	PurpleConnection *gc = od->gc;
 #ifdef TODOFT
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	PurpleXfer *xfer;
 #endif
 	va_list ap;
@@ -3135,7 +3141,7 @@
 	}
 #endif
 
-	/* Data is assumed to be the destination sn */
+	/* Data is assumed to be the destination bn */
 	buf = g_strdup_printf(_("Unable to send message: %s"), (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason."));
 	if (!purple_conv_present_error(data, purple_connection_get_account(gc), buf)) {
 		g_free(buf);
@@ -3152,25 +3158,25 @@
 	PurpleConnection *gc = od->gc;
 	va_list ap;
 	guint16 type1, type2;
-	char *sn;
+	char *bn;
 
 	va_start(ap, fr);
 	type1 = (guint16) va_arg(ap, unsigned int);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	type2 = (guint16) va_arg(ap, unsigned int);
 	va_end(ap);
 
 	switch (type2) {
 		case 0x0000: { /* Text has been cleared */
-			serv_got_typing_stopped(gc, sn);
+			serv_got_typing_stopped(gc, bn);
 		} break;
 
 		case 0x0001: { /* Paused typing */
-			serv_got_typing(gc, sn, 0, PURPLE_TYPED);
+			serv_got_typing(gc, bn, 0, PURPLE_TYPED);
 		} break;
 
 		case 0x0002: { /* Typing */
-			serv_got_typing(gc, sn, 0, PURPLE_TYPING);
+			serv_got_typing(gc, bn, 0, PURPLE_TYPING);
 		} break;
 
 		default: {
@@ -3178,7 +3184,7 @@
 			 * It looks like iChat sometimes sends typing notification
 			 * with type1=0x0001 and type2=0x000f.  Not sure why.
 			 */
-			purple_debug_info("oscar", "Received unknown typing notification message from %s.  Type1 is 0x%04x and type2 is 0x%04hx.\n", sn, type1, type2);
+			purple_debug_info("oscar", "Received unknown typing notification message from %s.  Type1 is 0x%04x and type2 is 0x%04hx.\n", bn, type1, type2);
 		} break;
 	}
 
@@ -3239,7 +3245,7 @@
 
 	oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
 
-	if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !aim_snvalid_sms(userinfo->sn)) {
+	if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) {
 		/* An SMS contact is always online; its Online Since valid is not useful */
 		time_t t = userinfo->onlinesince;
 		oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
@@ -3273,11 +3279,11 @@
 
 	purple_notify_user_info_add_section_break(user_info);
 	tmp = g_strdup_printf("<a href=\"http://profiles.aim.com/%s\">%s</a>",
-			purple_normalize(account, userinfo->sn), _("View web profile"));
+			purple_normalize(account, userinfo->bn), _("View web profile"));
 	purple_notify_user_info_add_pair(user_info, NULL, tmp);
 	g_free(tmp);
 
-	purple_notify_userinfo(gc, userinfo->sn, user_info, NULL, NULL);
+	purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
 	purple_notify_user_info_destroy(user_info);
 
 	return 1;
@@ -3299,14 +3305,14 @@
 	userinfo = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
-	b = purple_find_buddy(account, userinfo->sn);
+	b = purple_find_buddy(account, userinfo->bn);
 	if (b == NULL)
 		return 1;
 
-	if (!aim_snvalid_icq(userinfo->sn))
+	if (!oscar_util_valid_name_icq(userinfo->bn))
 	{
-		if (strcmp(purple_buddy_get_name(b), userinfo->sn) != 0)
-			serv_got_alias(gc, purple_buddy_get_name(b), userinfo->sn);
+		if (strcmp(purple_buddy_get_name(b), userinfo->bn) != 0)
+			serv_got_alias(gc, purple_buddy_get_name(b), userinfo->bn);
 		else
 			serv_got_alias(gc, purple_buddy_get_name(b), NULL);
 	}
@@ -3323,7 +3329,7 @@
 		                                 userinfo->away,
 		                                 userinfo->away_len);
 		g_free(charset);
-		purple_prpl_got_user_status(account, userinfo->sn,
+		purple_prpl_got_user_status(account, userinfo->bn,
 				purple_status_get_id(status),
 				"message", message, NULL);
 		g_free(message);
@@ -3444,7 +3450,7 @@
 		return 1;
 
 	for (i = 0; i < count; i++)
-		purple_conv_chat_add_user(PURPLE_CONV_CHAT(c->conv), info[i].sn, NULL, PURPLE_CBFLAGS_NONE, TRUE);
+		purple_conv_chat_add_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL, PURPLE_CBFLAGS_NONE, TRUE);
 
 	return 1;
 }
@@ -3467,7 +3473,7 @@
 		return 1;
 
 	for (i = 0; i < count; i++)
-		purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c->conv), info[i].sn, NULL);
+		purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL);
 
 	return 1;
 }
@@ -3536,7 +3542,7 @@
 	if (utf8 == NULL)
 		/* The conversion failed! */
 		utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
-	serv_got_chat_in(gc, ccon->id, info->sn, 0, utf8, time((time_t)NULL));
+	serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time((time_t)NULL));
 	g_free(utf8);
 
 	return 1;
@@ -3544,11 +3550,15 @@
 
 static int purple_email_parseupdate(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	va_list ap;
-	PurpleConnection *gc = od->gc;
+	PurpleConnection *gc;
+	PurpleAccount *account;
 	struct aim_emailinfo *emailinfo;
 	int havenewmail;
 	char *alertitle, *alerturl;
 
+	gc = od->gc;
+	account = purple_connection_get_account(gc);
+
 	va_start(ap, fr);
 	emailinfo = va_arg(ap, struct aim_emailinfo *);
 	havenewmail = va_arg(ap, int);
@@ -3556,12 +3566,13 @@
 	alerturl  = va_arg(ap, char *);
 	va_end(ap);
 
-	if ((emailinfo != NULL) && purple_account_get_check_mail(gc->account)) {
-		gchar *to = g_strdup_printf("%s%s%s", purple_account_get_username(purple_connection_get_account(gc)),
-									emailinfo->domain ? "@" : "",
-									emailinfo->domain ? emailinfo->domain : "");
-		if (emailinfo->unread && havenewmail)
-			purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, (const char **)&to, (const char **)&emailinfo->url, NULL, NULL);
+	if (account != NULL && emailinfo != NULL && emailinfo->unread && havenewmail) {
+		gchar *to = g_strdup_printf("%s%s%s",
+				purple_account_get_username(account),
+				emailinfo->domain ? "@" : "",
+				emailinfo->domain ? emailinfo->domain : "");
+		purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL,
+				(const char **)&to, (const char **)&emailinfo->url, NULL, NULL);
 		g_free(to);
 	}
 
@@ -3574,12 +3585,12 @@
 static int purple_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	PurpleConnection *gc = od->gc;
 	va_list ap;
-	char *sn;
+	char *bn;
 	guint8 iconcsumtype, *iconcsum, *icon;
 	guint16 iconcsumlen, iconlen;
 
 	va_start(ap, fr);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	iconcsumtype = va_arg(ap, int);
 	iconcsum = va_arg(ap, guint8 *);
 	iconcsumlen = va_arg(ap, int);
@@ -3594,7 +3605,7 @@
 	if ((iconlen > 0) && (iconlen != 90)) {
 		char *b16 = purple_base16_encode(iconcsum, iconcsumlen);
 		purple_buddy_icons_set_for_user(purple_connection_get_account(gc),
-									  sn, g_memdup(icon, iconlen), iconlen, b16);
+									  bn, g_memdup(icon, iconlen), iconlen, b16);
 		g_free(b16);
 	}
 
@@ -3604,7 +3615,7 @@
 static void
 purple_icons_fetch(PurpleConnection *gc)
 {
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	aim_userinfo_t *userinfo;
 	FlapConnection *conn;
 
@@ -3651,14 +3662,14 @@
 static int purple_parse_msgack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	va_list ap;
 	guint16 type;
-	char *sn;
+	char *bn;
 
 	va_start(ap, fr);
 	type = (guint16) va_arg(ap, unsigned int);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	va_end(ap);
 
-	purple_debug_info("oscar", "Sent message to %s.\n", sn);
+	purple_debug_info("oscar", "Sent message to %s.\n", bn);
 
 	return 1;
 }
@@ -3718,7 +3729,7 @@
 	userinfo = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
-	purple_prpl_got_account_warning_level(account, (userinfo && userinfo->sn) ? userinfo->sn : NULL, (newevil/10.0) + 0.5);
+	purple_prpl_got_account_warning_level(account, (userinfo && userinfo->bn) ? userinfo->bn : NULL, (newevil/10.0) + 0.5);
 #endif
 
 	return 1;
@@ -3733,7 +3744,7 @@
 	info = va_arg(ap, aim_userinfo_t *);
 	va_end(ap);
 
-	purple_connection_set_display_name(od->gc, info->sn);
+	purple_connection_set_display_name(od->gc, info->bn);
 
 	/*
 	 * What's with the + 0.5?
@@ -3842,13 +3853,13 @@
 	PurpleAccount *account;
 	PurpleStatus *status;
 	PurplePresence *presence;
-	const char *message, *itmsurl;
+	const char *username, *message, *itmsurl;
 	char *tmp;
 	va_list ap;
 	guint16 maxpermits, maxdenies;
 
 	gc = od->gc;
-	od = (OscarData *)gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	va_start(ap, fr);
@@ -3871,12 +3882,13 @@
 	if (purple_account_get_user_info(account) != NULL)
 		serv_set_info(gc, purple_account_get_user_info(account));
 
-	if (!od->icq && strcmp(purple_account_get_username(account), purple_connection_get_display_name(gc)) != 0)
+	username = purple_account_get_username(account);
+	if (!od->icq && strcmp(username, purple_connection_get_display_name(gc)) != 0)
 		/*
-		 * Format the screen name for AIM accounts if it's different
+		 * Format the username for AIM accounts if it's different
 		 * than what's currently set.
 		 */
-		oscar_format_screenname(gc, account->username);
+		oscar_format_username(gc, username);
 
 	/* Set our available message based on the current status */
 	status = purple_account_get_active_status(account);
@@ -3910,13 +3922,13 @@
 	/*
 	 * The "if" statement here is a pathetic attempt to not attempt to
 	 * connect to the alerts servce (aka email notification) if this
-	 * screen name does not support it.  I think mail notification
+	 * username does not support it.  I think mail notification
 	 * works for @mac.com accounts but does not work for the newer
 	 * @anythingelse.com accounts.  If that's true then this change
 	 * breaks mail notification for @mac.com accounts, but it gets rid
 	 * of an annoying error at signon for @anythingelse.com accounts.
 	 */
-	if ((od->authinfo->email != NULL) && ((strchr(gc->account->username, '@') == NULL)))
+	if (od->authinfo->email != NULL && strchr(username, '@') == NULL)
 		aim_srv_requestnew(od, SNAC_FAMILY_ALERT);
 
 	return 1;
@@ -3980,9 +3992,9 @@
 	user_info = purple_notify_user_info_new();
 
 	g_snprintf(who, sizeof(who), "%u", info->uin);
-	buddy = purple_find_buddy(purple_connection_get_account(gc), who);
+	buddy = purple_find_buddy(account, who);
 	if (buddy != NULL)
-		bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(buddy->account, buddy->name));
+		bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
 	else
 		bi = NULL;
 
@@ -3999,7 +4011,7 @@
 	}
 	oscar_user_info_convert_and_add(account, user_info, _("First Name"), info->first);
 	oscar_user_info_convert_and_add(account, user_info, _("Last Name"), info->last);
-	if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->email))) {
+	if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, info->email))) {
 		buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
 		purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
 		g_free(buf);
@@ -4008,7 +4020,7 @@
 	if (info->numaddresses && info->email2) {
 		int i;
 		for (i = 0; i < info->numaddresses; i++) {
-			if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(gc->account, info->email2[i]))) {
+			if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, info->email2[i]))) {
 				buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
 				purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
 				g_free(buf);
@@ -4043,7 +4055,7 @@
 		snprintf(age, sizeof(age), "%hhd", info->age);
 		purple_notify_user_info_add_pair(user_info, _("Age"), age);
 	}
-	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->personalwebpage))) {
+	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, info->personalwebpage))) {
 		buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
 		purple_notify_user_info_add_pair(user_info, _("Personal Web Page"), buf);
 		g_free(buf);
@@ -4079,7 +4091,7 @@
 		oscar_user_info_convert_and_add(account, user_info, _("Division"), info->workdivision);
 		oscar_user_info_convert_and_add(account, user_info, _("Position"), info->workposition);
 
-		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->workwebpage))) {
+		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, info->workwebpage))) {
 			char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
 			purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
 			g_free(webpage);
@@ -4113,7 +4125,7 @@
 	if (info->uin && info->nick && info->nick[0] && (utf8 = oscar_utf8_try_convert(account, info->nick))) {
 		g_snprintf(who, sizeof(who), "%u", info->uin);
 		serv_got_alias(gc, who, utf8);
-		if ((b = purple_find_buddy(gc->account, who))) {
+		if ((b = purple_find_buddy(account, who))) {
 			purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8);
 		}
 		g_free(utf8);
@@ -4243,7 +4255,7 @@
 	PurpleConnection *gc = od->gc;
 	va_list ap;
 	guint16 perms, err;
-	char *url, *sn, *email;
+	char *url, *bn, *email;
 	int change;
 
 	va_start(ap, fr);
@@ -4251,15 +4263,15 @@
 	perms = (guint16) va_arg(ap, unsigned int);
 	err = (guint16) va_arg(ap, unsigned int);
 	url = va_arg(ap, char *);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	email = va_arg(ap, char *);
 	va_end(ap);
 
 	purple_debug_misc("oscar",
-					"account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, sn=%s, email=%s\n",
+					"account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, bn=%s, email=%s\n",
 					change ? "change" : "request", perms, err,
 					(url != NULL) ? url : "(null)",
-					(sn != NULL) ? sn : "(null)",
+					(bn != NULL) ? bn : "(null)",
 					(email != NULL) ? email : "(null)");
 
 	if ((err > 0) && (url != NULL)) {
@@ -4301,7 +4313,7 @@
 	OscarData *od;
 	FlapConnection *conn;
 
-	od = (OscarData *)gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	conn = flap_connection_getbytype(od, SNAC_FAMILY_LOCATE);
 	if (conn != NULL)
 		flap_connection_send_keepalive(od, conn);
@@ -4313,7 +4325,7 @@
 	OscarData *od;
 	PeerConnection *conn;
 
-	od = (OscarData *)gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
 
 	if ((conn != NULL) && (conn->ready))
@@ -4323,7 +4335,7 @@
 	else {
 		/* Don't send if this turkey is in our deny list */
 		GSList *list;
-		for (list=gc->account->deny; (list && aim_sncmp(name, list->data)); list=list->next);
+		for (list=gc->account->deny; (list && oscar_util_name_compare(name, list->data)); list=list->next);
 		if (!list) {
 			struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(gc->account, name));
 			if (bi && bi->typingnot) {
@@ -4409,7 +4421,7 @@
 
 	/* Convert the message to a good encoding */
 	purple_plugin_oscar_convert_to_best_encoding(conn->od->gc,
-			conn->sn, msg->str, &tmp, &tmplen, &charset, &charsubset);
+			conn->bn, msg->str, &tmp, &tmplen, &charset, &charsubset);
 	g_string_free(msg, TRUE);
 	msg = g_string_new_len(tmp, tmplen);
 	g_free(tmp);
@@ -4435,11 +4447,11 @@
 	char *tmp1, *tmp2;
 	gboolean is_sms, is_html;
 
-	od = (OscarData *)gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 	ret = 0;
 
-	is_sms = aim_snvalid_sms(name);
+	is_sms = oscar_util_valid_name_sms(name);
 
 	if (od->icq && is_sms) {
 		/*
@@ -4478,7 +4490,7 @@
 			                        "You must be Direct Connected to send IM Images."),
 			                        PURPLE_MESSAGE_ERROR, time(NULL));
 
-		buddy = purple_find_buddy(gc->account, name);
+		buddy = purple_find_buddy(account, name);
 
 		bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
 		if (!bi) {
@@ -4553,17 +4565,17 @@
 			purple_imgstore_unref(img);
 		}
 
-		args.destsn = name;
+		args.destbn = name;
 
 		/*
 		 * If we're IMing an SMS user or an ICQ user from an ICQ account, then strip HTML.
 		 */
-		if (aim_snvalid_sms(name)) {
+		if (oscar_util_valid_name_sms(name)) {
 			/* Messaging an SMS (mobile) user */
 			tmp2 = purple_markup_strip_html(tmp1);
 			is_html = FALSE;
-		} else if (aim_snvalid_icq(purple_account_get_username(account))) {
-			if (aim_snvalid_icq(name)) {
+		} else if (oscar_util_valid_name_icq(purple_account_get_username(account))) {
+			if (oscar_util_valid_name_icq(name)) {
 				/* From ICQ to ICQ */
 				tmp2 = purple_markup_strip_html(tmp1);
 				is_html = FALSE;
@@ -4623,9 +4635,9 @@
  * AIM users can only request AIM info.
  */
 void oscar_get_info(PurpleConnection *gc, const char *name) {
-	OscarData *od = (OscarData *)gc->proto_data;
-
-	if (od->icq && aim_snvalid_icq(name))
+	OscarData *od = purple_connection_get_protocol_data(gc);
+
+	if (od->icq && oscar_util_valid_name_icq(name))
 		aim_icq_getallinfo(od, name);
 	else
 		aim_locate_getinfoshort(od, name, 0x00000003);
@@ -4635,14 +4647,14 @@
 static void oscar_set_dir(PurpleConnection *gc, const char *first, const char *middle, const char *last,
 			  const char *maiden, const char *city, const char *state, const char *country, int web) {
 	/* XXX - some of these things are wrong, but i'm lazy */
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	aim_locate_setdirinfo(od, first, middle, last,
 				maiden, NULL, NULL, city, state, NULL, 0, web);
 }
 #endif
 
 void oscar_set_idle(PurpleConnection *gc, int time) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	aim_srv_setidle(od, time);
 }
 
@@ -4688,7 +4700,7 @@
 	const gchar *status_id;
 	guint32 data = 0x00000000;
 
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 	status = purple_account_get_active_status(account);
 	status_id = purple_status_get_id(status);
@@ -4722,20 +4734,21 @@
 						  gboolean setstatus, PurpleStatus *status)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	PurpleStatusType *status_type;
 	PurpleStatusPrimitive primitive;
 
-	char *htmlinfo;
 	char *info_encoding = NULL;
 	char *info = NULL;
 	gsize infolen = 0;
 
-	const char *htmlaway;
 	char *away_encoding = NULL;
 	char *away = NULL;
 	gsize awaylen = 0;
 
+	char *status_text = NULL;
+	const char *itmsurl = NULL;
+
 	status_type = purple_status_get_type(status);
 	primitive = purple_status_type_get_primitive(status_type);
 
@@ -4753,7 +4766,7 @@
 	}
 	else if (rawinfo != NULL)
 	{
-		htmlinfo = purple_strdup_withhtml(rawinfo);
+		char *htmlinfo = purple_strdup_withhtml(rawinfo);
 		info = purple_prpl_oscar_convert_to_infotext(htmlinfo, &infolen, &info_encoding);
 		g_free(htmlinfo);
 
@@ -4770,16 +4783,54 @@
 		}
 	}
 
-	if (!setstatus)
+	if (setstatus)
 	{
-		/* Do nothing! */
-	}
-	else if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
-	{
-		const char *status_html, *itmsurl;
-		char *status_text = NULL;
+		const char *status_html;
 
 		status_html = purple_status_get_attr_string(status, "message");
+
+		if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
+		{
+			/* This is needed for us to un-set any previous away message. */
+			away = g_strdup("");
+		}
+		else
+		{
+			gchar *linkified;
+
+			/* We do this for icq too so that they work for old third party clients */
+			linkified = purple_markup_linkify(status_html);
+			away = purple_prpl_oscar_convert_to_infotext(linkified, &awaylen, &away_encoding);
+			g_free(linkified);
+
+			if (awaylen > od->rights.maxawaymsglen)
+			{
+				gchar *errstr;
+
+				errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte "
+										 "has been exceeded.  It has been truncated for you.",
+										 "The maximum away message length of %d bytes "
+										 "has been exceeded.  It has been truncated for you.",
+										 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
+				purple_notify_warning(gc, NULL, _("Away message too long."), errstr);
+				g_free(errstr);
+			}
+		}
+	}
+
+	aim_locate_setprofile(od,
+			info_encoding, info, MIN(infolen, od->rights.maxsiglen),
+			away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
+	g_free(info);
+	g_free(away);
+
+	if (setstatus)
+	{
+		const char *status_html;
+
+		status_html = purple_status_get_attr_string(status, "message");
+		if (od->icq && (status_html == NULL || status_html[0] == '\0'))
+			status_html = purple_status_type_get_name(status_type);
 		if (status_html != NULL)
 		{
 			status_text = purple_markup_strip_html(status_html);
@@ -4790,82 +4841,28 @@
 				strcpy(tmp, "...");
 			}
 		}
+
 		itmsurl = purple_status_get_attr_string(status, "itmsurl");
 
+		/* TODO: Combine these two calls! */
 		aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, itmsurl);
-		g_free(status_text);
-
-		/* This is needed for us to un-set any previous away message. */
-		away = g_strdup("");
-	}
-	else
-	{
-		char *status_text = NULL;
-		
-		htmlaway = purple_status_get_attr_string(status, "message");
-		if ((htmlaway == NULL) || (*htmlaway == '\0'))
-			htmlaway = purple_status_type_get_name(status_type);
-		
-		/* ICQ 6.x seems to use an available message for all statuses so set one */
-		if (od->icq) 
-		{
-			status_text = purple_markup_strip_html(htmlaway);
-			/* If the status_text is longer than 251 characters then truncate it */
-			if (strlen(status_text) > MAXAVAILMSGLEN)
-			{
-				char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]);
-				strcpy(tmp, "...");
-			}
-			aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, NULL);
-		}
-		g_free(status_text);
-
-		/* Set a proper away message for icq too so that they work for old third party clients */
-		
-		away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding);
-
-		if (awaylen > od->rights.maxawaymsglen)
-		{
-			gchar *errstr;
-
-			errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte "
-									 "has been exceeded.  It has been truncated for you.",
-									 "The maximum away message length of %d bytes "
-									 "has been exceeded.  It has been truncated for you.",
-									 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
-			purple_notify_warning(gc, NULL, _("Away message too long."), errstr);
-			g_free(errstr);
-		}
-	}
-
-	if (setstatus)
 		oscar_set_extendedstatus(gc);
-
-	aim_locate_setprofile(od, info_encoding, info, MIN(infolen, od->rights.maxsiglen),
-									away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
-	g_free(info);
-	g_free(away);
+	}
 }
 
 static void
-oscar_set_status_icq(PurpleAccount *account, PurpleStatus *status)
+oscar_set_status_icq(PurpleAccount *account)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
-	OscarData *od = NULL;
-
-	if (gc)
-		od = (OscarData *)gc->proto_data;
-	if (!od)
-		return;
-
-	if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_INVISIBLE)
-		account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
-	else
-		account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
-
-	if ((od->ssi.received_data) && (aim_ssi_getpermdeny(od->ssi.local) != account->perm_deny))
-		aim_ssi_setpermdeny(od, account->perm_deny, 0xffffffff);
-
+
+	/* Our permit/deny setting affects our invisibility */
+	oscar_set_permit_deny(gc);
+
+	/*
+	 * TODO: I guess we should probably wait and do this after we get
+	 * confirmation from the above SSI call?  Right now other people
+	 * see our status blip to "invisible" before we appear offline.
+	 */
 	oscar_set_extendedstatus(gc);
 }
 
@@ -4884,14 +4881,14 @@
 	oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
 
 	/* Set the ICQ status for ICQ accounts only */
-	if (aim_snvalid_icq(purple_account_get_username(account)))
-		oscar_set_status_icq(account, status);
+	if (oscar_util_valid_name_icq(purple_account_get_username(account)))
+		oscar_set_status_icq(account);
 }
 
 #ifdef CRAZY_WARN
 void
 oscar_warn(PurpleConnection *gc, const char *name, gboolean anonymous) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	aim_im_warn(od, od->conn, name, anonymous ? AIM_WARN_ANON : 0);
 }
 #endif
@@ -4900,14 +4897,17 @@
 oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
 	OscarData *od;
 	PurpleAccount *account;
-
-	od = (OscarData *)gc->proto_data;
+	const char *bname, *gname;
+
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
-
-	if (!aim_snvalid(buddy->name)) {
+	bname = purple_buddy_get_name(buddy);
+	gname = purple_group_get_name(group);
+
+	if (!oscar_util_valid_name(bname)) {
 		gchar *buf;
-		buf = g_strdup_printf(_("Could not add the buddy %s because the username is invalid.  Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), buddy->name);
-		if (!purple_conv_present_error(buddy->name, account, buf))
+		buf = g_strdup_printf(_("Could not add the buddy %s because the username is invalid.  Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), bname);
+		if (!purple_conv_present_error(bname, account, buf))
 			purple_notify_error(gc, NULL, _("Unable to Add"), buf);
 		g_free(buf);
 
@@ -4917,39 +4917,40 @@
 		return;
 	}
 
-	if ((od->ssi.received_data) && !(aim_ssi_itemlist_finditem(od->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))) {
+	if ((od->ssi.received_data) && !(aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY))) {
 		purple_debug_info("oscar",
-				   "ssi: adding buddy %s to group %s\n", buddy->name, group->name);
-		aim_ssi_addbuddy(od, buddy->name, group->name, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
+				   "ssi: adding buddy %s to group %s\n", bname, gname);
+		aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
 
 		/* Mobile users should always be online */
-		if (buddy->name[0] == '+') {
+		if (bname[0] == '+') {
 			purple_prpl_got_user_status(account,
-					purple_buddy_get_name(buddy),
-					OSCAR_STATUS_ID_AVAILABLE, NULL);
+					bname, OSCAR_STATUS_ID_AVAILABLE, NULL);
 			purple_prpl_got_user_status(account,
-					purple_buddy_get_name(buddy),
-					OSCAR_STATUS_ID_MOBILE, NULL);
+					bname, OSCAR_STATUS_ID_MOBILE, NULL);
 		}
 	}
 
 	/* XXX - Should this be done from AIM accounts, as well? */
 	if (od->icq)
-		aim_icq_getalias(od, buddy->name);
+		aim_icq_getalias(od, bname);
 }
 
 void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	if (od->ssi.received_data) {
+		const char *gname = purple_group_get_name(group);
+		const char *bname = purple_buddy_get_name(buddy);
 		purple_debug_info("oscar",
-				   "ssi: deleting buddy %s from group %s\n", buddy->name, group->name);
-		aim_ssi_delbuddy(od, buddy->name, group->name);
+				   "ssi: deleting buddy %s from group %s\n", bname, gname);
+		aim_ssi_delbuddy(od, bname, gname);
 	}
 }
 
 void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
+
 	if (od->ssi.received_data && strcmp(old_group, new_group)) {
 		purple_debug_info("oscar",
 				   "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group);
@@ -4958,7 +4959,8 @@
 }
 
 void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
+
 	if (od->ssi.received_data) {
 		char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
 		if (gname) {
@@ -4973,10 +4975,11 @@
  * FYI, the OSCAR SSI code removes empty groups automatically.
  */
 void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	if (od->ssi.received_data) {
-		if (aim_ssi_itemlist_finditem(od->ssi.local, group->name, NULL, AIM_SSI_TYPE_GROUP)) {
+		const char *gname = purple_group_get_name(group);
+		if (aim_ssi_itemlist_finditem(od->ssi.local, gname, NULL, AIM_SSI_TYPE_GROUP)) {
 			GList *cur, *groups = NULL;
 			PurpleAccount *account = purple_connection_get_account(gc);
 
@@ -4986,25 +4989,25 @@
 				/* node is PurpleBuddy, parent is a PurpleContact.
 				 * We must go two levels up to get the Group */
 				groups = g_list_append(groups,
-						node->parent->parent);
+						purple_buddy_get_group((PurpleBuddy*)node));
 			}
 
 			purple_account_remove_buddies(account, moved_buddies, groups);
 			purple_account_add_buddies(account, moved_buddies);
 			g_list_free(groups);
 			purple_debug_info("oscar",
-					   "ssi: moved all buddies from group %s to %s\n", old_name, group->name);
+					   "ssi: moved all buddies from group %s to %s\n", old_name, gname);
 		} else {
-			aim_ssi_rename_group(od, old_name, group->name);
+			aim_ssi_rename_group(od, old_name, gname);
 			purple_debug_info("oscar",
-					   "ssi: renamed group %s to %s\n", old_name, group->name);
+					   "ssi: renamed group %s to %s\n", old_name, gname);
 		}
 	}
 }
 
 void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group)
 {
-	aim_ssi_delgroup(gc->proto_data, group->name);
+	aim_ssi_delgroup(purple_connection_get_protocol_data(gc), purple_group_get_name(group));
 }
 
 static gboolean purple_ssi_rerequestdata(gpointer data) {
@@ -5035,7 +5038,7 @@
 		return 1;
 	}
 
-	oscar_set_extendedstatus(gc);
+	oscar_set_status_icq(purple_connection_get_account(gc));
 
 	return 1;
 }
@@ -5085,7 +5088,7 @@
 	guint32 timestamp;
 
 	gc = od->gc;
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	va_start(ap, fr);
@@ -5113,33 +5116,44 @@
 		/* Buddies */
 		cur = NULL;
 		if ((blist = purple_get_blist()) != NULL) {
-			for (gnode = blist->root; gnode; gnode = gnode->next) {
+			for (gnode = purple_blist_get_root(); gnode;
+					gnode = purple_blist_node_get_sibling_next(gnode)) {
+				const char *gname;
 				if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 					continue;
 				g = (PurpleGroup *)gnode;
-				for (cnode = gnode->child; cnode; cnode = cnode->next) {
+				gname = purple_group_get_name(g);
+				for (cnode = purple_blist_node_get_first_child(gnode);
+						cnode;
+						cnode = purple_blist_node_get_sibling_next(cnode)) {
 					if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 						continue;
-					for (bnode = cnode->child; bnode; bnode = bnode->next) {
+					for (bnode = purple_blist_node_get_first_child(cnode);
+							bnode;
+							bnode = purple_blist_node_get_sibling_next(bnode)) {
+						const char *bname;
 						if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 							continue;
 						b = (PurpleBuddy *)bnode;
-						if (b->account == gc->account) {
-							if (aim_ssi_itemlist_exists(od->ssi.local, b->name)) {
+						bname = purple_buddy_get_name(b);
+						if (purple_buddy_get_account(b) == account) {
+							if (aim_ssi_itemlist_exists(od->ssi.local, bname)) {
 								/* If the buddy is an ICQ user then load his nickname */
 								const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick");
 								char *alias;
+								const char *balias;
 								if (servernick)
-									serv_got_alias(gc, b->name, servernick);
+									serv_got_alias(gc, bname, servernick);
 
 								/* Store local alias on server */
-								alias = aim_ssi_getalias(od->ssi.local, g->name, b->name);
-								if (!alias && b->alias && strlen(b->alias))
-									aim_ssi_aliasbuddy(od, g->name, b->name, b->alias);
+								alias = aim_ssi_getalias(od->ssi.local, gname, bname);
+								balias = purple_buddy_get_local_buddy_alias(b);
+								if (!alias && balias && *balias)
+									aim_ssi_aliasbuddy(od, gname, bname, balias);
 								g_free(alias);
 							} else {
 								purple_debug_info("oscar",
-										"ssi: removing buddy %s from local list\n", b->name);
+										"ssi: removing buddy %s from local list\n", bname);
 								/* We can't actually remove now because it will screw up our looping */
 								cur = g_slist_prepend(cur, b);
 							}
@@ -5156,8 +5170,8 @@
 		}
 
 		/* Permit list */
-		if (gc->account->permit) {
-			next = gc->account->permit;
+		if (account->permit) {
+			next = account->permit;
 			while (next != NULL) {
 				cur = next;
 				next = next->next;
@@ -5170,8 +5184,8 @@
 		}
 
 		/* Deny list */
-		if (gc->account->deny) {
-			next = gc->account->deny;
+		if (account->deny) {
+			next = account->deny;
 			while (next != NULL) {
 				cur = next;
 				next = next->next;
@@ -5215,7 +5229,7 @@
 						if (g_utf8_validate(gname, -1, NULL))
 							gname_utf8 = g_strdup(gname);
 						else
-							gname_utf8 = oscar_utf8_try_convert(gc->account, gname);
+							gname_utf8 = oscar_utf8_try_convert(account, gname);
 					} else
 						gname_utf8 = NULL;
 
@@ -5235,18 +5249,18 @@
 					} else
 						alias_utf8 = NULL;
 
-					b = purple_find_buddy_in_group(gc->account, curitem->name, g);
+					b = purple_find_buddy_in_group(account, curitem->name, g);
 					if (b) {
 						/* Get server stored alias */
 						purple_blist_alias_buddy(b, alias_utf8);
 					} else {
-						b = purple_buddy_new(gc->account, curitem->name, alias_utf8);
+						b = purple_buddy_new(account, curitem->name, alias_utf8);
 
 						purple_debug_info("oscar",
-								   "ssi: adding buddy %s to group %s to local list\n", curitem->name, g->name);
+								   "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname);
 						purple_blist_add_buddy(b, NULL, g, NULL);
 					}
-					if (!aim_sncmp(curitem->name, account->username)) {
+					if (!oscar_util_name_compare(curitem->name, purple_account_get_username(account))) {
 						char *comment = aim_ssi_getcomment(od->ssi.local, gname, curitem->name);
 						if (comment != NULL)
 						{
@@ -5256,7 +5270,7 @@
 					}
 
 					/* Mobile users should always be online */
-					if (b->name[0] == '+') {
+					if (curitem->name[0] == '+') {
 						purple_prpl_got_user_status(account,
 								purple_buddy_get_name(b),
 								OSCAR_STATUS_ID_AVAILABLE, NULL);
@@ -5279,7 +5293,7 @@
 					if (g_utf8_validate(gname, -1, NULL))
 						gname_utf8 = g_strdup(gname);
 					else
-						gname_utf8 = oscar_utf8_try_convert(gc->account, gname);
+						gname_utf8 = oscar_utf8_try_convert(account, gname);
 				} else
 					gname_utf8 = NULL;
 
@@ -5294,7 +5308,7 @@
 				if (curitem->name) {
 					/* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */
 					GSList *list;
-					for (list=account->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
+					for (list=account->permit; (list && oscar_util_name_compare(curitem->name, list->data)); list=list->next);
 					if (!list) {
 						purple_debug_info("oscar",
 								   "ssi: adding permit buddy %s to local list\n", curitem->name);
@@ -5306,7 +5320,7 @@
 			case 0x0003: { /* Deny buddy */
 				if (curitem->name) {
 					GSList *list;
-					for (list=account->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
+					for (list=account->deny; (list && oscar_util_name_compare(curitem->name, list->data)); list=list->next);
 					if (!list) {
 						purple_debug_info("oscar",
 								   "ssi: adding deny buddy %s to local list\n", curitem->name);
@@ -5316,15 +5330,19 @@
 			} break;
 
 			case 0x0004: { /* Permit/deny setting */
-				if (curitem->data) {
-					guint8 permdeny;
-					if ((permdeny = aim_ssi_getpermdeny(od->ssi.local)) && (permdeny != account->perm_deny)) {
+				/*
+				 * We don't inherit the permit/deny setting from the server
+				 * for ICQ because, for ICQ, this setting controls who can
+				 * see your online status when you are invisible.  Thus it is
+				 * a part of your status and not really related to blocking.
+				 */
+				if (!od->icq && curitem->data) {
+					guint8 perm_deny = aim_ssi_getpermdeny(od->ssi.local);
+					if (perm_deny != 0 && perm_deny != account->perm_deny)
+					{
 						purple_debug_info("oscar",
-								   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny);
-						account->perm_deny = permdeny;
-						if (od->icq && account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) {
-							purple_presence_set_status_active(account->presence, OSCAR_STATUS_ID_INVISIBLE, TRUE);
-						}
+								   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, perm_deny);
+						account->perm_deny = perm_deny;
 					}
 				}
 			} break;
@@ -5335,7 +5353,7 @@
 		} /* End of switch on curitem->type */
 	} /* End of for loop */
 
-	oscar_set_extendedstatus(gc);
+	oscar_set_status_icq(account);
 
 	/* Activate SSI */
 	/* Sending the enable causes other people to be able to see you, and you to see them */
@@ -5469,13 +5487,11 @@
 		purple_blist_add_buddy(b, NULL, g, NULL);
 
 		/* Mobile users should always be online */
-		if (b->name[0] == '+') {
+		if (name[0] == '+') {
 			purple_prpl_got_user_status(account,
-					purple_buddy_get_name(b),
-					OSCAR_STATUS_ID_AVAILABLE, NULL);
+					name, OSCAR_STATUS_ID_AVAILABLE, NULL);
 			purple_prpl_got_user_status(account,
-					purple_buddy_get_name(b),
-					OSCAR_STATUS_ID_MOBILE, NULL);
+					name, OSCAR_STATUS_ID_MOBILE, NULL);
 		}
 
 	}
@@ -5498,36 +5514,36 @@
 static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	PurpleConnection *gc = od->gc;
 	va_list ap;
-	char *sn, *msg;
+	char *bn, *msg;
 	gchar *dialog_msg, *nombre;
 	struct name_data *data;
 	PurpleBuddy *buddy;
 
 	va_start(ap, fr);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
 	purple_debug_info("oscar",
-			   "ssi: %s has given you permission to add him to your buddy list\n", sn);
-
-	buddy = purple_find_buddy(gc->account, sn);
+			   "ssi: %s has given you permission to add him to your buddy list\n", bn);
+
+	buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
 	if (buddy && (purple_buddy_get_alias_only(buddy)))
-		nombre = g_strdup_printf("%s (%s)", sn, purple_buddy_get_alias_only(buddy));
+		nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
 	else
-		nombre = g_strdup(sn);
+		nombre = g_strdup(bn);
 
 	dialog_msg = g_strdup_printf(_("The user %s has given you permission to add him or her to your buddy list.  Do you want to add this user?"), nombre);
 	g_free(nombre);
 
 	data = g_new(struct name_data, 1);
 	data->gc = gc;
-	data->name = g_strdup(sn);
+	data->name = g_strdup(bn);
 	data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL);
 
 	purple_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
 						PURPLE_DEFAULT_ACTION_NONE,
-						purple_connection_get_account(gc), sn, NULL,
+						purple_connection_get_account(gc), bn, NULL,
 						data,
 						G_CALLBACK(purple_icq_buddyadd),
 						G_CALLBACK(oscar_free_name_data));
@@ -5539,7 +5555,7 @@
 static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	PurpleConnection *gc = od->gc;
 	va_list ap;
-	char *sn;
+	char *bn;
 	char *msg;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	gchar *reason = NULL;
@@ -5547,24 +5563,24 @@
 	PurpleBuddy *buddy;
 
 	va_start(ap, fr);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
 	purple_debug_info("oscar",
-			   "ssi: received authorization request from %s\n", sn);
-
-	buddy = purple_find_buddy(account, sn);
+			   "ssi: received authorization request from %s\n", bn);
+
+	buddy = purple_find_buddy(account, bn);
 
 	if (msg != NULL)
-		reason = purple_plugin_oscar_decode_im_part(account, sn, AIM_CHARSET_CUSTOM, 0x0000, msg, strlen(msg));
+		reason = purple_plugin_oscar_decode_im_part(account, bn, AIM_CHARSET_CUSTOM, 0x0000, msg, strlen(msg));
 
 	data = g_new(struct name_data, 1);
 	data->gc = gc;
-	data->name = g_strdup(sn);
+	data->name = g_strdup(bn);
 	data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL);
 
-	purple_account_request_authorization(account, sn, NULL,
+	purple_account_request_authorization(account, bn, NULL,
 			(buddy ? purple_buddy_get_alias_only(buddy) : NULL),
 			reason, buddy != NULL, purple_auth_grant,
 			purple_auth_dontgrant_msgprompt, data);
@@ -5576,25 +5592,25 @@
 static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	PurpleConnection *gc = od->gc;
 	va_list ap;
-	char *sn, *msg;
+	char *bn, *msg;
 	gchar *dialog_msg, *nombre;
 	guint8 reply;
 	PurpleBuddy *buddy;
 
 	va_start(ap, fr);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	reply = (guint8)va_arg(ap, int);
 	msg = va_arg(ap, char *);
 	va_end(ap);
 
 	purple_debug_info("oscar",
-			   "ssi: received authorization reply from %s.  Reply is 0x%04hhx\n", sn, reply);
-
-	buddy = purple_find_buddy(gc->account, sn);
+			   "ssi: received authorization reply from %s.  Reply is 0x%04hhx\n", bn, reply);
+
+	buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
 	if (buddy && (purple_buddy_get_alias_only(buddy)))
-		nombre = g_strdup_printf("%s (%s)", sn, purple_buddy_get_alias_only(buddy));
+		nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
 	else
-		nombre = g_strdup(sn);
+		nombre = g_strdup(bn);
 
 	if (reply) {
 		/* Granted */
@@ -5613,17 +5629,19 @@
 
 static int purple_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
 	PurpleConnection *gc = od->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
 	va_list ap;
-	char *sn;
+	char *bn;
 	PurpleBuddy *buddy;
 
 	va_start(ap, fr);
-	sn = va_arg(ap, char *);
+	bn = va_arg(ap, char *);
 	va_end(ap);
 
-	buddy = purple_find_buddy(gc->account, sn);
-	purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", sn);
-	purple_account_notify_added(gc->account, sn, NULL, (buddy ? purple_buddy_get_alias_only(buddy) : NULL), NULL);
+	buddy = purple_find_buddy(account, bn);
+	purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", bn);
+	purple_account_notify_added(account, bn, NULL,
+			(buddy ? purple_buddy_get_alias_only(buddy) : NULL), NULL);
 
 	return 1;
 }
@@ -5672,7 +5690,7 @@
 void
 oscar_join_chat(PurpleConnection *gc, GHashTable *data)
 {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	FlapConnection *conn;
 	char *name, *exchange;
 	int exchange_int;
@@ -5707,7 +5725,7 @@
 void
 oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
 {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	struct chat_connection *ccon = find_oscar_chat(gc, id);
 
 	if (ccon == NULL)
@@ -5727,14 +5745,16 @@
 
 	g_return_if_fail(conv != NULL);
 
-	purple_debug_info("oscar", "Leaving chat room %s\n", conv->name);
+	purple_debug_info("oscar", "Leaving chat room %s\n",
+			purple_conversation_get_name(conv));
 
 	cc = find_oscar_chat(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
 	oscar_chat_kill(gc, cc);
 }
 
-int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) {
-	OscarData *od = (OscarData *)gc->proto_data;
+int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
+{
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	PurpleConversation *conv = NULL;
 	struct chat_connection *c = NULL;
 	char *buf, *buf2, *buf3;
@@ -5801,30 +5821,32 @@
 
 const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b)
 {
-	if ((b == NULL) || (b->name == NULL) || aim_snvalid_sms(b->name))
+	const char *name = b ? purple_buddy_get_name(b) : NULL;
+	if ((b == NULL) || (name == NULL) || oscar_util_valid_name_sms(name))
 	{
-		if (a == NULL || aim_snvalid_icq(purple_account_get_username(a)))
+		if (a == NULL || oscar_util_valid_name_icq(purple_account_get_username(a)))
 			return "icq";
 		else
 			return "aim";
 	}
 
-	if (aim_snvalid_icq(b->name))
+	if (oscar_util_valid_name_icq(name))
 		return "icq";
 	return "aim";
 }
 
 const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b)
 {
-	if ((b == NULL) || (b->name == NULL) || aim_snvalid_sms(b->name))
+	const char *name = b ? purple_buddy_get_name(b) : NULL;
+	if ((b == NULL) || (name == NULL) || oscar_util_valid_name_sms(name))
 	{
-		if (a != NULL && aim_snvalid_icq(purple_account_get_username(a)))
+		if (a != NULL && oscar_util_valid_name_icq(purple_account_get_username(a)))
 			return "icq";
 		else
 			return "aim";
 	}
 
-	if (aim_snvalid_icq(b->name))
+	if (oscar_util_valid_name_icq(name))
 		return "icq";
 	return "aim";
 }
@@ -5838,14 +5860,16 @@
 	PurpleStatus *status;
 	const char *status_id;
 	aim_userinfo_t *userinfo = NULL;
-
-	account = b->account;
+	const char *name;
+
+	account = purple_buddy_get_account(b);
+	name = purple_buddy_get_name(b);
 	if (account != NULL)
-		gc = account->gc;
+		gc = purple_account_get_connection(account);
 	if (gc != NULL)
-		od = gc->proto_data;
+		od = purple_connection_get_protocol_data(gc);
 	if (od != NULL)
-		userinfo = aim_locate_finduserinfo(od, b->name);
+		userinfo = aim_locate_finduserinfo(od, name);
 
 	presence = purple_buddy_get_presence(b);
 	status = purple_presence_get_active_status(presence);
@@ -5853,9 +5877,9 @@
 
 	if (purple_presence_is_online(presence) == FALSE) {
 		char *gname;
-		if ((b->name) && (od) && (od->ssi.received_data) &&
-			(gname = aim_ssi_itemlist_findparentname(od->ssi.local, b->name)) &&
-			(aim_ssi_waitingforauth(od->ssi.local, gname, b->name))) {
+		if ((name) && (od) && (od->ssi.received_data) &&
+			(gname = aim_ssi_itemlist_findparentname(od->ssi.local, name)) &&
+			(aim_ssi_waitingforauth(od->ssi.local, gname, name))) {
 			return "not-authorized";
 		}
 	}
@@ -5878,15 +5902,17 @@
 void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	PurpleConnection *gc;
+	PurpleAccount *account;
 	OscarData *od;
 	aim_userinfo_t *userinfo;
 
 	if (!PURPLE_BUDDY_IS_ONLINE(b))
 		return;
 
-	gc = b->account->gc;
-	od = gc->proto_data;
-	userinfo = aim_locate_finduserinfo(od, b->name);
+	account = purple_buddy_get_account(b);
+	gc = purple_account_get_connection(account);
+	od = purple_connection_get_protocol_data(gc);
+	userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
 
 	oscar_user_info_append_status(gc, user_info, b, userinfo, /* strip_html_tags */ TRUE);
 
@@ -5907,15 +5933,16 @@
 
 	gc = purple_account_get_connection(purple_buddy_get_account(b));
 	account = purple_connection_get_account(gc);
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	presence = purple_buddy_get_presence(b);
 	status = purple_presence_get_active_status(presence);
 	id = purple_status_get_id(status);
 
 	if ((od != NULL) && !purple_presence_is_online(presence))
 	{
-		char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, b->name);
-		if (aim_ssi_waitingforauth(od->ssi.local, gname, b->name))
+		const char *name = purple_buddy_get_name(b);
+		char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
+		if (aim_ssi_waitingforauth(od->ssi.local, gname, name))
 			ret = g_strdup(_("Not Authorized"));
 		else
 			ret = g_strdup(_("Offline"));
@@ -6006,55 +6033,62 @@
 
 void oscar_set_permit_deny(PurpleConnection *gc) {
 	PurpleAccount *account = purple_connection_get_account(gc);
-	OscarData *od = (OscarData *)gc->proto_data;
-
-	if (od->ssi.received_data) {
-		switch (account->perm_deny) {
-			case PURPLE_PRIVACY_ALLOW_ALL:
-				aim_ssi_setpermdeny(od, 0x01, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
-				aim_ssi_setpermdeny(od, 0x05, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_ALLOW_USERS:
-				aim_ssi_setpermdeny(od, 0x03, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_DENY_ALL:
-				aim_ssi_setpermdeny(od, 0x02, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_DENY_USERS:
-				aim_ssi_setpermdeny(od, 0x04, 0xffffffff);
-				break;
-			default:
-				aim_ssi_setpermdeny(od, 0x01, 0xffffffff);
-				break;
-		}
-	}
+	OscarData *od = purple_connection_get_protocol_data(gc);
+	PurplePrivacyType perm_deny;
+
+	/*
+	 * For ICQ the permit/deny setting controls who you can see you
+	 * online when you set your status to "invisible."  If we're ICQ
+	 * and we're invisible then we need to use one of
+	 * PURPLE_PRIVACY_ALLOW_USERS or PURPLE_PRIVACY_ALLOW_BUDDYLIST or
+	 * PURPLE_PRIVACY_DENY_USERS if we actually want to be invisible
+	 * to anyone.
+	 *
+	 * These three permit/deny settings correspond to:
+	 * 1. Invisible to everyone except the people on my "permit" list
+	 * 2. Invisible to everyone except the people on my buddy list
+	 * 3. Invisible only to the people on my "deny" list
+	 *
+	 * It would be nice to allow cases 2 and 3, but our UI doesn't have
+	 * a nice way to do it.  For now we just force case 1.
+	 */
+	if (od->icq && purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE))
+		perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
+	else
+		perm_deny = account->perm_deny;
+
+	if (od->ssi.received_data)
+		/*
+		 * Conveniently there is a one-to-one mapping between the
+		 * values of libpurple's PurplePrivacyType and the values used
+		 * by the oscar protocol.
+		 */
+		aim_ssi_setpermdeny(od, perm_deny, 0xffffffff);
 }
 
 void oscar_add_permit(PurpleConnection *gc, const char *who) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	purple_debug_info("oscar", "ssi: About to add a permit\n");
 	if (od->ssi.received_data)
 		aim_ssi_addpermit(od, who);
 }
 
 void oscar_add_deny(PurpleConnection *gc, const char *who) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	purple_debug_info("oscar", "ssi: About to add a deny\n");
 	if (od->ssi.received_data)
 		aim_ssi_adddeny(od, who);
 }
 
 void oscar_rem_permit(PurpleConnection *gc, const char *who) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	purple_debug_info("oscar", "ssi: About to delete a permit\n");
 	if (od->ssi.received_data)
 		aim_ssi_delpermit(od, who);
 }
 
 void oscar_rem_deny(PurpleConnection *gc, const char *who) {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	purple_debug_info("oscar", "ssi: About to delete a deny\n");
 	if (od->ssi.received_data)
 		aim_ssi_deldeny(od, who);
@@ -6070,7 +6104,7 @@
 	g_return_val_if_fail(account != NULL, NULL);
 
 	/* Used to flag some statuses as "user settable" or not */
-	is_icq = aim_snvalid_icq(purple_account_get_username(account));
+	is_icq = oscar_util_valid_name_icq(purple_account_get_username(account));
 
 	/* Common status types */
 	/* Really the available message should only be settable for AIM accounts */
@@ -6136,24 +6170,33 @@
 }
 
 static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
-	PurpleConnection *gc = data->gc;
-	OscarData *od = gc->proto_data;
+	PurpleConnection *gc;
+	PurpleAccount *account;
+	OscarData *od;
 	PurpleBuddy *b;
 	PurpleGroup *g;
-
-	if (!(b = purple_find_buddy(purple_connection_get_account(data->gc), data->name))) {
+	const char *username;
+
+	gc = data->gc;
+	od = purple_connection_get_protocol_data(gc);
+	account = purple_connection_get_account(gc);
+
+	b = purple_find_buddy(account, data->name);
+	if (b == NULL) {
 		oscar_free_name_data(data);
 		return;
 	}
 
-	if (!(g = purple_buddy_get_group(b))) {
+	g = purple_buddy_get_group(b);
+	if (g == NULL) {
 		oscar_free_name_data(data);
 		return;
 	}
 
-	aim_ssi_editcomment(od, g->name, data->name, text);
-
-	if (!aim_sncmp(data->name, gc->account->username))
+	aim_ssi_editcomment(od, purple_group_get_name(g), data->name, text);
+
+	username = purple_account_get_username(account);
+	if (!oscar_util_name_compare(data->name, username))
 		purple_check_comment(od, text);
 
 	oscar_free_name_data(data);
@@ -6169,23 +6212,27 @@
 	char *comment;
 	gchar *comment_utf8;
 	gchar *title;
+	PurpleAccount *account;
+	const char *name;
 
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-	od = gc->proto_data;
+	name = purple_buddy_get_name(buddy);
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
+	od = purple_connection_get_protocol_data(gc);
 
 	if (!(g = purple_buddy_get_group(buddy)))
 		return;
 
 	data = g_new(struct name_data, 1);
 
-	comment = aim_ssi_getcomment(od->ssi.local, g->name, buddy->name);
-	comment_utf8 = comment ? oscar_utf8_try_convert(gc->account, comment) : NULL;
+	comment = aim_ssi_getcomment(od->ssi.local, purple_group_get_name(g), name);
+	comment_utf8 = comment ? oscar_utf8_try_convert(account, comment) : NULL;
 
 	data->gc = gc;
-	data->name = g_strdup(purple_buddy_get_name(buddy));
+	data->name = g_strdup(name);
 	data->nick = g_strdup(purple_buddy_get_alias_only(buddy));
 
 	title = g_strdup_printf(_("Buddy Comment for %s"), data->name);
@@ -6193,7 +6240,7 @@
 					   comment_utf8, TRUE, FALSE, NULL,
 					   _("_OK"), G_CALLBACK(oscar_ssi_editcomment),
 					   _("_Cancel"), G_CALLBACK(oscar_free_name_data),
-					   purple_connection_get_account(gc), data->name, NULL,
+					   account, data->name, NULL,
 					   data);
 	g_free(title);
 
@@ -6225,26 +6272,28 @@
 	PurpleConnection *gc;
 	gchar *buf;
 	struct oscar_ask_directim_data *data;
+	PurpleAccount *account;
 
 	node = object;
 
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *)node;
-	gc = purple_account_get_connection(buddy->account);
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
 
 	data = g_new0(struct oscar_ask_directim_data, 1);
-	data->who = g_strdup(buddy->name);
-	data->od = gc->proto_data;
+	data->who = g_strdup(purple_buddy_get_name(buddy));
+	data->od = purple_connection_get_protocol_data(gc);
 	buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
-			buddy->name);
+			data->who);
 
 	purple_request_action(gc, NULL, buf,
 			_("Because this reveals your IP address, it "
 			  "may be considered a security risk.  Do you "
 			  "wish to continue?"),
 			0, /* Default action is "connect" */
-			purple_connection_get_account(gc), data->who, NULL,
+			account, data->who, NULL,
 			data, 2,
 			_("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
 			_("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
@@ -6260,9 +6309,10 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *)node;
-	gc = purple_account_get_connection(buddy->account);
-
-	aim_locate_getinfoshort(gc->proto_data, purple_buddy_get_name(buddy), 0x00000003);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+
+	aim_locate_getinfoshort(purple_connection_get_protocol_data(gc),
+			purple_buddy_get_name(buddy), 0x00000003);
 }
 
 static GList *
@@ -6273,13 +6323,16 @@
 	GList *menu;
 	PurpleMenuAction *act;
 	aim_userinfo_t *userinfo;
-
-	gc = purple_account_get_connection(buddy->account);
-	od = gc->proto_data;
-	userinfo = aim_locate_finduserinfo(od, buddy->name);
+	PurpleAccount *account;
+	const char *bname = purple_buddy_get_name(buddy);
+
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
+	od = purple_connection_get_protocol_data(gc);
+	userinfo = aim_locate_finduserinfo(od, bname);
 	menu = NULL;
 
-	if (od->icq && aim_snvalid_icq(purple_buddy_get_name(buddy)))
+	if (od->icq && oscar_util_valid_name_icq(bname))
 	{
 		act = purple_menu_action_new(_("Get AIM Info"),
 								   PURPLE_CALLBACK(oscar_get_aim_info_cb),
@@ -6307,7 +6360,7 @@
 #endif
 
 	if (userinfo &&
-		aim_sncmp(purple_account_get_username(buddy->account), buddy->name) &&
+		oscar_util_name_compare(purple_account_get_username(account), bname) &&
 		PURPLE_BUDDY_IS_ONLINE(buddy))
 	{
 		if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM)
@@ -6331,8 +6384,8 @@
 	if (od->ssi.received_data && purple_buddy_get_group(buddy) != NULL)
 	{
 		char *gname;
-		gname = aim_ssi_itemlist_findparentname(od->ssi.local, buddy->name);
-		if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, buddy->name))
+		gname = aim_ssi_itemlist_findparentname(od->ssi.local, bname);
+		if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, bname))
 		{
 			act = purple_menu_action_new(_("Re-request Authorization"),
 			                           PURPLE_CALLBACK(purple_auth_sendrequest_menu),
@@ -6358,7 +6411,7 @@
 static void
 oscar_icq_privacy_opts(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleRequestField *f;
 	gboolean auth, web_aware;
@@ -6409,13 +6462,13 @@
 						gc);
 }
 
-static void oscar_format_screenname(PurpleConnection *gc, const char *nick) {
-	OscarData *od = gc->proto_data;
-	if (!aim_sncmp(purple_account_get_username(purple_connection_get_account(gc)), nick)) {
+static void oscar_format_username(PurpleConnection *gc, const char *nick) {
+	OscarData *od = purple_connection_get_protocol_data(gc);
+	if (!oscar_util_name_compare(purple_account_get_username(purple_connection_get_account(gc)), nick)) {
 		if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) {
 			od->setnick = TRUE;
-			g_free(od->newsn);
-			od->newsn = g_strdup(nick);
+			g_free(od->newformatting);
+			od->newformatting = g_strdup(nick);
 			aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
 		} else {
 			aim_admin_setnick(od, flap_connection_getbytype(od, SNAC_FAMILY_ADMIN), nick);
@@ -6433,7 +6486,7 @@
 	FlapConnection *conn;
 
 	gc = (PurpleConnection *)action->context;
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 
 	conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
 	if (conn != NULL) {
@@ -6447,7 +6500,7 @@
 static void oscar_show_email(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
 
 	if (conn) {
@@ -6460,7 +6513,7 @@
 
 static void oscar_change_email(PurpleConnection *gc, const char *email)
 {
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
 
 	if (conn) {
@@ -6486,29 +6539,40 @@
 static void oscar_show_awaitingauth(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	gchar *nombre, *text, *tmp;
 	PurpleBlistNode *gnode, *cnode, *bnode;
+	PurpleAccount *account;
 	int num=0;
 
 	text = g_strdup("");
-
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+	account = purple_connection_get_account(gc);
+
+	for (gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
 		PurpleGroup *group = (PurpleGroup *)gnode;
+		const char *gname;
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for (cnode = gnode->child; cnode; cnode = cnode->next) {
+		gname = purple_group_get_name(group);
+		for (cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for (bnode = cnode->child; bnode; bnode = bnode->next) {
+			for (bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode)) {
 				PurpleBuddy *buddy = (PurpleBuddy *)bnode;
+				const char *bname;
 				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
-				if (buddy->account == gc->account && aim_ssi_waitingforauth(od->ssi.local, group->name, buddy->name)) {
+				bname = purple_buddy_get_name(buddy);
+				if (purple_buddy_get_account(buddy) == account && aim_ssi_waitingforauth(od->ssi.local, gname, bname)) {
 					if (purple_buddy_get_alias_only(buddy))
-						nombre = g_strdup_printf(" %s (%s)", buddy->name, purple_buddy_get_alias_only(buddy));
+						nombre = g_strdup_printf(" %s (%s)", bname, purple_buddy_get_alias_only(buddy));
 					else
-						nombre = g_strdup_printf(" %s", buddy->name);
+						nombre = g_strdup_printf(" %s", bname);
 					tmp = g_strdup_printf("%s%s<br>", text, nombre);
 					g_free(text);
 					text = tmp;
@@ -6534,7 +6598,7 @@
 
 static void search_by_email_cb(PurpleConnection *gc, const char *email)
 {
-	OscarData *od = (OscarData *)gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	aim_search_address(od, email);
 }
@@ -6574,7 +6638,7 @@
 static void oscar_show_chpassurl(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	gchar *substituted = purple_strreplace(od->authinfo->chpassurl, "%s", purple_account_get_username(purple_connection_get_account(gc)));
 	purple_notify_uri(gc, substituted);
 	g_free(substituted);
@@ -6588,7 +6652,7 @@
 
 void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	if (img == NULL) {
 		aim_ssi_delicon(od);
@@ -6617,7 +6681,7 @@
 	OscarData *od;
 	PurpleAccount *account;
 
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	if (od != NULL)
@@ -6631,7 +6695,7 @@
 		 */
 		if (((userinfo == NULL) ||
 			(userinfo->capabilities & OSCAR_CAPABILITY_SENDFILE)) &&
-			aim_sncmp(who, purple_account_get_username(account)))
+			oscar_util_name_compare(who, purple_account_get_username(account)))
 		{
 			return TRUE;
 		}
@@ -6648,7 +6712,7 @@
 	PurpleAccount *account;
 	PeerConnection *conn;
 
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	xfer = purple_xfer_new(account, PURPLE_XFER_SEND, who);
@@ -6692,7 +6756,7 @@
 oscar_actions(PurplePlugin *plugin, gpointer context)
 {
 	PurpleConnection *gc = (PurpleConnection *) context;
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 	GList *menu = NULL;
 	PurplePluginAction *act;
 
@@ -6772,7 +6836,7 @@
 
 void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new)
 {
-	OscarData *od = gc->proto_data;
+	OscarData *od = purple_connection_get_protocol_data(gc);
 
 	if (od->icq) {
 		aim_icq_changepasswd(od, new);
@@ -6796,7 +6860,7 @@
 	OscarData *od;
 	PeerConnection *conn;
 
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	conn = peer_connection_find_by_type(od, who, OSCAR_CAPABILITY_DIRECTIM);
 
 	if (conn != NULL)
@@ -6889,14 +6953,14 @@
 
 	/* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */
 	if (!g_ascii_strcasecmp(cmd, "GoIM")) {
-		char *sname = g_hash_table_lookup(params, "screenname");
-		if (sname) {
+		char *bname = g_hash_table_lookup(params, "screenname");
+		if (bname) {
 			char *message = g_hash_table_lookup(params, "message");
 
 			PurpleConversation *conv = purple_find_conversation_with_account(
-				PURPLE_CONV_TYPE_IM, sname, acct);
+				PURPLE_CONV_TYPE_IM, bname, acct);
 			if (conv == NULL)
-				conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
+				conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, bname);
 			purple_conversation_present(conv);
 
 			if (message) {
@@ -6929,9 +6993,9 @@
 	}
 	/* aim:AddBuddy?screenname=SCREENNAME&groupname=GROUPNAME*/
 	else if (!g_ascii_strcasecmp(cmd, "AddBuddy")) {
-		char *sname = g_hash_table_lookup(params, "screenname");
+		char *bname = g_hash_table_lookup(params, "screenname");
 		char *gname = g_hash_table_lookup(params, "groupname");
-		purple_blist_request_add_buddy(acct, sname, gname, NULL);
+		purple_blist_request_add_buddy(acct, bname, gname, NULL);
 		return TRUE;
 	}
 
@@ -6961,7 +7025,7 @@
 	option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins",
 											OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS);
 	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
-	
+
 	if (init)
 		return;
 	init = TRUE;
--- a/libpurple/protocols/oscar/oscar.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Sat Mar 07 01:59:40 2009 +0000
@@ -75,7 +75,7 @@
 #define FAIM_SNAC_HASH_SIZE 16
 
 /*
- * Current Maximum Length for Screen Names (not including NULL)
+ * Current Maximum Length for usernames (not including NULL)
  *
  * Currently only names up to 16 characters can be registered
  * however it is apparently legal for them to be larger.
@@ -479,7 +479,7 @@
 	gboolean setemail;
 	char *email;
 	gboolean setnick;
-	char *newsn;
+	char *newformatting;
 	gboolean chpass;
 	char *oldp;
 	char *newp;
@@ -582,7 +582,7 @@
 
 struct aim_authresp_info
 {
-	char *sn;
+	char *bn;
 	guint16 errorcode;
 	char *errorurl;
 	guint16 regstatus;
@@ -611,8 +611,8 @@
 	} chat;
 };
 
-int aim_request_login(OscarData *od, FlapConnection *conn, const char *sn);
-int aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins);
+int aim_request_login(OscarData *od, FlapConnection *conn, const char *bn);
+int aim_send_login(OscarData *od, FlapConnection *conn, const char *bn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins);
 /* 0x000b */ int aim_auth_securid_send(OscarData *od, const char *securid);
 
 void oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags);
@@ -665,7 +665,7 @@
 /* 0x0014 */ void aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32);
 /* 0x0016 */ void aim_srv_nop(OscarData *od, FlapConnection *conn);
 /* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn);
-/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setavailmsg, const char *availmsg, const char *itmsurl);
+/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setstatusmsg, const char *statusmsg, const char *itmsurl);
 
 
 void aim_bos_reqrights(OscarData *od, FlapConnection *conn);
@@ -811,7 +811,7 @@
 {
 
 	/* These are _required_ */
-	const char *destsn;
+	const char *destbn;
 	guint32 flags; /* often 0 */
 
 	/* Only required if not using multipart messages */
@@ -840,7 +840,7 @@
  */
 struct aim_sendrtfmsg_args
 {
-	const char *destsn;
+	const char *destbn;
 	guint32 fgcolor;
 	guint32 bgcolor;
 	const char *rtfmsg; /* must be in RTF */
@@ -954,28 +954,28 @@
 /* 0x0002 */ int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params);
 /* 0x0004 */ int aim_im_reqparams(OscarData *od);
 /* 0x0006 */ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args);
-/* 0x0006 */ int aim_im_sendch1(OscarData *, const char *destsn, guint16 flags, const char *msg);
-/* 0x0006 */ int aim_im_sendch2_chatinvite(OscarData *od, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
-/* 0x0006 */ int aim_im_sendch2_icon(OscarData *od, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
+/* 0x0006 */ int aim_im_sendch1(OscarData *, const char *destbn, guint16 flags, const char *msg);
+/* 0x0006 */ int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
+/* 0x0006 */ int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
 /* 0x0006 */ int aim_im_sendch2_rtfmsg(OscarData *od, struct aim_sendrtfmsg_args *args);
 
 /* 0x0006 */ void aim_im_sendch2_cancel(PeerConnection *peer_conn);
 /* 0x0006 */ void aim_im_sendch2_connected(PeerConnection *peer_conn);
-/* 0x0006 */ void aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 port, guint16 requestnumber);
-/* 0x0006 */ void aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 pin, guint16 requestnumber);
-/* 0x0006 */ void aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
-/* 0x0006 */ void aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *sn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
+/* 0x0006 */ void aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber);
+/* 0x0006 */ void aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber);
+/* 0x0006 */ void aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
+/* 0x0006 */ void aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles);
 
-/* 0x0006 */ int aim_im_sendch2_geticqaway(OscarData *od, const char *sn, int type);
-/* 0x0006 */ int aim_im_sendch4(OscarData *od, const char *sn, guint16 type, const char *message);
-/* 0x0008 */ int aim_im_warn(OscarData *od, FlapConnection *conn, const char *destsn, guint32 flags);
-/* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *sn, const guchar *cookie, guint16 code);
+/* 0x0006 */ int aim_im_sendch2_geticqaway(OscarData *od, const char *bn, int type);
+/* 0x0006 */ int aim_im_sendch4(OscarData *od, const char *bn, guint16 type, const char *message);
+/* 0x0008 */ int aim_im_warn(OscarData *od, FlapConnection *conn, const char *destbn, guint32 flags);
+/* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code);
 /* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od);
-/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2);
+/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2);
 void aim_icbm_makecookie(guchar* cookie);
 gchar *oscar_encoding_extract(const char *encoding);
 gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen);
-gchar *purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcesn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen);
+gchar *purple_plugin_oscar_decode_im_part(PurpleAccount *account, const char *sourcebn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen);
 
 
 /* 0x0002 - family_locate.c */
@@ -1010,13 +1010,13 @@
 
 struct userinfo_node
 {
-	char *sn;
+	char *bn;
 	struct userinfo_node *next;
 };
 
 typedef struct aim_userinfo_s
 {
-	char *sn;
+	char *bn;
 	guint16 warnlevel; /* evil percent * 10 (999 = 99.9%) */
 	guint16 idletime; /* in seconds */
 	guint16 flags;
@@ -1062,7 +1062,7 @@
 
 struct aim_invite_priv
 {
-	char *sn;
+	char *bn;
 	char *roomname;
 	guint16 exchange;
 	guint16 instance;
@@ -1085,7 +1085,7 @@
 #define AIM_COOKIETYPE_OFTIMAGE 0x14
 #define AIM_COOKIETYPE_OFTICON  0x15
 
-aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *sn);
+aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn);
 void aim_locate_dorequest(OscarData *od);
 
 /* 0x0002 */ int aim_locate_reqrights(OscarData *od);
@@ -1093,11 +1093,11 @@
 /* 0x0004 */ int aim_locate_setprofile(OscarData *od, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len);
 /* 0x0005 */ int aim_locate_getinfo(OscarData *od, const char *, guint16);
 /* 0x0009 */ int aim_locate_setdirinfo(OscarData *od, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy);
-/* 0x000b */ int aim_locate_000b(OscarData *od, const char *sn);
+/* 0x000b */ int aim_locate_000b(OscarData *od, const char *bn);
 /* 0x000f */ int aim_locate_setinterests(OscarData *od, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy);
-/* 0x0015 */ int aim_locate_getinfoshort(OscarData *od, const char *sn, guint32 flags);
+/* 0x0015 */ int aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags);
 
-void aim_locate_autofetch_away_message(OscarData *od, const char *sn);
+void aim_locate_autofetch_away_message(OscarData *od, const char *bn);
 guint32 aim_locate_getcaps(OscarData *od, ByteStream *bs, int len);
 guint32 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len);
 void aim_info_free(aim_userinfo_t *);
@@ -1164,7 +1164,7 @@
 	char *country;
 	char *state;
 	char *city;
-	char *sn;
+	char *bn;
 	char *interest;
 	char *nick;
 	char *zip;
@@ -1181,7 +1181,7 @@
 
 /* 0x0010 - family_bart.c */
 int aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen);
-int aim_bart_request(OscarData *od, const char *sn, guint8 iconcsumtype, const guint8 *iconstr, guint16 iconstrlen);
+int aim_bart_request(OscarData *od, const char *bn, guint8 iconcsumtype, const guint8 *iconstr, guint16 iconstrlen);
 
 
 
@@ -1231,20 +1231,20 @@
 /* 0x0007 */ int aim_ssi_enable(OscarData *od);
 /* 0x0011 */ int aim_ssi_modbegin(OscarData *od);
 /* 0x0012 */ int aim_ssi_modend(OscarData *od);
-/* 0x0014 */ int aim_ssi_sendauth(OscarData *od, char *sn, char *msg);
-/* 0x0018 */ int aim_ssi_sendauthrequest(OscarData *od, char *sn, const char *msg);
-/* 0x001a */ int aim_ssi_sendauthreply(OscarData *od, char *sn, guint8 reply, const char *msg);
+/* 0x0014 */ int aim_ssi_sendauth(OscarData *od, char *bn, char *msg);
+/* 0x0018 */ int aim_ssi_sendauthrequest(OscarData *od, char *bn, const char *msg);
+/* 0x001a */ int aim_ssi_sendauthreply(OscarData *od, char *bn, guint8 reply, const char *msg);
 
 /* Client functions for retrieving SSI data */
 struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid);
-struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *sn, guint16 type);
-struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *sn);
-char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *sn);
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type);
+struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn);
+char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn);
 int aim_ssi_getpermdeny(struct aim_ssi_item *list);
 guint32 aim_ssi_getpresence(struct aim_ssi_item *list);
-char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *sn);
-char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *sn);
-gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *sn);
+char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn);
+char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn);
+gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn);
 
 /* Client functions for changing SSI data */
 int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *tlvlist, const char *alias, const char *comment, const char *smsnum, gboolean needauth);
@@ -1254,9 +1254,9 @@
 int aim_ssi_delgroup(OscarData *od, const char *group);
 int aim_ssi_delpermit(OscarData *od, const char *name);
 int aim_ssi_deldeny(OscarData *od, const char *name);
-int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *sn);
-int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *sn, const char *alias);
-int aim_ssi_editcomment(OscarData *od, const char *gn, const char *sn, const char *alias);
+int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn);
+int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias);
+int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *alias);
 int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn);
 int aim_ssi_cleanlist(OscarData *od);
 int aim_ssi_deletelist(OscarData *od);
@@ -1501,11 +1501,10 @@
 int aimutil_itemcnt(char *toSearch, char dl);
 char *aimutil_itemindex(char *toSearch, int theindex, char dl);
 
-gboolean aim_snvalid(const char *sn);
-gboolean aim_snvalid_icq(const char *sn);
-gboolean aim_snvalid_sms(const char *sn);
-int aim_snlen(const char *sn);
-int aim_sncmp(const char *sn1, const char *sn2);
+gboolean oscar_util_valid_name(const char *bn);
+gboolean oscar_util_valid_name_icq(const char *bn);
+gboolean oscar_util_valid_name_sms(const char *bn);
+int oscar_util_name_compare(const char *bn1, const char *bn2);
 
 
 
@@ -1595,6 +1594,18 @@
 int byte_stream_putuid(ByteStream *bs, OscarData *od);
 int byte_stream_putcaps(ByteStream *bs, guint32 caps);
 
+/**
+ * Inserts a BART asset block into the given byte stream.  The flags
+ * and length are set appropriately based on the value of data.
+ */
+void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data);
+
+/**
+ * A helper function that calls byte_stream_put_bart_asset with the
+ * appropriate data ByteStream given the datastr.
+ */
+void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr);
+
 /*
  * Generic SNAC structure.  Rarely if ever used.
  */
--- a/libpurple/protocols/oscar/peer.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/peer.c	Sat Mar 07 01:59:40 2009 +0000
@@ -69,7 +69,7 @@
 #endif
 
 PeerConnection *
-peer_connection_find_by_type(OscarData *od, const char *sn, OscarCapability type)
+peer_connection_find_by_type(OscarData *od, const char *bn, OscarCapability type)
 {
 	GSList *cur;
 	PeerConnection *conn;
@@ -77,7 +77,7 @@
 	for (cur = od->peer_connections; cur != NULL; cur = cur->next)
 	{
 		conn = cur->data;
-		if ((conn->type == type) && !aim_sncmp(conn->sn, sn))
+		if ((conn->type == type) && !oscar_util_name_compare(conn->bn, bn))
 			return conn;
 	}
 
@@ -88,7 +88,7 @@
  * @param cookie This must be exactly 8 characters.
  */
 PeerConnection *
-peer_connection_find_by_cookie(OscarData *od, const char *sn, const guchar *cookie)
+peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie)
 {
 	GSList *cur;
 	PeerConnection *conn;
@@ -96,7 +96,7 @@
 	for (cur = od->peer_connections; cur != NULL; cur = cur->next)
 	{
 		conn = cur->data;
-		if (!memcmp(conn->cookie, cookie, 8) && !aim_sncmp(conn->sn, sn))
+		if (!memcmp(conn->cookie, cookie, 8) && !oscar_util_name_compare(conn->bn, bn))
 			return conn;
 	}
 
@@ -104,7 +104,7 @@
 }
 
 PeerConnection *
-peer_connection_new(OscarData *od, OscarCapability type, const char *sn)
+peer_connection_new(OscarData *od, OscarCapability type, const char *bn)
 {
 	PeerConnection *conn;
 	PurpleAccount *account;
@@ -114,7 +114,7 @@
 	conn = g_new0(PeerConnection, 1);
 	conn->od = od;
 	conn->type = type;
-	conn->sn = g_strdup(sn);
+	conn->bn = g_strdup(bn);
 	conn->buffer_outgoing = purple_circ_buffer_new(0);
 	conn->listenerfd = -1;
 	conn->fd = -1;
@@ -228,7 +228,7 @@
 		conn->xfer = NULL;
 	}
 
-	g_free(conn->sn);
+	g_free(conn->bn);
 	g_free(conn->error_message);
 	g_free(conn->proxyip);
 	g_free(conn->clientip);
@@ -698,20 +698,20 @@
 	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
 	{
 		aim_im_sendch2_odc_requestdirect(od,
-				conn->cookie, conn->sn, purple_network_ip_atoi(listener_ip),
+				conn->cookie, conn->bn, purple_network_ip_atoi(listener_ip),
 				listener_port, ++conn->lastrequestnumber);
 
 		/* Print a message to a local conversation window */
-		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn);
+		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
 		tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
-				"Direct IM."), conn->sn, listener_ip, listener_port);
+				"Direct IM."), conn->bn, listener_ip, listener_port);
 		purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
 		g_free(tmp);
 	}
 	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
 	{
 		aim_im_sendch2_sendfile_requestdirect(od,
-				conn->cookie, conn->sn,
+				conn->cookie, conn->bn,
 				purple_network_ip_atoi(listener_ip),
 				listener_port, ++conn->lastrequestnumber,
 				(const gchar *)conn->xferdata.name,
@@ -790,7 +790,7 @@
 			PurpleConversation *conv;
 			tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
 					conn->verifiedip, conn->port);
-			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn);
+			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
 			purple_conversation_write(conv, NULL, tmp,
 					PURPLE_MESSAGE_SYSTEM, time(NULL));
 			g_free(tmp);
@@ -863,7 +863,7 @@
 			gchar *tmp;
 			PurpleConversation *conv;
 			tmp = g_strdup(_("Attempting to connect via proxy server."));
-			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn);
+			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
 			purple_conversation_write(conv, NULL, tmp,
 					PURPLE_MESSAGE_SYSTEM, time(NULL));
 			g_free(tmp);
@@ -888,13 +888,13 @@
  * Initiate a peer connection with someone.
  */
 void
-peer_connection_propose(OscarData *od, OscarCapability type, const char *sn)
+peer_connection_propose(OscarData *od, OscarCapability type, const char *bn)
 {
 	PeerConnection *conn;
 
 	if (type == OSCAR_CAPABILITY_DIRECTIM)
 	{
-		conn = peer_connection_find_by_type(od, sn, type);
+		conn = peer_connection_find_by_type(od, bn, type);
 		if (conn != NULL)
 		{
 			if (conn->ready)
@@ -903,10 +903,10 @@
 				PurpleConversation *conv;
 
 				purple_debug_info("oscar", "Already have a direct IM "
-						"session with %s.\n", sn);
+						"session with %s.\n", bn);
 				account = purple_connection_get_account(od->gc);
 				conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-						sn, account);
+						bn, account);
 				if (conv != NULL)
 					purple_conversation_present(conv);
 				return;
@@ -917,7 +917,7 @@
 		}
 	}
 
-	conn = peer_connection_new(od, type, sn);
+	conn = peer_connection_new(od, type, bn);
 	conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
 	conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
 	aim_icbm_makecookie(conn->cookie);
@@ -954,7 +954,7 @@
 
 	conn = data;
 
-	aim_im_denytransfer(conn->od, conn->sn, conn->cookie,
+	aim_im_denytransfer(conn->od, conn->bn, conn->cookie,
 			AIM_TRANSFER_DENY_DECLINE);
 	peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
 }
@@ -963,7 +963,7 @@
  * Someone else wants to establish a peer connection with us.
  */
 void
-peer_connection_got_proposition(OscarData *od, const gchar *sn, const gchar *message, IcbmArgsCh2 *args)
+peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args)
 {
 	PurpleConnection *gc;
 	PurpleAccount *account;
@@ -979,7 +979,7 @@
 	 * and we should try connecting to them, instead.  Or they want
 	 * to go through a proxy.
 	 */
-	conn = peer_connection_find_by_cookie(od, sn, args->cookie);
+	conn = peer_connection_find_by_cookie(od, bn, args->cookie);
 	if ((conn != NULL) && (conn->type == args->type))
 	{
 		purple_debug_info("oscar", "Remote user wants to try a "
@@ -1003,12 +1003,12 @@
 	/* If this is a direct IM, then close any existing session */
 	if (args->type == OSCAR_CAPABILITY_DIRECTIM)
 	{
-		conn = peer_connection_find_by_type(od, sn, args->type);
+		conn = peer_connection_find_by_type(od, bn, args->type);
 		if (conn != NULL)
 		{
 			/* Close the old direct IM and start a new one */
 			purple_debug_info("oscar", "Received new direct IM request "
-				"from %s.  Destroying old connection.\n", sn);
+				"from %s.  Destroying old connection.\n", bn);
 			peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
 		}
 	}
@@ -1022,12 +1022,12 @@
 		{
 			purple_debug_warning("oscar",
 					"%s tried to send you a file with incomplete "
-					"information.\n", sn);
+					"information.\n", bn);
 			return;
 		}
 	}
 
-	conn = peer_connection_new(od, args->type, sn);
+	conn = peer_connection_new(od, args->type, bn);
 	memcpy(conn->cookie, args->cookie, 8);
 	if (args->use_proxy)
 		conn->proxyip = g_strdup(args->proxyip);
@@ -1040,7 +1040,7 @@
 	if (args->type == OSCAR_CAPABILITY_DIRECTIM)
 	{
 		buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
-				sn, purple_account_get_username(account));
+				bn, purple_account_get_username(account));
 
 		purple_request_action(conn, NULL, buf,
 						_("This requires a direct connection between "
@@ -1049,7 +1049,7 @@
 						  "revealed, this may be considered a privacy "
 						  "risk."),
 						PURPLE_DEFAULT_ACTION_NONE,
-						account, sn, NULL,
+						account, bn, NULL,
 						conn, 2,
 						_("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
 						_("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
@@ -1058,7 +1058,7 @@
 	{
 		gchar *filename;
 
-		conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, sn);
+		conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, bn);
 		if (conn->xfer)
 		{
 			conn->xfer->data = conn;
--- a/libpurple/protocols/oscar/peer.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/peer.h	Sat Mar 07 01:59:40 2009 +0000
@@ -83,7 +83,7 @@
 	/* Unknown */
 	guint16 flags;                /* 38 */
 	/* Unknown */
-	guchar sn[32];                /* 44 */
+	guchar bn[32];                /* 44 */
 	/* Unknown */
 	ByteStream payload;           /* 76 */
 };
@@ -137,7 +137,7 @@
 {
 	OscarData *od;
 	OscarCapability type;
-	char *sn;
+	char *bn;
 	guchar magic[4];
 	guchar cookie[8];
 	guint16 lastrequestnumber;
@@ -228,12 +228,12 @@
  * @param type The type of the peer connection.  One of
  *        OSCAR_CAPABILITY_DIRECTIM or OSCAR_CAPABILITY_SENDFILE.
  */
-PeerConnection *peer_connection_new(OscarData *od, OscarCapability type, const char *sn);
+PeerConnection *peer_connection_new(OscarData *od, OscarCapability type, const char *bn);
 
 void peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
 void peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message);
-PeerConnection *peer_connection_find_by_type(OscarData *od, const char *sn, OscarCapability type);
-PeerConnection *peer_connection_find_by_cookie(OscarData *od, const char *sn, const guchar *cookie);
+PeerConnection *peer_connection_find_by_type(OscarData *od, const char *bn, OscarCapability type);
+PeerConnection *peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie);
 
 void peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond);
 void peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond);
@@ -241,8 +241,8 @@
 
 void peer_connection_trynext(PeerConnection *conn);
 void peer_connection_finalize_connection(PeerConnection *conn);
-void peer_connection_propose(OscarData *od, OscarCapability type, const char *sn);
-void peer_connection_got_proposition(OscarData *od, const gchar *sn, const gchar *message, IcbmArgsCh2 *args);
+void peer_connection_propose(OscarData *od, OscarCapability type, const char *bn);
+void peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args);
 
 /*
  * For ODC
--- a/libpurple/protocols/oscar/peer_proxy.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/peer_proxy.c	Sat Mar 07 01:59:40 2009 +0000
@@ -63,19 +63,19 @@
 {
 	ProxyFrame frame;
 	PurpleAccount *account;
-	const gchar *sn;
-	guint8 sn_length;
+	const gchar *bn;
+	guint8 bn_length;
 
 	memset(&frame, 0, sizeof(ProxyFrame));
 	frame.type = PEER_PROXY_TYPE_CREATE;
 	frame.flags = 0x0000;
 
 	account = purple_connection_get_account(conn->od->gc);
-	sn = purple_account_get_username(account);
-	sn_length = strlen(sn);
-	byte_stream_new(&frame.payload, 1 + sn_length + 8 + 20);
-	byte_stream_put8(&frame.payload, sn_length);
-	byte_stream_putraw(&frame.payload, (const guint8 *)sn, sn_length);
+	bn = purple_account_get_username(account);
+	bn_length = strlen(bn);
+	byte_stream_new(&frame.payload, 1 + bn_length + 8 + 20);
+	byte_stream_put8(&frame.payload, bn_length);
+	byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length);
 	byte_stream_putraw(&frame.payload, conn->cookie, 8);
 
 	byte_stream_put16(&frame.payload, 0x0001); /* Type */
@@ -99,19 +99,19 @@
 {
 	ProxyFrame frame;
 	PurpleAccount *account;
-	const gchar *sn;
-	guint8 sn_length;
+	const gchar *bn;
+	guint8 bn_length;
 
 	memset(&frame, 0, sizeof(ProxyFrame));
 	frame.type = PEER_PROXY_TYPE_JOIN;
 	frame.flags = 0x0000;
 
 	account = purple_connection_get_account(conn->od->gc);
-	sn = purple_account_get_username(account);
-	sn_length = strlen(sn);
-	byte_stream_new(&frame.payload, 1 + sn_length + 2 + 8 + 20);
-	byte_stream_put8(&frame.payload, sn_length);
-	byte_stream_putraw(&frame.payload, (const guint8 *)sn, sn_length);
+	bn = purple_account_get_username(account);
+	bn_length = strlen(bn);
+	byte_stream_new(&frame.payload, 1 + bn_length + 2 + 8 + 20);
+	byte_stream_put8(&frame.payload, bn_length);
+	byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length);
 	byte_stream_put16(&frame.payload, pin);
 	byte_stream_putraw(&frame.payload, conn->cookie, 8);
 
@@ -149,11 +149,11 @@
 		if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
 			aim_im_sendch2_odc_requestproxy(conn->od,
 					conn->cookie,
-					conn->sn, ip, pin, ++conn->lastrequestnumber);
+					conn->bn, ip, pin, ++conn->lastrequestnumber);
 		else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
 		{
 			aim_im_sendch2_sendfile_requestproxy(conn->od,
-					conn->cookie, conn->sn,
+					conn->cookie, conn->bn,
 					ip, pin, ++conn->lastrequestnumber,
 					(const gchar *)conn->xferdata.name,
 					conn->xferdata.size, conn->xferdata.totfiles);
--- a/libpurple/protocols/oscar/snactypes.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/snactypes.h	Sat Mar 07 01:59:40 2009 +0000
@@ -252,7 +252,6 @@
  * SNAC Family: Authorizer
  *
  * Used only in protocol versions three and above.
- *
  */
 #define SNAC_SUBTYPE_AUTH_ERROR 0x0001
 #define SNAC_SUBTYPE_AUTH_LOGINREQEST 0x0002
@@ -266,8 +265,7 @@
  * SNAC Family: Email
  *
  * Used for getting information on the email address
- * associated with your screen name.
- *
+ * associated with your username.
  */
 #define SNAC_SUBTYPE_ALERT_ERROR 0x0001
 #define SNAC_SUBTYPE_ALERT_SENDCOOKIES 0x0006
@@ -280,7 +278,6 @@
  * This isn't truly a SNAC family either, but using
  * these, we can integrated non-SNAC services into
  * the SNAC-centered libfaim callback structure.
- *
  */
 #define AIM_CB_SPECIAL_CONNERR 0x0003
 #define AIM_CB_SPECIAL_CONNINITDONE 0x0006
--- a/libpurple/protocols/oscar/util.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/oscar/util.c	Sat Mar 07 01:59:40 2009 +0000
@@ -136,27 +136,27 @@
 }
 
 /**
- * Check if the given screen name is a valid AIM screen name.
+ * Check if the given name is a valid AIM username.
  * Example: BobDole
  * Example: Henry_Ford@mac.com
  * Example: 1KrazyKat@example.com
  *
- * @return TRUE if the screen name is valid, FALSE if not.
+ * @return TRUE if the name is valid, FALSE if not.
  */
 static gboolean
-aim_snvalid_aim(const char *sn)
+oscar_util_valid_name_aim(const char *name)
 {
 	int i;
 
-	if (purple_email_is_valid(sn))
+	if (purple_email_is_valid(name))
 		return TRUE;
 
-	/* Normal AIM screen names can't start with a number */
-	if (isdigit(sn[0]))
+	/* Normal AIM usernames can't start with a number */
+	if (isdigit(name[0]))
 		return FALSE;
 
-	for (i = 0; sn[i] != '\0'; i++) {
-		if (!isalnum(sn[i]) && (sn[i] != ' '))
+	for (i = 0; name[i] != '\0'; i++) {
+		if (!isalnum(name[i]) && (name[i] != ' '))
 			return FALSE;
 	}
 
@@ -164,18 +164,18 @@
 }
 
 /**
- * Check if the given screen name is a valid ICQ screen name.
+ * Check if the given name is a valid ICQ username.
  * Example: 1234567
  *
- * @return TRUE if the screen name is valid, FALSE if not.
+ * @return TRUE if the name is valid, FALSE if not.
  */
 gboolean
-aim_snvalid_icq(const char *sn)
+oscar_util_valid_name_icq(const char *name)
 {
 	int i;
 
-	for (i = 0; sn[i] != '\0'; i++) {
-		if (!isdigit(sn[i]))
+	for (i = 0; name[i] != '\0'; i++) {
+		if (!isdigit(name[i]))
 			return FALSE;
 	}
 
@@ -183,21 +183,21 @@
 }
 
 /**
- * Check if the given screen name is a valid SMS screen name.
+ * Check if the given name is a valid SMS username.
  * Example: +19195551234
  *
- * @return TRUE if the screen name is valid, FALSE if not.
+ * @return TRUE if the name is valid, FALSE if not.
  */
 gboolean
-aim_snvalid_sms(const char *sn)
+oscar_util_valid_name_sms(const char *name)
 {
 	int i;
 
-	if (sn[0] != '+')
+	if (name[0] != '+')
 		return FALSE;
 
-	for (i = 1; sn[i] != '\0'; i++) {
-		if (!isdigit(sn[i]))
+	for (i = 1; name[i] != '\0'; i++) {
+		if (!isdigit(name[i]))
 			return FALSE;
 	}
 
@@ -205,44 +205,46 @@
 }
 
 /**
- * Check if the given screen name is a valid oscar screen name.
+ * Check if the given name is a valid oscar username.
  *
- * @return TRUE if the screen name is valid, FALSE if not.
+ * @return TRUE if the name is valid, FALSE if not.
  */
 gboolean
-aim_snvalid(const char *sn)
+oscar_util_valid_name(const char *name)
 {
-	if ((sn == NULL) || (*sn == '\0'))
+	if ((name == NULL) || (*name == '\0'))
 		return FALSE;
 
-	return aim_snvalid_icq(sn) || aim_snvalid_sms(sn) || aim_snvalid_aim(sn);
+	return oscar_util_valid_name_icq(name)
+			|| oscar_util_valid_name_sms(name)
+			|| oscar_util_valid_name_aim(name);
 }
 
 /**
- * This takes two screen names and compares them using the rules
- * on screen names for AIM/AOL.  Mainly, this means case and space
+ * This takes two names and compares them using the rules
+ * on usernames for AIM/AOL.  Mainly, this means case and space
  * insensitivity (all case differences and spacing differences are
- * ignored, with the exception that screen names can not start with
+ * ignored, with the exception that usernames can not start with
  * a space).
  *
  * @return 0 if equal, non-0 if different
  */
 /* TODO: Do something different for email addresses. */
 int
-aim_sncmp(const char *sn1, const char *sn2)
+oscar_util_name_compare(const char *name1, const char *name2)
 {
 
-	if ((sn1 == NULL) || (sn2 == NULL))
+	if ((name1 == NULL) || (name2 == NULL))
 		return -1;
 
 	do {
-		while (*sn2 == ' ')
-			sn2++;
-		while (*sn1 == ' ')
-			sn1++;
-		if (toupper(*sn1) != toupper(*sn2))
+		while (*name2 == ' ')
+			name2++;
+		while (*name1 == ' ')
+			name1++;
+		if (toupper(*name1) != toupper(*name2))
 			return 1;
-	} while ((*sn1 != '\0') && sn1++ && sn2++);
+	} while ((*name1 != '\0') && name1++ && name2++);
 
 	return 0;
 }
--- a/libpurple/protocols/qq/buddy_info.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Sat Mar 07 01:59:40 2009 +0000
@@ -614,7 +614,7 @@
 	gchar *alias_utf8;
 	PurpleAccount *account = purple_connection_get_account(gc);
 
-	qd = (qq_data *) gc->proto_data;
+	qd = (qq_data *)purple_connection_get_protocol_data(gc);
 
 	uid = strtoul(segments[QQ_INFO_UID], NULL, 10);
 	who = uid_to_purple_name(uid);
@@ -633,15 +633,16 @@
 		buddy = purple_find_buddy(gc->account, who);
 	}
 
-	if (buddy == NULL || buddy->proto_data == NULL) {
+	/* if the buddy is null, the api will catch it and return null here */
+	bd = purple_buddy_get_protocol_data(buddy);
+
+	if (buddy == NULL || bd) {
 		g_free(who);
 		g_free(alias_utf8);
 		return;
 	}
 
 	/* update buddy list (including myself, if myself is the buddy) */
-	bd = (qq_buddy_data *)buddy->proto_data;
-
 	bd->age = strtol(segments[QQ_INFO_AGE], NULL, 10);
 	bd->gender = strtol(segments[QQ_INFO_GENDER], NULL, 10);
 	bd->face = strtol(segments[QQ_INFO_FACE], NULL, 10);
@@ -770,8 +771,7 @@
 	for (it = buddies; it; it = it->next) {
 		buddy = it->data;
 		if (buddy == NULL) continue;
-		if (buddy->proto_data == NULL) continue;
-		bd = (qq_buddy_data *)buddy->proto_data;
+		if ((bd = purple_buddy_get_protocol_data(buddy)) == NULL) continue;
 		if (bd->uid == 0) continue;	/* keep me as end of packet*/
 		if (bd->uid == qd->uid) continue;
 		bytes += qq_put32(buf + bytes, bd->uid);
--- a/libpurple/protocols/qq/buddy_list.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Sat Mar 07 01:59:40 2009 +0000
@@ -233,7 +233,7 @@
 			/* create no-auth buddy */
 			buddy = qq_buddy_new(gc, bs.uid);
 		}
-		bd = (buddy == NULL) ? NULL : (qq_buddy_data *)buddy->proto_data;
+		bd = (buddy == NULL) ? NULL : (qq_buddy_data *)purple_buddy_get_protocol_data(buddy);
 		if (bd == NULL) {
 			purple_debug_error("QQ",
 					"Got an online buddy %u, but not in my buddy list\n", bs.uid);
@@ -335,7 +335,7 @@
 #endif
 
 		buddy = qq_buddy_find_or_new(gc, bd.uid);
-		if (buddy == NULL || buddy->proto_data == NULL) {
+		if (buddy == NULL || purple_buddy_get_protocol_data(buddy) == NULL) {
 			g_free(bd.nickname);
 			continue;
 		}
@@ -343,7 +343,7 @@
 		bd.last_update = time(NULL);
 		qq_update_buddy_status(gc, bd.uid, bd.status, bd.comm_flag);
 
-		g_memmove(buddy->proto_data, &bd, sizeof(qq_buddy_data));
+		g_memmove(purple_buddy_get_protocol_data(buddy), &bd, sizeof(qq_buddy_data));
 		/* nickname has been copy to buddy_data do not free
 		   g_free(bd.nickname);
 		*/
@@ -570,7 +570,7 @@
 		/* create no-auth buddy */
 		buddy = qq_buddy_new(gc, bs.uid);
 	}
-	bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data;
+	bd = (buddy == NULL) ? NULL : (qq_buddy_data *)purple_buddy_get_protocol_data(buddy);
 	if (bd == NULL) {
 		purple_debug_warning("QQ", "Got status of no-auth buddy %u\n", bs.uid);
 		return;
@@ -662,9 +662,10 @@
 	for (it = buddies; it; it = it->next) {
 		buddy = it->data;
 		if (buddy == NULL) continue;
-		if (buddy->proto_data == NULL) continue;
 
-		bd = (qq_buddy_data *)buddy->proto_data;
+		bd = purple_buddy_get_protocol_data(buddy);
+		if (bd == NULL) continue;
+
 		if (bd->uid == 0) continue;
 		if (bd->uid == qd->uid) continue;	/* my status is always online in my buddy list */
 		if (tm_limit < bd->last_update) continue;
@@ -684,16 +685,20 @@
 	GSList *buddies, *it;
 	gint count = 0;
 
-	qd = (qq_data *) (gc->proto_data);
+	qd = (qq_data *)purple_connection_get_protocol_data(gc);
 
 	buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
 	for (it = buddies; it; it = it->next) {
+		qq_buddy_data *qbd = NULL;
+
 		buddy = it->data;
 		if (buddy == NULL) continue;
-		if (buddy->proto_data == NULL) continue;
 
-		qq_buddy_data_free(buddy->proto_data);
-		buddy->proto_data = NULL;
+		qbd = purple_buddy_get_protocol_data(buddy);
+		if (qbd == NULL) continue;
+
+		qq_buddy_data_free(qbd);
+		purple_buddy_set_protocol_data(buddy, NULL);
 
 		count++;
 	}
--- a/libpurple/protocols/qq/buddy_opt.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Sat Mar 07 01:59:40 2009 +0000
@@ -101,6 +101,7 @@
 {
 	gchar *who;
 	PurpleBuddy *buddy;
+	qq_buddy_data *bd;
 
 	g_return_val_if_fail(gc != NULL, NULL);
 
@@ -113,11 +114,12 @@
 		purple_debug_error("QQ", "Can not find purple buddy of %u\n", uid);
 		return NULL;
 	}
-	if (buddy->proto_data == NULL) {
+	
+	if ((bd = purple_buddy_get_protocol_data(buddy)) == NULL) {
 		purple_debug_error("QQ", "Can not find buddy data of %u\n", uid);
 		return NULL;
 	}
-	return (qq_buddy_data *)buddy->proto_data;
+	return bd;
 }
 
 void qq_buddy_data_free(qq_buddy_data *bd)
@@ -149,7 +151,7 @@
 	purple_debug_info("QQ", "Add new purple buddy: [%u]\n", uid);
 	who = uid_to_purple_name(uid);
 	buddy = purple_buddy_new(gc->account, who, NULL);	/* alias is NULL */
-	buddy->proto_data = NULL;
+	purple_buddy_set_protocol_data(buddy, NULL);
 
 	g_free(who);
 
@@ -162,11 +164,14 @@
 
 static void qq_buddy_free(PurpleBuddy *buddy)
 {
+	qq_buddy_data *bd;
+
 	g_return_if_fail(buddy);
-	if (buddy->proto_data)	{
-		qq_buddy_data_free(buddy->proto_data);
+
+	if ((bd = purple_buddy_get_protocol_data(buddy)) != NULL) {
+		qq_buddy_data_free(bd);
 	}
-	buddy->proto_data = NULL;
+	purple_buddy_set_protocol_data(buddy, NULL);
 	purple_blist_remove_buddy(buddy);
 }
 
@@ -186,6 +191,7 @@
 PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, guint32 uid)
 {
 	PurpleBuddy *buddy;
+	qq_buddy_data *bd;
 
 	g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
 
@@ -197,11 +203,12 @@
 		}
 	}
 
-	if (buddy->proto_data != NULL) {
+	if (purple_buddy_get_protocol_data(buddy) != NULL) {
 		return buddy;
 	}
 
-	buddy->proto_data = qq_buddy_data_new(uid);
+	bd = qq_buddy_data_new(uid);
+	purple_buddy_set_protocol_data(buddy, bd);
 	return buddy;
 }
 
@@ -691,7 +698,7 @@
 	if (!qd->is_login)
 		return;		/* IMPORTANT ! */
 
-	uid = purple_name_to_uid(buddy->name);
+	uid = purple_name_to_uid(purple_buddy_get_name(buddy));
 	if (uid > 0) {
 		if (qd->client_version > 2005) {
 			request_add_buddy_no_auth_ex(gc, uid);
@@ -782,6 +789,7 @@
 	gchar **segments;
 	gchar *dest_uid, *reply;
 	PurpleBuddy *buddy;
+	qq_buddy_data *bd;
 
 	g_return_if_fail(data != NULL && data_len != 0);
 	g_return_if_fail(uid != 0);
@@ -826,10 +834,10 @@
 	if (buddy == NULL) {
 		buddy = qq_buddy_new(gc, uid);
 	}
-	if (buddy != NULL && buddy->proto_data != NULL) {
+	if (buddy != NULL && (bd = purple_buddy_get_protocol_data(buddy)) != NULL) {
 		/* Not authorized now, free buddy data */
-		qq_buddy_data_free(buddy->proto_data);
-		buddy->proto_data = NULL;
+		qq_buddy_data_free(bd);
+		purple_buddy_set_protocol_data(buddy, NULL);
 	}
 
 	add_buddy_authorize_input(gc, uid, NULL, 0);
@@ -905,6 +913,7 @@
 void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	qq_data *qd;
+	qq_buddy_data *bd;
 	guint32 uid;
 
 	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -914,7 +923,7 @@
 	if (!qd->is_login)
 		return;
 
-	uid = purple_name_to_uid(buddy->name);
+	uid = purple_name_to_uid(purple_buddy_get_name(buddy));
 	if (uid > 0 && uid != qd->uid) {
 		if (qd->client_version > 2005) {
 			qq_request_auth_code(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_REMOVE_BUDDY, uid);
@@ -924,11 +933,11 @@
 		}
 	}
 
-	if (buddy->proto_data) {
-		qq_buddy_data_free(buddy->proto_data);
-		buddy->proto_data = NULL;
+	if ((bd = purple_buddy_get_protocol_data(buddy)) != NULL) {
+		qq_buddy_data_free(bd);
+		purple_buddy_set_protocol_data(buddy, NULL);
 	} else {
-		purple_debug_warning("QQ", "Empty buddy data of %s\n", buddy->name);
+		purple_debug_warning("QQ", "Empty buddy data of %s\n", purple_buddy_get_name(buddy));
 	}
 
 	/* Do not call purple_blist_remove_buddy,
@@ -1216,6 +1225,7 @@
 	gint bytes;
 	gchar **segments;
 	gchar *primary, *secondary;
+	qq_buddy_data *bd;
 
 	g_return_if_fail(from != NULL && to != NULL);
 
@@ -1255,10 +1265,10 @@
 	g_return_if_fail(uid != 0);
 
 	buddy = qq_buddy_find(gc, uid);
-	if (buddy != NULL && buddy->proto_data != NULL) {
+	if (buddy != NULL && (bd = purple_buddy_get_protocol_data(buddy)) != NULL) {
 		/* Not authorized now, free buddy data */
-		qq_buddy_data_free(buddy->proto_data);
-		buddy->proto_data = NULL;
+		qq_buddy_data_free(bd);
+		purple_buddy_set_protocol_data(buddy, NULL);
 	}
 }
 
--- a/libpurple/protocols/qq/group_internal.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Sat Mar 07 01:59:40 2009 +0000
@@ -103,16 +103,21 @@
 
 void qq_room_update_chat_info(PurpleChat *chat, qq_room_data *rmd)
 {
+	GHashTable *components;
+
 	if (rmd->title_utf8 != NULL && strlen(rmd->title_utf8) > 0) {
 		purple_blist_alias_chat(chat, rmd->title_utf8);
 	}
-	g_hash_table_replace(chat->components,
+
+	components = purple_chat_get_components(chat);
+
+	g_hash_table_replace(components,
 		     g_strdup(QQ_ROOM_KEY_INTERNAL_ID),
 		     g_strdup_printf("%u", rmd->id));
-	g_hash_table_replace(chat->components,
+	g_hash_table_replace(components,
 		     g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
 		     g_strdup_printf("%u", rmd->ext_id));
-	g_hash_table_replace(chat->components,
+	g_hash_table_replace(components,
 		     g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8));
 }
 
@@ -251,11 +256,13 @@
 		member->uid = member_uid;
 		buddy = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(member_uid));
 		if (buddy != NULL) {
-			bd = (qq_buddy_data *) buddy->proto_data;
+			const gchar *alias = NULL;
+
+			bd = purple_buddy_get_protocol_data(buddy);
 			if (bd != NULL && bd->nickname != NULL)
 				member->nickname = g_strdup(bd->nickname);
-			else if (buddy->alias != NULL)
-				member->nickname = g_strdup(buddy->alias);
+			else if ((alias = purple_buddy_get_alias(buddy)) != NULL)
+				member->nickname = g_strdup(alias);
 		}
 		rmd->members = g_list_append(rmd->members, member);
 	}
@@ -384,16 +391,19 @@
 	}
 
 	count = 0;
-	for (node = ((PurpleBlistNode *) purple_group)->child; node != NULL; node = node->next) {
+	for (node = purple_blist_node_get_first_child((PurpleBlistNode *)purple_group);
+	     node != NULL;
+		 node = purple_blist_node_get_sibling_next(node))
+	{
 		if ( !PURPLE_BLIST_NODE_IS_CHAT(node)) {
 			continue;
 		}
 		/* got one */
 		chat = (PurpleChat *) node;
-		if (account != chat->account)	/* not qq account*/
+		if (account != purple_chat_get_account(chat))	/* not qq account*/
 			continue;
 
-		rmd = room_data_new_by_hashtable(gc, chat->components);
+		rmd = room_data_new_by_hashtable(gc, purple_chat_get_components(chat));
 		qd->groups = g_list_append(qd->groups, rmd);
 		count++;
 	}
--- a/libpurple/protocols/qq/im.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/qq/im.c	Sat Mar 07 01:59:40 2009 +0000
@@ -788,7 +788,7 @@
 		/* create no-auth buddy */
 		buddy = qq_buddy_new(gc, im_header->uid_from);
 	}
-	bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data;
+	bd = (buddy == NULL) ? NULL : purple_buddy_get_protocol_data(buddy);
 	if (bd != NULL) {
 		bd->client_tag = im_header->version_from;
 		bd->face = im_text.sender_icon;
@@ -889,7 +889,7 @@
 		/* create no-auth buddy */
 		buddy = qq_buddy_new(gc, im_header->uid_from);
 	}
-	bd = (buddy == NULL) ? NULL : (qq_buddy_data *) buddy->proto_data;
+	bd = (buddy == NULL) ? NULL : purple_buddy_get_protocol_data(buddy);
 	if (bd != NULL) {
 		bd->client_tag = im_header->version_from;
 		bd->face = im_text.sender_icon;
--- a/libpurple/protocols/qq/qq.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/qq/qq.c	Sat Mar 07 01:59:40 2009 +0000
@@ -248,7 +248,7 @@
 	qq_buddy_data *bd;
 	GString *status;
 
-	bd = (qq_buddy_data *) b->proto_data;
+	bd = purple_buddy_get_protocol_data(b);
 	if (bd == NULL)
 		return NULL;
 
@@ -291,7 +291,7 @@
 
 	g_return_if_fail(b != NULL);
 
-	bd = (qq_buddy_data *) b->proto_data;
+	bd = purple_buddy_get_protocol_data(b);
 	if (bd == NULL)
 		return;
 
@@ -382,11 +382,12 @@
 	qq_data *qd;
 	qq_buddy_data *buddy;
 
-	if (!b || !(account = b->account) ||
-			!(gc = purple_account_get_connection(account)) || !(qd = gc->proto_data))
+	if (!b || !(account = purple_buddy_get_account(b)) ||
+		!(gc = purple_account_get_connection(account)) ||
+		!(qd = purple_connection_get_protocol_data(gc)))
 		return NULL;
 
-	buddy = (qq_buddy_data *)b->proto_data;
+	buddy = purple_buddy_get_protocol_data(b);
 	if (!buddy) {
 		return "not-authorized";
 	}
@@ -708,8 +709,9 @@
 static void action_chat_quit(PurpleBlistNode * node)
 {
 	PurpleChat *chat = (PurpleChat *)node;
-	PurpleConnection *gc = purple_account_get_connection(chat->account);
-	GHashTable *components = chat -> components;
+	PurpleAccount *account = purple_chat_get_account(chat);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	GHashTable *components = purple_chat_get_components(chat);
 	gchar *num_str;
 	guint32 room_id;
 
@@ -727,8 +729,9 @@
 static void action_chat_get_info(PurpleBlistNode * node)
 {
 	PurpleChat *chat = (PurpleChat *)node;
-	PurpleConnection *gc = purple_account_get_connection(chat->account);
-	GHashTable *components = chat -> components;
+	PurpleAccount *account = purple_chat_get_account(chat);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	GHashTable *components = purple_chat_get_components(chat);
 	gchar *num_str;
 	guint32 room_id;
 
@@ -815,7 +818,7 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 
 	qq_add_buddy(gc, buddy, NULL);
 }
@@ -830,12 +833,12 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *)node;
-	g_return_if_fail(NULL != buddy && NULL != buddy->proto_data);
+	g_return_if_fail(NULL != buddy);
 
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 	g_return_if_fail(NULL != gc);
 
-	bd = (qq_buddy_data *)buddy->proto_data;
+	bd = (qq_buddy_data *)purple_buddy_get_protocol_data(buddy);
 	g_return_if_fail(NULL != bd);
 	bd_uid = bd->uid;
 
@@ -849,7 +852,7 @@
 {
 	GList *m = NULL;
 	PurpleMenuAction *act;
-	qq_buddy_data *bd = (qq_buddy_data *)buddy->proto_data;
+	qq_buddy_data *bd = purple_buddy_get_protocol_data(buddy);
 
 	if (bd == NULL) {
 		act = purple_menu_action_new(_("Add Buddy"),
--- a/libpurple/protocols/qq/send_file.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/qq/send_file.c	Sat Mar 07 01:59:40 2009 +0000
@@ -808,7 +808,7 @@
 			    "Received a FACE ip detect from %d, so he/she must be online :)\n", sender_uid);
 
 		b = purple_find_buddy(gc->account, sender_name);
-		bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data;
+		bd = (b == NULL) ? NULL : purple_buddy_get_protocol_data(b);
 		if (bd) {
 			if(0 != info->remote_real_ip) {
 				g_memmove(&(bd->ip), &info->remote_real_ip, sizeof(bd->ip));
--- a/libpurple/protocols/sametime/sametime.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Sat Mar 07 01:59:40 2009 +0000
@@ -663,7 +663,6 @@
   */
 
   PurpleAccount *acct;
-  PurpleBuddyList *blist;
   PurpleBlistNode *gn, *cn, *bn;
   PurpleGroup *grp;
   PurpleBuddy *bdy;
@@ -674,10 +673,8 @@
   acct = purple_connection_get_account(gc);
   g_return_if_fail(acct != NULL);
 
-  blist = purple_get_blist();
-  g_return_if_fail(blist != NULL);
-
-  for(gn = blist->root; gn; gn = gn->next) {
+  for(gn = purple_blist_get_root(); gn;
+		  gn = purple_blist_node_get_sibling_next(gn)) {
     const char *owner;
     const char *gname;
     enum mwSametimeGroupType gtype;
@@ -702,13 +699,13 @@
     /* the group's actual name may be different from the purple group's
        name. Find whichever is there */
     gname = purple_blist_node_get_string(gn, GROUP_KEY_NAME);
-    if(! gname) gname = grp->name;
+    if(! gname) gname = purple_group_get_name(grp);
 
     /* we save this, but never actually honor it */
     gopen = ! purple_blist_node_get_bool(gn, GROUP_KEY_COLLAPSED);
 
     stg = mwSametimeGroup_new(stlist, gtype, gname);
-    mwSametimeGroup_setAlias(stg, grp->name);
+    mwSametimeGroup_setAlias(stg, purple_group_get_name(grp));
     mwSametimeGroup_setOpen(stg, gopen);
 
     /* don't attempt to put buddies in a dynamic group, it breaks
@@ -716,27 +713,31 @@
     if(gtype == mwSametimeGroup_DYNAMIC)
       continue;
 
-    for(cn = gn->child; cn; cn = cn->next) {
+    for(cn = purple_blist_node_get_first_child(gn);
+			cn;
+			cn = purple_blist_node_get_sibling_next(cn)) {
       if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue;
 
-      for(bn = cn->child; bn; bn = bn->next) {
+      for(bn = purple_blist_node_get_first_child(cn);
+			  bn;
+			  bn = purple_blist_node_get_sibling_next(bn)) {
 	if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue;
 	if(! PURPLE_BLIST_NODE_SHOULD_SAVE(bn)) continue;
 
 	bdy = (PurpleBuddy *) bn;
 
-	if(bdy->account == acct) {
+	if(purple_buddy_get_account(bdy) == acct) {
 	  struct mwSametimeUser *stu;
 	  enum mwSametimeUserType utype;
 
-	  idb.user = bdy->name;
+	  idb.user = (char *)purple_buddy_get_name(bdy);
 
 	  utype = purple_blist_node_get_int(bn, BUDDY_KEY_TYPE);
 	  if(! utype) utype = mwSametimeUser_NORMAL;
 
 	  stu = mwSametimeUser_new(stg, utype, &idb);
-	  mwSametimeUser_setShortName(stu, bdy->server_alias);
-	  mwSametimeUser_setAlias(stu, bdy->alias);
+	  mwSametimeUser_setShortName(stu, purple_buddy_get_server_alias(bdy));
+	  mwSametimeUser_setAlias(stu, purple_buddy_get_local_buddy_alias(bdy));
 	}
       }
     }
@@ -816,7 +817,7 @@
 
 static gboolean buddy_is_external(PurpleBuddy *b) {
   g_return_val_if_fail(b != NULL, FALSE);
-  return purple_str_has_prefix(b->name, "@E ");
+  return purple_str_has_prefix(purple_buddy_get_name(b), "@E ");
 }
 
 
@@ -825,7 +826,7 @@
 static void buddy_add(struct mwPurplePluginData *pd,
 		      PurpleBuddy *buddy) {
 
-  struct mwAwareIdBlock idb = { mwAware_USER, (char *) buddy->name, NULL };
+  struct mwAwareIdBlock idb = { mwAware_USER, (char *) purple_buddy_get_name(buddy), NULL };
   struct mwAwareList *list;
 
   PurpleGroup *group;
@@ -890,7 +891,7 @@
   GList *add;
   
   n = purple_blist_node_get_string((PurpleBlistNode *) group, GROUP_KEY_NAME);
-  if(! n) n = group->name;
+  if(! n) n = purple_group_get_name(group);
 
   idb.user = (char *) n;
   add = g_list_prepend(NULL, &idb);
@@ -926,7 +927,8 @@
 	     NSTR(name), NSTR(alias));
 
   /* first attempt at finding the group, by the name key */
-  for(gn = blist->root; gn; gn = gn->next) {
+  for(gn = purple_blist_get_root(); gn;
+		  gn = purple_blist_node_get_sibling_next(gn)) {
     const char *n, *o;
     if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue;
     n = purple_blist_node_get_string(gn, GROUP_KEY_NAME);
@@ -1006,23 +1008,27 @@
 
   g_return_if_fail(group != NULL);
 
-  DEBUG_INFO("clearing members from pruned group %s\n", NSTR(group->name));
+  DEBUG_INFO("clearing members from pruned group %s\n", NSTR(purple_group_get_name(group)));
 
   gc = purple_account_get_connection(acct);
   g_return_if_fail(gc != NULL);
 
   gn = (PurpleBlistNode *) group;
 
-  for(cn = gn->child; cn; cn = cn->next) {
+  for(cn = purple_blist_node_get_first_child(gn);
+		  cn;
+		  cn = purple_blist_node_get_sibling_next(cn)) {
     if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue;
 
-    for(bn = cn->child; bn; bn = bn->next) {
+    for(bn = purple_blist_node_get_first_child(cn);
+			bn;
+			bn = purple_blist_node_get_sibling_next(bn)) {
       PurpleBuddy *gb = (PurpleBuddy *) bn;
 
       if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue;
       
-      if(gb->account == acct) {
-	DEBUG_INFO("clearing %s from group\n", NSTR(gb->name));
+      if(purple_buddy_get_account(gb) == acct) {
+	DEBUG_INFO("clearing %s from group\n", NSTR(purple_buddy_get_name(gb)));
 	prune = g_list_prepend(prune, gb);
       }
     }
@@ -1059,7 +1065,7 @@
 
   g_return_if_fail(group != NULL);
 
-  DEBUG_INFO("pruning membership of group %s\n", NSTR(group->name));
+  DEBUG_INFO("pruning membership of group %s\n", NSTR(purple_group_get_name(group)));
 
   acct = purple_connection_get_account(gc);
   g_return_if_fail(acct != NULL);
@@ -1078,18 +1084,22 @@
 
   gn = (PurpleBlistNode *) group;
 
-  for(cn = gn->child; cn; cn = cn->next) {
+  for(cn = purple_blist_node_get_first_child(gn);
+		  cn;
+		  cn = purple_blist_node_get_sibling_next(cn)) {
     if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue;
 
-    for(bn = cn->child; bn; bn = bn->next) {
+    for(bn = purple_blist_node_get_first_child(cn);
+			bn;
+			bn = purple_blist_node_get_sibling_next(bn)) {
       PurpleBuddy *gb = (PurpleBuddy *) bn;
 
       if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue;
 
       /* if the account is correct and they're not in our table, mark
 	 them for pruning */
-      if(gb->account == acct && !g_hash_table_lookup(stusers, gb->name)) {
-	DEBUG_INFO("marking %s for pruning\n", NSTR(gb->name));
+      if(purple_buddy_get_account(gb) == acct && !g_hash_table_lookup(stusers, purple_buddy_get_name(gb))) {
+	DEBUG_INFO("marking %s for pruning\n", NSTR(purple_buddy_get_name(gb)));
 	prune = g_list_prepend(prune, gb);
       }
     }
@@ -1145,7 +1155,8 @@
   g_list_free(gtl);
 
   /* find all groups which should be pruned from the local list */
-  for(gn = blist->root; gn; gn = gn->next) {
+  for(gn = purple_blist_get_root(); gn;
+		  gn = purple_blist_node_get_sibling_next(gn)) {
     PurpleGroup *grp = (PurpleGroup *) gn;
     const char *gname, *owner;
     struct mwSametimeGroup *stgrp;
@@ -1164,12 +1175,12 @@
     /* we actually are synching by this key as opposed to the group
        title, which can be different things in the st list */
     gname = purple_blist_node_get_string(gn, GROUP_KEY_NAME);
-    if(! gname) gname = grp->name;
+    if(! gname) gname = purple_group_get_name(grp);
 
     stgrp = g_hash_table_lookup(stgroups, gname);
     if(! stgrp) {
       /* remove the whole group */
-      DEBUG_INFO("marking group %s for pruning\n", grp->name);
+      DEBUG_INFO("marking group %s for pruning\n", purple_group_get_name(grp));
       g_prune = g_list_prepend(g_prune, grp);
 
     } else {
@@ -1284,6 +1295,7 @@
 
   GString *str;
   char *tmp;
+  const char *gname;
 
   g_return_if_fail(pd != NULL);
 
@@ -1295,11 +1307,12 @@
   str = g_string_new(NULL);
 
   tmp = (char *) purple_blist_node_get_string(node, GROUP_KEY_NAME);
-
-  g_string_append_printf(str, _("<b>Group Title:</b> %s<br>"), group->name);
+  gname = purple_group_get_name(group);
+
+  g_string_append_printf(str, _("<b>Group Title:</b> %s<br>"), gname);
   g_string_append_printf(str, _("<b>Notes Group ID:</b> %s<br>"), tmp);
 
-  tmp = g_strdup_printf(_("Info for Group %s"), group->name);
+  tmp = g_strdup_printf(_("Info for Group %s"), gname);
 
   purple_notify_formatted(gc, tmp, _("Notes Address Book Information"),
 			NULL, str->str, NULL, NULL);
@@ -1356,19 +1369,24 @@
   PurpleBlistNode *gnode, *cnode, *bnode;
   GList *add_buds = NULL;
 
-  for(gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+  for(gnode = purple_blist_get_root(); gnode;
+		  gnode = purple_blist_node_get_sibling_next(gnode)) {
     if(! PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
 
-    for(cnode = gnode->child; cnode; cnode = cnode->next) {
+    for(cnode = purple_blist_node_get_first_child(gnode);
+			cnode;
+			cnode = purple_blist_node_get_sibling_next(cnode)) {
       if(! PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 	continue;
-      for(bnode = cnode->child; bnode; bnode = bnode->next) {
+      for(bnode = purple_blist_node_get_first_child(cnode);
+			  bnode;
+			  bnode = purple_blist_node_get_sibling_next(bnode)) {
 	PurpleBuddy *b;
 	if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 	  continue;
-	
+
 	b = (PurpleBuddy *)bnode;
-	if(b->account == acct) {
+	if(purple_buddy_get_account(b) == acct) {
 	  add_buds = g_list_append(add_buds, b);
 	}
       }
@@ -1388,7 +1406,6 @@
   PurpleConnection *gc;
   PurpleAccount *acct;
   struct mwStorageUnit *unit;
-  PurpleBuddyList *blist;
   PurpleBlistNode *l;
 
   gc = pd->gc;
@@ -1399,8 +1416,8 @@
   mwServiceStorage_load(pd->srvc_store, unit, fetch_blist_cb, pd, NULL); 
 
   /* find all the NAB groups and subscribe to them */
-  blist = purple_get_blist();
-  for(l = blist->root; l; l = l->next) {
+  for(l = purple_blist_get_root(); l;
+		  l = purple_blist_node_get_sibling_next(l)) {
     PurpleGroup *group = (PurpleGroup *) l;
     enum mwSametimeGroupType gt;
     const char *owner;
@@ -3239,10 +3256,10 @@
 static char *mw_prpl_status_text(PurpleBuddy *b) {
   PurpleConnection *gc;
   struct mwPurplePluginData *pd;
-  struct mwAwareIdBlock t = { mwAware_USER, b->name, NULL };
+  struct mwAwareIdBlock t = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL };
   const char *ret = NULL;
 
-  if ((gc = purple_account_get_connection(b->account))
+  if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
       && (pd = gc->proto_data))
     ret = mwServiceAware_getText(pd->srvc_aware, &t);
 
@@ -3299,13 +3316,13 @@
 static void mw_prpl_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) {
   PurpleConnection *gc;
   struct mwPurplePluginData *pd = NULL;
-  struct mwAwareIdBlock idb = { mwAware_USER, b->name, NULL };
+  struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL };
 
   const char *message = NULL;
   const char *status;
   char *tmp;
 
-  if ((gc = purple_account_get_connection(b->account))
+  if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
       && (pd = gc->proto_data))
      message = mwServiceAware_getText(pd->srvc_aware, &idb);
 
@@ -3321,7 +3338,7 @@
   }
 
   if(full && pd != NULL) {
-    tmp = user_supports_text(pd->srvc_aware, b->name);
+    tmp = user_supports_text(pd->srvc_aware, purple_buddy_get_name(b));
     if(tmp) {
 	  purple_notify_user_info_add_pair(user_info, _("Supports"), tmp);
       g_free(tmp);
@@ -3333,34 +3350,34 @@
   }
 }
 
-
-static GList *mw_prpl_status_types(PurpleAccount *acct) {
-  GList *types = NULL;
-  PurpleStatusType *type;
-
-  type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, MW_STATE_ACTIVE,
-			      NULL, TRUE);
-  purple_status_type_add_attr(type, MW_STATE_MESSAGE, _("Message"),
-			    purple_value_new(PURPLE_TYPE_STRING));
-  types = g_list_append(types, type);
-
-  type = purple_status_type_new(PURPLE_STATUS_AWAY, MW_STATE_AWAY,
-			      NULL, TRUE);
-  purple_status_type_add_attr(type, MW_STATE_MESSAGE, _("Message"),
-			    purple_value_new(PURPLE_TYPE_STRING));
-  types = g_list_append(types, type);
-  
-  type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, MW_STATE_BUSY,
-			      _("Do Not Disturb"), TRUE);
-  purple_status_type_add_attr(type, MW_STATE_MESSAGE, _("Message"),
-			    purple_value_new(PURPLE_TYPE_STRING));
-  types = g_list_append(types, type);
-  
-  type = purple_status_type_new(PURPLE_STATUS_OFFLINE, MW_STATE_OFFLINE,
-			      NULL, TRUE);
-  types = g_list_append(types, type);
-
-  return types;
+static GList *mw_prpl_status_types(PurpleAccount *acct)
+{
+	GList *types = NULL;
+	PurpleStatusType *type;
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+			MW_STATE_ACTIVE, NULL, TRUE, TRUE, FALSE,
+			MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
+			MW_STATE_AWAY, NULL, TRUE, TRUE, FALSE,
+			MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
+			MW_STATE_BUSY, _("Do Not Disturb"), TRUE, TRUE, FALSE,
+			MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
+			MW_STATE_OFFLINE, NULL, TRUE, TRUE, FALSE);
+	types = g_list_append(types, type);
+
+	return types;
 }
 
 
@@ -3383,7 +3400,7 @@
   struct mwConference *conf;
   struct mwIdBlock idb = { NULL, NULL };
 
-  acct = buddy->account;
+  acct = purple_buddy_get_account(buddy);
   gc = purple_account_get_connection(acct);
   pd = gc->proto_data;
   srvc = pd->srvc_conf;
@@ -3397,7 +3414,7 @@
   conf = mwConference_new(srvc, topic);
   mwConference_open(conf);
 
-  idb.user = buddy->name;
+  idb.user = (char *)purple_buddy_get_name(buddy);
   mwConference_invite(conf, &idb, invite);
 }
 
@@ -3417,7 +3434,7 @@
   
   g_return_if_fail(buddy != NULL);
 
-  acct = buddy->account;
+  acct = purple_buddy_get_account(buddy);
   g_return_if_fail(acct != NULL);
 
   gc = purple_account_get_connection(acct);
@@ -3437,7 +3454,7 @@
   msgA = _("Create conference with user");
   msgB = _("Please enter a topic for the new conference, and an invitation"
 	   " message to be sent to %s");
-  msg1 = g_strdup_printf(msgB, buddy->name);
+  msg1 = g_strdup_printf(msgB, purple_buddy_get_name(buddy));
 
   purple_request_fields(gc, _("New Conference"),
 		      msgA, msg1, fields,
@@ -3474,7 +3491,7 @@
       blist_menu_conf_create(buddy, msg);
 
     } else {
-      struct mwIdBlock idb = { buddy->name, NULL };
+      struct mwIdBlock idb = { (char *)purple_buddy_get_name(buddy), NULL };
       mwConference_invite(d, &idb, msg);
     }
   }
@@ -3495,7 +3512,7 @@
   const char *msgB;
   char *msg;
 
-  acct = buddy->account;
+  acct = purple_buddy_get_account(buddy);
   g_return_if_fail(acct != NULL);
 
   gc = purple_account_get_connection(acct);
@@ -3523,7 +3540,7 @@
   msgB = _("Select a conference from the list below to send an invite to"
 	   " user %s. Select \"Create New Conference\" if you'd like to"
 	   " create a new conference to invite this user to.");
-  msg = g_strdup_printf(msgB, buddy->name);
+  msg = g_strdup_printf(msgB, purple_buddy_get_name(buddy));
 
   purple_request_fields(gc, _("Invite to Conference"),
 		      msgA, msg, fields,
@@ -3545,7 +3562,7 @@
   g_return_if_fail(node != NULL);
   g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
-  acct = buddy->account;
+  acct = purple_buddy_get_account(buddy);
   g_return_if_fail(acct != NULL);
 
   gc = purple_account_get_connection(acct);
@@ -4186,8 +4203,8 @@
   if(b) {
     guint32 type;
 
-    if(b->server_alias) {
-		purple_notify_user_info_add_pair(user_info, _("Full Name"), b->server_alias);
+    if(purple_buddy_get_server_alias(b)) {
+		purple_notify_user_info_add_pair(user_info, _("Full Name"), purple_buddy_get_server_alias(b));
     }
 
     type = purple_blist_node_get_int((PurpleBlistNode *) b, BUDDY_KEY_CLIENT);
@@ -4329,10 +4346,10 @@
 
 static void notify_add(PurpleConnection *gc, GList *row, void *user_data) {
   BuddyAddData *data = user_data;
-  char *group_name = NULL;
+  const char *group_name = NULL;
   
   if (data && data->group) {
-    group_name = data->group->name;
+    group_name = purple_group_get_name(data->group);
   }
 
   purple_blist_request_add_buddy(purple_connection_get_account(gc),
@@ -4414,7 +4431,7 @@
 
   buddy = data->buddy;
 
-  gc = purple_account_get_connection(buddy->account);
+  gc = purple_account_get_connection(purple_buddy_get_account(buddy));
   pd = gc->proto_data;
 
   if(results)
@@ -4515,7 +4532,7 @@
 
   srvc = pd->srvc_resolve;
 
-  query = g_list_prepend(NULL, buddy->name);
+  query = g_list_prepend(NULL, (char *)purple_buddy_get_name(buddy));
   flags = mwResolveFlag_FIRST | mwResolveFlag_USERS;
 
   req = mwServiceResolve_resolve(srvc, query, flags, add_buddy_resolved,
@@ -4568,7 +4585,7 @@
 
     /* convert PurpleBuddy into a mwAwareIdBlock */
     idb->type = mwAware_USER;
-    idb->user = (char *) b->name;
+    idb->user = (char *) purple_buddy_get_name(b);
     idb->community = NULL;
 
     /* put idb into the list associated with the buddy's group */
@@ -4593,7 +4610,7 @@
 				 PurpleBuddy *buddy, PurpleGroup *group) {
 
   struct mwPurplePluginData *pd;
-  struct mwAwareIdBlock idb = { mwAware_USER, buddy->name, NULL };
+  struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(buddy), NULL };
   struct mwAwareList *list;
 
   GList *rem = g_list_prepend(NULL, &idb);
--- a/libpurple/protocols/silc/buddy.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/silc/buddy.c	Sat Mar 07 01:59:40 2009 +0000
@@ -322,9 +322,12 @@
 silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data)
 {
 	PurpleBuddy *buddy;
+	PurpleAccount *account;
 
 	buddy = (PurpleBuddy *)node;
-	silcpurple_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE);
+	account = purple_buddy_get_account(buddy);
+	silcpurple_buddy_keyagr_do(purple_account_get_connection(account),
+			purple_buddy_get_name(buddy), FALSE);
 }
 
 
@@ -341,12 +344,12 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	b = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(b->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(b));
 	sg = gc->proto_data;
 
 	/* Find client entry */
 	clients = silc_client_get_clients_local(sg->client, sg->conn,
-						b->name, FALSE);
+						purple_buddy_get_name(b), FALSE);
 	if (!clients)
 		return;
 
@@ -467,9 +470,9 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 
-	silcpurple_buddy_privkey(gc, buddy->name);
+	silcpurple_buddy_privkey(gc, purple_buddy_get_name(buddy));
 }
 
 
@@ -596,9 +599,9 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 
-	silcpurple_buddy_getkey(gc, buddy->name);
+	silcpurple_buddy_getkey(gc, purple_buddy_get_name(buddy));
 }
 
 static void
@@ -613,7 +616,7 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	b = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(b->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(b));
 	sg = gc->proto_data;
 
 	pkfile = purple_blist_node_get_string(node, "public-key");
@@ -624,7 +627,7 @@
 		return;
 	}
 
-	silcpurple_show_public_key(sg, b->name, public_key, NULL, NULL);
+	silcpurple_show_public_key(sg, purple_buddy_get_name(b), public_key, NULL, NULL);
 	silc_pkcs_public_key_free(public_key);
 }
 
@@ -686,6 +689,7 @@
 	if (b) {
 		/* See if we have this buddy's public key.  If we do use that
 		   to search the details. */
+		gpointer proto_data;
 		filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
 		if (filename) {
 			/* Call WHOIS.  The user info is displayed in the WHOIS
@@ -695,15 +699,15 @@
 			return;
 		}
 
-		if (!b->proto_data) {
+		if (!(proto_data = purple_buddy_get_protocol_data(b))) {
 			g_snprintf(tmp, sizeof(tmp),
-				   _("User %s is not present in the network"), b->name);
+				   _("User %s is not present in the network"), purple_buddy_get_name(b));
 			purple_notify_error(gc, _("User Information"),
 					  _("Cannot get user information"), tmp);
 			return;
 		}
 
-		client_entry = silc_client_get_client_by_id(client, conn, b->proto_data);
+		client_entry = silc_client_get_client_by_id(client, conn, proto_data);
 		if (client_entry) {
 			/* Call WHOIS.  The user info is displayed in the WHOIS
 			   command reply. */
@@ -721,7 +725,7 @@
 {
 	char tmp[512];
 	g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
-		   r->b->name);
+		   purple_buddy_get_name(r->b));
 	purple_notify_error(r->client->application, _("Add Buddy"), tmp,
 			    _("You cannot receive buddy notifications until you "
 			      "import his/her public key.  You can use the Get Public Key "
@@ -1033,7 +1037,7 @@
 
 	/* Now verify the public key */
 	r->offline_pk = silc_pkcs_public_key_encode(r->public_key, &r->offline_pk_len);
-	silcpurple_verify_public_key(r->client, r->conn, r->b->name,
+	silcpurple_verify_public_key(r->client, r->conn, purple_buddy_get_name(r->b),
 				     SILC_CONN_CLIENT, r->public_key,
 				     silcpurple_add_buddy_save, r);
 }
@@ -1071,7 +1075,7 @@
 {
 	char tmp[512];
 	g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
-		   r->b->name);
+		   purple_buddy_get_name(r->b));
 	purple_request_action(r->client->application, _("Add Buddy"), tmp,
 			      _("To add the buddy you must import his/her public key. "
 				"Press Import to import a public key."), 0,
@@ -1209,6 +1213,7 @@
 	const char *filename;
 	SilcClientEntry client_entry = NULL;
 	SilcUInt16 cmd_ident;
+	const char *name;
 
 	filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
 
@@ -1246,17 +1251,19 @@
 	silc_dlist_start(clients);
 	client_entry = silc_dlist_get(clients);
 
+	name = purple_buddy_get_name(b);
+
 	/* If we searched using public keys and more than one entry was found
 	   the same person is logged on multiple times. */
-	if (silc_dlist_count(clients) > 1 && r->pubkey_search && b->name) {
+	if (silc_dlist_count(clients) > 1 && r->pubkey_search && name) {
 		if (r->init) {
 			/* Find the entry that closest matches to the
 			   buddy nickname. */
 			SilcClientEntry entry;
 			silc_dlist_start(clients);
 			while ((entry = silc_dlist_get(clients))) {
-				if (!g_ascii_strncasecmp(b->name, entry->nickname,
-						 strlen(b->name))) {
+				if (!g_ascii_strncasecmp(name, entry->nickname,
+						 strlen(name))) {
 					client_entry = entry;
 					break;
 				}
@@ -1271,7 +1278,7 @@
 	/* The client was found.  Now get its public key and verify
 	   that before adding the buddy. */
 	memset(&userpk, 0, sizeof(userpk));
-	b->proto_data = silc_memdup(&client_entry->id, sizeof(client_entry->id));
+	purple_buddy_set_protocol_data(b, silc_memdup(&client_entry->id, sizeof(client_entry->id)));
 	r->client_id = client_entry->id;
 
 	/* Get the public key from attributes, if not present then
@@ -1335,7 +1342,7 @@
 	SilcClientConnection conn = sg->conn;
 	SilcPurpleBuddyRes r;
 	SilcBuffer attrs;
-	const char *filename, *name = b->name;
+	const char *filename, *name = purple_buddy_get_name(b);
 
 	r = silc_calloc(1, sizeof(*r));
 	if (!r)
@@ -1395,31 +1402,33 @@
 
 void silcpurple_send_buddylist(PurpleConnection *gc)
 {
-	PurpleBuddyList *blist;
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleBuddy *buddy;
 	PurpleAccount *account;
 
 	account = purple_connection_get_account(gc);
 
-	if ((blist = purple_get_blist()) != NULL)
+	for (gnode = purple_blist_get_root();
+			gnode != NULL;
+			gnode = purple_blist_node_get_sibling_next(gnode))
 	{
-		for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
+		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+			continue;
+		for (cnode = purple_blist_node_get_first_child(gnode);
+				cnode != NULL;
+				cnode = purple_blist_node_get_sibling_next(cnode))
 		{
-			if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
+			for (bnode = purple_blist_node_get_first_child(cnode);
+					bnode != NULL;
+					bnode = purple_blist_node_get_sibling_next(bnode))
 			{
-				if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
-				for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
-				{
-					if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
-						continue;
-					buddy = (PurpleBuddy *)bnode;
-					if (purple_buddy_get_account(buddy) == account)
-						silcpurple_add_buddy_i(gc, buddy, TRUE);
-				}
+				buddy = (PurpleBuddy *)bnode;
+				if (purple_buddy_get_account(buddy) == account)
+					silcpurple_add_buddy_i(gc, buddy, TRUE);
 			}
 		}
 	}
@@ -1428,7 +1437,7 @@
 void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 			   PurpleGroup *group)
 {
-	silc_free(buddy->proto_data);
+	silc_free(purple_buddy_get_protocol_data(buddy));
 }
 
 void silcpurple_idle_set(PurpleConnection *gc, int idle)
@@ -1469,10 +1478,12 @@
 
 char *silcpurple_status_text(PurpleBuddy *b)
 {
-	SilcPurple sg = b->account->gc->proto_data;
+	PurpleAccount *account = purple_buddy_get_account(b);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
-	SilcClientID *client_id = b->proto_data;
+	SilcClientID *client_id = purple_buddy_get_protocol_data(b);
 	SilcClientEntry client_entry;
 	SilcAttributePayload attr;
 	SilcAttributeMood mood = 0;
@@ -1533,10 +1544,12 @@
 
 void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
-	SilcPurple sg = b->account->gc->proto_data;
+	PurpleAccount *account = purple_buddy_get_account(b);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
-	SilcClientID *client_id = b->proto_data;
+	SilcClientID *client_id = purple_buddy_get_protocol_data(b);
 	SilcClientEntry client_entry;
 	char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
 	char tmp[256];
@@ -1610,12 +1623,12 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	b = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(b->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(b));
 	sg = gc->proto_data;
 
 	/* Call KILL */
 	silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
-				 b->name, "Killed by operator", NULL);
+				 purple_buddy_get_name(b), "Killed by operator", NULL);
 }
 
 typedef struct {
@@ -1633,7 +1646,8 @@
 
 GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
 {
-	PurpleConnection *gc = purple_account_get_connection(buddy->account);
+	PurpleAccount *account = purple_buddy_get_account(buddy);
+	PurpleConnection *gc = purple_account_get_connection(account);
 	SilcPurple sg = gc->proto_data;
 	SilcClientConnection conn = sg->conn;
 	const char *pkfile = NULL;
@@ -1645,7 +1659,7 @@
 	pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key");
 	client_entry = silc_client_get_client_by_id(sg->client,
 						    sg->conn,
-						    buddy->proto_data);
+						    purple_buddy_get_protocol_data(buddy));
 
 	if (client_entry &&
 	    silc_client_private_message_key_is_set(sg->client,
--- a/libpurple/protocols/silc/chat.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/silc/chat.c	Sat Mar 07 01:59:40 2009 +0000
@@ -182,7 +182,9 @@
 silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data)
 {
 	PurpleChat *chat = (PurpleChat *)node;
-	silcpurple_chat_getinfo(chat->account->gc, chat->components);
+	PurpleAccount *account = purple_chat_get_account(chat);
+	silcpurple_chat_getinfo(purple_account_get_connection(account),
+			purple_chat_get_components(chat));
 }
 
 
@@ -496,11 +498,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "+C", NULL);
 }
 
@@ -549,7 +551,7 @@
 	g_hash_table_replace(comp, "passphrase", g_strdup(passphrase));
 
 	cn = purple_chat_new(sg->account, alias, comp);
-	g = (PurpleGroup *)p->c->node.parent;
+	g = purple_chat_get_group(p->c);
 	purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c);
 
 	/* Associate to a real channel */
@@ -583,7 +585,7 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	p = silc_calloc(1, sizeof(*p));
@@ -591,7 +593,7 @@
 		return;
 	p->sg = sg;
 
-	p->channel = g_hash_table_lookup(chat->components, "channel");
+	p->channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
 	p->c = purple_blist_find_chat(sg->account, p->channel);
 
 	fields = purple_request_fields_new();
@@ -633,11 +635,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "-f", NULL);
 }
 
@@ -652,7 +654,7 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	if (!sg->conn)
@@ -663,7 +665,7 @@
 	   (default key). */
 
 	/* Call CMODE */
-	channel = g_hash_table_lookup(chat->components, "channel");
+	channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
 				 "+f", NULL);
 }
@@ -729,13 +731,13 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	if (!sg->conn)
 		return;
 
-	ch = g_strdup(g_hash_table_lookup(chat->components, "channel"));
+	ch = g_strdup(g_hash_table_lookup(purple_chat_get_components(chat), "channel"));
 	channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
 	if (!channel)
 		return;
@@ -764,11 +766,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "-t", NULL);
 }
 
@@ -782,11 +784,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "+t", NULL);
 }
 
@@ -800,11 +802,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "-p", NULL);
 }
 
@@ -818,11 +820,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "+p", NULL);
 }
 
@@ -836,11 +838,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "-s", NULL);
 }
 
@@ -854,11 +856,11 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
 
 	chat = (PurpleChat *) node;
-	gc = purple_account_get_connection(chat->account);
+	gc = purple_account_get_connection(purple_chat_get_account(chat));
 	sg = gc->proto_data;
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
-				 g_hash_table_lookup(chat->components, "channel"),
+				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
 				 "+s", NULL);
 }
 
@@ -877,8 +879,8 @@
 
 GList *silcpurple_chat_menu(PurpleChat *chat)
 {
-	GHashTable *components = chat->components;
-	PurpleConnection *gc = purple_account_get_connection(chat->account);
+	GHashTable *components = purple_chat_get_components(chat);
+	PurpleConnection *gc = purple_account_get_connection(purple_chat_get_account(chat));
 	SilcPurple sg = gc->proto_data;
 	SilcClientConnection conn = sg->conn;
 	const char *chname = NULL;
--- a/libpurple/protocols/silc/ops.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/silc/ops.c	Sat Mar 07 01:59:40 2009 +0000
@@ -431,6 +431,7 @@
 	va_list va;
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
+	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleConversation *convo;
 	SilcClientEntry client_entry, client_entry2;
 	SilcChannelEntry channel;
@@ -856,19 +857,22 @@
 				silc_free(pk);
 
 				/* Find buddy by associated public key */
-				for (gnode = purple_get_blist()->root; gnode;
-				     gnode = gnode->next) {
+				for (gnode = purple_blist_get_root(); gnode;
+				     gnode = purple_blist_node_get_sibling_next(gnode)) {
 					if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 						continue;
-					for (cnode = gnode->child; cnode; cnode = cnode->next) {
+					for (cnode = purple_blist_node_get_first_child(gnode);
+							cnode;
+							cnode = purple_blist_node_get_sibling_next(cnode)) {
 						if( !PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 							continue;
-						for (bnode = cnode->child; bnode;
-						     bnode = bnode->next) {
+						for (bnode = purple_blist_node_get_first_child(cnode);
+								bnode;
+								bnode = purple_blist_node_get_sibling_next(bnode)) {
 							if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 								continue;
 							b = (PurpleBuddy *)bnode;
-							if (b->account != gc->account)
+							if (purple_buddy_get_account(b) != account)
 								continue;
 							f = purple_blist_node_get_string(bnode, "public-key");
 							if (f && !strcmp(f, buf))
@@ -889,9 +893,9 @@
 				}
 			}
 
-			silc_free(b->proto_data);
-			b->proto_data = silc_memdup(&client_entry->id,
-						    sizeof(client_entry->id));
+			silc_free(purple_buddy_get_protocol_data(b));
+			purple_buddy_set_protocol_data(b, silc_memdup(&client_entry->id,
+						    sizeof(client_entry->id)));
 			if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
 				break;
 			} else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
--- a/libpurple/protocols/simple/simple.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/simple/simple.c	Sat Mar 07 01:59:40 2009 +0000
@@ -196,33 +196,41 @@
 {
 	struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
 	struct simple_buddy *b;
-	if(strncmp(buddy->name, "sip:", 4)) {
-		gchar *buf = g_strdup_printf("sip:%s", buddy->name);
+	const char *name = purple_buddy_get_name(buddy);
+	if(strncmp(name, "sip:", 4)) {
+		gchar *buf = g_strdup_printf("sip:%s", name);
 		purple_blist_rename_buddy(buddy, buf);
 		g_free(buf);
 	}
-	if(!g_hash_table_lookup(sip->buddies, buddy->name)) {
+	if(!g_hash_table_lookup(sip->buddies, name)) {
 		b = g_new0(struct simple_buddy, 1);
-		purple_debug_info("simple", "simple_add_buddy %s\n", buddy->name);
-		b->name = g_strdup(buddy->name);
+		purple_debug_info("simple", "simple_add_buddy %s\n", name);
+		b->name = g_strdup(name);
 		g_hash_table_insert(sip->buddies, b->name, b);
 	} else {
-		purple_debug_info("simple", "buddy %s already in internal list\n", buddy->name);
+		purple_debug_info("simple", "buddy %s already in internal list\n", name);
 	}
 }
 
 static void simple_get_buddies(PurpleConnection *gc) {
 	PurpleBlistNode *gnode, *cnode, *bnode;
+	PurpleAccount *account;
 
 	purple_debug_info("simple", "simple_get_buddies\n");
 
-	for(gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+	account = purple_connection_get_account(gc);
+	for(gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
+		for(cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
-			for(bnode = cnode->child; bnode; bnode = bnode->next) {
+			for(bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode)) {
 				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
-				if(((PurpleBuddy*)bnode)->account == gc->account)
+				if(purple_buddy_get_account((PurpleBuddy*)bnode) == account)
 					simple_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
 			}
 		}
@@ -231,9 +239,10 @@
 
 static void simple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
+	const char *name = purple_buddy_get_name(buddy);
 	struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
-	struct simple_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
-	g_hash_table_remove(sip->buddies, buddy->name);
+	struct simple_buddy *b = g_hash_table_lookup(sip->buddies, name);
+	g_hash_table_remove(sip->buddies, name);
 	g_free(b->name);
 	g_free(b);
 }
@@ -922,7 +931,7 @@
 			purple_blist_add_buddy(b, NULL, g, NULL);
 			purple_blist_alias_buddy(b, uri);
 			bs = g_new0(struct simple_buddy, 1);
-			bs->name = g_strdup(b->name);
+			bs->name = g_strdup(purple_buddy_get_name(b));
 			g_hash_table_insert(sip->buddies, bs->name, bs);
 		}
 		xmlnode_free(isc);
--- a/libpurple/protocols/toc/toc.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/toc/toc.c	Sat Mar 07 01:59:40 2009 +0000
@@ -119,7 +119,7 @@
 
 #define TOC_CONNECT_STEPS 3
 
-static void toc_login_callback(gpointer, gint, PurpleInputCondition);
+static void toc_login_callback(gpointer, gint, const gchar *);
 static void toc_callback(gpointer, gint, PurpleInputCondition);
 
 /* ok. this function used to take username/password, and return 0 on success.
@@ -152,7 +152,7 @@
 	}
 }
 
-static void toc_login_callback(gpointer data, gint source, PurpleInputCondition cond)
+static void toc_login_callback(gpointer data, gint source, const gchar *error_message)
 {
 	PurpleConnection *gc = data;
 	struct toc_data *tdt;
@@ -260,7 +260,8 @@
 	}
 }
 
-char *escape_message(const char *msg)
+static char *
+escape_message(const char *msg)
 {
 	char *ret;
 	int i, j;
@@ -305,7 +306,8 @@
  * Duplicates the input string, replacing each \n with a <BR>, and 
  * escaping a few other characters.
  */
-char *escape_text(const char *msg)
+static char *
+escape_text(const char *msg)
 {
 	char *ret;
 	int i, j;
@@ -435,7 +437,7 @@
 	if (buflen < ntohs(hdr->len)) {
 		/* fake like there's a read error */
 		purple_debug(PURPLE_DEBUG_ERROR, "toc",
-				   "buffer too small (have %d, need %d)\n",
+				   "buffer too small (have %" G_GSIZE_FORMAT ", need %d)\n",
 				   buflen, ntohs(hdr->len));
 		return -1;
 	}
--- a/libpurple/protocols/yahoo/yahoo.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sat Mar 07 01:59:40 2009 +0000
@@ -385,7 +385,7 @@
 	for (i = list; i; i = i->next) {
 		b = i->data;
 		g = purple_buddy_get_group(b);
-		if (!purple_utf8_strcasecmp(group, g->name)) {
+		if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) {
 			purple_debug(PURPLE_DEBUG_MISC, "yahoo",
 				"Oh good, %s is in the right group (%s).\n", name, group);
 			list = g_slist_delete_link(list, i);
@@ -423,7 +423,8 @@
 	for (i = list; i; i = i->next) {
 		b = i->data;
 		g = purple_buddy_get_group(b);
-		purple_debug(PURPLE_DEBUG_MISC, "yahoo", "Deleting Buddy %s from group %s.\n", name, g->name);
+		purple_debug(PURPLE_DEBUG_MISC, "yahoo", "Deleting Buddy %s from group %s.\n", name,
+				purple_group_get_name(g));
 		purple_blist_remove_buddy(b);
 	}
 }
@@ -2027,21 +2028,23 @@
 		return;
 
 	group = purple_buddy_get_group(buddy);
-	name = g_strdup(buddy->name);
-	account = buddy->account;
+	name = g_strdup(purple_buddy_get_name(buddy));
+	account = purple_buddy_get_account(buddy);
 
 	purple_debug(PURPLE_DEBUG_INFO, "blist",
-		"Removing '%s' from buddy list.\n", buddy->name);
+		"Removing '%s' from buddy list.\n", name);
 	purple_account_remove_buddy(account, buddy, group);
 	purple_blist_remove_buddy(buddy);
 
-	serv_add_deny(account->gc, name);
+	serv_add_deny(purple_account_get_connection(account), name);
 
 	g_free(name);
 }
 
-static void keep_buddy(PurpleBuddy *b) {
-	purple_privacy_deny_remove(b->account, b->name, 1);
+static void keep_buddy(PurpleBuddy *b)
+{
+	purple_privacy_deny_remove(purple_buddy_get_account(b),
+			purple_buddy_get_name(b), 1);
 }
 
 static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) {
@@ -3128,11 +3131,12 @@
 	YahooFriend *f;
 	PurplePresence *presence;
 
-	if (!b || !(account = b->account) || !(gc = purple_account_get_connection(account)) ||
-					     !(yd = gc->proto_data))
+	if (!b || !(account = purple_buddy_get_account(b)) ||
+			!(gc = purple_account_get_connection(account)) ||
+			!(yd = gc->proto_data))
 		return NULL;
 
-	f = yahoo_friend_find(gc, b->name);
+	f = yahoo_friend_find(gc, purple_buddy_get_name(b));
 	if (!f) {
 		return "not-authorized";
 	}
@@ -3192,7 +3196,7 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 	yd = gc->proto_data;
 	id = yd->conf_id;
 
@@ -3204,7 +3208,7 @@
 	yahoo_c_join(gc, components);
 	g_hash_table_destroy(components);
 
-	yahoo_c_invite(gc, id, "Join my conference...", buddy->name);
+	yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy));
 }
 
 static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) {
@@ -3213,9 +3217,9 @@
 	int presence_val = GPOINTER_TO_INT(data);
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-
-	yahoo_friend_update_presence(gc, buddy->name, presence_val);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+
+	yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val);
 }
 
 static void yahoo_game(PurpleBlistNode *node, gpointer data) {
@@ -3233,10 +3237,10 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 	yd = (struct yahoo_data *) gc->proto_data;
 
-	f = yahoo_friend_find(gc, buddy->name);
+	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
 	if (!f)
 		return;
 
@@ -3258,8 +3262,10 @@
 	YahooFriend *f = NULL;
 	const char *msg;
 	char *msg2;
-
-	f = yahoo_friend_find(b->account->gc, b->name);
+	PurpleAccount *account;
+
+	account = purple_buddy_get_account(b);
+	f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
 	if (!f)
 		return g_strdup(_("Not on server list"));
 
@@ -3288,8 +3294,10 @@
 	char *escaped;
 	char *status = NULL;
 	const char *presence = NULL;
-
-	f = yahoo_friend_find(b->account->gc, b->name);
+	PurpleAccount *account;
+
+	account = purple_buddy_get_account(b);
+	f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
 	if (!f)
 		status = g_strdup_printf("\n%s", _("Not on server list"));
 	else {
@@ -3340,7 +3348,7 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 
 	yahoo_add_buddy(gc, buddy, NULL);
 }
@@ -3354,9 +3362,9 @@
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
 	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-
-	yahoo_chat_goto(gc, buddy->name);
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+
+	yahoo_chat_goto(gc, purple_buddy_get_name(buddy));
 }
 
 static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
@@ -3400,9 +3408,10 @@
 static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data)
 {
 	PurpleBuddy *b = (PurpleBuddy *)node;
-	PurpleConnection *gc = b->account->gc;
-
-	yahoo_doodle_initiate(gc, b->name);
+	PurpleAccount *account = purple_buddy_get_account(b);
+	PurpleConnection *gc = purple_account_get_connection(account);
+
+	yahoo_doodle_initiate(gc, purple_buddy_get_name(b));
 }
 
 static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
@@ -3410,12 +3419,12 @@
 	GList *m = NULL;
 	PurpleMenuAction *act;
 
-	PurpleConnection *gc = purple_account_get_connection(buddy->account);
+	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 	struct yahoo_data *yd = gc->proto_data;
 	static char buf2[1024];
 	YahooFriend *f;
 
-	f = yahoo_friend_find(gc, buddy->name);
+	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
 
 	if (!f && !yd->wm) {
 		act = purple_menu_action_new(_("Add Buddy"),
@@ -3942,19 +3951,20 @@
 	const char *group = NULL;
 	char *group2;
 	YahooFriend *f;
+	const char *bname;
 
 	if (!yd->logged_in)
 		return;
 
-	if (!purple_privacy_check(purple_connection_get_account(gc),
-			purple_buddy_get_name(buddy)))
+	bname = purple_buddy_get_name(buddy);
+	if (!purple_privacy_check(purple_connection_get_account(gc), bname))
 		return;
 
-	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
+	f = yahoo_friend_find(gc, bname);
 
 	g = purple_buddy_get_group(buddy);
 	if (g)
-		group = g->name;
+		group = purple_group_get_name(g);
 	else
 		group = "Buddies";
 
@@ -3967,7 +3977,7 @@
 	                  1, purple_connection_get_display_name(gc),
 	                  302, "319",
 	                  300, "319",
-	                  7, buddy->name,
+	                  7, bname,
 	                  334, "0",
 	                  301, "319",
 	                  303, "319"
@@ -3981,19 +3991,22 @@
 static void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
-        struct yahoo_packet *pkt;
+	struct yahoo_packet *pkt;
 	GSList *buddies, *l;
 	PurpleGroup *g;
 	gboolean remove = TRUE;
 	char *cg;
-
-	if (!(yahoo_friend_find(gc, buddy->name)))
+	const char *bname, *gname;
+
+	bname = purple_buddy_get_name(buddy);
+	if (!(yahoo_friend_find(gc, bname)))
 		return;
 
-	buddies = purple_find_buddies(purple_connection_get_account(gc), buddy->name);
+	gname = purple_group_get_name(group);
+	buddies = purple_find_buddies(purple_connection_get_account(gc), bname);
 	for (l = buddies; l; l = l->next) {
 		g = purple_buddy_get_group(l->data);
-		if (purple_utf8_strcasecmp(group->name, g->name)) {
+		if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) {
 			remove = FALSE;
 			break;
 		}
@@ -4002,12 +4015,12 @@
 	g_slist_free(buddies);
 
 	if (remove)
-		g_hash_table_remove(yd->friends, buddy->name);
-
-	cg = yahoo_string_encode(gc, group->name, NULL);
+		g_hash_table_remove(yd->friends, bname);
+
+	cg = yahoo_string_encode(gc, gname, NULL);
 	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0);
 	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
-	                  7, buddy->name, 65, cg);
+	                  7, bname, 65, cg);
 	yahoo_packet_send_and_free(pkt, yd);
 	g_free(cg);
 }
@@ -4116,7 +4129,7 @@
 	struct yahoo_packet *pkt;
 	char *gpn, *gpo;
 
-	gpn = yahoo_string_encode(gc, group->name, NULL);
+	gpn = yahoo_string_encode(gc, purple_group_get_name(group), NULL);
 	gpo = yahoo_string_encode(gc, old_name, NULL);
 	if (!strcmp(gpn, gpo)) {
 		g_free(gpn);
@@ -4271,7 +4284,7 @@
 }
 
 /* This may not be the best way to do this, but we find the first key w/o a value
- * and assume it is the screenname */
+ * and assume it is the buddy name */
 static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data)
 {
 	char **retval = user_data;
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Sat Mar 07 01:59:40 2009 +0000
@@ -569,7 +569,7 @@
 		checksum &= ~g;
 	}
 
-	purple_debug_misc("yahoo", "Calculated buddy icon checksum: %d", checksum);
+	purple_debug_misc("yahoo", "Calculated buddy icon checksum: %d\n", checksum);
 
 	return checksum;
 } 
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Sat Mar 07 01:59:40 2009 +0000
@@ -699,8 +699,9 @@
 			info_data->name);
 
 	if (b) {
-		if(b->alias && b->alias[0]) {
-			char *aliastext = g_markup_escape_text(b->alias, -1);
+		const char *balias = purple_buddy_get_local_buddy_alias(b);
+		if(balias && balias[0]) {
+			char *aliastext = g_markup_escape_text(balias, -1);
 			purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext); 
 			g_free(aliastext);
 		}
@@ -715,7 +716,7 @@
 		/* Add the normal tooltip pairs */
 		yahoo_tooltip_text(b, user_info, TRUE);
 
-		if ((f = yahoo_friend_find(info_data->gc, b->name))) {
+		if ((f = yahoo_friend_find(info_data->gc, purple_buddy_get_name(b)))) {
 			const char *ip;
 			if ((ip = yahoo_friend_get_ip(f)))
 				purple_notify_user_info_add_pair(user_info, _("IP Address"), ip);
@@ -1213,7 +1214,9 @@
 				 * in which case the user may or may not actually exist.
 				 * Hence this extra step.
 				 */
-				f = yahoo_friend_find(b->account->gc, b->name);
+				PurpleAccount *account = purple_buddy_get_account(b);
+				f = yahoo_friend_find(purple_account_get_connection(account),
+						purple_buddy_get_name(b));
 			}
 			str = f ? _("Could not retrieve the user's profile. "
 					  "This most likely is a temporary server-side problem. "
--- a/libpurple/protocols/yahoo/yahoochat.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Sat Mar 07 01:59:40 2009 +0000
@@ -519,7 +519,7 @@
 		GList *l;
 		GList *flags = NULL;
 		for (l = members; l; l = l->next)
-			flags = g_list_append(flags, GINT_TO_POINTER(PURPLE_CBFLAGS_NONE));
+			flags = g_list_prepend(flags, GINT_TO_POINTER(PURPLE_CBFLAGS_NONE));
 		if (c && purple_conv_chat_has_left(PURPLE_CONV_CHAT(c))) {
 			/* this might be a hack, but oh well, it should nicely */
 			char *tmpmsg;
--- a/libpurple/protocols/zephyr/zephyr.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Sat Mar 07 01:59:40 2009 +0000
@@ -732,7 +732,7 @@
 	return ret;
 }
 
-static gboolean pending_zloc(zephyr_account *zephyr,char *who)
+static gboolean pending_zloc(zephyr_account *zephyr, const char *who)
 {
 	GList *curr;
 
@@ -771,6 +771,8 @@
 			int nlocs;
 			char *user;
 			PurpleBuddy *b;
+			const char *bname;
+
 			/* XXX add real error reporting */
 			if (ZParseLocations(&notice, NULL, &nlocs, &user) != ZERR_NONE)
 				return;
@@ -780,15 +782,19 @@
 				b = purple_find_buddy(gc->account,stripped_user);
 				g_free(stripped_user);
 			}
-			if ((b && pending_zloc(zephyr,b->name)) || pending_zloc(zephyr,user)) {
+
+			bname = b ? purple_buddy_get_name(b) : NULL;
+			if ((b && pending_zloc(zephyr,bname)) || pending_zloc(zephyr,user)) {
 				ZLocations_t locs;
 				int one = 1;
 				PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
 				char *tmp;
+				const char *balias;
 
-				purple_notify_user_info_add_pair(user_info, _("User"), (b ? b->name : user));
-				if (b && b->alias)
-					purple_notify_user_info_add_pair(user_info, _("Alias"), b->alias);
+				purple_notify_user_info_add_pair(user_info, _("User"), (b ? bname : user));
+				balias = purple_buddy_get_local_buddy_alias(b);
+				if (b && balias)
+					purple_notify_user_info_add_pair(user_info, _("Alias"), balias);
 
 				if (!nlocs) {
 					purple_notify_user_info_add_pair(user_info, NULL, _("Hidden or not logged-in"));
@@ -801,14 +807,14 @@
 					purple_notify_user_info_add_pair(user_info, _("Location"), tmp);
 					g_free(tmp);
 				}
-				purple_notify_userinfo(gc, (b ? b->name : user), 
+				purple_notify_userinfo(gc, (b ? bname : user), 
 						     user_info, NULL, NULL);
 				purple_notify_user_info_destroy(user_info);
 			} else {
 				if (nlocs>0) 
-					purple_prpl_got_user_status(gc->account, b ? b->name : user, "available", NULL);
+					purple_prpl_got_user_status(gc->account, b ? bname : user, "available", NULL);
 				else 
-					purple_prpl_got_user_status(gc->account, b ? b->name : user, "offline", NULL);
+					purple_prpl_got_user_status(gc->account, b ? bname : user, "offline", NULL);
 			}
 
 			g_free(user);
@@ -1141,6 +1147,7 @@
 				/* XXX fix */
 				char *user; 
 				PurpleBuddy *b;
+				const char *bname;
 				int nlocs = 0;
 				parse_tree *locations;
 				gchar *locval;
@@ -1160,15 +1167,18 @@
 					nlocs = 1;
 				}
 	
-				if ((b && pending_zloc(zephyr,b->name)) || pending_zloc(zephyr,user) || pending_zloc(zephyr,local_zephyr_normalize(zephyr,user))){
+				bname = b ? purple_buddy_get_name(b) : NULL;
+				if ((b && pending_zloc(zephyr,bname)) || pending_zloc(zephyr,user) || pending_zloc(zephyr,local_zephyr_normalize(zephyr,user))){
 					PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
 					char *tmp;
+					const char *balias;
 
-					purple_notify_user_info_add_pair(user_info, _("User"), (b ? b->name : user));
+					purple_notify_user_info_add_pair(user_info, _("User"), (b ? bname : user));
 
-					if (b && b->alias)
-						purple_notify_user_info_add_pair(user_info, _("Alias"), b->alias);
-											
+					balias = b ? purple_buddy_get_local_buddy_alias(b) : NULL;
+					if (balias)
+						purple_notify_user_info_add_pair(user_info, _("Alias"), balias);
+
 					if (!nlocs) {
 						purple_notify_user_info_add_pair(user_info, NULL, _("Hidden or not logged-in"));
 					} else {
@@ -1179,14 +1189,14 @@
 						g_free(tmp);
 					}
 
-					purple_notify_userinfo(gc, b ? b->name : user,
+					purple_notify_userinfo(gc, b ? bname : user,
 							     user_info, NULL, NULL);
 					purple_notify_user_info_destroy(user_info);
 				} else {
 					if (nlocs>0) 
-						purple_prpl_got_user_status(gc->account, b ? b->name : user, "available", NULL);
+						purple_prpl_got_user_status(gc->account, b ? bname : user, "available", NULL);
 					else 
-						purple_prpl_got_user_status(gc->account, b ? b->name : user, "offline", NULL);
+						purple_prpl_got_user_status(gc->account, b ? bname : user, "offline", NULL);
 				}
 			}
 			else if (!g_ascii_strncasecmp(spewtype,"subscribed",10)) {
@@ -1246,38 +1256,44 @@
 
 static gint check_loc(gpointer_data)
 {
-        PurpleBlistNode *gnode, *cnode, *bnode;
-        ZLocations_t locations;
-        int numlocs;
-        int one = 1;
+	PurpleBlistNode *gnode, *cnode, *bnode;
+	ZLocations_t locations;
+	int numlocs;
+	int one = 1;
 
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for (cnode = gnode->child; cnode; cnode = cnode->next) {
+		for (cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for (bnode = cnode->child; bnode; bnode = bnode->next) {
+			for (bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode)) {
 				PurpleBuddy *b = (PurpleBuddy *) bnode;
 
 				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
-				if (b->account->gc == zgc) {
+				if (purple_buddy_get_account(b)->gc == zgc) {
 					char *chk;
-                                        chk = local_zephyr_normalize(b->name);
-                                        ZLocateUser(chk,&numlocs, ZAUTH);
-                                        if (numlocs) {
-                                                int i;
-                                                for(i=0;i<numlocs;i++) {
-                                                        ZGetLocations(&locations,&one);
-                                                        serv_got_update(zgc,b->name,1,0,0,0,0);
-                                                }
-                                        }
-                                }
-                        }
-                }
-        }
-        return TRUE;
+					const char *bname = purple_buddy_get_name(b);
+					chk = local_zephyr_normalize(bname);
+					ZLocateUser(chk,&numlocs, ZAUTH);
+					if (numlocs) {
+						int i;
+						for(i=0;i<numlocs;i++) {
+							ZGetLocations(&locations,&one);
+							serv_got_update(zgc,bname,1,0,0,0,0);
+						}
+					}
+				}
+			}
+		}
+	}
+	return TRUE;
 }
 
 #else
@@ -1288,6 +1304,7 @@
 	ZAsyncLocateData_t ald;
 	PurpleConnection *gc = (PurpleConnection *)data;
 	zephyr_account *zephyr = gc->proto_data;
+	PurpleAccount *account = purple_connection_get_account(gc);
 
 	if (use_zeph02(zephyr)) {
 		ald.user = NULL;
@@ -1295,22 +1312,28 @@
 		ald.version = NULL;
 	}
 
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+	for (gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for (cnode = gnode->child; cnode; cnode = cnode->next) {
+		for (cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for (bnode = cnode->child; bnode; bnode = bnode->next) {
+			for (bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode)) {
 				PurpleBuddy *b = (PurpleBuddy *) bnode;
 
 				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
-				if (b->account->gc == gc) {
+				if (purple_buddy_get_account(b) == account) {
 					const char *chk;
+					const char *name = purple_buddy_get_name(b);
 
-					chk = local_zephyr_normalize(zephyr,b->name);
-					purple_debug_info("zephyr","chk: %s b->name %s\n",chk,b->name);
+					chk = local_zephyr_normalize(zephyr,name);
+					purple_debug_info("zephyr","chk: %s b->name %s\n",chk,name);
 					/* XXX add real error reporting */
 					/* doesn't matter if this fails or not; we'll just move on to the next one */
 					if (use_zeph02(zephyr)) {
@@ -1323,9 +1346,9 @@
 							for(i=0;i<numlocs;i++) {
 								ZGetLocations(&locations,&one);
 								if (nlocs>0) 
-									purple_prpl_got_user_status(gc->account,b->name,"available",NULL);
+									purple_prpl_got_user_status(account,name,"available",NULL);
 								else 
-									purple_prpl_got_user_status(gc->account,b->name,"offline",NULL);
+									purple_prpl_got_user_status(account,name,"offline",NULL);
 							}
 						}
 #else
@@ -1936,6 +1959,7 @@
 	PurpleBuddy *b;
 	char *fname;
 	FILE *fd;
+	PurpleAccount *account;
 	zephyr_account* zephyr = gc->proto_data;
 	fname = g_strdup_printf("%s/.anyone", purple_home_dir());
 	fd = g_fopen(fname, "w");
@@ -1944,18 +1968,25 @@
 		return;
 	}
 
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
+	account = purple_connection_get_account(gc);
+	for (gnode = purple_blist_get_root();
+			gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
 		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for (cnode = gnode->child; cnode; cnode = cnode->next) {
+		for (cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for (bnode = cnode->child; bnode; bnode = bnode->next) {
+			for (bnode = purple_blist_node_get_first_child(cnode);
+					bnode;
+					bnode = purple_blist_node_get_sibling_next(bnode)) {
 				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 				b = (PurpleBuddy *) bnode;
-				if (b->account == gc->account) {
-					gchar *stripped_user = zephyr_strip_local_realm(zephyr,b->name);
+				if (purple_buddy_get_account(b) == account) {
+					gchar *stripped_user = zephyr_strip_local_realm(zephyr, purple_buddy_get_name(b));
 					fprintf(fd, "%s\n", stripped_user);
 					g_free(stripped_user);
 				}
@@ -2498,26 +2529,31 @@
 	PurpleBlistNode *gnode, *cnode;
 
 	/* XXX needs to be %host%,%canon%, and %me% clean */
-	for(gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
+	for(gnode = purple_blist_get_root(); gnode;
+			gnode = purple_blist_node_get_sibling_next(gnode)) {
+		for(cnode = purple_blist_node_get_first_child(gnode);
+				cnode;
+				cnode = purple_blist_node_get_sibling_next(cnode)) {
 			PurpleChat *chat = (PurpleChat*)cnode;
 			char *zclass, *inst, *recip;
 			char** triple;
+			GHashTable *components;
 			if(!PURPLE_BLIST_NODE_IS_CHAT(cnode))
 				continue;
-			if(chat->account !=account)
-				continue;
-			if(!(zclass = g_hash_table_lookup(chat->components, "class")))
+			if(purple_chat_get_account(chat) != account)
 				continue;
-			if(!(inst = g_hash_table_lookup(chat->components, "instance")))
+			components = purple_chat_get_components(chat);
+			if(!(zclass = g_hash_table_lookup(components, "class")))
+				continue;
+			if(!(inst = g_hash_table_lookup(components, "instance")))
 				inst = g_strdup("");
-			if(!(recip = g_hash_table_lookup(chat->components, "recipient")))
+			if(!(recip = g_hash_table_lookup(components, "recipient")))
 				recip = g_strdup("");
 			/*			purple_debug_info("zephyr","in zephyr_find_blist_chat name: %s\n",name?name:""); */
 			triple = g_strsplit(name,",",3);
 			if (!g_ascii_strcasecmp(triple[0],zclass) && !g_ascii_strcasecmp(triple[1],inst) && !g_ascii_strcasecmp(triple[2],recip))
 				return chat;
-			
+
 		}
 	}
 	return NULL;
--- a/libpurple/proxy.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/proxy.c	Sat Mar 07 01:59:40 2009 +0000
@@ -207,6 +207,16 @@
 	return global_proxy_info;
 }
 
+void
+purple_global_proxy_set_info(PurpleProxyInfo *info)
+{
+	g_return_if_fail(info != NULL);
+
+	purple_proxy_info_destroy(global_proxy_info);
+
+	global_proxy_info = info;
+}
+
 static PurpleProxyInfo *
 purple_gnome_proxy_get_info(void)
 {
@@ -228,13 +238,13 @@
 	g_free(err);
 	err = NULL;
 
-	if (!strcmp(tmp, "none\n")) {
+	if (purple_strequal(tmp, "none\n")) {
 		info.type = PURPLE_PROXY_NONE;
 		g_free(tmp);
 		return &info;
 	}
 
-	if (strcmp(tmp, "manual\n")) {
+	if (purple_strequal(tmp, "manual\n")) {
 		/* Unknown setting.  Fallback to using our global proxy settings. */
 		g_free(tmp);
 		return purple_global_proxy_get_info();
@@ -263,7 +273,7 @@
 	g_free(err);
 	err = NULL;
 
-	if (!strcmp(tmp, "true\n"))
+	if (purple_strequal(tmp, "true\n"))
 		use_same_proxy = TRUE;
 	g_free(tmp);
 	tmp = NULL;
@@ -2300,31 +2310,31 @@
 {
 	PurpleProxyInfo *info = purple_global_proxy_get_info();
 
-	if (!strcmp(name, "/purple/proxy/type")) {
+	if (purple_strequal(name, "/purple/proxy/type")) {
 		int proxytype;
 		const char *type = value;
 
-		if (!strcmp(type, "none"))
+		if (purple_strequal(type, "none"))
 			proxytype = PURPLE_PROXY_NONE;
-		else if (!strcmp(type, "http"))
+		else if (purple_strequal(type, "http"))
 			proxytype = PURPLE_PROXY_HTTP;
-		else if (!strcmp(type, "socks4"))
+		else if (purple_strequal(type, "socks4"))
 			proxytype = PURPLE_PROXY_SOCKS4;
-		else if (!strcmp(type, "socks5"))
+		else if (purple_strequal(type, "socks5"))
 			proxytype = PURPLE_PROXY_SOCKS5;
-		else if (!strcmp(type, "envvar"))
+		else if (purple_strequal(type, "envvar"))
 			proxytype = PURPLE_PROXY_USE_ENVVAR;
 		else
 			proxytype = -1;
 
 		purple_proxy_info_set_type(info, proxytype);
-	} else if (!strcmp(name, "/purple/proxy/host"))
+	} else if (purple_strequal(name, "/purple/proxy/host"))
 		purple_proxy_info_set_host(info, value);
-	else if (!strcmp(name, "/purple/proxy/port"))
+	else if (purple_strequal(name, "/purple/proxy/port"))
 		purple_proxy_info_set_port(info, GPOINTER_TO_INT(value));
-	else if (!strcmp(name, "/purple/proxy/username"))
+	else if (purple_strequal(name, "/purple/proxy/username"))
 		purple_proxy_info_set_username(info, value);
-	else if (!strcmp(name, "/purple/proxy/password"))
+	else if (purple_strequal(name, "/purple/proxy/password"))
 		purple_proxy_info_set_password(info, value);
 }
 
--- a/libpurple/proxy.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/proxy.h	Sat Mar 07 01:59:40 2009 +0000
@@ -186,6 +186,14 @@
  */
 PurpleProxyInfo *purple_global_proxy_get_info(void);
 
+/**
+ * Set purple's global proxy information.
+ *
+ * @param info     The proxy information.
+ * @since 2.6.0
+ */
+void purple_global_proxy_set_info(PurpleProxyInfo *info);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/prpl.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/prpl.c	Sat Mar 07 01:59:40 2009 +0000
@@ -75,7 +75,7 @@
 purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name)
 {
 	g_return_if_fail(type != NULL);
-	
+
 	type->icon_name = name;
 }
 
@@ -400,7 +400,7 @@
 	PurpleConversation *conv;
 	gboolean (*send_attention)(PurpleConnection *, const char *, guint);
 	PurpleBuddy *buddy;
-	const char *alias;	
+	const char *alias;
 	gchar *description;
 	time_t mtime;
 
@@ -425,7 +425,7 @@
 	} else {
 		description = g_strdup_printf(_("Requesting %s's attention..."), alias);
 	}
-	
+
 	flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM;
 
 	purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n",
@@ -511,7 +511,7 @@
 	for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
 		plugin = (PurplePlugin *)l->data;
 
-		if (!strcmp(plugin->info->id, id))
+		if (purple_strequal(plugin->info->id, id))
 			return plugin;
 	}
 
--- a/libpurple/prpl.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/prpl.h	Sat Mar 07 01:59:40 2009 +0000
@@ -178,9 +178,11 @@
 	OPT_PROTO_USE_POINTSIZE = 0x00000100,
 
 	/**
-	 * Set the Register button active when screenname is not given.
+	 * Set the Register button active even when the username has not
+	 * been specified.
 	 *
-	 * Gadu-Gadu doesn't need a screenname to register new account.
+	 * Gadu-Gadu doesn't need a username to register new account (because
+	 * usernames are assigned by the server).
 	 */
 	OPT_PROTO_REGISTER_NOSCREENNAME = 0x00000200,
 
@@ -211,7 +213,7 @@
 
 	/**
 	 * Returns the base icon name for the given buddy and account.
-	 * If buddy is NULL and the account is non-NULL, it will return the 
+	 * If buddy is NULL and the account is non-NULL, it will return the
 	 * name to use for the account's icon. If both are NULL, it will
 	 * return the name to use for the protocol's icon.
 	 *
@@ -413,7 +415,7 @@
 	 * reasons.
 	 */
 	void (*unregister_user)(PurpleAccount *, PurpleAccountUnregistrationCb cb, void *user_data);
-	
+
 	/* Attention API for sending & receiving zaps/nudges/buzzes etc. */
 	gboolean (*send_attention)(PurpleConnection *gc, const char *username, guint type);
 	GList *(*get_attention_types)(PurpleAccount *acct);
@@ -594,7 +596,7 @@
 /*@{*/
 
 /**
- * Notifies Purple that an account's idle state and time have changed.
+ * Notifies Purple that our account's idle state and time have changed.
  *
  * This is meant to be called from protocol plugins.
  *
@@ -606,7 +608,7 @@
 								time_t idle_time);
 
 /**
- * Notifies Purple of an account's log-in time.
+ * Notifies Purple of our account's log-in time.
  *
  * This is meant to be called from protocol plugins.
  *
@@ -616,7 +618,7 @@
 void purple_prpl_got_account_login_time(PurpleAccount *account, time_t login_time);
 
 /**
- * Notifies Purple that an account's status has changed.
+ * Notifies Purple that our account's status has changed.
  *
  * This is meant to be called from protocol plugins.
  *
@@ -627,13 +629,14 @@
  */
 void purple_prpl_got_account_status(PurpleAccount *account,
 								  const char *status_id, ...) G_GNUC_NULL_TERMINATED;
+
 /**
- * Notifies Purple that a user's idle state and time have changed.
+ * Notifies Purple that a buddy's idle state and time have changed.
  *
  * This is meant to be called from protocol plugins.
  *
  * @param account   The account the user is on.
- * @param name      The screen name of the user.
+ * @param name      The name of the buddy.
  * @param idle      The user's idle state.
  * @param idle_time The user's idle time.  This is the time at
  *                  which the user became idle, in seconds since
@@ -644,24 +647,24 @@
 							 gboolean idle, time_t idle_time);
 
 /**
- * Notifies Purple of a user's log-in time.
+ * Notifies Purple of a buddy's log-in time.
  *
  * This is meant to be called from protocol plugins.
  *
  * @param account    The account the user is on.
- * @param name       The screen name of the user.
+ * @param name       The name of the buddy.
  * @param login_time The user's log-in time.
  */
 void purple_prpl_got_user_login_time(PurpleAccount *account, const char *name,
 								   time_t login_time);
 
 /**
- * Notifies Purple that a user's status has been activated.
+ * Notifies Purple that a buddy's status has been activated.
  *
  * This is meant to be called from protocol plugins.
  *
  * @param account   The account the user is on.
- * @param name      The screen name of the user.
+ * @param name      The name of the buddy.
  * @param status_id The status ID.
  * @param ...       A NULL-terminated list of attribute IDs and values,
  *                  beginning with the value for @a attr_id.
@@ -670,19 +673,19 @@
 							   const char *status_id, ...) G_GNUC_NULL_TERMINATED;
 
 /**
- * Notifies libpurple that a user's status has been deactivated
+ * Notifies libpurple that a buddy's status has been deactivated
  *
  * This is meant to be called from protocol plugins.
  *
  * @param account   The account the user is on.
- * @param name      The screen name of the user.
+ * @param name      The name of the buddy.
  * @param status_id The status ID.
  */
 void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name,
 					const char *status_id);
- 
+
 /**
- * Informs the server that an account's status changed.
+ * Informs the server that our account's status changed.
  *
  * @param account    The account the user is on.
  * @param old_status The previous status.
@@ -703,37 +706,43 @@
  */
 GList *purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence);
 
-/** Send an attention request message.
+/**
+ * Send an attention request message.
  *
  * @param gc The connection to send the message on.
  * @param who Whose attention to request.
  * @param type_code An index into the prpl's attention_types list determining the type
- * 	of the attention request command to send. 0 if prpl only defines one
- * 	(for example, Yahoo and MSN), but some protocols define more (MySpaceIM).
+ *        of the attention request command to send. 0 if prpl only defines one
+ *        (for example, Yahoo and MSN), but some protocols define more (MySpaceIM).
  *
  * Note that you can't send arbitrary PurpleAttentionType's, because there is
  * only a fixed set of attention commands.
+ *
  * @since 2.5.0
  */
 void purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code);
 
-/** Process an incoming attention message. 
+/**
+ * Process an incoming attention message.
  *
  * @param gc The connection that received the attention message.
  * @param who Who requested your attention.
  * @param type_code An index into the prpl's attention_types list determining the type
- * 	of the attention request command to send.
+ *        of the attention request command to send.
+ *
  * @since 2.5.0
  */
 void purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code);
 
-/** Process an incoming attention message in a chat. 
+/**
+ * Process an incoming attention message in a chat.
  *
  * @param gc The connection that received the attention message.
  * @param id The chat id.
  * @param who Who requested your attention.
  * @param type_code An index into the prpl's attention_types list determining the type
- * 	of the attention request command to send. 
+ *        of the attention request command to send.
+ *
  * @since 2.5.0
  */
 void purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code);
--- a/libpurple/purple-send-async	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/purple-send-async	Sat Mar 07 01:59:40 2009 +0000
@@ -2,18 +2,18 @@
 
 METHOD_NAME=$1
 
-if test -z "$METHOD_NAME" 
+if test -z "$METHOD_NAME"
 then
-    cat <<EOF 
-This program calls purple API functions using DBus.  As opposed to purple-send, 
+    cat <<EOF
+This program calls purple API functions using DBus.  As opposed to purple-send,
 it does not print the return value.
 
 Usage:
 
    $0 method-name type1:parameter1 type2:parameter2 ...
 
-This shell script just invokes dbus-send, see man dbus-send for how 
-to specify the parameters.  
+This shell script just invokes dbus-send, see man dbus-send for how
+to specify the parameters.
 
 Examples:
 
@@ -27,4 +27,4 @@
 shift
 dbus-send --dest=im.pidgin.purple.PurpleService --type=method_call /im/pidgin/purple/PurpleObject im.pidgin.purple.PurpleInterface.$METHOD_NAME "$@"
 
-echo 
+echo
--- a/libpurple/purple-url-handler	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/purple-url-handler	Sat Mar 07 01:59:40 2009 +0000
@@ -207,11 +207,7 @@
 
     def correct_server(account):
         username = cpurple.PurpleAccountGetUsername(account)
-        user_split = (username.split("@"))
-        # Not all accounts have a split, so append an empty string so the
-        # [1] doesn't throw an IndexError.
-        user_split.append("")
-        return (server == user_split[1])
+        return ("@" in username) and (server == (username.split("@"))[1])
 
     account = findaccount(protocol, matcher=correct_server)
 
--- a/libpurple/request.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/request.c	Sat Mar 07 01:59:40 2009 +0000
@@ -23,6 +23,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#define _PURPLE_REQUEST_C_
+
 #include "internal.h"
 
 #include "notify.h"
@@ -137,6 +139,23 @@
 	return purple_request_field_is_required(field);
 }
 
+gpointer
+purple_request_field_get_ui_data(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+
+	return field->ui_data;
+}
+
+void
+purple_request_field_set_ui_data(PurpleRequestField *field,
+                                 gpointer ui_data)
+{
+	g_return_if_fail(field != NULL);
+
+	field->ui_data = ui_data;
+}
+
 gboolean
 purple_request_fields_all_required_filled(const PurpleRequestFields *fields)
 {
@@ -443,6 +462,14 @@
 	return field->type;
 }
 
+PurpleRequestFieldGroup *
+purple_request_field_get_group(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+
+	return field->group;
+}
+
 const char *
 purple_request_field_get_id(const PurpleRequestField *field)
 {
--- a/libpurple/request.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/request.h	Sat Mar 07 01:59:40 2009 +0000
@@ -30,6 +30,9 @@
 #include <glib-object.h>
 #include <glib.h>
 
+/** @copydoc _PurpleRequestField */
+typedef struct _PurpleRequestField PurpleRequestField;
+
 #include "account.h"
 
 #define PURPLE_DEFAULT_ACTION_NONE	-1
@@ -93,10 +96,11 @@
 
 } PurpleRequestFieldGroup;
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_REQUEST_C_)
 /**
  * A request field.
  */
-typedef struct
+struct _PurpleRequestField
 {
 	PurpleRequestFieldType type;
 	PurpleRequestFieldGroup *group;
@@ -176,7 +180,8 @@
 
 	void *ui_data;
 
-} PurpleRequestField;
+};
+#endif
 
 /**
  * Request UI operations.
@@ -521,6 +526,17 @@
 PurpleRequestFieldType purple_request_field_get_type(const PurpleRequestField *field);
 
 /**
+ * Returns the group for the field.
+ *
+ * @param field The field.
+ *
+ * @return The UI data.
+ *
+ * @since 2.6.0
+ */
+PurpleRequestFieldGroup *purple_request_field_get_group(const PurpleRequestField *field);
+
+/**
  * Returns the ID of a field.
  *
  * @param field The field.
@@ -565,6 +581,30 @@
  */
 gboolean purple_request_field_is_required(const PurpleRequestField *field);
 
+/**
+ * Returns the ui_data for a field.
+ *
+ * @param field The field.
+ *
+ * @return The UI data.
+ *
+ * @since 2.6.0
+ */
+gpointer purple_request_field_get_ui_data(const PurpleRequestField *field);
+
+/**
+ * Sets the ui_data for a field.
+ *
+ * @param field The field.
+ * @param ui_data The UI data.
+ *
+ * @return The UI data.
+ *
+ * @since 2.6.0
+ */
+void purple_request_field_set_ui_data(PurpleRequestField *field,
+                                      gpointer ui_data);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/savedstatuses.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/savedstatuses.c	Sat Mar 07 01:59:40 2009 +0000
@@ -461,7 +461,7 @@
 	ret = g_new0(PurpleSavedStatus, 1);
 
 	attrib = xmlnode_get_attrib(status, "transient");
-	if ((attrib == NULL) || (strcmp(attrib, "true")))
+	if (!purple_strequal(attrib, "true"))
 	{
 		/* Read the title */
 		attrib = xmlnode_get_attrib(status, "name");
@@ -940,7 +940,7 @@
 	for (iter = saved_statuses; iter != NULL; iter = iter->next)
 	{
 		status = (PurpleSavedStatus *)iter->data;
-		if ((status->title != NULL) && !strcmp(status->title, title))
+		if (purple_strequal(status->title, title))
 			return status;
 	}
 
@@ -975,8 +975,7 @@
 		status = (PurpleSavedStatus *)iter->data;
 		if ((status->type == type) && purple_savedstatus_is_transient(status) &&
 			!purple_savedstatus_has_substatuses(status) &&
-			(((status->message == NULL) && (message == NULL)) ||
-			((status->message != NULL) && (message != NULL) && !strcmp(status->message, message))))
+			purple_strequal(status->message, message))
 		{
 			return status;
 		}
--- a/libpurple/server.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/server.c	Sat Mar 07 01:59:40 2009 +0000
@@ -152,7 +152,7 @@
 	auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
 	if((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
 			!purple_presence_is_available(presence) &&
-			strcmp(auto_reply_pref, "never")) {
+			!purple_strequal(auto_reply_pref, "never")) {
 
 		struct last_auto_response *lar;
 		lar = get_last_auto_response(gc, name);
@@ -172,7 +172,7 @@
 
 	if(gc)
 		prpl = purple_connection_get_prpl(gc);
-	
+
 	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
@@ -188,7 +188,7 @@
 
 	if(gc)
 		prpl = purple_connection_get_prpl(gc);
-	
+
 	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
@@ -230,7 +230,7 @@
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
 	if(b && prpl_info && prpl_info->alias_buddy) {
-		prpl_info->alias_buddy(gc, b->name, b->alias);
+		prpl_info->alias_buddy(gc, purple_buddy_get_name(b), purple_buddy_get_local_buddy_alias(b));
 	}
 }
 
@@ -247,20 +247,20 @@
 
 	while (buddies != NULL)
 	{
+		const char *server_alias;
+
 		b = buddies->data;
 		buddies = g_slist_delete_link(buddies, buddies);
 
-		if((b->server_alias == NULL && alias == NULL) ||
-		    (b->server_alias && alias && !strcmp(b->server_alias, alias)))
-		{
+		server_alias = purple_buddy_get_server_alias(b);
+
+		if (purple_strequal(server_alias, alias))
 			continue;
-		}
 
 		purple_blist_server_alias_buddy(b, alias);
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, b->name, account);
-		if(conv != NULL && alias != NULL &&
-		   who != NULL && strcmp(alias, who))
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, purple_buddy_get_name(b), account);
+		if (conv != NULL && alias != NULL && purple_strequal(alias, who))
 		{
 			char *escaped = g_markup_escape_text(who, -1);
 			char *escaped2 = g_markup_escape_text(alias, -1);
@@ -289,11 +289,13 @@
 	buddies = purple_find_buddies(account, who);
 
 	while(buddies != NULL) {
+		const char *balias;
 		b = buddies->data;
 
 		buddies = g_slist_delete_link(buddies, buddies);
 
-		if((!b->alias && !alias) || (b->alias && alias && !strcmp(b->alias, alias)))
+		balias = purple_buddy_get_local_buddy_alias(b);
+		if (purple_strequal(balias, alias))
 			continue;
 
 		purple_blist_alias_buddy(b, alias);
@@ -367,7 +369,9 @@
 
 	if(gc && og && ng) {
 		if (prpl_info && prpl_info->group_buddy) {
-			prpl_info->group_buddy(gc, b->name, og->name, ng->name);
+			prpl_info->group_buddy(gc, purple_buddy_get_name(b),
+			                       purple_group_get_name(og),
+								   purple_group_get_name(ng));
 		}
 	}
 }
@@ -666,8 +670,8 @@
 		if ((primitive == PURPLE_STATUS_AVAILABLE) ||
 			(primitive == PURPLE_STATUS_INVISIBLE) ||
 			mobile ||
-		    !strcmp(auto_reply_pref, "never") ||
-		    (!purple_presence_is_idle(presence) && !strcmp(auto_reply_pref, "awayidle")))
+		    purple_strequal(auto_reply_pref, "never") ||
+		    (!purple_presence_is_idle(presence) && purple_strequal(auto_reply_pref, "awayidle")))
 		{
 			g_free(name);
 			return;
--- a/libpurple/server.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/server.h	Sat Mar 07 01:59:40 2009 +0000
@@ -55,8 +55,8 @@
 void serv_move_buddy(PurpleBuddy *, PurpleGroup *, PurpleGroup *);
 int  serv_send_im(PurpleConnection *, const char *, const char *, PurpleMessageFlags flags);
 
-/** Get information about an account's attention commands, from the prpl. 
- * 
+/** Get information about an account's attention commands, from the prpl.
+ *
  * @return The attention command numbered 'code' from the prpl's attention_types, or NULL.
  */
 PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code);
@@ -76,14 +76,14 @@
  */
 void serv_send_attention(PurpleConnection *gc, const char *who, guint type_code);
 
-/** Process an incoming attention message. 
+/** Process an incoming attention message.
  *
  * @deprecated Use purple_prpl_got_attention() instead.
  *
  * @param gc The connection that received the attention message.
  * @param who Who requested your attention.
  * @param type_code An index into the prpl's attention_types list determining the type
- * 	of the attention request command to send. 
+ * 	of the attention request command to send.
  */
 void serv_got_attention(PurpleConnection *gc, const char *who, guint type_code);
 
@@ -108,7 +108,7 @@
  * aliases are the aliases or display names that buddies set for themselves.
  *
  * @param gc The connection on which the alias was received.
- * @param who The screen name of the buddy whose alias was received.
+ * @param who The name of the buddy whose alias was received.
  * @param alias The alias that was received.
  */
 void purple_serv_got_private_alias(PurpleConnection *gc, const char *who, const char *alias);
@@ -180,7 +180,7 @@
  *                function should be g_str_equal().
  */
 void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data);
-	
+
 void serv_got_chat_left(PurpleConnection *g, int id);
 void serv_got_chat_in(PurpleConnection *g, int id, const char *who,
 					  PurpleMessageFlags flags, const char *message, time_t mtime);
--- a/libpurple/signals.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/signals.c	Sat Mar 07 01:59:40 2009 +0000
@@ -487,7 +487,7 @@
 	}
 
 #ifdef HAVE_DBUS
-	purple_dbus_signal_emit_purple(signal, signal_data->num_values, 
+	purple_dbus_signal_emit_purple(signal, signal_data->num_values,
 				   signal_data->values, args);
 #endif	/* HAVE_DBUS */
 
@@ -539,7 +539,7 @@
 
 #ifdef HAVE_DBUS
 	G_VA_COPY(tmp, args);
-	purple_dbus_signal_emit_purple(signal, signal_data->num_values, 
+	purple_dbus_signal_emit_purple(signal, signal_data->num_values,
 				   signal_data->values, tmp);
 	va_end(tmp);
 #endif	/* HAVE_DBUS */
--- a/libpurple/signals.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/signals.h	Sat Mar 07 01:59:40 2009 +0000
@@ -137,7 +137,7 @@
 /**
  * Connects a signal handler to a signal for a particular object.
  * (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.
  *
--- a/libpurple/smiley.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/smiley.c	Sat Mar 07 01:59:40 2009 +0000
@@ -722,7 +722,7 @@
 		smiley = purple_smiley_new_from_stream(shortcut, smiley_data,
 				smiley_data_len);
 	}
-	
+
 	return smiley;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-theme-loader.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,111 @@
+/*
+ * SoundThemeLoader for libpurple
+ *
+ * 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 "sound-theme-loader.h"
+#include "sound-theme.h"
+#include "util.h"
+#include "xmlnode.h"
+
+/*****************************************************************************
+ * Sound Theme Builder
+ *****************************************************************************/
+
+static PurpleTheme *
+purple_sound_loader_build(const gchar *dir)
+{
+	xmlnode *root_node = NULL, *sub_node;
+	gchar *filename_full, *data;
+	PurpleSoundTheme *theme = NULL;
+
+	/* Find the theme file */
+	g_return_val_if_fail(dir != NULL, NULL);
+	filename_full = g_build_filename(dir, "theme.xml", NULL);
+
+	if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR))
+		root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-theme-loader");
+
+	g_free(filename_full);
+	g_return_val_if_fail(root_node != NULL, NULL);
+
+	/* Parse the tree */
+	sub_node = xmlnode_get_child(root_node, "description");
+	data = xmlnode_get_data(sub_node);
+
+	if (xmlnode_get_attrib(root_node, "name") != NULL) {
+		theme = g_object_new(PURPLE_TYPE_SOUND_THEME,
+				"type", "sound",
+				"name", xmlnode_get_attrib(root_node, "name"),
+				"author", xmlnode_get_attrib(root_node, "author"),
+				"image", xmlnode_get_attrib(root_node, "image"),
+				"directory", dir,
+				"description", data, NULL);
+
+		sub_node = xmlnode_get_child(root_node, "event");
+
+		while (sub_node) {
+			purple_sound_theme_set_file(theme,
+					xmlnode_get_attrib(sub_node, "name"),
+					xmlnode_get_attrib(sub_node, "file"));
+			sub_node = xmlnode_get_next_twin(sub_node);
+		}
+	}
+
+	xmlnode_free(root_node);
+	g_free(data);
+	return PURPLE_THEME(theme);
+}
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+purple_sound_theme_loader_class_init(PurpleSoundThemeLoaderClass *klass)
+{
+	PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass);
+
+	loader_klass->purple_theme_loader_build = purple_sound_loader_build;
+}
+
+GType
+purple_sound_theme_loader_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleSoundThemeLoaderClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)purple_sound_theme_loader_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PurpleSoundThemeLoader),
+			0, /* n_preallocs */
+			NULL, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PURPLE_TYPE_THEME_LOADER,
+				"PurpleSoundThemeLoader", &info, 0);
+	}
+	return type;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-theme-loader.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,71 @@
+/**
+ * @file sound-theme-loader.h  Purple Sound Theme Loader Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * 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 PURPLE_SOUND_THEME_LOADER_H
+#define PURPLE_SOUND_THEME_LOADER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme-loader.h"
+
+/**
+ * A purple sound theme loader. extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build sound themes
+ *
+ * PurpleSoundThemeLoader is a GObject.
+ */
+typedef struct _PurpleSoundThemeLoader        PurpleSoundThemeLoader;
+typedef struct _PurpleSoundThemeLoaderClass   PurpleSoundThemeLoaderClass;
+
+#define PURPLE_TYPE_SOUND_THEME_LOADER            (purple_sound_theme_loader_get_type())
+#define PURPLE_SOUND_THEME_LOADER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoader))
+#define PURPLE_SOUND_THEME_LOADER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoaderClass))
+#define PURPLE_IS_SOUND_THEME_LOADER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SOUND_THEME_LOADER))
+#define PURPLE_IS_SOUND_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SOUND_THEME_LOADER))
+#define PURPLE_SOUND_THEME_LOADER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SOUND_THEME_LOADER, PurpleSoundThemeLoaderClass))
+
+struct _PurpleSoundThemeLoader
+{
+	PurpleThemeLoader parent;
+};
+
+struct _PurpleSoundThemeLoaderClass
+{
+	PurpleThemeLoaderClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Theme-Loader API                                         */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_sound_theme_loader_get_type(void);
+
+G_END_DECLS
+#endif /* PURPLE_SOUND_THEME_LOADER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-theme.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,158 @@
+/*
+ * Sound Themes for libpurple
+ *
+ * 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 "sound-theme.h"
+
+#define PURPLE_SOUND_THEME_GET_PRIVATE(Gobject) \
+	((PurpleSoundThemePrivate *) ((PURPLE_SOUND_THEME(Gobject))->priv))
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+typedef struct {
+	/* used to store filenames of diffrent sounds */
+	GHashTable *sound_files;
+} PurpleSoundThemePrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+purple_sound_theme_init(GTypeInstance *instance,
+		gpointer klass)
+{
+	PurpleSoundThemePrivate *priv;
+
+	(PURPLE_SOUND_THEME(instance))->priv = g_new0(PurpleSoundThemePrivate, 1);
+
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(instance);
+
+	priv->sound_files = g_hash_table_new_full(g_str_hash,
+			g_str_equal, g_free, g_free);
+}
+
+static void
+purple_sound_theme_finalize(GObject *obj)
+{
+	PurpleSoundThemePrivate *priv;
+
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(obj);
+
+	g_hash_table_destroy(priv->sound_files);
+
+	parent_class->finalize(obj);
+}
+
+static void
+purple_sound_theme_class_init(PurpleSoundThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	obj_class->finalize = purple_sound_theme_finalize;
+}
+
+GType
+purple_sound_theme_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleSoundThemeClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)purple_sound_theme_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PurpleSoundTheme),
+			0, /* n_preallocs */
+			purple_sound_theme_init, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PURPLE_TYPE_THEME,
+				"PurpleSoundTheme", &info, 0);
+	}
+	return type;
+}
+
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+const gchar *
+purple_sound_theme_get_file(PurpleSoundTheme *theme,
+		const gchar *event)
+{
+	PurpleSoundThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_SOUND_THEME(theme), NULL);
+
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(theme);
+
+	return g_hash_table_lookup(priv->sound_files, event);
+}
+
+gchar *
+purple_sound_theme_get_file_full(PurpleSoundTheme *theme,
+		const gchar *event)
+{
+	const gchar *filename;
+
+	g_return_val_if_fail(PURPLE_IS_SOUND_THEME(theme), NULL);
+
+	filename = purple_sound_theme_get_file(theme, event);
+
+	g_return_val_if_fail(filename, NULL);
+
+	return g_build_filename(purple_theme_get_dir(PURPLE_THEME(theme)), filename, NULL);
+}
+
+void
+purple_sound_theme_set_file(PurpleSoundTheme *theme,
+		const gchar *event,
+		const gchar *filename)
+{
+	PurpleSoundThemePrivate *priv;
+	g_return_if_fail(PURPLE_IS_SOUND_THEME(theme));
+
+	priv = PURPLE_SOUND_THEME_GET_PRIVATE(theme);
+
+	if (filename != NULL)
+		g_hash_table_replace(priv->sound_files,
+				g_strdup(event), g_strdup(filename));
+	else
+		g_hash_table_remove(priv->sound_files, event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-theme.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,105 @@
+/**
+ * @file sound-theme.h  Purple Sound Theme Abstact Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * 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 PURPLE_SOUND_THEME_H
+#define PURPLE_SOUND_THEME_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme.h"
+#include "sound.h"
+
+/**
+ * extends PurpleTheme (theme.h)
+ * A purple sound theme.
+ * This is an object for Purple to represent a sound theme.
+ *
+ * PurpleSoundTheme is a PurpleTheme Object.
+ */
+typedef struct _PurpleSoundTheme        PurpleSoundTheme;
+typedef struct _PurpleSoundThemeClass   PurpleSoundThemeClass;
+
+#define PURPLE_TYPE_SOUND_THEME             (purple_sound_theme_get_type())
+#define PURPLE_SOUND_THEME(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SOUND_THEME, PurpleSoundTheme))
+#define PURPLE_SOUND_THEME_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemeClass))
+#define PURPLE_IS_SOUND_THEME(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SOUND_THEME))
+#define PURPLE_IS_SOUND_THEME_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SOUND_THEME))
+#define PURPLE_SOUND_THEME_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemeClass))
+
+struct _PurpleSoundTheme
+{
+	PurpleTheme parent;
+	gpointer priv;
+};
+
+struct _PurpleSoundThemeClass
+{
+	PurpleThemeClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Sound Theme API                                          */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_sound_theme_get_type(void);
+
+/**
+ * Returns a copy of the filename for the sound event.
+ *
+ * @param event The purple sound event to look up.
+ *
+ * @returns The filename of the sound event.
+ */
+const gchar *purple_sound_theme_get_file(PurpleSoundTheme *theme,
+		const gchar *event);
+
+/**
+ * Returns a copy of the directory and filename for the sound event
+ *
+ * @param event The purple sound event to look up
+ *
+ * @returns The directory + '/' + filename of the sound event.  This is
+ *          a newly allocated string that should be freed with g_free.
+ */
+gchar *purple_sound_theme_get_file_full(PurpleSoundTheme *theme,
+		const gchar *event);
+
+/**
+ * Sets the filename for a given sound event
+ *
+ * @param event		the purple sound event to look up
+ * @param filename		the name of the file to be used for the event
+ */
+void purple_sound_theme_set_file(PurpleSoundTheme *theme,
+		const gchar *event,
+		const gchar *filename);
+
+G_END_DECLS
+#endif /* PURPLE_SOUND_THEME_H */
--- a/libpurple/sound.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/sound.c	Sat Mar 07 01:59:40 2009 +0000
@@ -25,6 +25,8 @@
 #include "blist.h"
 #include "prefs.h"
 #include "sound.h"
+#include "sound-theme-loader.h"
+#include "theme-manager.h"
 
 static PurpleSoundUiOps *sound_ui_ops = NULL;
 
@@ -134,6 +136,8 @@
 	purple_prefs_add_none("/purple/sound");
 	purple_prefs_add_int("/purple/sound/while_status", STATUS_AVAILABLE);
 	memset(last_played, 0, sizeof(last_played));
+
+	purple_theme_manager_register_type(g_object_new(PURPLE_TYPE_SOUND_THEME_LOADER, "type", "sound", NULL));
 }
 
 void
--- a/libpurple/sslconn.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/sslconn.c	Sat Mar 07 01:59:40 2009 +0000
@@ -216,7 +216,7 @@
 	/* TODO: Move this elsewhere */
 	gsc->verifier = purple_certificate_find_verifier("x509","tls_cached");
 
-    
+
 	ops = purple_ssl_get_ops();
 	ops->connectfunc(gsc);
 
--- a/libpurple/sslconn.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/sslconn.h	Sat Mar 07 01:59:40 2009 +0000
@@ -66,7 +66,7 @@
 
 	/** File descriptor used to refer to the socket */
 	int fd;
-	/** Glib event source ID; used to refer to the received data callback 
+	/** Glib event source ID; used to refer to the received data callback
 	 * in the glib eventloop */
 	guint inpa;
 	/** Data related to the underlying TCP connection */
@@ -134,7 +134,7 @@
 	 *              list can be guaranteed.
 	 */
 	GList * (* get_peer_certificates)(PurpleSslConnection * gsc);
-	
+
 	void (*_purple_reserved2)(void);
 	void (*_purple_reserved3)(void);
 	void (*_purple_reserved4)(void);
--- a/libpurple/status.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/status.c	Sat Mar 07 01:59:40 2009 +0000
@@ -23,6 +23,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#define _PURPLE_STATUS_C_
+
 #include "internal.h"
 
 #include "blist.h"
@@ -203,7 +205,7 @@
 
     for (i = 0; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
     {
-        if (!strcmp(id, status_primitive_map[i].id))
+		if (purple_strequal(id, status_primitive_map[i].id))
             return status_primitive_map[i].type;
     }
 
@@ -451,7 +453,7 @@
 	{
 		PurpleStatusAttr *attr = (PurpleStatusAttr *)l->data;
 
-		if (!strcmp(purple_status_attr_get_id(attr), id))
+		if (purple_strequal(purple_status_attr_get_id(attr), id))
 			return attr;
 	}
 
@@ -477,7 +479,7 @@
 	{
 		status_type = status_types->data;
 
-		if (!strcmp(id, status_type->id))
+		if (purple_strequal(id, status_type->id))
 			return status_type;
 
 		status_types = status_types->next;
@@ -612,7 +614,8 @@
 
 		if (old_status != NULL)
 		{
-			tmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias, buddy->name,
+			tmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias,
+			                      purple_buddy_get_name(buddy),
 			                      purple_status_get_name(old_status),
 			                      purple_status_get_name(new_status));
 			logtmp = g_markup_escape_text(tmp, -1);
@@ -623,19 +626,21 @@
 
 			if (purple_status_is_active(new_status))
 			{
-				tmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name,
+				tmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias,
+				                      purple_buddy_get_name(buddy),
 				                      purple_status_get_name(new_status));
 				logtmp = g_markup_escape_text(tmp, -1);
 			}
 			else
 			{
-				tmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name,
+				tmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias,
+				                      purple_buddy_get_name(buddy),
 				                      purple_status_get_name(new_status));
 				logtmp = g_markup_escape_text(tmp, -1);
 			}
 		}
 
-		log = purple_account_get_log(buddy->account, FALSE);
+		log = purple_account_get_log(purple_buddy_get_account(buddy), FALSE);
 		if (log != NULL)
 		{
 			purple_log_write(log, PURPLE_MESSAGE_SYSTEM, buddy_alias,
@@ -779,12 +784,8 @@
 		{
 			const gchar *string_data = l->data;
 			l = l->next;
-			if (((string_data == NULL) && (value->data.string_data == NULL)) ||
-				((string_data != NULL) && (value->data.string_data != NULL) &&
-				!strcmp(string_data, value->data.string_data)))
-			{
+			if (purple_strequal(string_data, value->data.string_data))
 				continue;
-			}
 			purple_status_set_attr_string(status, id, string_data);
 			changed = TRUE;
 		}
@@ -1146,13 +1147,13 @@
 	PurpleAccount *account;
 
 	g_return_val_if_fail(buddy != NULL, NULL);
-	account = buddy->account;
+	account = purple_buddy_get_account(buddy);
 
 	presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_BUDDY);
 
-	presence->u.buddy.name    = g_strdup(buddy->name);
-	presence->u.buddy.account = buddy->account;
-	presence->statuses = purple_prpl_get_statuses(buddy->account, presence);
+	presence->u.buddy.name    = g_strdup(purple_buddy_get_name(buddy));
+	presence->u.buddy.account = account;
+	presence->statuses = purple_prpl_get_statuses(account, presence);
 
 	presence->u.buddy.buddy = buddy;
 
@@ -1248,12 +1249,13 @@
 		time_t current_time, gboolean old_idle, gboolean idle)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleAccount *account = purple_buddy_get_account(buddy);
 
 	if (!old_idle && idle)
 	{
 		if (purple_prefs_get_bool("/purple/logging/log_system"))
 		{
-			PurpleLog *log = purple_account_get_log(buddy->account, FALSE);
+			PurpleLog *log = purple_account_get_log(account, FALSE);
 
 			if (log != NULL)
 			{
@@ -1273,7 +1275,7 @@
 	{
 		if (purple_prefs_get_bool("/purple/logging/log_system"))
 		{
-			PurpleLog *log = purple_account_get_log(buddy->account, FALSE);
+			PurpleLog *log = purple_account_get_log(account, FALSE);
 
 			if (log != NULL)
 			{
@@ -1461,7 +1463,7 @@
 		{
 			PurpleStatus *temp_status = l->data;
 
-			if (!strcmp(status_id, purple_status_get_id(temp_status)))
+			if (purple_strequal(status_id, purple_status_get_id(temp_status)))
 				status = temp_status;
 		}
 
--- a/libpurple/status.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/status.h	Sat Mar 07 01:59:40 2009 +0000
@@ -252,6 +252,7 @@
  */
 void purple_status_type_destroy(PurpleStatusType *status_type);
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Sets a status type's primary attribute.
  *
@@ -261,10 +262,14 @@
  *
  * @param status_type The status type.
  * @param attr_id     The ID of the primary attribute.
+ *
+ * @deprecated This function isn't used and should be removed in 3.0.0.
  */
 void purple_status_type_set_primary_attr(PurpleStatusType *status_type,
 									   const char *attr_id);
+#endif
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Adds an attribute to a status type.
  *
@@ -272,10 +277,16 @@
  * @param id          The ID of the attribute.
  * @param name        The name presented to the user.
  * @param value       The value type of this attribute.
+ *
+ * @deprecated This function isn't needed and should be removed in 3.0.0.
+ *             Status type attributes should be set when the status type
+ *             is created, in the call to purple_status_type_new_with_attrs.
  */
 void purple_status_type_add_attr(PurpleStatusType *status_type, const char *id,
 							   const char *name, PurpleValue *value);
+#endif
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Adds multiple attributes to a status type.
  *
@@ -284,18 +295,29 @@
  * @param name        The description of the first attribute.
  * @param value       The value type of the first attribute attribute.
  * @param ...         Additional attribute information.
+ *
+ * @deprecated This function isn't needed and should be removed in 3.0.0.
+ *             Status type attributes should be set when the status type
+ *             is created, in the call to purple_status_type_new_with_attrs.
  */
 void purple_status_type_add_attrs(PurpleStatusType *status_type, const char *id,
 								const char *name, PurpleValue *value, ...) G_GNUC_NULL_TERMINATED;
+#endif
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Adds multiple attributes to a status type using a va_list.
  *
  * @param status_type The status type to add the attribute to.
  * @param args        The va_list of attributes.
+ *
+ * @deprecated This function isn't needed and should be removed in 3.0.0.
+ *             Status type attributes should be set when the status type
+ *             is created, in the call to purple_status_type_new_with_attrs.
  */
 void purple_status_type_add_attrs_vargs(PurpleStatusType *status_type,
 									  va_list args);
+#endif
 
 /**
  * Returns the primitive type of a status type.
@@ -378,14 +400,18 @@
  */
 gboolean purple_status_type_is_available(const PurpleStatusType *status_type);
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Returns a status type's primary attribute ID.
  *
  * @param type The status type.
  *
  * @return The primary attribute's ID.
+ *
+ * @deprecated This function isn't used and should be removed in 3.0.0.
  */
 const char *purple_status_type_get_primary_attr(const PurpleStatusType *type);
+#endif
 
 /**
  * Returns the attribute with the specified ID.
@@ -537,35 +563,50 @@
 void purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
 											GList *attrs);
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Sets the boolean value of an attribute in a status with the specified ID.
  *
  * @param status The status.
  * @param id     The attribute ID.
  * @param value  The boolean value.
+ *
+ * @deprecated This function is only used by status.c and should be made
+ *             static in 3.0.0.
  */
 void purple_status_set_attr_boolean(PurpleStatus *status, const char *id,
 								  gboolean value);
+#endif
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Sets the integer value of an attribute in a status with the specified ID.
  *
  * @param status The status.
  * @param id     The attribute ID.
  * @param value  The integer value.
+ *
+ * @deprecated This function is only used by status.c and should be made
+ *             static in 3.0.0.
  */
 void purple_status_set_attr_int(PurpleStatus *status, const char *id,
 							  int value);
+#endif
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Sets the string value of an attribute in a status with the specified ID.
  *
  * @param status The status.
  * @param id     The attribute ID.
  * @param value  The string value.
+ *
+ * @deprecated This function is only used by status.c and should be made
+ *             static in 3.0.0.
  */
 void purple_status_set_attr_string(PurpleStatus *status, const char *id,
 								 const char *value);
+#endif
 
 /**
  * Returns the status's type.
@@ -773,22 +814,31 @@
  */
 void purple_presence_destroy(PurplePresence *presence);
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Adds a status to a presence.
  *
  * @param presence The presence.
  * @param status   The status to add.
+ *
+ * @deprecated This function is only used by purple_presence_add_list,
+ *             and both should be removed in 3.0.0.
  */
 void purple_presence_add_status(PurplePresence *presence, PurpleStatus *status);
+#endif
 
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
 /**
  * Adds a list of statuses to the presence.
  *
  * @param presence    The presence.
  * @param source_list The source list of statuses to add, which is not
  *                    modified or freed by this function.
+ *
+ * @deprecated This function isn't used and should be removed in 3.0.0.
  */
 void purple_presence_add_list(PurplePresence *presence, GList *source_list);
+#endif
 
 /**
  * Sets the active state of a status in a presence.
--- a/libpurple/stun.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/stun.c	Sat Mar 07 01:59:40 2009 +0000
@@ -388,9 +388,7 @@
 		/** Deal with the server name having changed since we did the
 		    lookup */
 		if (servername && strlen(servername) > 1
-				&& ((nattype.servername
-					&& strcmp(servername, nattype.servername))
-				|| !nattype.servername)) {
+				&& !purple_strequal(servername, nattype.servername)) {
 			use_cached_result = FALSE;
 		}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-loader.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,183 @@
+/*
+ * ThemeLoaders for libpurple
+ *
+ * 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 "theme-loader.h"
+
+#define PURPLE_THEME_LOADER_GET_PRIVATE(PurpleThemeLoader) \
+	((PurpleThemeLoaderPrivate *) ((PurpleThemeLoader)->priv))
+
+void purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type);
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+typedef struct {
+	gchar *type;
+} PurpleThemeLoaderPrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+enum {
+	PROP_ZERO = 0,
+	PROP_TYPE,
+};
+
+/******************************************************************************
+ * GObject Stuff                                                              *
+ *****************************************************************************/
+
+static void
+purple_theme_loader_get_property(GObject *obj, guint param_id, GValue *value,
+						 GParamSpec *psec)
+{
+	PurpleThemeLoader *theme_loader = PURPLE_THEME_LOADER(obj);
+
+	switch (param_id) {
+		case PROP_TYPE:
+			g_value_set_string(value, purple_theme_loader_get_type_string(theme_loader));
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_loader_set_property(GObject *obj, guint param_id, const GValue *value,
+						 GParamSpec *psec)
+{
+	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(obj);
+
+	switch (param_id) {
+		case PROP_TYPE:
+			purple_theme_loader_set_type_string(loader, g_value_get_string(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_loader_init(GTypeInstance *instance,
+			gpointer klass)
+{
+	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(instance);
+	loader->priv = g_new0(PurpleThemeLoaderPrivate, 1);
+}
+
+static void
+purple_theme_loader_finalize(GObject *obj)
+{
+	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(obj);
+	PurpleThemeLoaderPrivate *priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader);
+
+	g_free(priv->type);
+
+	parent_class->finalize(obj);
+}
+
+static void
+purple_theme_loader_class_init(PurpleThemeLoaderClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	obj_class->get_property = purple_theme_loader_get_property;
+	obj_class->set_property = purple_theme_loader_set_property;
+	obj_class->finalize = purple_theme_loader_finalize;
+
+	/* TYPE STRING (read only) */
+	pspec = g_param_spec_string("type", "Type",
+				    "The string represtenting the type of the theme",
+				    NULL,
+				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property(obj_class, PROP_TYPE, pspec);
+}
+
+GType
+purple_theme_loader_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleThemeLoaderClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)purple_theme_loader_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PurpleThemeLoader),
+			0, /* n_preallocs */
+			purple_theme_loader_init, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static(G_TYPE_OBJECT,
+				"PurpleThemeLoader", &info, G_TYPE_FLAG_ABSTRACT);
+	}
+	return type;
+}
+
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+const gchar *
+purple_theme_loader_get_type_string(PurpleThemeLoader *theme_loader)
+{
+	PurpleThemeLoaderPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_THEME_LOADER(theme_loader), NULL);
+
+	priv = PURPLE_THEME_LOADER_GET_PRIVATE(theme_loader);
+	return priv->type;
+}
+
+/* < private > */
+void
+purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type)
+{
+	PurpleThemeLoaderPrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME_LOADER(loader));
+
+	priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader);
+
+	g_free(priv->type);
+	priv->type = g_strdup(type);
+}
+
+PurpleTheme *
+purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir)
+{
+	return PURPLE_THEME_LOADER_GET_CLASS(loader)->purple_theme_loader_build(dir);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-loader.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,92 @@
+/**
+ * @file theme-loader.h  Purple Theme Loader Abstact Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * 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 PURPLE_THEME_LOADER_H
+#define PURPLE_THEME_LOADER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme.h"
+
+/**
+ * A purple theme loader.
+ * This is an abstract class for Purple to use with the Purple theme manager.
+ * The loader is responsible for building each type of theme
+ *
+ * PurpleThemeLoader is a GObject.
+ */
+typedef struct _PurpleThemeLoader        PurpleThemeLoader;
+typedef struct _PurpleThemeLoaderClass   PurpleThemeLoaderClass;
+
+#define PURPLE_TYPE_THEME_LOADER            (purple_theme_loader_get_type())
+#define PURPLE_THEME_LOADER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoader))
+#define PURPLE_THEME_LOADER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderClass))
+#define PURPLE_IS_THEME_LOADER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_THEME_LOADER))
+#define PURPLE_IS_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_THEME_LOADER))
+#define PURPLE_THEME_LOADER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderClass))
+
+struct _PurpleThemeLoader
+{
+	GObject parent;
+	gpointer priv;
+};
+
+struct _PurpleThemeLoaderClass
+{
+	GObjectClass parent_class;
+	PurpleTheme *((*purple_theme_loader_build)(const gchar*));
+};
+
+/**************************************************************************/
+/** @name Purple Theme-Loader API                                         */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_theme_loader_get_type(void);
+
+/**
+ * Returns the string represtenting the type of the theme loader
+ *
+ * @param self The theme loader
+ *
+ * @returns The string represting this type
+ */
+const gchar *purple_theme_loader_get_type_string(PurpleThemeLoader *self);
+
+/**
+ * Creates a new PurpleTheme
+ *
+ * @param dir The directory containing the theme
+ *
+ * @returns A PurpleTheme containing the information from the directory
+ */
+PurpleTheme *purple_theme_loader_build(PurpleThemeLoader *loader, const gchar *dir);
+
+G_END_DECLS
+#endif /* PURPLE_THEME_LOADER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-manager.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,295 @@
+/*
+ * Themes for libpurple
+ *
+ * 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 <glib.h>
+#include <string.h>
+
+#include "internal.h"
+#include "theme-manager.h"
+#include "util.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GHashTable *theme_table = NULL;
+
+/*****************************************************************************
+ * GObject Stuff
+ ****************************************************************************/
+
+GType
+purple_theme_manager_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleThemeManagerClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			NULL, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PurpleThemeManager),
+			0, /* n_preallocs */
+			NULL, /* instance_init */
+			NULL, /* Value Table */
+		};
+		type = g_type_register_static(G_TYPE_OBJECT,
+				"PurpleThemeManager", &info, 0);
+	}
+	return type;
+}
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+
+/* makes a key of <type> + '/' + <name> */
+static gchar *
+purple_theme_manager_make_key(const gchar *name, const gchar *type)
+{
+	g_return_val_if_fail(name && *name, NULL);
+	g_return_val_if_fail(type && *type, NULL);
+	return g_strconcat(type, "/", name, NULL);
+}
+
+/* returns TRUE if theme is of type "user_data" */
+static gboolean
+purple_theme_manager_is_theme_type(gchar *key,
+		gpointer value,
+		gchar *user_data)
+{
+	return g_str_has_prefix(key, g_strconcat(user_data, "/", NULL));
+}
+
+static gboolean
+purple_theme_manager_is_theme(gchar *key,
+		gpointer value,
+		gchar *user_data)
+{
+	return PURPLE_IS_THEME(value);
+}
+
+static void
+purple_theme_manager_function_wrapper(gchar *key,
+		gpointer value,
+		PTFunc user_data)
+{
+	if (PURPLE_IS_THEME(value))
+		(* user_data)(value);
+}
+
+static void
+purple_theme_manager_build_dir(const gchar *root)
+{
+	gchar *purple_dir, *theme_dir;
+	const gchar *name = NULL, *type = NULL;
+	GDir *rdir, *tdir;
+	PurpleThemeLoader *loader;
+
+	rdir = g_dir_open(root, 0, NULL);
+
+	if (!rdir)
+		return;
+
+	/* Parses directory by root/name/purple/type */
+	while ((name = g_dir_read_name(rdir))) {
+		purple_dir = g_build_filename(root, name, "purple", NULL);
+		tdir = g_dir_open(purple_dir, 0, NULL);
+
+		if (!tdir) {
+			g_free(purple_dir);
+
+			continue;
+		}
+
+		while ((type = g_dir_read_name(tdir))) {
+			if ((loader = g_hash_table_lookup(theme_table, type))) {
+				PurpleTheme *theme = NULL;
+
+				theme_dir = g_build_filename(purple_dir, type, NULL);
+
+				theme = purple_theme_loader_build(loader, theme_dir);
+
+				if (PURPLE_IS_THEME(theme))
+					purple_theme_manager_add_theme(theme);
+			}
+		}
+
+		g_dir_close(tdir);
+		g_free(purple_dir);
+	}
+
+	g_dir_close(rdir);
+}
+
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+void
+purple_theme_manager_init(void)
+{
+	theme_table = g_hash_table_new_full(g_str_hash,
+			g_str_equal, g_free, g_object_unref);
+}
+
+void
+purple_theme_manager_refresh(void)
+{
+	gchar *path = NULL;
+	const gchar *xdg = NULL;
+	gint i = 0;
+
+	g_hash_table_foreach_remove(theme_table,
+			(GHRFunc) purple_theme_manager_is_theme, NULL);
+
+	/* Add themes from ~/.purple */
+	path = g_build_filename(purple_user_dir(), "themes", NULL);
+	purple_theme_manager_build_dir(path);
+	g_free(path);
+
+	/* look for XDG_DATA_HOME.  If we don't have it use ~/.local, and add it */
+	if ((xdg = g_getenv("XDG_DATA_HOME")) != NULL)
+		path = g_build_filename(xdg, "themes", NULL);
+	else
+		path = g_build_filename(purple_home_dir(), ".local", "themes", NULL);
+
+	purple_theme_manager_build_dir(path);
+	g_free(path);
+
+	/* now dig through XDG_DATA_DIRS and add those too */
+	xdg = g_getenv("XDG_DATA_DIRS");
+	if (xdg) {
+		gchar **xdg_dirs = g_strsplit(xdg, G_SEARCHPATH_SEPARATOR_S, 0);
+
+		for (i = 0; xdg_dirs[i]; i++) {
+			path = g_build_filename(xdg_dirs[i], "themes", NULL);
+			purple_theme_manager_build_dir(path);
+			g_free(path);
+		}
+
+		g_strfreev(xdg_dirs);
+	}
+}
+
+void
+purple_theme_manager_uninit(void)
+{
+	g_hash_table_destroy(theme_table);
+}
+
+void
+purple_theme_manager_register_type(PurpleThemeLoader *loader)
+{
+	gchar *type;
+
+	g_return_if_fail(PURPLE_IS_THEME_LOADER(loader));
+
+	type = g_strdup(purple_theme_loader_get_type_string(loader));
+	g_return_if_fail(type);
+
+	/* if something is already there do nothing */
+	if (!g_hash_table_lookup(theme_table, type))
+		g_hash_table_insert(theme_table, type, loader);
+}
+
+void
+purple_theme_manager_unregister_type(PurpleThemeLoader *loader)
+{
+	const gchar *type;
+
+	g_return_if_fail(PURPLE_IS_THEME_LOADER(loader));
+
+	type = purple_theme_loader_get_type_string(loader);
+	g_return_if_fail(type);
+
+	if (g_hash_table_lookup(theme_table, type) == loader)
+	{
+		g_hash_table_remove(theme_table, type);
+
+		g_hash_table_foreach_remove(theme_table,
+				(GHRFunc)purple_theme_manager_is_theme_type, (gpointer)type);
+	} /* only free if given registered loader */
+}
+
+PurpleTheme *
+purple_theme_manager_find_theme(const gchar *name,
+		const gchar *type)
+{
+	gchar *key;
+	PurpleTheme *theme;
+
+	key = purple_theme_manager_make_key(name, type);
+
+	g_return_val_if_fail(key, NULL);
+
+	theme = g_hash_table_lookup(theme_table, key);
+
+	g_free(key);
+
+	return theme;
+}
+
+void
+purple_theme_manager_add_theme(PurpleTheme *theme)
+{
+	gchar *key;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	key = purple_theme_manager_make_key(purple_theme_get_name(theme),
+			purple_theme_get_type_string(theme));
+
+	g_return_if_fail(key);
+
+	/* if something is already there do nothing */
+	if (g_hash_table_lookup(theme_table, key) == NULL)
+		g_hash_table_insert(theme_table, key, theme);
+}
+
+void
+purple_theme_manager_remove_theme(PurpleTheme *theme)
+{
+	gchar *key;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	key = purple_theme_manager_make_key(purple_theme_get_name(theme),
+			purple_theme_get_type_string(theme));
+
+	g_return_if_fail(key);
+
+	g_hash_table_remove(theme_table, key);
+
+	g_free(key);
+}
+
+void
+purple_theme_manager_for_each_theme(PTFunc func)
+{
+	g_return_if_fail(func);
+
+	g_hash_table_foreach(theme_table,
+			(GHFunc) purple_theme_manager_function_wrapper, func);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme-manager.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,131 @@
+/**
+ * @file thememanager.h  Theme Manager API
+ */
+
+/*
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * 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 PURPLE_THEME_MANAGER_H
+#define PURPLE_THEME_MANAGER_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include "theme.h"
+#include "theme-loader.h"
+
+typedef void (*PTFunc) (PurpleTheme *);
+
+typedef struct _PurpleThemeManager PurpleThemeManager;
+typedef struct _PurpleThemeManagerClass PurpleThemeManagerClass;
+
+#define PURPLE_TYPE_THEME_MANAGER            (purple_theme_manager_get_type())
+#define PURPLE_THEME_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManager))
+#define PURPLE_THEME_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManagerClass))
+#define PURPLE_IS_THEME_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_THEME_MANAGER))
+#define PURPLE_IS_THEME_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_THEME_MANAGER))
+#define PURPLE_GET_THEME_MANAGER_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_THEME_MANAGER, PurpleThemeManagerClass))
+
+struct _PurpleThemeManager {
+	GObject parent;
+};
+
+struct _PurpleThemeManagerClass {
+	GObjectClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Theme Manager API                                        */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ *
+ * @internal.
+ */
+GType purple_theme_manager_get_type(void);
+
+/**
+ * Initalizes the theme manager.
+ */
+void purple_theme_manager_init(void);
+
+/**
+ * Uninitalizes the manager then frees all the themes and loaders it is
+ * responsible for.
+ */
+void purple_theme_manager_uninit(void);
+
+/**
+ * Rebuilds all the themes in the theme manager.
+ * (Removes all current themes but keeps the added loaders.)
+ */
+void purple_theme_manager_refresh(void);
+
+/**
+ * Finds the PurpleTheme object stored by the theme manager.
+ *
+ * @param name The name of the PurpleTheme.
+ * @param type The type of the PurpleTheme.
+ *
+ * @returns The PurpleTheme, or NULL if it wasn't found.
+ */
+PurpleTheme *purple_theme_manager_find_theme(const gchar *name, const gchar *type);
+
+/**
+ * Adds a PurpleTheme to the theme manager.  If the theme already exists
+ * then this function does nothing.
+ *
+ * @param theme The PurpleTheme to add to the manager.
+ */
+void purple_theme_manager_add_theme(PurpleTheme *theme);
+
+/**
+ * Removes a PurpleTheme from the theme manager and frees the theme.
+ *
+ * @param theme The PurpleTheme to remove from the manager.
+ */
+void purple_theme_manager_remove_theme(PurpleTheme *theme);
+
+/**
+ * Adds a loader to the theme manager so it knows how to build themes.
+ *
+ * @param loader The PurpleThemeLoader to add.
+ */
+void purple_theme_manager_register_type(PurpleThemeLoader *loader);
+
+/**
+ * Removes the loader and all themes of the same type from the loader.
+ *
+ * @param loader The PurpleThemeLoader to be removed.
+ */
+void purple_theme_manager_unregister_type(PurpleThemeLoader *loader);
+
+/**
+ * Calls the given function on each purple theme.
+ *
+ * @param func The PTFunc to be applied to each theme.
+ */
+void purple_theme_manager_for_each_theme(PTFunc func);
+
+G_END_DECLS
+#endif /* PURPLE_THEME_MANAGER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,408 @@
+/*
+ * Themes for libpurple
+ *
+ * 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 <glib.h>
+#include <string.h>
+
+#include "internal.h"
+#include "theme.h"
+#include "util.h"
+
+#define PURPLE_THEME_GET_PRIVATE(PurpleTheme) \
+	((PurpleThemePrivate *) ((PurpleTheme)->priv))
+
+void purple_theme_set_type_string(PurpleTheme *theme, const gchar *type);
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+typedef struct {
+	gchar *name;
+	gchar *description;
+	gchar *author;
+	gchar *type;
+	gchar *dir;
+	gchar *img;
+} PurpleThemePrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+enum {
+	PROP_ZERO = 0,
+	PROP_NAME,
+	PROP_DESCRIPTION,
+	PROP_AUTHOR,
+	PROP_TYPE,
+	PROP_DIR,
+	PROP_IMAGE
+};
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+purple_theme_get_property(GObject *obj, guint param_id, GValue *value,
+		GParamSpec *psec)
+{
+	PurpleTheme *theme = PURPLE_THEME(obj);
+
+	switch (param_id) {
+		case PROP_NAME:
+			g_value_set_string(value, purple_theme_get_name(theme));
+			break;
+		case PROP_DESCRIPTION:
+			g_value_set_string(value, purple_theme_get_description(theme));
+			break;
+		case PROP_AUTHOR:
+			g_value_set_string(value, purple_theme_get_author(theme));
+			break;
+		case PROP_TYPE:
+			g_value_set_string(value, purple_theme_get_type_string(theme));
+			break;
+		case PROP_DIR:
+			g_value_set_string(value, purple_theme_get_dir(theme));
+			break;
+		case PROP_IMAGE:
+			g_value_set_string(value, purple_theme_get_image(theme));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_set_property(GObject *obj, guint param_id, const GValue *value,
+		GParamSpec *psec)
+{
+	PurpleTheme *theme = PURPLE_THEME(obj);
+
+	switch (param_id) {
+		case PROP_NAME:
+			purple_theme_set_name(theme, g_value_get_string(value));
+			break;
+		case PROP_DESCRIPTION:
+			purple_theme_set_description(theme, g_value_get_string(value));
+			break;
+		case PROP_AUTHOR:
+			purple_theme_set_author(theme, g_value_get_string(value));
+			break;
+		case PROP_TYPE:
+			purple_theme_set_type_string(theme, g_value_get_string(value));
+			break;
+		case PROP_DIR:
+			purple_theme_set_dir(theme, g_value_get_string(value));
+			break;
+		case PROP_IMAGE:
+			purple_theme_set_image(theme, g_value_get_string(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+purple_theme_init(GTypeInstance *instance,
+		gpointer klass)
+{
+	PurpleTheme *theme = PURPLE_THEME(instance);
+	theme->priv = g_new0(PurpleThemePrivate, 1);
+}
+
+static void
+purple_theme_finalize(GObject *obj)
+{
+	PurpleTheme *theme = PURPLE_THEME(obj);
+	PurpleThemePrivate *priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->name);
+	g_free(priv->description);
+	g_free(priv->author);
+	g_free(priv->type);
+	g_free(priv->dir);
+	g_free(priv->img);
+
+	G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static void
+purple_theme_class_init(PurpleThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	obj_class->get_property = purple_theme_get_property;
+	obj_class->set_property = purple_theme_set_property;
+	obj_class->finalize = purple_theme_finalize;
+
+	/* NAME */
+	pspec = g_param_spec_string("name", "Name",
+			"The name of the theme",
+			NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_NAME, pspec);
+
+	/* DESCRIPTION */
+	pspec = g_param_spec_string("description", "Description",
+			"The description of the theme",
+			NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_DESCRIPTION, pspec);
+
+	/* AUTHOR */
+	pspec = g_param_spec_string("author", "Author",
+			"The author of the theme",
+			NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_AUTHOR, pspec);
+
+	/* TYPE STRING (read only) */
+	pspec = g_param_spec_string("type", "Type",
+			"The string represtenting the type of the theme",
+			NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property(obj_class, PROP_TYPE, pspec);
+
+	/* DIRECTORY */
+	pspec = g_param_spec_string("directory", "Directory",
+			"The directory that contains the theme and all its files",
+			NULL,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(obj_class, PROP_DIR, pspec);
+
+	/* PREVIEW IMAGE */
+	pspec = g_param_spec_string("image", "Image",
+			"A preview image of the theme",
+			NULL,
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_IMAGE, pspec);
+}
+
+
+GType
+purple_theme_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleThemeClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)purple_theme_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PurpleTheme),
+			0, /* n_preallocs */
+			purple_theme_init, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static (G_TYPE_OBJECT,
+				"PurpleTheme", &info, G_TYPE_FLAG_ABSTRACT);
+	}
+	return type;
+}
+
+/******************************************************************************
+ * Helper Functions
+ *****************************************************************************/
+
+static gchar *
+theme_clean_text(const gchar *text)
+{
+	gchar *clean_text = g_markup_escape_text(text, -1);
+	g_strdelimit(clean_text, "\n", ' ');
+	purple_str_strip_char(clean_text, '\r');
+	return clean_text;
+}
+
+/*****************************************************************************
+ * Public API function
+ *****************************************************************************/
+
+const gchar *
+purple_theme_get_name(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->name;
+}
+
+void
+purple_theme_set_name(PurpleTheme *theme, const gchar *name)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->name);
+	priv->name = theme_clean_text(name);
+}
+
+const gchar *
+purple_theme_get_description(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->description;
+}
+
+void
+purple_theme_set_description(PurpleTheme *theme, const gchar *description)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->description);
+	priv->description = theme_clean_text(description);
+}
+
+const gchar *
+purple_theme_get_author(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->author;
+}
+
+void
+purple_theme_set_author(PurpleTheme *theme, const gchar *author)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->author);
+	priv->author = theme_clean_text(author);
+}
+
+const gchar *
+purple_theme_get_type_string(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->type;
+}
+
+/* < private > */
+void
+purple_theme_set_type_string(PurpleTheme *theme, const gchar *type)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->type);
+	priv->type = g_strdup(type);
+}
+
+const gchar *
+purple_theme_get_dir(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+	return priv->dir;
+}
+
+void
+purple_theme_set_dir(PurpleTheme *theme, const gchar *dir)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->dir);
+	priv->dir = g_strdup(dir);
+}
+
+const gchar *
+purple_theme_get_image(PurpleTheme *theme)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_val_if_fail(PURPLE_IS_THEME(theme), NULL);
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	return priv->img;
+}
+
+gchar *
+purple_theme_get_image_full(PurpleTheme *theme)
+{
+	const gchar *filename = purple_theme_get_image(theme);
+
+	g_return_val_if_fail(filename, NULL);
+
+	return g_build_filename(purple_theme_get_dir(PURPLE_THEME(theme)), filename, NULL);
+}
+
+void
+purple_theme_set_image(PurpleTheme *theme, const gchar *img)
+{
+	PurpleThemePrivate *priv;
+
+	g_return_if_fail(PURPLE_IS_THEME(theme));
+
+	priv = PURPLE_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->img);
+	priv->img = g_strdup(img);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/theme.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,175 @@
+/**
+ * @file theme.h  Purple Theme Abstact Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * 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 PURPLE_THEME_H
+#define PURPLE_THEME_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "imgstore.h"
+
+/**
+ * A purple theme.
+ * This is an abstract class for Purple to use with the Purple theme manager.
+ *
+ * PurpleTheme is a GObject.
+ */
+typedef struct _PurpleTheme        PurpleTheme;
+typedef struct _PurpleThemeClass   PurpleThemeClass;
+
+#define PURPLE_TYPE_THEME            (purple_theme_get_type ())
+#define PURPLE_THEME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PURPLE_TYPE_THEME, PurpleTheme))
+#define PURPLE_THEME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_THEME, PurpleThemeClass))
+#define PURPLE_IS_THEME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PURPLE_TYPE_THEME))
+#define PURPLE_IS_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_THEME))
+#define PURPLE_THEME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_THEME, PurpleThemeClass))
+
+struct _PurpleTheme
+{
+	GObject parent;
+	gpointer priv;
+};
+
+struct _PurpleThemeClass
+{
+	GObjectClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Purple Theme API                                                */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType purple_theme_get_type(void);
+
+/**
+ * Returns the name of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ *
+ * @return The string representating the name of the theme.
+ */
+const gchar *purple_theme_get_name(PurpleTheme *theme);
+
+/**
+ * Sets the name of the PurpleTheme object.
+ *
+ * @param theme The purple theme.
+ * @param name  The name of the PurpleTheme object.
+ */
+void purple_theme_set_name(PurpleTheme *theme, const gchar *name);
+
+/**
+ * Returns the description of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ *
+ * @return A short description of the theme.
+ */
+const gchar *purple_theme_get_description(PurpleTheme *theme);
+
+/**
+ * Sets the description of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ * @param description The description of the PurpleTheme object.
+ */
+void purple_theme_set_description(PurpleTheme *theme, const gchar *description);
+
+/**
+ * Returns the author of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ *
+ * @return The author of the theme.
+ */
+const gchar *purple_theme_get_author(PurpleTheme *theme);
+
+/**
+ * Sets the author of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ * @param author The author of the PurpleTheme object.
+ */
+void purple_theme_set_author(PurpleTheme *theme, const gchar *author);
+
+/**
+ * Returns the type (string) of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ *
+ * @return The string represtenting the type.
+ */
+const gchar *purple_theme_get_type_string(PurpleTheme *theme);
+
+/**
+ * Returns the directory of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ *
+ * @return The string represtenting the theme directory.
+ */
+const gchar *purple_theme_get_dir(PurpleTheme *theme);
+
+/**
+ * Sets the directory of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ * @param dir    The directory of the PurpleTheme object.
+ */
+void purple_theme_set_dir(PurpleTheme *theme, const gchar *dir);
+
+/**
+ * Returns the image preview of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ *
+ * @return The image preview of the PurpleTheme object.
+ */
+const gchar *purple_theme_get_image(PurpleTheme *theme);
+
+/**
+ * Returns the image preview and directory of the PurpleTheme object.
+ *
+ * @param theme  The purple theme.
+ *
+ * @return The image preview of the PurpleTheme object.
+ */
+gchar *purple_theme_get_image_full(PurpleTheme *theme);
+
+/**
+ * Sets the directory of the PurpleTheme object.
+ *
+ * @param theme	 The purple theme.
+ * @param img    The image preview of the PurpleTheme object.
+ */
+void purple_theme_set_image(PurpleTheme *theme, const gchar *img);
+
+G_END_DECLS
+#endif /* PURPLE_THEME_H */
--- a/libpurple/upnp.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/upnp.c	Sat Mar 07 01:59:40 2009 +0000
@@ -1049,7 +1049,7 @@
 purple_upnp_get_handle(void)
 {
 	static int handle;
-	
+
 	return &handle;
 }
 
--- a/libpurple/util.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/util.c	Sat Mar 07 01:59:40 2009 +0000
@@ -98,7 +98,7 @@
 void
 purple_util_init(void)
 {
-	/* This does nothing right now.  It exists for symmetry with 
+	/* This does nothing right now.  It exists for symmetry with
 	 * purple_util_uninit() and forwards compatibility. */
 }
 
@@ -1409,7 +1409,7 @@
 						struct purple_parse_tag *pt = tags->data;
 						if(xhtml)
 							g_string_append_printf(xhtml, "</%s>", pt->dest_tag);
-						if(plain && !strcmp(pt->src_tag, "a")) {
+						if(plain && purple_strequal(pt->src_tag, "a")) {
 							/* if this is a link, we have to add the url to the plaintext, too */
 							if (cdata && url &&
 									(!g_string_equal(cdata, url) && (g_ascii_strncasecmp(url->str, "mailto:", 7) != 0 ||
@@ -2693,7 +2693,7 @@
 		return FALSE;
 	}
 #endif
-    
+
 	/* Close file */
 	if (fclose(file) != 0)
 	{
@@ -2774,70 +2774,7 @@
 xmlnode *
 purple_util_read_xml_from_file(const char *filename, const char *description)
 {
-	const char *user_dir = purple_user_dir();
-	gchar *filename_full;
-	GError *error = NULL;
-	gchar *contents = NULL;
-	gsize length;
-	xmlnode *node = NULL;
-
-	g_return_val_if_fail(user_dir != NULL, NULL);
-
-	purple_debug_info("util", "Reading file %s from directory %s\n",
-					filename, user_dir);
-
-	filename_full = g_build_filename(user_dir, filename, NULL);
-
-	if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
-	{
-		purple_debug_info("util", "File %s does not exist (this is not "
-						"necessarily an error)\n", filename_full);
-		g_free(filename_full);
-		return NULL;
-	}
-
-	if (!g_file_get_contents(filename_full, &contents, &length, &error))
-	{
-		purple_debug_error("util", "Error reading file %s: %s\n",
-						 filename_full, error->message);
-		g_error_free(error);
-	}
-
-	if ((contents != NULL) && (length > 0))
-	{
-		node = xmlnode_from_str(contents, length);
-
-		/* If we were unable to parse the file then save its contents to a backup file */
-		if (node == NULL)
-		{
-			gchar *filename_temp;
-
-			filename_temp = g_strdup_printf("%s~", filename);
-			purple_debug_error("util", "Error parsing file %s.  Renaming old "
-							 "file to %s\n", filename_full, filename_temp);
-			purple_util_write_data_to_file(filename_temp, contents, length);
-			g_free(filename_temp);
-		}
-
-		g_free(contents);
-	}
-
-	/* If we could not parse the file then show the user an error message */
-	if (node == NULL)
-	{
-		gchar *title, *msg;
-		title = g_strdup_printf(_("Error Reading %s"), filename);
-		msg = g_strdup_printf(_("An error was encountered reading your "
-					"%s.  They have not been loaded, and the old file "
-					"has been renamed to %s~."), description, filename_full);
-		purple_notify_error(NULL, NULL, title, msg);
-		g_free(title);
-		g_free(msg);
-	}
-
-	g_free(filename_full);
-
-	return node;
+	return xmlnode_from_file(purple_user_dir(), filename, description, "util");
 }
 
 /*
@@ -3012,7 +2949,7 @@
 	g_free(tmp);
 
 	session = g_getenv("KDE_FULL_SESSION");
-	if (session != NULL && !strcmp(session, "true"))
+	if (purple_strequal(session, "true"))
 		return TRUE;
 
 	/* If you run Purple from Konsole under !KDE, this will provide a
@@ -3053,6 +2990,17 @@
 /**************************************************************************
  * String Functions
  **************************************************************************/
+gboolean
+purple_strequal(const gchar *left, const gchar *right)
+{
+#if GLIB_CHECK_VERSION(2,16,0)
+	return (g_strcmp0(left, right) == 0);
+#else
+	return ((left == NULL && right == NULL) ||
+	        (left != NULL && right != NULL && strcmp(left, right) == 0));
+#endif
+}
+
 const char *
 purple_normalize(const PurpleAccount *account, const char *str)
 {
@@ -3167,7 +3115,7 @@
 	g_return_val_if_fail(x != NULL, FALSE);
 
 	off = strlen(s) - strlen(x);
-	return (off >= 0 && !strcmp(s + off, x));
+	return (off >= 0 && purple_strequal(s + off, x));
 #endif
 }
 
@@ -4764,7 +4712,7 @@
 
 const char *_purple_oscar_convert(const char *act, const char *protocol)
 {
-	if (protocol && act && strcmp(protocol, "prpl-oscar") == 0) {
+	if (act && purple_strequal(protocol, "prpl-oscar")) {
 		int i;
 		for (i = 0; act[i] != '\0'; i++)
 			if (!isdigit(act[i]))
--- a/libpurple/util.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/util.h	Sat Mar 07 01:59:40 2009 +0000
@@ -775,6 +775,21 @@
 /*@{*/
 
 /**
+ * Tests two strings for equality.
+ *
+ * Unlike strcmp(), this function will not crash if one or both of the
+ * strings are @c NULL.
+ *
+ * @param left	A string
+ * @param right A string to compare with left
+ *
+ * @return @c TRUE if the strings are the same, else @c FALSE.
+ *
+ * @since 2.6.0
+ */
+gboolean purple_strequal(const gchar *left, const gchar *right);
+
+/**
  * Normalizes a string, so that it is suitable for comparison.
  *
  * The returned string will point to a static buffer, so if the
--- a/libpurple/whiteboard.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/whiteboard.c	Sat Mar 07 01:59:40 2009 +0000
@@ -115,7 +115,7 @@
 	{
 		wb = l->data;
 
-		if(wb->account == account && !strcmp(wb->who, who))
+		if(wb->account == account && purple_strequal(wb->who, who))
 			return wb;
 
 		l = l->next;
--- a/libpurple/xmlnode.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/xmlnode.c	Sat Mar 07 01:59:40 2009 +0000
@@ -129,7 +129,7 @@
 	for(attr_node = node->child; attr_node; attr_node = attr_node->next)
 	{
 		if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
-				!strcmp(attr_node->name, attr))
+				purple_strequal(attr_node->name, attr))
 		{
 			if(sibling == NULL) {
 				node->child = attr_node->next;
@@ -146,20 +146,6 @@
 	}
 }
 
-/* Compare two nullable xmlns strings.
- * They are considered equal if they're both NULL or the strings are equal
- */
-static gboolean _xmlnode_compare_xmlns(const char *xmlns1, const char *xmlns2) {
-	gboolean equal = FALSE;
-
-	if (xmlns1 == NULL && xmlns2 == NULL)
-		equal = TRUE;
-	else if (xmlns1 != NULL && xmlns2 != NULL && !strcmp(xmlns1, xmlns2))
-		equal = TRUE;
-
-	return equal;
-}
-
 void
 xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
 {
@@ -171,8 +157,8 @@
 	for(attr_node = node->child; attr_node; attr_node = attr_node->next)
 	{
 		if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
-		   !strcmp(attr_node->name, attr) &&
-		   _xmlnode_compare_xmlns(xmlns, attr_node->xmlns))
+		   purple_strequal(attr,  attr_node->name) &&
+		   purple_strequal(xmlns, attr_node->xmlns))
 		{
 			if(sibling == NULL) {
 				node->child = attr_node->next;
@@ -252,7 +238,7 @@
 	g_return_val_if_fail(attr != NULL, NULL);
 
 	for(x = node->child; x; x = x->next) {
-		if(x->type == XMLNODE_TYPE_ATTRIB && !strcmp(attr, x->name)) {
+		if(x->type == XMLNODE_TYPE_ATTRIB && purple_strequal(attr, x->name)) {
 			return x->data;
 		}
 	}
@@ -270,8 +256,8 @@
 
 	for(x = node->child; x; x = x->next) {
 		if(x->type == XMLNODE_TYPE_ATTRIB &&
-		   !strcmp(attr, x->name) &&
-		   _xmlnode_compare_xmlns(xmlns, x->xmlns)) {
+		   purple_strequal(attr,  x->name) &&
+		   purple_strequal(xmlns, x->xmlns)) {
 			return x->data;
 		}
 	}
@@ -382,8 +368,8 @@
 		if(ns)
 			xmlns = xmlnode_get_namespace(x);
 
-		if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name)
-				&& (!ns || (xmlns && !strcmp(ns, xmlns)))) {
+		if(x->type == XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
+				&& purple_strequal(ns, xmlns)) {
 			ret = x;
 			break;
 		}
@@ -471,7 +457,7 @@
 		g_hash_table_foreach(node->namespace_map,
 			(GHFunc)xmlnode_to_str_foreach_append_ns, text);
 	} else if (node->xmlns) {
-		if(!node->parent || !node->parent->xmlns || strcmp(node->xmlns, node->parent->xmlns))
+		if(!node->parent || !purple_strequal(node->xmlns, node->parent->xmlns))
 		{
 			char *xmlns = g_markup_escape_text(node->xmlns, -1);
 			g_string_append_printf(text, " xmlns='%s'", xmlns);
@@ -642,7 +628,7 @@
 
 	if(!xpd->current || xpd->error)
 		return;
-	
+
 	if(!text || !text_len)
 		return;
 
@@ -730,6 +716,78 @@
 	return ret;
 }
 
+xmlnode *
+xmlnode_from_file(const char *dir,const char *filename, const char *description, const char *process)
+{
+	gchar *filename_full;
+	GError *error = NULL;
+	gchar *contents = NULL;
+	gsize length;
+	xmlnode *node = NULL;
+
+	g_return_val_if_fail(dir != NULL, NULL);
+
+	purple_debug_info(process, "Reading file %s from directory %s\n",
+					filename, dir);
+
+	filename_full = g_build_filename(dir, filename, NULL);
+
+	if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
+	{
+		purple_debug_info(process, "File %s does not exist (this is not "
+						"necessarily an error)\n", filename_full);
+		g_free(filename_full);
+		return NULL;
+	}
+
+	if (!g_file_get_contents(filename_full, &contents, &length, &error))
+	{
+		purple_debug_error(process, "Error reading file %s: %s\n",
+						 filename_full, error->message);
+		g_error_free(error);
+	}
+
+	if ((contents != NULL) && (length > 0))
+	{
+		node = xmlnode_from_str(contents, length);
+
+		/* If we were unable to parse the file then save its contents to a backup file */
+		if (node == NULL)
+		{
+			gchar *filename_temp, *filename_temp_full;
+
+			filename_temp = g_strdup_printf("%s~", filename);
+			filename_temp_full = g_build_filename(dir, filename_temp, NULL);
+
+			purple_debug_error("util", "Error parsing file %s.  Renaming old "
+							 "file to %s\n", filename_full, filename_temp);
+			purple_util_write_data_to_file_absolute(filename_temp_full, contents, length);
+
+			g_free(filename_temp_full);
+			g_free(filename_temp);
+		}
+
+		g_free(contents);
+	}
+
+	/* If we could not parse the file then show the user an error message */
+	if (node == NULL)
+	{
+		gchar *title, *msg;
+		title = g_strdup_printf(_("Error Reading %s"), filename);
+		msg = g_strdup_printf(_("An error was encountered reading your "
+					"%s.  The file has not been loaded, and the old file "
+					"has been renamed to %s~."), description, filename_full);
+		purple_notify_error(NULL, NULL, title, msg);
+		g_free(title);
+		g_free(msg);
+	}
+
+	g_free(filename_full);
+
+	return node;
+}
+
 static void
 xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
 {
@@ -794,8 +852,8 @@
 		if(ns)
 			xmlns = xmlnode_get_namespace(sibling);
 
-		if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) &&
-				(!ns || (xmlns && !strcmp(ns, xmlns))))
+		if(sibling->type == XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
+				purple_strequal(ns, xmlns))
 			return sibling;
 	}
 
--- a/libpurple/xmlnode.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/libpurple/xmlnode.h	Sat Mar 07 01:59:40 2009 +0000
@@ -26,6 +26,8 @@
 #ifndef _PURPLE_XMLNODE_H_
 #define _PURPLE_XMLNODE_H_
 
+#include <glib.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -297,6 +299,22 @@
  */
 void xmlnode_free(xmlnode *node);
 
+/**
+ * Creates a node from a XML File.  Calling this on the
+ * root node of an XML document will parse the entire document
+ * into a tree of nodes, and return the xmlnode of the root.
+ *
+ * @param str  The string of xml.
+ * @param description  The description of the file being parsed
+ * @process  The utility that is calling xmlnode_from_file
+ *
+ * @return The new node.
+ *
+ * @since 2.6.0
+ */
+xmlnode *xmlnode_from_file(const char *dir, const char *filename,
+			   const char *description, const char *process);
+
 #ifdef __cplusplus
 }
 #endif
--- a/pidgin/Makefile.am	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/Makefile.am	Sat Mar 07 01:59:40 2009 +0000
@@ -78,6 +78,8 @@
 	pidginstock.c \
 	gtkaccount.c \
 	gtkblist.c \
+	gtkblist-theme.c \
+	gtkblist-theme-loader.c \
 	gtkcelllayout.c \
 	gtkcellrendererexpander.c \
 	gtkcellrendererprogress.c \
@@ -94,6 +96,8 @@
 	gtkeventloop.c \
 	gtkexpander.c \
 	gtkft.c \
+	gtkicon-theme.c \
+	gtkicon-theme-loader.c \
 	gtkidle.c \
 	gtkimhtml.c \
 	gtkimhtmltoolbar.c \
@@ -116,6 +120,7 @@
 	gtksourceiter.c \
 	gtksourceundomanager.c \
 	gtksourceview-marshal.c \
+	gtkstatus-icon-theme.c \
 	gtkstatusbox.c \
 	gtkthemes.c \
 	gtkutils.c \
@@ -127,6 +132,8 @@
 	eggtrayicon.h \
 	gtkaccount.h \
 	gtkblist.h \
+	gtkblist-theme.h \
+	gtkblist-theme-loader.h \
 	gtkcelllayout.h \
 	gtkcellrendererexpander.h \
 	gtkcellrendererprogress.h \
@@ -146,6 +153,8 @@
 	gtkeventloop.h \
 	gtkexpander.h \
 	gtkft.h \
+	gtkicon-theme.h \
+	gtkicon-theme-loader.h \
 	gtkidle.h \
 	gtkgaim-compat.h \
 	gtkimhtml.h \
@@ -169,6 +178,7 @@
 	gtksourceiter.h \
 	gtksourceundomanager.h \
 	gtksourceview-marshal.h \
+	gtkstatus-icon-theme.h \
 	gtkstatusbox.h \
 	pidginstock.h \
 	gtkthemes.h \
--- a/pidgin/Makefile.mingw	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/Makefile.mingw	Sat Mar 07 01:59:40 2009 +0000
@@ -56,6 +56,8 @@
 PIDGIN_C_SRC =	\
 			gtkaccount.c \
 			gtkblist.c \
+			gtkblist-theme.c \
+			gtkblist-theme-loader.c \
 			gtkcertmgr.c \
 			gtkcellrendererexpander.c \
 			gtkcellrendererprogress.c \
@@ -68,6 +70,8 @@
 			gtkeventloop.c \
 			gtkexpander.c \
 			gtkft.c \
+			gtkicon-theme.c \
+			gtkicon-theme-loader.c \
 			gtkidle.c \
 			gtkimhtml.c \
 			gtkimhtmltoolbar.c \
@@ -88,6 +92,7 @@
 			gtksound.c \
 			gtksourceiter.c \
 			gtksourceundomanager.c \
+			gtkstatus-icon-theme.c \
 			gtkstatusbox.c \
 			gtkthemes.c \
 			gtkutils.c \
--- a/pidgin/eggtrayicon.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/eggtrayicon.c	Sat Mar 07 01:59:40 2009 +0000
@@ -39,7 +39,7 @@
   PROP_0,
   PROP_ORIENTATION
 };
-         
+
 static GtkPlugClass *parent_class = NULL;
 
 static void egg_tray_icon_init (EggTrayIcon *icon);
@@ -102,7 +102,7 @@
 {
   icon->stamp = 1;
   icon->orientation = GTK_ORIENTATION_HORIZONTAL;
-  
+
   gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
 }
 
@@ -250,7 +250,7 @@
 	  egg_tray_icon_manager_window_destroyed (icon);
 	}
     }
-  
+
   return GDK_FILTER_CONTINUE;
 }
 
@@ -296,7 +296,7 @@
 {
   XClientMessageEvent ev;
   Display *display;
-  
+
   ev.type = ClientMessage;
   ev.window = window;
   ev.message_type = icon->system_tray_opcode_atom;
@@ -335,7 +335,7 @@
 				     gboolean     dock_if_realized)
 {
   Display *xdisplay;
-  
+
   if (icon->manager_window != None)
     return;
 
@@ -345,7 +345,7 @@
     return;
 
   XGrabServer (xdisplay);
-  
+
   icon->manager_window = XGetSelectionOwner (xdisplay,
 					     icon->selection_atom);
 
@@ -355,7 +355,7 @@
 
   XUngrabServer (xdisplay);
   XFlush (xdisplay);
-  
+
   if (icon->manager_window != None)
     {
       GdkWindow *gdkwin;
@@ -380,7 +380,7 @@
 egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon)
 {
   GdkWindow *gdkwin;
-  
+
   g_return_if_fail (icon->manager_window != None);
 
 #if GTK_CHECK_VERSION(2,1,0)
@@ -458,9 +458,9 @@
 	      screen);
 
   icon->selection_atom = XInternAtom (xdisplay, buffer, False);
-  
+
   icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
-  
+
   icon->system_tray_opcode_atom = XInternAtom (xdisplay,
 						   "_NET_SYSTEM_TRAY_OPCODE",
 						   False);
@@ -514,11 +514,11 @@
 			    gint         len)
 {
   guint stamp;
-  
+
   g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
   g_return_val_if_fail (timeout >= 0, 0);
   g_return_val_if_fail (message != NULL, 0);
-		     
+
   if (icon->manager_window == None)
     return 0;
 
@@ -526,7 +526,7 @@
     len = strlen (message);
 
   stamp = icon->stamp++;
-  
+
   /* Get ready to send the message */
   egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
 				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
@@ -576,7 +576,7 @@
 {
   g_return_if_fail (EGG_IS_TRAY_ICON (icon));
   g_return_if_fail (id > 0);
-  
+
   egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
 				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
 				      id, 0, 0);
--- a/pidgin/eggtrayicon.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/eggtrayicon.h	Sat Mar 07 01:59:40 2009 +0000
@@ -33,7 +33,7 @@
 #define EGG_IS_TRAY_ICON(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON))
 #define EGG_IS_TRAY_ICON_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON))
 #define EGG_TRAY_ICON_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass))
-	
+
 typedef struct _EggTrayIcon	  EggTrayIcon;
 typedef struct _EggTrayIconClass  EggTrayIconClass;
 
@@ -42,7 +42,7 @@
   GtkPlug parent_instance;
 
   guint stamp;
-  
+
   Atom selection_atom;
   Atom manager_atom;
   Atom system_tray_opcode_atom;
@@ -74,7 +74,7 @@
 					   guint        id);
 
 GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon);
-					    
+
 G_END_DECLS
 
 #endif /* __EGG_TRAY_ICON_H__ */
--- a/pidgin/getopt.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/getopt.c	Sat Mar 07 01:59:40 2009 +0000
@@ -178,7 +178,7 @@
 {
   REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
 } ordering;
-
+
 #ifdef	__GNU_LIBRARY__
 /* We want to avoid inclusion of string.h with non-GNU libraries
    because there are many ways it can cause trouble.
@@ -219,7 +219,7 @@
     to[i] = from[i];
 }
 #endif				/* GNU C library.  */
-
+
 /* Handle permutation of arguments.  */
 
 /* Describe the part of ARGV that contains non-options that have
@@ -259,7 +259,7 @@
   first_nonopt += (optind - last_nonopt);
   last_nonopt = optind;
 }
-
+
 /* Scan elements of ARGV (whose length is ARGC) for option characters
    given in OPTSTRING.
 
@@ -663,7 +663,7 @@
 }
 
 #endif	/* _LIBC or not __GNU_LIBRARY__.  */
-
+
 #ifdef TEST
 
 /* Compile with -DTEST to make an executable for use in testing
--- a/pidgin/getopt1.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/getopt1.c	Sat Mar 07 01:59:40 2009 +0000
@@ -81,7 +81,7 @@
 
 
 #endif	/* _LIBC or not __GNU_LIBRARY__.  */
-
+
 #ifdef TEST
 
 #include <stdio.h>
--- a/pidgin/gtkaccount.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkaccount.c	Sat Mar 07 01:59:40 2009 +0000
@@ -51,7 +51,7 @@
 {
 	COLUMN_ICON,
 	COLUMN_BUDDYICON,
-	COLUMN_SCREENNAME,
+	COLUMN_USERNAME,
 	COLUMN_ENABLED,
 	COLUMN_PROTOCOL,
 	COLUMN_DATA,
@@ -78,7 +78,7 @@
 	GtkListStore *model;
 	GtkTreeIter drag_iter;
 
-	GtkTreeViewColumn *screenname_col;
+	GtkTreeViewColumn *username_col;
 
 } AccountsWindow;
 
@@ -115,7 +115,7 @@
 	GtkWidget *login_frame;
 	GtkWidget *protocol_menu;
 	GtkWidget *password_box;
-	GtkWidget *screenname_entry;
+	GtkWidget *username_entry;
 	GtkWidget *password_entry;
 	GtkWidget *alias_entry;
 	GtkWidget *remember_pass_check;
@@ -256,7 +256,7 @@
 }
 
 static gboolean
-screenname_focus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
+username_focus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
 {
 	GHashTable *table;
 	const char *label;
@@ -275,7 +275,7 @@
 }
 
 static void
-screenname_changed_cb(GtkEntry *entry, AccountPrefsDialog *dialog)
+username_changed_cb(GtkEntry *entry, AccountPrefsDialog *dialog)
 {
 	if (dialog->ok_button)
 		gtk_widget_set_sensitive(dialog->ok_button,
@@ -290,7 +290,7 @@
 }
 
 static gboolean
-screenname_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
+username_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
 {
 	GdkColor color = {0, 34952, 35466, 34181};
 	GHashTable *table = NULL;
@@ -301,13 +301,13 @@
 		label = g_hash_table_lookup(table, "login_label");
 
 		if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') {
-			/* We have to avoid hitting the screenname_changed_cb function 
+			/* We have to avoid hitting the username_changed_cb function
 			 * because it enables buttons we don't want enabled yet ;)
 			 */
-			g_signal_handlers_block_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
+			g_signal_handlers_block_by_func(widget, G_CALLBACK(username_changed_cb), dialog);
 			gtk_entry_set_text(GTK_ENTRY(widget), label);
 			/* Make sure we can hit it again */
-			g_signal_handlers_unblock_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
+			g_signal_handlers_unblock_by_func(widget, G_CALLBACK(username_changed_cb), dialog);
 			gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color);
 		}
 
@@ -393,7 +393,7 @@
 
 	set = !(purple_account_is_connected(dialog->account) || purple_account_is_connecting(dialog->account));
 	gtk_widget_set_sensitive(dialog->protocol_menu, set);
-	gtk_widget_set_sensitive(dialog->screenname_entry, set);
+	gtk_widget_set_sensitive(dialog->username_entry, set);
 
 	for (l = dialog->user_split_entries ; l != NULL ; l = l->next)
 		gtk_widget_set_sensitive((GtkWidget *)l->data, set);
@@ -449,13 +449,13 @@
 
 	gtk_widget_unref(dialog->protocol_menu);
 
-	/* Screen name */
-	dialog->screenname_entry = gtk_entry_new();
+	/* Username */
+	dialog->username_entry = gtk_entry_new();
 #if GTK_CHECK_VERSION(2,10,0)
-	g_object_set(G_OBJECT(dialog->screenname_entry), "truncate-multiline", TRUE, NULL);
+	g_object_set(G_OBJECT(dialog->username_entry), "truncate-multiline", TRUE, NULL);
 #endif
 
-	add_pref_box(dialog, vbox, _("_Username:"), dialog->screenname_entry);
+	add_pref_box(dialog, vbox, _("_Username:"), dialog->username_entry);
 
 	if (dialog->account != NULL)
 		username = g_strdup(purple_account_get_username(dialog->account));
@@ -468,17 +468,17 @@
 		table = dialog->prpl_info->get_account_text_table(NULL);
 		label = g_hash_table_lookup(table, "login_label");
 
-		gtk_entry_set_text(GTK_ENTRY(dialog->screenname_entry), label);
-		g_signal_connect(G_OBJECT(dialog->screenname_entry), "focus-in-event",
-				G_CALLBACK(screenname_focus_cb), dialog);
-		g_signal_connect(G_OBJECT(dialog->screenname_entry), "focus-out-event",
-				G_CALLBACK(screenname_nofocus_cb), dialog);
-		gtk_widget_modify_text(dialog->screenname_entry, GTK_STATE_NORMAL, &color);
+		gtk_entry_set_text(GTK_ENTRY(dialog->username_entry), label);
+		g_signal_connect(G_OBJECT(dialog->username_entry), "focus-in-event",
+				G_CALLBACK(username_focus_cb), dialog);
+		g_signal_connect(G_OBJECT(dialog->username_entry), "focus-out-event",
+				G_CALLBACK(username_nofocus_cb), dialog);
+		gtk_widget_modify_text(dialog->username_entry, GTK_STATE_NORMAL, &color);
 		g_hash_table_destroy(table);
 	}
 
-	g_signal_connect(G_OBJECT(dialog->screenname_entry), "changed",
-					 G_CALLBACK(screenname_changed_cb), dialog);
+	g_signal_connect(G_OBJECT(dialog->username_entry), "changed",
+					 G_CALLBACK(username_changed_cb), dialog);
 
 	/* Do the user split thang */
 	if (dialog->prpl_info == NULL)
@@ -547,7 +547,7 @@
 	}
 
 	if (username != NULL)
-		gtk_entry_set_text(GTK_ENTRY(dialog->screenname_entry), username);
+		gtk_entry_set_text(GTK_ENTRY(dialog->username_entry), username);
 
 	g_free(username);
 
@@ -590,7 +590,7 @@
 		gtk_widget_hide(dialog->remember_pass_check);
 	}
 
-	/* Do not let the user change the protocol/screenname while connected. */
+	/* Do not let the user change the protocol/username while connected. */
 	update_editable(NULL, dialog);
 	purple_signal_connect(purple_connections_get_handle(), "signing-on", dialog,
 					G_CALLBACK(update_editable), dialog);
@@ -1193,7 +1193,7 @@
 	PurpleAccount *account;
 
 	/* Build the username string. */
-	username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->screenname_entry)));
+	username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->username_entry)));
 
 	if (dialog->prpl_info != NULL)
 	{
@@ -1933,7 +1933,7 @@
 	gtk_tree_view_column_set_resizable(column, FALSE);
 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
 
-	/* Screen Name column */
+	/* Username column */
 	column = gtk_tree_view_column_new();
 	gtk_tree_view_column_set_title(column, _("Username"));
 	gtk_tree_view_column_set_resizable(column, TRUE);
@@ -1945,12 +1945,12 @@
 	gtk_tree_view_column_add_attribute(column, renderer,
 					   "pixbuf", COLUMN_BUDDYICON);
 
-	/* Screen Name */
+	/* Username */
 	renderer = gtk_cell_renderer_text_new();
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer,
-					   "text", COLUMN_SCREENNAME);
-	dialog->screenname_col = column;
+					   "text", COLUMN_USERNAME);
+	dialog->username_col = column;
 
 
 	/* Protocol name */
@@ -2016,7 +2016,7 @@
 	gtk_list_store_set(store, iter,
 			COLUMN_ICON, pixbuf,
 			COLUMN_BUDDYICON, buddyicon,
-			COLUMN_SCREENNAME, purple_account_get_username(account),
+			COLUMN_USERNAME, purple_account_get_username(account),
 			COLUMN_ENABLED, purple_account_get_enabled(account, PIDGIN_UI),
 			COLUMN_PROTOCOL, purple_account_get_protocol_name(account),
 			COLUMN_DATA, account,
@@ -2190,7 +2190,7 @@
 	dialog->model = gtk_list_store_new(NUM_COLUMNS,
 					GDK_TYPE_PIXBUF,   /* COLUMN_ICON */
 					GDK_TYPE_PIXBUF,   /* COLUMN_BUDDYICON */
-					G_TYPE_STRING,     /* COLUMN_SCREENNAME */
+					G_TYPE_STRING,     /* COLUMN_USERNAME */
 					G_TYPE_BOOLEAN,    /* COLUMN_ENABLED */
 					G_TYPE_STRING,     /* COLUMN_PROTOCOL */
 					G_TYPE_POINTER     /* COLUMN_DATA */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkblist-theme-loader.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,279 @@
+/*
+ * GTKBlistThemeLoader for 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 <stdlib.h>
+
+#include "xmlnode.h"
+
+#include "gtkblist-theme-loader.h"
+#include "gtkblist-theme.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+#define DEFAULT_TEXT_COLOR "black"
+
+/*****************************************************************************
+ * Buddy List Theme Builder
+ *****************************************************************************/
+
+static PurpleTheme *
+pidgin_blist_loader_build(const gchar *dir)
+{
+	xmlnode *root_node = NULL, *sub_node, *sub_sub_node;
+	gchar *filename_full, *data;
+	const gchar *temp;
+	gboolean success = TRUE;
+	GdkColor *bgcolor, *expanded_bgcolor, *collapsed_bgcolor, *contact_color;
+	GdkColor color;
+	FontColorPair *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *message_nick_said, *status;
+	PidginBlistLayout *layout;
+	PidginBlistTheme *theme;
+
+	/* Find the theme file */
+	g_return_val_if_fail(dir != NULL, NULL);
+	filename_full = g_build_filename(dir, "theme.xml", NULL);
+
+	if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR))
+		root_node = xmlnode_from_file(dir, "theme.xml", "buddy list themes", "blist-loader");
+
+	g_free(filename_full);
+	g_return_val_if_fail(root_node != NULL, NULL);
+
+	sub_node = xmlnode_get_child(root_node, "description");
+	data = xmlnode_get_data(sub_node);
+
+	/* init all structs and colors */
+	bgcolor = g_new0(GdkColor, 1);
+	expanded_bgcolor = g_new0(GdkColor, 1);
+	collapsed_bgcolor = g_new0(GdkColor, 1);
+
+	layout = g_new0(PidginBlistLayout, 1);
+
+	contact_color = g_new0(GdkColor, 1);
+
+	expanded = g_new0(FontColorPair, 1);
+	collapsed = g_new0(FontColorPair, 1);
+	contact = g_new0(FontColorPair, 1);
+	online = g_new0(FontColorPair, 1);
+	away = g_new0(FontColorPair, 1);
+	offline = g_new0(FontColorPair, 1);
+	idle = g_new0(FontColorPair, 1);
+	message = g_new0(FontColorPair, 1);
+	message_nick_said = g_new0(FontColorPair, 1);
+	status = g_new0(FontColorPair, 1);
+
+	/* <blist> */
+	if ((success = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL)) {
+		if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, bgcolor))
+			gdk_colormap_alloc_color(gdk_colormap_get_system(), bgcolor, FALSE, TRUE);
+		else {
+			g_free(bgcolor);
+			bgcolor = NULL;
+		}
+	}
+
+	/* <groups> */
+	if ((success = (success && (sub_node = xmlnode_get_child(root_node, "groups")) != NULL
+		     && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL)))
+	{
+		expanded->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+
+		if ((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color))
+			expanded->color = g_strdup(temp);
+		else expanded->color = g_strdup(DEFAULT_TEXT_COLOR);
+
+		if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, expanded_bgcolor))
+			gdk_colormap_alloc_color(gdk_colormap_get_system(), expanded_bgcolor, FALSE, TRUE);
+		else {
+			g_free(expanded_bgcolor);
+			expanded_bgcolor = NULL;
+		}
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL)))
+	{
+		collapsed->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+
+		if((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color))
+			collapsed->color = g_strdup(temp);
+		else collapsed->color = g_strdup(DEFAULT_TEXT_COLOR);
+
+		if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, collapsed_bgcolor))
+			gdk_colormap_alloc_color(gdk_colormap_get_system(), collapsed_bgcolor, FALSE, TRUE);
+		else {
+			g_free(collapsed_bgcolor);
+			collapsed_bgcolor = NULL;
+		}
+	}
+
+	/* <buddys> */
+	if ((success = (success && (sub_node = xmlnode_get_child(root_node, "buddys")) != NULL &&
+		     (sub_sub_node = xmlnode_get_child(sub_node, "placement")) != NULL)))
+	{
+		layout->status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0;
+		layout->text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1;
+		layout->emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2;
+		layout->protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3;
+		layout->buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4;
+		layout->show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1;
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL))) {
+		if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), contact_color))
+			gdk_colormap_alloc_color(gdk_colormap_get_system(), contact_color, FALSE, TRUE);
+		else {
+			g_free(contact_color);
+			contact_color = NULL;
+		}
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "contact_text")) != NULL))) {
+		contact->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			contact->color = g_strdup(temp);
+		else contact->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "online_text")) != NULL))) {
+		online->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			online->color = g_strdup(temp);
+		else online->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "away_text")) != NULL))) {
+		away->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			away->color = g_strdup(temp);
+		else away->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "offline_text")) != NULL))) {
+		offline->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			online->color = g_strdup(temp);
+		else online->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "idle_text")) != NULL))) {
+		idle->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			idle->color = g_strdup(temp);
+		else online->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "message_text")) != NULL))) {
+		message->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			message->color = g_strdup(temp);
+		else message->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "message_nick_said_text")) != NULL))) {
+		message_nick_said->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			message_nick_said->color = g_strdup(temp);
+		else message_nick_said->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "status_text")) != NULL))) {
+		status->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font"));
+		if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color))
+			status->color = g_strdup(temp);
+		else status->color = g_strdup(DEFAULT_TEXT_COLOR);
+	}
+
+	/* name is required for theme manager */
+	success = (success && xmlnode_get_attrib(root_node, "name") != NULL);
+
+	/* the new theme */
+	theme = g_object_new(PIDGIN_TYPE_BLIST_THEME,
+			"type", "blist",
+			"name", xmlnode_get_attrib(root_node, "name"),
+			"author", xmlnode_get_attrib(root_node, "author"),
+			"image", xmlnode_get_attrib(root_node, "image"),
+			"directory", dir,
+			"description", data,
+			"background-color", bgcolor,
+			"layout", layout,
+			"expanded-color", expanded_bgcolor,
+			"expanded-text", expanded,
+			"collapsed-color", collapsed_bgcolor,
+			"collapsed-text", collapsed,
+			"contact-color", contact_color,
+			"contact", contact,
+			"online", online,
+			"away", away,
+			"offline", offline,
+			"idle", idle,
+			"message", message,
+			"message_nick_said", message_nick_said,
+			"status", status, NULL);
+
+	xmlnode_free(root_node);
+	g_free(data);
+
+	/* malformed xml file - also frees all partial data*/
+	if (!success) {
+		g_object_unref(theme);
+		theme = NULL;
+	}
+
+	return PURPLE_THEME(theme);
+}
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+pidgin_blist_theme_loader_class_init(PidginBlistThemeLoaderClass *klass)
+{
+	PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass);
+
+	loader_klass->purple_theme_loader_build = pidgin_blist_loader_build;
+}
+
+GType
+pidgin_blist_theme_loader_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PidginBlistThemeLoaderClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_blist_theme_loader_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PidginBlistThemeLoader),
+			0, /* n_preallocs */
+			NULL, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PURPLE_TYPE_THEME_LOADER,
+				"PidginBlistThemeLoader", &info, 0);
+	}
+	return type;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkblist-theme-loader.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,71 @@
+/**
+ * @file gtkblist-loader.h  Pidgin Buddy List Theme Loader Class API
+ */
+
+/* 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_BLIST_THEME_LOADER_H
+#define PIDGIN_BLIST_THEME_LOADER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme-loader.h"
+
+/**
+ * A pidgin buddy list theme loader. extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build sound themes
+ *
+ * PidginBlistThemeLoader is a GObject.
+ */
+typedef struct _PidginBlistThemeLoader        PidginBlistThemeLoader;
+typedef struct _PidginBlistThemeLoaderClass   PidginBlistThemeLoaderClass;
+
+#define PIDGIN_TYPE_BLIST_THEME_LOADER            (pidgin_blist_theme_loader_get_type ())
+#define PIDGIN_BLIST_THEME_LOADER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoader))
+#define PIDGIN_BLIST_THEME_LOADER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoaderClass))
+#define PIDGIN_IS_BLIST_THEME_LOADER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER))
+#define PIDGIN_IS_BLIST_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME_LOADER))
+#define PIDGIN_BLIST_THEME_LOADER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME_LOADER, PidginBlistThemeLoaderClass))
+
+struct _PidginBlistThemeLoader
+{
+	PurpleThemeLoader parent;
+};
+
+struct _PidginBlistThemeLoaderClass
+{
+	PurpleThemeLoaderClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Buddy List Theme-Loader API                                     */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_blist_theme_loader_get_type(void);
+
+G_END_DECLS
+#endif /* PIDGIN_BLIST_THEME_LOADER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkblist-theme.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,784 @@
+/*
+ * Buddy List Themes for 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 "gtkblist-theme.h"
+
+#define PIDGIN_BLIST_THEME_GET_PRIVATE(Gobject) \
+	((PidginBlistThemePrivate *) ((PIDGIN_BLIST_THEME(Gobject))->priv))
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+typedef struct {
+	/* Buddy list */
+	gdouble opacity;
+	GdkColor *bgcolor;
+	PidginBlistLayout *layout;
+
+	/* groups */
+	GdkColor *expanded_color;
+	FontColorPair *expanded;
+
+	GdkColor *collapsed_color;
+	FontColorPair *collapsed;
+
+	/* buddy */
+	GdkColor *contact_color;
+
+	FontColorPair *contact;
+
+	FontColorPair *online;
+	FontColorPair *away;
+	FontColorPair *offline;
+	FontColorPair *idle;
+	FontColorPair *message;
+	FontColorPair *message_nick_said;
+
+	FontColorPair *status;
+
+} PidginBlistThemePrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+enum {
+	PROP_ZERO = 0,
+	PROP_BACKGROUND_COLOR,
+	PROP_OPACITY,
+	PROP_LAYOUT,
+	PROP_EXPANDED_COLOR,
+	PROP_EXPANDED_TEXT,
+	PROP_COLLAPSED_COLOR,
+	PROP_COLLAPSED_TEXT,
+	PROP_CONTACT_COLOR,
+	PROP_CONTACT,
+	PROP_ONLINE,
+	PROP_AWAY,
+	PROP_OFFLINE,
+	PROP_IDLE,
+	PROP_MESSAGE,
+	PROP_MESSAGE_NICK_SAID,
+	PROP_STATUS,
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+
+void
+free_font_and_color(FontColorPair *pair)
+{
+	if (pair != NULL) {
+		if (pair->font)
+			g_free(pair->font);
+		if (pair->color)
+			g_free(pair->color);
+		g_free(pair);
+	}
+}
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+pidgin_blist_theme_init(GTypeInstance *instance,
+		gpointer klass)
+{
+	(PIDGIN_BLIST_THEME(instance))->priv = g_new0(PidginBlistThemePrivate, 1);
+}
+
+static void
+pidgin_blist_theme_get_property(GObject *obj, guint param_id, GValue *value,
+		GParamSpec *psec)
+{
+	PidginBlistTheme *theme = PIDGIN_BLIST_THEME(obj);
+
+	switch (param_id) {
+		case PROP_BACKGROUND_COLOR:
+			g_value_set_pointer(value, pidgin_blist_theme_get_background_color(theme));
+			break;
+		case PROP_OPACITY:
+			g_value_set_double(value, pidgin_blist_theme_get_opacity(theme));
+			break;
+		case PROP_LAYOUT:
+			g_value_set_pointer(value, pidgin_blist_theme_get_layout(theme));
+			break;
+		case PROP_EXPANDED_COLOR:
+			g_value_set_pointer(value, pidgin_blist_theme_get_expanded_background_color(theme));
+			break;
+		case PROP_EXPANDED_TEXT:
+			g_value_set_pointer(value, pidgin_blist_theme_get_expanded_text_info(theme));
+			break;
+		case PROP_COLLAPSED_COLOR:
+			g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_background_color(theme));
+			break;
+		case PROP_COLLAPSED_TEXT:
+			g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_text_info(theme));
+			break;
+		case PROP_CONTACT_COLOR:
+			g_value_set_pointer(value, pidgin_blist_theme_get_contact_color(theme));
+			break;
+		case PROP_CONTACT:
+			g_value_set_pointer(value, pidgin_blist_theme_get_contact_text_info(theme));
+			break;
+		case PROP_ONLINE:
+			g_value_set_pointer(value, pidgin_blist_theme_get_online_text_info(theme));
+			break;
+		case PROP_AWAY:
+			g_value_set_pointer(value, pidgin_blist_theme_get_away_text_info(theme));
+			break;
+		case PROP_OFFLINE:
+			g_value_set_pointer(value, pidgin_blist_theme_get_offline_text_info(theme));
+			break;
+		case PROP_IDLE:
+			g_value_set_pointer(value, pidgin_blist_theme_get_idle_text_info(theme));
+			break;
+		case PROP_MESSAGE:
+			g_value_set_pointer(value, pidgin_blist_theme_get_unread_message_text_info(theme));
+			break;
+		case PROP_MESSAGE_NICK_SAID:
+			g_value_set_pointer(value, pidgin_blist_theme_get_unread_message_nick_said_text_info(theme));
+			break;
+		case PROP_STATUS:
+			g_value_set_pointer(value, pidgin_blist_theme_get_status_text_info(theme));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+pidgin_blist_theme_set_property(GObject *obj, guint param_id, const GValue *value,
+		GParamSpec *psec)
+{
+	PidginBlistTheme *theme = PIDGIN_BLIST_THEME(obj);
+
+	switch (param_id) {
+		case PROP_BACKGROUND_COLOR:
+			pidgin_blist_theme_set_background_color(theme, g_value_get_pointer(value));
+			break;
+		case PROP_OPACITY:
+			pidgin_blist_theme_set_opacity(theme, g_value_get_double(value));
+			break;
+		case PROP_LAYOUT:
+			pidgin_blist_theme_set_layout(theme, g_value_get_pointer(value));
+			break;
+		case PROP_EXPANDED_COLOR:
+			pidgin_blist_theme_set_expanded_background_color(theme, g_value_get_pointer(value));
+			break;
+		case PROP_EXPANDED_TEXT:
+			pidgin_blist_theme_set_expanded_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_COLLAPSED_COLOR:
+			pidgin_blist_theme_set_collapsed_background_color(theme, g_value_get_pointer(value));
+			break;
+		case PROP_COLLAPSED_TEXT:
+			pidgin_blist_theme_set_collapsed_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_CONTACT_COLOR:
+			pidgin_blist_theme_set_contact_color(theme, g_value_get_pointer(value));
+			break;
+		case PROP_CONTACT:
+			pidgin_blist_theme_set_contact_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_ONLINE:
+			pidgin_blist_theme_set_online_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_AWAY:
+			pidgin_blist_theme_set_away_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_OFFLINE:
+			pidgin_blist_theme_set_offline_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_IDLE:
+			pidgin_blist_theme_set_idle_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_MESSAGE:
+			pidgin_blist_theme_set_unread_message_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_MESSAGE_NICK_SAID:
+			pidgin_blist_theme_set_unread_message_nick_said_text_info(theme, g_value_get_pointer(value));
+			break;
+		case PROP_STATUS:
+			pidgin_blist_theme_set_status_text_info(theme, g_value_get_pointer(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+pidgin_blist_theme_finalize(GObject *obj)
+{
+	PidginBlistThemePrivate *priv;
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(obj);
+
+	/* Buddy List */
+	g_free(priv->layout);
+
+	/* Group */
+	free_font_and_color(priv->expanded);
+	free_font_and_color(priv->collapsed);
+
+	/* Buddy */
+	free_font_and_color(priv->contact);
+	free_font_and_color(priv->online);
+	free_font_and_color(priv->away);
+	free_font_and_color(priv->offline);
+	free_font_and_color(priv->message);
+	free_font_and_color(priv->message_nick_said);
+	free_font_and_color(priv->status);
+
+	g_free(priv);
+
+	parent_class->finalize (obj);
+}
+
+static void
+pidgin_blist_theme_class_init(PidginBlistThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	obj_class->get_property = pidgin_blist_theme_get_property;
+	obj_class->set_property = pidgin_blist_theme_set_property;
+	obj_class->finalize = pidgin_blist_theme_finalize;
+
+	/* Buddy List */
+	pspec = g_param_spec_pointer("background-color", "Background Color",
+			"The background color for the buddy list",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_BACKGROUND_COLOR, pspec);
+
+	pspec = g_param_spec_pointer("layout", "Layout",
+			"The layout of icons, name, and status of the blist",
+			G_PARAM_READWRITE);
+
+	g_object_class_install_property(obj_class, PROP_LAYOUT, pspec);
+
+	/* Group */
+	pspec = g_param_spec_pointer("expanded-color", "Expanded Background Color",
+			"The background color of an expanded group",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_EXPANDED_COLOR, pspec);
+
+	pspec = g_param_spec_pointer("expanded-text", "Expanded Text",
+			"The text information for when a group is expanded",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_EXPANDED_TEXT, pspec);
+
+	pspec = g_param_spec_pointer("collapsed-color", "Collapsed Background Color",
+			"The background color of a collapsed group",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_COLLAPSED_COLOR, pspec);
+
+	pspec = g_param_spec_pointer("collapsed-text", "Collapsed Text",
+			"The text information for when a group is collapsed",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_COLLAPSED_TEXT, pspec);
+
+	/* Buddy */
+	pspec = g_param_spec_pointer("contact-color", "Contact/Chat Background Color",
+			"The background color of a contact or chat",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_CONTACT_COLOR, pspec);
+
+	pspec = g_param_spec_pointer("contact", "Contact Text",
+			"The text information for when a contact is expanded",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_CONTACT, pspec);
+
+	pspec = g_param_spec_pointer("online", "On-line Text",
+			"The text information for when a buddy is online",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_ONLINE, pspec);
+
+	pspec = g_param_spec_pointer("away", "Away Text",
+			"The text information for when a buddy is away",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_AWAY, pspec);
+
+	pspec = g_param_spec_pointer("offline", "Off-line Text",
+			"The text information for when a buddy is off-line",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_OFFLINE, pspec);
+
+	pspec = g_param_spec_pointer("idle", "Idle Text",
+			"The text information for when a buddy is idle",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_IDLE, pspec);
+
+	pspec = g_param_spec_pointer("message", "Message Text",
+			"The text information for when a buddy has an unread message",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_MESSAGE, pspec);
+
+	pspec = g_param_spec_pointer("message_nick_said", "Message (Nick Said) Text",
+			"The text information for when a chat has an unread message that mentions your nick",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_MESSAGE_NICK_SAID, pspec);
+
+	pspec = g_param_spec_pointer("status", "Status Text",
+			"The text information for a buddy's status",
+			G_PARAM_READWRITE);
+	g_object_class_install_property(obj_class, PROP_STATUS, pspec);
+}
+
+GType
+pidgin_blist_theme_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static GTypeInfo info = {
+			sizeof(PidginBlistThemeClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_blist_theme_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PidginBlistTheme),
+			0, /* n_preallocs */
+			pidgin_blist_theme_init, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static (PURPLE_TYPE_THEME,
+				"PidginBlistTheme", &info, 0);
+	}
+	return type;
+}
+
+
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+/* get methods */
+
+GdkColor *
+pidgin_blist_theme_get_background_color(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->bgcolor;
+}
+
+gdouble
+pidgin_blist_theme_get_opacity(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), 1.0);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->opacity;
+}
+
+PidginBlistLayout *
+pidgin_blist_theme_get_layout(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->layout;
+}
+
+GdkColor *
+pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->expanded_color;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->expanded;
+}
+
+GdkColor *
+pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->collapsed_color;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->collapsed;
+}
+
+GdkColor *
+pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->contact_color;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->contact;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->online;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->away;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->offline;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->idle;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->message;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->message_nick_said;
+}
+
+FontColorPair *
+pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	return priv->status;
+}
+
+/* Set Methods */
+void
+pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	priv->bgcolor = color;
+}
+
+void
+pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme) || opacity < 0.0 || opacity > 1.0);
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	priv->opacity = opacity;
+}
+
+void
+pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	g_free(priv->layout);
+	priv->layout = layout;
+}
+
+void
+pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	priv->expanded_color = color;
+}
+
+void
+pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->expanded);
+	priv->expanded = pair;
+}
+
+void
+pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	priv->collapsed_color = color;
+}
+
+void
+pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->collapsed);
+	priv->collapsed = pair;
+}
+
+void
+pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	priv->contact_color = color;
+}
+
+void
+pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->contact);
+	priv->contact = pair;
+}
+
+void
+pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->online);
+	priv->online = pair;
+}
+
+void
+pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->away);
+	priv->away = pair;
+}
+
+void
+pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->offline);
+	priv->offline = pair;
+}
+
+void
+pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->idle);
+	priv->idle = pair;
+}
+
+void
+pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->message);
+	priv->message = pair;
+}
+
+void
+pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->message_nick_said);
+	priv->message_nick_said = pair;
+}
+
+void
+pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair)
+{
+	PidginBlistThemePrivate *priv;
+
+	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
+
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+
+	free_font_and_color(priv->status);
+	priv->status = pair;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkblist-theme.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,332 @@
+/**
+ * @file gtkblist-theme.h GTK+ Buddy List Theme API
+ */
+
+/* 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_BLIST_THEME_H
+#define PIDGIN_BLIST_THEME_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "theme.h"
+
+/**
+ * A pidgin buddy list theme.
+ * This is an object for Purple to represent a buddy list theme.
+ *
+ * PidginBlistTheme is a PurpleTheme Object.
+ */
+typedef struct _PidginBlistTheme        PidginBlistTheme;
+typedef struct _PidginBlistThemeClass   PidginBlistThemeClass;
+
+#define PIDGIN_TYPE_BLIST_THEME            (pidgin_blist_theme_get_type ())
+#define PIDGIN_BLIST_THEME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_BLIST_THEME, PidginBlistTheme))
+#define PIDGIN_BLIST_THEME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemeClass))
+#define PIDGIN_IS_BLIST_THEME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_BLIST_THEME))
+#define PIDGIN_IS_BLIST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_BLIST_THEME))
+#define PIDGIN_BLIST_THEME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemeClass))
+
+struct _PidginBlistTheme
+{
+	PurpleTheme parent;
+	gpointer priv;
+};
+
+struct _PidginBlistThemeClass
+{
+	PurpleThemeClass parent_class;
+};
+
+typedef struct
+{
+	gchar *font;
+	gchar *color;
+
+} FontColorPair;
+
+typedef struct
+{
+	gint status_icon;
+	gint text;
+	gint emblem;
+	gint protocol_icon;
+	gint buddy_icon;
+	gboolean show_status;
+
+} PidginBlistLayout;
+
+/**************************************************************************/
+/** @name FontColorPair API                                               */
+/**************************************************************************/
+
+/**
+ * Frees a font and color pair
+ */
+void free_font_and_color(FontColorPair *pair);
+
+/**************************************************************************/
+/** @name Purple Buddy List Theme API                                     */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_blist_theme_get_type(void);
+
+/* get methods */
+
+/**
+ * Returns the background color of the buddy list.
+ *
+ * @returns A gdk color.
+ */
+ GdkColor *pidgin_blist_theme_get_background_color(PidginBlistTheme *theme);
+
+/**
+ * Returns the opacity of the buddy list window
+ * (0.0 or clear to 1.0 fully opaque).
+ *
+ * @returns The opacity
+ */
+gdouble pidgin_blist_theme_get_opacity(PidginBlistTheme *theme);
+
+/**
+ * Returns the layout to be used with the buddy list.
+ *
+ * @returns The buddy list layout.
+ */
+ PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme);
+
+/**
+ * Returns the background color to be used with expanded groups.
+ *
+ * @returns A gdk color.
+ */
+ GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used with expanded groups.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the background color to be used with collapsed groups.
+ *
+ * @returns A gdk color.
+ */
+ GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used with collapsed groups.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the colors to be used for contacts and chats.
+ *
+ * @returns A gdkcolor for contacts and chats.
+ */
+ GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for expanded contacts.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for online buddies.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for away and idle buddies.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for offline buddies.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for idle buddies.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for buddies with unread messages.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for chats with unread messages
+ * that mention your nick.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme);
+
+/**
+ * Returns the text font and color to be used for a buddy's status message.
+ *
+ * @returns A font and color pair.
+ */
+ FontColorPair *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme);
+
+/* Set Methods */
+
+/**
+ * Sets the background color to be used for this buddy list theme.
+ *
+ * @param color The new background color.
+ */
+void pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color);
+
+/**
+ * Sets the opacity to be used for this buddy list theme.
+ *
+ * @param opacity The new opacity setting.
+ */
+void pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity);
+
+/**
+ * Sets the buddy list layout to be used for this buddy list theme.
+ *
+ * @param layout The new layout.
+ */
+void pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout);
+
+/**
+ * Sets the background color to be used for expanded groups.
+ *
+ * @param color The new background color.
+ */
+void pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color);
+
+/**
+ * Sets the text color and font to be used for expanded groups.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the background color to be used for collapsed groups.
+ *
+ * @param color The new background color.
+ */
+void pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color);
+
+/**
+ * Sets the text color and font to be used for expanded groups.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the background color to be used for contacts and chats.
+ *
+ * @param color The color to use for contacts and chats.
+ */
+void pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color);
+
+/**
+ * Sets the text color and font to be used for expanded contacts.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the text color and font to be used for online buddies.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the text color and font to be used for away and idle buddies.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the text color and font to be used for offline buddies.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the text color and font to be used for idle buddies.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the text color and font to be used for buddies with unread messages.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the text color and font to be used for a chat with unread messages
+ * that mention your nick.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+/**
+ * Sets the text color and font to be used for buddy status messages.
+ *
+ * @param pair The new text font at color pair.
+ */
+void pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair);
+
+G_END_DECLS
+#endif /* PIDGIN_BLIST_THEME_H */
--- a/pidgin/gtkblist.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkblist.c	Sat Mar 07 01:59:40 2009 +0000
@@ -38,6 +38,8 @@
 #include "request.h"
 #include "signals.h"
 #include "pidginstock.h"
+#include "theme-loader.h"
+#include "theme-manager.h"
 #include "util.h"
 
 #include "gtkaccount.h"
@@ -58,6 +60,8 @@
 #include "gtkstatusbox.h"
 #include "gtkscrollbook.h"
 #include "gtksmiley.h"
+#include "gtkblist-theme.h"
+#include "gtkblist-theme-loader.h"
 #include "gtkutils.h"
 #include "pidgin/minidialog.h"
 #include "pidgin/pidgintooltip.h"
@@ -121,6 +125,9 @@
 	 *  is showing; @c NULL otherwise.
 	 */
 	PidginMiniDialog *signed_on_elsewhere;
+
+	PidginBlistTheme *current_theme;
+
 } PidginBuddyListPrivate;
 
 #define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \
@@ -142,7 +149,7 @@
 #if GTK_CHECK_VERSION(2,2,1)
 static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
 static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
-static void sort_method_log(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
+static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
 #endif
 static PidginBuddyList *gtkblist = NULL;
 
@@ -164,7 +171,8 @@
 static void set_urgent(void);
 
 typedef enum {
-	PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE    =  1 << 0,  /* Whether there's pending message in a conversation */
+	PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE            =  1 << 0,  /* Whether there's pending message in a conversation */
+	PIDGIN_BLIST_CHAT_HAS_PENDING_MESSAGE_WITH_NICK	 =  1 << 1,  /* Whether there's a pending message in a chat that mentions our nick */
 } PidginBlistNodeFlags;
 
 typedef struct _pidgin_blist_node {
@@ -179,17 +187,6 @@
 	} conv;
 } PidginBlistNode;
 
-static char dim_grey_string[8] = "";
-static char *dim_grey(void)
-{
-	if (!gtkblist)
-		return "dim grey";
-	if (!dim_grey_string[0]) {
-		snprintf(dim_grey_string, sizeof(dim_grey_string), "%s", pidgin_get_dim_grey_string(gtkblist->treeview)); 
-	}
-	return dim_grey_string;
-}
-
 /***************************************************
  *              Callbacks                          *
  ***************************************************/
@@ -329,17 +326,24 @@
 
 static void gtk_blist_menu_info_cb(GtkWidget *w, PurpleBuddy *b)
 {
-	pidgin_retrieve_user_info(b->account->gc, purple_buddy_get_name(b));
+	PurpleAccount *account = purple_buddy_get_account(b);
+
+	pidgin_retrieve_user_info(purple_account_get_connection(account),
+	                          purple_buddy_get_name(b));
 }
 
 static void gtk_blist_menu_im_cb(GtkWidget *w, PurpleBuddy *b)
 {
-	pidgin_dialogs_im_with_user(b->account, b->name);
+	pidgin_dialogs_im_with_user(purple_buddy_get_account(b),
+	                            purple_buddy_get_name(b));
 }
 
 static void gtk_blist_menu_send_file_cb(GtkWidget *w, PurpleBuddy *b)
 {
-	serv_send_file(b->account->gc, b->name, NULL);
+	PurpleAccount *account = purple_buddy_get_account(b);
+
+	serv_send_file(purple_account_get_connection(account),
+	               purple_buddy_get_name(b), NULL);
 }
 
 static void gtk_blist_menu_move_to_cb(GtkWidget *w, PurpleBlistNode *node)
@@ -364,7 +368,7 @@
 static PurpleConversation *
 find_conversation_with_buddy(PurpleBuddy *buddy)
 {
-	PidginBlistNode *ui = buddy->node.ui_data;
+	PidginBlistNode *ui = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
 	if (ui)
 		return ui->conv.conv;
 	return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
@@ -374,15 +378,20 @@
 
 static void gtk_blist_join_chat(PurpleChat *chat)
 {
+	PurpleAccount *account;
 	PurpleConversation *conv;
 	PurplePluginProtocolInfo *prpl_info;
+	GHashTable *components;
 	const char *name;
 	char *chat_name;
 
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(chat->account)));
+	account = purple_chat_get_account(chat);
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
+
+	components = purple_chat_get_components(chat);
 
 	if (prpl_info && prpl_info->get_chat_name)
-		chat_name = prpl_info->get_chat_name(chat->components);
+		chat_name = prpl_info->get_chat_name(components);
 	else
 		chat_name = NULL;
 
@@ -392,14 +401,14 @@
 		name = purple_chat_get_name(chat);
 
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name,
-											   chat->account);
+	                                             account);
 
 	if (conv != NULL) {
 		pidgin_conv_attach_to_conversation(conv);
 		purple_conversation_present(conv);
 	}
 
-	serv_join_chat(chat->account->gc, chat->components);
+	serv_join_chat(purple_account_get_connection(account), components);
 	g_free(chat_name);
 }
 
@@ -434,15 +443,15 @@
 	gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val);
 	node = g_value_get_pointer(&val);
 
-	switch (node->type) {
+	switch (purple_blist_node_get_type(node)) {
 	case PURPLE_BLIST_CONTACT_NODE:
-		text = purple_contact_get_alias((PurpleContact *)node);
+		text = purple_contact_get_alias(PURPLE_CONTACT(node));
 		break;
 	case PURPLE_BLIST_BUDDY_NODE:
-		text = purple_buddy_get_alias((PurpleBuddy *)node);
+		text = purple_buddy_get_alias(PURPLE_BUDDY(node));
 		break;
 	case PURPLE_BLIST_GROUP_NODE:
-		text = ((PurpleGroup *)node)->name;
+		text = purple_group_get_name(PURPLE_GROUP(node));
 		break;
 	default:
 		g_return_if_reached();
@@ -470,17 +479,24 @@
 	for (tmp = merges; tmp; tmp = tmp->next) {
 		PurpleBlistNode *node = tmp->data;
 		PurpleBlistNode *b;
+		PurpleBlistNodeType type;
 		int i = 0;
 
-		if (node->type == PURPLE_BLIST_BUDDY_NODE)
-			node = node->parent;
-
-		if (node->type != PURPLE_BLIST_CONTACT_NODE)
+		type = purple_blist_node_get_type(node);
+
+		if(type == PURPLE_BLIST_BUDDY_NODE)
+			node = purple_blist_node_get_parent(node);
+
+		if(type == PURPLE_BLIST_CONTACT_NODE)
 			continue;
-		
-
-		for (b = node->child; b; b = b->next)
+
+		for (b = purple_blist_node_get_first_child(node);
+		     b;
+		     b = purple_blist_node_get_sibling_next(b))
+		{
 			i++;
+		}
+
 		if (i > max) {
 			contact = node;
 			max = i;
@@ -493,8 +509,8 @@
 	/* Merge all those buddies into this contact */
 	for (tmp = merges; tmp; tmp = tmp->next) {
 		PurpleBlistNode *node = tmp->data;
-		if (node->type == PURPLE_BLIST_BUDDY_NODE)
-			node = node->parent;
+		if (purple_blist_node_get_type(node) == PURPLE_BLIST_BUDDY_NODE)
+			node = purple_blist_node_get_parent(node);
 
 		if (node == contact)
 			continue;
@@ -516,9 +532,11 @@
 	int i = 0;
 	char *a = g_utf8_casefold(alias, -1);
 
-	for (contact = group->child; contact; contact = contact->next) {
+	for (contact = purple_blist_node_get_first_child(group);
+	     contact != NULL;
+	     contact = purple_blist_node_get_sibling_next(contact)) {
 		char *node_alias;
-		if (contact->type != PURPLE_BLIST_CONTACT_NODE)
+		if (purple_blist_node_get_type(contact) != PURPLE_BLIST_CONTACT_NODE)
 			continue;
 
 		node_alias = g_utf8_casefold(purple_contact_get_alias((PurpleContact *)contact), -1);
@@ -530,11 +548,14 @@
 		}
 		g_free(node_alias);
 
-		for (buddy = contact->child; buddy; buddy = buddy->next) {
-			if (buddy->type != PURPLE_BLIST_BUDDY_NODE)
+		for (buddy = purple_blist_node_get_first_child(contact);
+		     buddy;
+		     buddy = purple_blist_node_get_sibling_next(buddy))
+		{
+			if (purple_blist_node_get_type(buddy) != PURPLE_BLIST_BUDDY_NODE)
 				continue;
 
-			node_alias = g_utf8_casefold(purple_buddy_get_alias((PurpleBuddy *)buddy), -1);
+			node_alias = g_utf8_casefold(purple_buddy_get_alias(PURPLE_BUDDY(buddy)), -1);
 			if (node_alias && !g_utf8_collate(node_alias, a)) {
 				merges = g_list_append(merges, buddy);
 				i++;
@@ -576,39 +597,45 @@
 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW(gtkblist->treeview), TRUE);
 	g_object_set(G_OBJECT(gtkblist->text_rend), "editable", FALSE, NULL);
 
-	switch (node->type)
+	switch (purple_blist_node_get_type(node))
 	{
 		case PURPLE_BLIST_CONTACT_NODE:
 			{
-				PurpleContact *contact = (PurpleContact *)node;
-				struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data;
-
-				if (contact->alias || gtknode->contact_expanded) {
+				PurpleContact *contact = PURPLE_CONTACT(node);
+				struct _pidgin_blist_node *gtknode =
+					(struct _pidgin_blist_node *)purple_blist_node_get_ui_data(node);
+
+				if (purple_contact_get_alias(contact) || gtknode->contact_expanded) {
 					purple_blist_alias_contact(contact, arg2);
-					gtk_blist_auto_personize(node->parent, arg2);
+					gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
 				} else {
 					PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact);
 					purple_blist_alias_buddy(buddy, arg2);
 					serv_alias_buddy(buddy);
-					gtk_blist_auto_personize(node->parent, arg2);
+					gtk_blist_auto_personize(purple_blist_node_get_parent(node), arg2);
 				}
 			}
 			break;
 
 		case PURPLE_BLIST_BUDDY_NODE:
-			purple_blist_alias_buddy((PurpleBuddy*)node, arg2);
-			serv_alias_buddy((PurpleBuddy *)node);
-			gtk_blist_auto_personize(node->parent->parent, arg2);
+			{
+				PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node));
+
+				purple_blist_alias_buddy(PURPLE_BUDDY(node), arg2);
+				serv_alias_buddy(PURPLE_BUDDY(node));
+				gtk_blist_auto_personize(PURPLE_BLIST_NODE(group), arg2);
+			}
 			break;
 		case PURPLE_BLIST_GROUP_NODE:
 			dest = purple_find_group(arg2);
-			if (dest != NULL && purple_utf8_strcasecmp(arg2, ((PurpleGroup*) node)->name)) {
-				pidgin_dialogs_merge_groups((PurpleGroup*) node, arg2);
-			} else
-				purple_blist_rename_group((PurpleGroup*)node, arg2);
+			if (dest != NULL && purple_utf8_strcasecmp(arg2, purple_group_get_name(PURPLE_GROUP(node)))) {
+				pidgin_dialogs_merge_groups(PURPLE_GROUP(node), arg2);
+			} else {
+				purple_blist_rename_group(PURPLE_GROUP(node), arg2);
+			}
 			break;
 		case PURPLE_BLIST_CHAT_NODE:
-			purple_blist_alias_chat((PurpleChat*)node, arg2);
+			purple_blist_alias_chat(PURPLE_CHAT(node), arg2);
 			break;
 		default:
 			break;
@@ -695,7 +722,7 @@
 
 	if (!(get_iter_from_node(node, &iter))) {
 		/* This is either a bug, or the buddy is in a collapsed contact */
-		node = node->parent;
+		node = purple_blist_node_get_parent(node);
 		if (!get_iter_from_node(node, &iter))
 			/* Now it's definitely a bug */
 			return;
@@ -718,7 +745,8 @@
 
 static void gtk_blist_menu_bp_cb(GtkWidget *w, PurpleBuddy *b)
 {
-	pidgin_pounce_editor_show(b->account, b->name, NULL);
+	pidgin_pounce_editor_show(purple_buddy_get_account(b),
+	                          purple_buddy_get_name(b), NULL);
 }
 
 static void gtk_blist_menu_showlog_cb(GtkWidget *w, PurpleBlistNode *node)
@@ -732,19 +760,19 @@
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		PurpleBuddy *b = (PurpleBuddy*) node;
 		type = PURPLE_LOG_IM;
-		name = g_strdup(b->name);
-		account = b->account;
+		name = g_strdup(purple_buddy_get_name(b));
+		account = purple_buddy_get_account(b);
 	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
-		PurpleChat *c = (PurpleChat*) node;
+		PurpleChat *c = PURPLE_CHAT(node);
 		PurplePluginProtocolInfo *prpl_info = NULL;
 		type = PURPLE_LOG_CHAT;
-		account = c->account;
+		account = purple_chat_get_account(c);
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account)));
 		if (prpl_info && prpl_info->get_chat_name) {
-			name = prpl_info->get_chat_name(c->components);
+			name = prpl_info->get_chat_name(purple_chat_get_components(c));
 		}
 	} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
-		pidgin_log_show_contact((PurpleContact *)node);
+		pidgin_log_show_contact(PURPLE_CONTACT(node));
 		pidgin_clear_cursor(gtkblist->window);
 		return;
 	} else {
@@ -757,10 +785,10 @@
 
 	if (name && account) {
 		pidgin_log_show(type, name, account);
-		g_free(name);
-
 		pidgin_clear_cursor(gtkblist->window);
 	}
+
+	g_free(name);
 }
 
 static void gtk_blist_menu_showoffline_cb(GtkWidget *w, PurpleBlistNode *node)
@@ -777,7 +805,10 @@
 		gboolean setting = !purple_blist_node_get_bool(node, "show_offline");
 
 		purple_blist_node_set_bool(node, "show_offline", setting);
-		for (bnode = node->child; bnode != NULL; bnode = bnode->next) {
+		for (bnode = purple_blist_node_get_first_child(node);
+		     bnode != NULL;
+		     bnode = purple_blist_node_get_sibling_next(bnode))
+		{
 			purple_blist_node_set_bool(bnode, "show_offline", setting);
 			pidgin_blist_update(purple_get_blist(), bnode);
 		}
@@ -786,9 +817,15 @@
 		gboolean setting = !purple_blist_node_get_bool(node, "show_offline");
 
 		purple_blist_node_set_bool(node, "show_offline", setting);
-		for (cnode = node->child; cnode != NULL; cnode = cnode->next) {
+		for (cnode = purple_blist_node_get_first_child(node);
+		     cnode != NULL;
+		     cnode = purple_blist_node_get_sibling_next(cnode))
+		{
 			purple_blist_node_set_bool(cnode, "show_offline", setting);
-			for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) {
+			for (bnode = purple_blist_node_get_first_child(cnode);
+			     bnode != NULL;
+			     bnode = purple_blist_node_get_sibling_next(bnode))
+			{
 				purple_blist_node_set_bool(bnode, "show_offline", setting);
 				pidgin_blist_update(purple_get_blist(), bnode);
 			}
@@ -900,9 +937,10 @@
 static void
 pidgin_blist_update_privacy_cb(PurpleBuddy *buddy)
 {
-	if (buddy->node.ui_data == NULL || ((struct _pidgin_blist_node*)buddy->node.ui_data)->row == NULL)
+	struct _pidgin_blist_node *ui_data = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
+	if (ui_data == NULL || ui_data->row == NULL)
 		return;
-	pidgin_blist_update_buddy(purple_get_blist(), (PurpleBlistNode*)(buddy), TRUE);
+	pidgin_blist_update_buddy(purple_get_blist(), PURPLE_BLIST_NODE(buddy), TRUE);
 }
 
 static void
@@ -1032,7 +1070,7 @@
 	GtkWidget *img = NULL;
 	PidginJoinChatData *data = NULL;
 
-	gtkblist = PIDGIN_BLIST(purple_get_blist());
+	gtkblist = purple_blist_get_ui_data();
 	img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
 					gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
 	data = g_new0(PidginJoinChatData, 1);
@@ -1452,7 +1490,7 @@
 	pidgin_new_item_from_stock(menu, _("Add Buddy _Pounce..."), NULL,
 			G_CALLBACK(gtk_blist_menu_bp_cb), buddy, 0, 0, NULL);
 
-	if (node->parent && node->parent->child->next && 
+	if (node->parent && node->parent->child->next &&
 	      !sub && !contact_expanded) {
 		pidgin_new_item_from_stock(menu, _("View _Log"), NULL,
 				G_CALLBACK(gtk_blist_menu_showlog_cb),
@@ -1474,7 +1512,7 @@
 	if (!contact_expanded && contact != NULL)
 		pidgin_append_blist_node_move_to_menu(menu, (PurpleBlistNode *)contact);
 
-	if (node->parent && node->parent->child->next && 
+	if (node->parent && node->parent->child->next &&
               !sub && !contact_expanded) {
 		pidgin_separator(menu);
 		pidgin_append_blist_node_privacy_menu(menu, node);
@@ -1792,7 +1830,8 @@
 	return handled;
 }
 
-static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data)
+static gboolean
+gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data)
 {
 	GtkTreePath *path;
 	PurpleBlistNode *node;
@@ -2496,7 +2535,7 @@
 				node = g_value_get_pointer(&val);
 
 				if (PURPLE_BLIST_NODE_IS_BUDDY(node) || PURPLE_BLIST_NODE_IS_CONTACT(node)) {
-					PurpleBuddy *b = PURPLE_BLIST_NODE_IS_BUDDY(node) ? (PurpleBuddy*)node : purple_contact_get_priority_buddy((PurpleContact*)node);
+					PurpleBuddy *b = PURPLE_BLIST_NODE_IS_BUDDY(node) ? PURPLE_BUDDY(node) : purple_contact_get_priority_buddy(PURPLE_CONTACT(node));
 					pidgin_dnd_file_manage(sd, b->account, b->name);
 					gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t);
 				} else {
@@ -2720,7 +2759,7 @@
  *
  *
  */
-#define STATUS_SIZE 16 
+#define STATUS_SIZE 16
 #define TOOLTIP_BORDER 12
 #define SMALL_SPACE 6
 #define LARGE_SPACE 12
@@ -2982,18 +3021,18 @@
 }
 
 static void
-pidgin_blist_align_tooltip(struct tooltip_data *td, GtkWidget *widget) 
-{ 
-	GtkTextDirection dir = gtk_widget_get_direction(widget); 
-
-	if (dir == GTK_TEXT_DIR_RTL) 
+pidgin_blist_align_tooltip(struct tooltip_data *td, GtkWidget *widget)
+{
+	GtkTextDirection dir = gtk_widget_get_direction(widget);
+
+	if (dir == GTK_TEXT_DIR_RTL)
 	{
 		char* layout_name = purple_markup_strip_html(pango_layout_get_text(td->name_layout));
 		PangoDirection dir = pango_find_base_dir(layout_name, -1);
 		if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_NEUTRAL)
-			pango_layout_set_alignment(td->name_layout, PANGO_ALIGN_RIGHT); 
+			pango_layout_set_alignment(td->name_layout, PANGO_ALIGN_RIGHT);
 		g_free(layout_name);
-		pango_layout_set_alignment(td->layout, PANGO_ALIGN_RIGHT); 
+		pango_layout_set_alignment(td->layout, PANGO_ALIGN_RIGHT);
 	}
 }
 
@@ -3082,7 +3121,7 @@
 	GValue val;
 	struct _pidgin_blist_node *gtknode;
 
-	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), 
+	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);
@@ -3168,7 +3207,7 @@
 	if (y < rect.y + (rect.height / 3) ||
 	    y > rect.y + (2 * (rect.height /3)))
 		return FALSE;
-	
+
 	rect.height = rect.height / 3;
 	rect.y += rect.height;
 
@@ -3287,10 +3326,10 @@
 	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL },
 	{ N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 1, "<Item>", NULL },
 	{ N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "<Item>", NULL },
+	{ N_("/Tools/Custom Smile_ys"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
 	{ N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
 	{ N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
 	{ N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL },
-	{ N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
 	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL },
 	{ N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_TRANSFER },
 	{ N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL },
@@ -3815,19 +3854,22 @@
 	return ret;
 }
 
-gchar *pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased)
-{
-	const char *name;
-	char *esc, *text = NULL;
+gchar *
+pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased)
+{
+	const char *name, *name_color, *name_font, *status_color, *status_font;
+	char *text = NULL;
 	PurplePlugin *prpl;
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleContact *contact;
 	PurplePresence *presence;
 	struct _pidgin_blist_node *gtkcontactnode = NULL;
-	char *idletime = NULL, *statustext = NULL;
-	time_t t;
+	char *idletime = NULL, *statustext = NULL, *nametext = NULL;
 	PurpleConversation *conv = find_conversation_with_buddy(b);
 	gboolean hidden_conv = FALSE;
+	gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
+	FontColorPair *pair = NULL;
+	PidginBlistTheme *theme;
 
 	if (conv != NULL) {
 		PidginBlistNode *ui = b->node.ui_data;
@@ -3841,178 +3883,197 @@
 	}
 
 	/* XXX Good luck cleaning up this crap */
-	contact = (PurpleContact*)((PurpleBlistNode*)b)->parent;
+	contact = PURPLE_CONTACT(PURPLE_BLIST_NODE(b)->parent);
 	if(contact)
-		gtkcontactnode = ((PurpleBlistNode*)contact)->ui_data;
-
-	if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias)
+		gtkcontactnode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(contact));
+
+	/* Name */
+	if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias)
 		name = contact->alias;
 	else
 		name = purple_buddy_get_alias(b);
-	
-	esc = g_markup_escape_text(name, strlen(name));
+
+	nametext = g_markup_escape_text(name, strlen(name));
 
 	presence = purple_buddy_get_presence(b);
 
-	if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons") && aliased)
-	{
-		if (!selected && purple_presence_is_idle(presence))
-		{
-			text = g_strdup_printf("<span color='%s'>%s</span>",
-					       dim_grey(), esc);
-			g_free(esc);
-			if (hidden_conv) {
-				char *tmp = text;
-				text = g_strdup_printf("<b>%s</b>", text);
+	/* Name is all that is needed */
+	if (aliased && biglist) {
+
+		/* Status Info */
+		prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
+
+		if (prpl != NULL)
+			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if (prpl_info && prpl_info->status_text && b->account->gc) {
+			char *tmp = prpl_info->status_text(b);
+			const char *end;
+
+			if(tmp && !g_utf8_validate(tmp, -1, &end)) {
+				char *new = g_strndup(tmp,
+						g_utf8_pointer_to_offset(tmp, end));
+				g_free(tmp);
+				tmp = new;
+			}
+			/* add ... to messages that are too long, GTK 2.6+ does it automatically */
+#if !GTK_CHECK_VERSION(2,6,0)
+			if(tmp) {
+				char buf[32];
+				char *c = tmp;
+				int length = 0, vis=0;
+				gboolean inside = FALSE;
+				g_strdelimit(tmp, "\n", ' ');
+				purple_str_strip_char(tmp, '\r');
+
+				while(*c && vis < 20) {
+					if(*c == '&')
+						inside = TRUE;
+					else if(*c == ';')
+						inside = FALSE;
+					if(!inside)
+						vis++;
+					c = g_utf8_next_char(c); /* this is fun */
+				}
+
+				length = c - tmp;
+
+				if(vis == 20)
+					g_snprintf(buf, sizeof(buf), "%%.%ds...", length);
+				else
+					g_snprintf(buf, sizeof(buf), "%%s ");
+
+				statustext = g_strdup_printf(buf, tmp);purple_presence_is_idle(presence)
+
 				g_free(tmp);
 			}
-			return text;
-		}
-		else if (hidden_conv)
-		{
-			char *tmp = esc;
-			esc = g_strdup_printf("<b>%s</b>", esc);
-			g_free(tmp);
-		}
-		return esc;
-	}
-
-	prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
-
-	if (prpl != NULL)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info && prpl_info->status_text && b->account->gc) {
-		char *tmp = prpl_info->status_text(b);
-		const char *end;
-
-		if(tmp && !g_utf8_validate(tmp, -1, &end)) {
-			char *new = g_strndup(tmp,
-					g_utf8_pointer_to_offset(tmp, end));
-			g_free(tmp);
-			tmp = new;
-		}
-
-#if !GTK_CHECK_VERSION(2,6,0)
-		if(tmp) {
-			char buf[32];
-			char *c = tmp;
-			int length = 0, vis=0;
-			gboolean inside = FALSE;
-			g_strdelimit(tmp, "\n", ' ');
-			purple_str_strip_char(tmp, '\r');
-
-			while(*c && vis < 20) {
-				if(*c == '&')
-					inside = TRUE;
-				else if(*c == ';')
-					inside = FALSE;
-				if(!inside)
-					vis++;
-				c = g_utf8_next_char(c); /* this is fun */
+#else
+			if(tmp) {
+				g_strdelimit(tmp, "\n", ' ');
+				purple_str_strip_char(tmp, '\r');
 			}
-
-			length = c - tmp;
-
-			if(vis == 20)
-				g_snprintf(buf, sizeof(buf), "%%.%ds...", length);
-			else
-				g_snprintf(buf, sizeof(buf), "%%s ");
-
-			statustext = g_strdup_printf(buf, tmp);
-
-			g_free(tmp);
-		}
-#else
-		if(tmp) {
-			g_strdelimit(tmp, "\n", ' ');
-			purple_str_strip_char(tmp, '\r');
-		}
-		statustext = tmp;
+			statustext = tmp;
 #endif
-	}
-
-	if(!purple_presence_is_online(presence) && !statustext)
-		statustext = g_strdup(_("Offline"));
-	else if (!statustext)
-		text = g_strdup(esc);
-
-	if (purple_presence_is_idle(presence)) {
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) {
+		}
+
+		if(!purple_presence_is_online(presence) && !statustext)
+				statustext = g_strdup(_("Offline"));
+
+		/* Idle Text */
+		if (purple_presence_is_idle(presence) && purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) {
 			time_t idle_secs = purple_presence_get_idle_time(presence);
 
 			if (idle_secs > 0) {
 				int iday, ihrs, imin;
+				time_t t;
 
 				time(&t);
 				iday = (t - idle_secs) / (24 * 60 * 60);
 				ihrs = ((t - idle_secs) / 60 / 60) % 24;
 				imin = ((t - idle_secs) / 60) % 60;
 
-                if (iday)
+				if (iday)
 					idletime = g_strdup_printf(_("Idle %dd %dh %02dm"), iday, ihrs, imin);
 				else if (ihrs)
 					idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin);
 				else
 					idletime = g_strdup_printf(_("Idle %dm"), imin);
-			}
-			else
+
+			} else
 				idletime = g_strdup(_("Idle"));
-
-			if (!selected) {
-				g_free(text);
-				text = g_strdup_printf("<span color='%s'>%s</span>\n"
-					"<span color='%s' size='smaller'>%s%s%s</span>",
-					dim_grey(), esc, dim_grey(),
-					idletime != NULL ? idletime : "",
-					(idletime != NULL && statustext != NULL) ? " - " : "",
-					statustext != NULL ? statustext : "");
-			}
-		}
-		else if (!selected && !statustext) {/* We handle selected text later */
-			g_free(text);
-			text = g_strdup_printf("<span color='%s'>%s</span>", dim_grey(), esc);
-		} else if (!selected && !text) {
-			g_free(text);
-			text = g_strdup_printf("<span color='%s'>%s</span>\n"
-				"<span color='%s' size='smaller'>%s</span>",
-				dim_grey(), esc, dim_grey(),
-				statustext != NULL ? statustext : "");
-		}
-	} else if (!PURPLE_BUDDY_IS_ONLINE(b)) {
-		if (!selected && !statustext) {/* We handle selected text later */
-			g_free(text);
-			text = g_strdup_printf("<span color='%s'>%s</span>", dim_grey(), esc);
-		} else if (!selected && !text)
-			text = g_strdup_printf("<span color='%s'>%s</span>\n"
-				"<span color='%s' size='smaller'>%s</span>",
-				dim_grey(), esc, dim_grey(),
-				statustext != NULL ? statustext : "");
-
-	}
-	/* Not idle and not selected */
-	else if (!selected && !text)
-	{
-		text = g_strdup_printf("%s\n"
-			"<span color='%s' size='smaller'>%s</span>",
-			esc, dim_grey(),
-			statustext != NULL ? statustext :  "");
-	}
-
-	/* It is selected. */
-	if ((selected && !text) || (selected && idletime)) {
-		g_free(text);
-		text = g_strdup_printf("%s\n"
-			"<span size='smaller'>%s%s%s</span>",
-			esc,
-			idletime != NULL ? idletime : "",
-			(idletime != NULL && statustext != NULL) ? " - " : "",
-			statustext != NULL ? statustext :  "");
-	}
-
+		}
+	}
+
+	/* choose the colors of the text */
+	theme = pidgin_blist_get_theme();
+
+	if (purple_presence_is_idle(presence)) {
+		if (theme)
+			pair = pidgin_blist_theme_get_idle_text_info(theme);
+		status_color = name_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey";
+		status_font = name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+	} else if (!purple_presence_is_online(presence)) {
+		if (theme)
+			pair = pidgin_blist_theme_get_offline_text_info(theme);
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL;
+		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+		if (theme)
+			pair = pidgin_blist_theme_get_status_text_info(theme);
+		status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey";
+		status_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+	} else if (purple_presence_is_available(presence)) {
+		if (theme)
+			pair = pidgin_blist_theme_get_online_text_info(theme);
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL;
+		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+		if (theme)
+			pair = pidgin_blist_theme_get_status_text_info(theme);
+		status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey";
+		status_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+	} else {
+		if (theme)
+			pair = pidgin_blist_theme_get_away_text_info(theme);
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL;
+		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+		if (theme)
+			pair = pidgin_blist_theme_get_status_text_info(theme);
+		status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey";
+		status_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+	}
+
+	if (aliased && selected) {
+		if (theme) {
+			name_color = "black";
+			status_color = "black";
+		} else {
+			name_color = NULL;
+			status_color = NULL;
+		}
+	}
+
+	/* Put it all together */
+	if (aliased && biglist && (statustext || idletime)) {
+		/* using <span size='smaller'> breaks the status, so it must be seperated into <small><span>*/
+		if (name_color) {
+			text = g_strdup_printf("<span font_desc='%s' foreground='%s'>%s</span>\n"
+				 		"<small><span font_desc='%s' foreground='%s'>%s%s%s</span></small>",
+						name_font, name_color, nametext, status_font, status_color,
+						idletime != NULL ? idletime : "",
+				    		(idletime != NULL && statustext != NULL) ? " - " : "",
+				    		statustext != NULL ? statustext : "");
+		} else if (status_color) {
+			text = g_strdup_printf("<span font_desc='%s'>%s</span>\n"
+				 		"<small><span font_desc='%s' foreground='%s'>%s%s%s</span></small>",
+						name_font, nametext, status_font, status_color,
+						idletime != NULL ? idletime : "",
+				    		(idletime != NULL && statustext != NULL) ? " - " : "",
+				    		statustext != NULL ? statustext : "");
+		} else {
+			text = g_strdup_printf("<span font_desc='%s'>%s</span>\n"
+				 		"<small><span font_desc='%s'>%s%s%s</span></small>",
+						name_font, nametext, status_font,
+						idletime != NULL ? idletime : "",
+				    		(idletime != NULL && statustext != NULL) ? " - " : "",
+				    		statustext != NULL ? statustext : "");
+		}
+	} else {
+		if (name_color) {
+			text = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>", 
+				name_font, name_color, nametext);
+		} else {
+			text = g_strdup_printf("<span font_desc='%s'>%s</span>", name_font,
+				nametext);
+		}
+	}
+	g_free(nametext);
+	g_free(statustext);
 	g_free(idletime);
-	g_free(statustext);
-	g_free(esc);
 
 	if (hidden_conv) {
 		char *tmp = text;
@@ -4062,7 +4123,7 @@
 	PurpleBlistNode *gnode, *cnode;
 
 	if (gtk_blist_visibility == GDK_VISIBILITY_FULLY_OBSCURED
-			|| !GTK_WIDGET_VISIBLE(gtkblist->window)) 
+			|| !GTK_WIDGET_VISIBLE(gtkblist->window))
 		return TRUE;
 
 	for(gnode = list->root; gnode; gnode = gnode->next) {
@@ -4331,6 +4392,10 @@
 			!(flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
 		return;
 	ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT
+			&& (flag & PURPLE_MESSAGE_NICK))
+		ui->conv.flags |= PIDGIN_BLIST_CHAT_HAS_PENDING_MESSAGE_WITH_NICK;
+
 	ui->conv.last_message = time(NULL);    /* XXX: for lack of better data */
 	pidgin_blist_update(purple_get_blist(), node);
 }
@@ -4341,7 +4406,8 @@
 	PidginBlistNode *ui = node->ui_data;
 	if (ui->conv.conv != gtkconv->active_conv)
 		return;
-	ui->conv.flags &= ~PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE;
+	ui->conv.flags &= ~(PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE |
+	                    PIDGIN_BLIST_CHAT_HAS_PENDING_MESSAGE_WITH_NICK);
 	pidgin_blist_update(purple_get_blist(), node);
 }
 
@@ -4450,7 +4516,7 @@
 #if GTK_CHECK_VERSION(2,2,1)
 	pidgin_blist_sort_method_reg("alphabetical", _("Alphabetically"), sort_method_alphabetical);
 	pidgin_blist_sort_method_reg("status", _("By status"), sort_method_status);
-	pidgin_blist_sort_method_reg("log_size", _("By log size"), sort_method_log);
+	pidgin_blist_sort_method_reg("log_size", _("By recent log activity"), sort_method_log_activity);
 #endif
 	pidgin_blist_sort_method_set(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type"));
 }
@@ -5261,11 +5327,144 @@
 }
 #endif
 
+/* builds the blist layout according to to the current theme */
+static void
+pidgin_blist_build_layout(PurpleBuddyList *list)
+{
+	GtkTreeViewColumn *column;
+	PidginBlistLayout *layout;
+	PidginBlistTheme *theme;
+	GtkCellRenderer *rend;
+	gint i, status_icon = 0, text = 1, emblem = 2, protocol_icon = 3, buddy_icon = 4;
+
+
+	column = gtkblist->text_column;
+
+	if ((theme = pidgin_blist_get_theme()) != NULL && (layout = pidgin_blist_theme_get_layout(theme)) != NULL) {
+		status_icon = layout->status_icon ;
+		text = layout->text;
+		emblem = layout->emblem;
+		protocol_icon = layout->protocol_icon;
+		buddy_icon = layout->buddy_icon;
+	}
+
+	gtk_tree_view_column_clear(column);
+
+	/* group */
+	rend = pidgin_cell_renderer_expander_new();
+	gtk_tree_view_column_pack_start(column, rend, FALSE);
+	gtk_tree_view_column_set_attributes(column, rend,
+					    "visible", GROUP_EXPANDER_VISIBLE_COLUMN,
+					    "expander-visible", GROUP_EXPANDER_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+					    "sensitive", GROUP_EXPANDER_COLUMN,
+					    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+					    NULL);
+
+	/* contact */
+	rend = pidgin_cell_renderer_expander_new();
+	gtk_tree_view_column_pack_start(column, rend, FALSE);
+	gtk_tree_view_column_set_attributes(column, rend,
+					    "visible", CONTACT_EXPANDER_VISIBLE_COLUMN,
+					    "expander-visible", CONTACT_EXPANDER_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+					    "sensitive", CONTACT_EXPANDER_COLUMN,
+					    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+					    NULL);
+
+	for (i = 0; i < 5; i++) {
+
+		if (status_icon == i) {
+			/* status icons */
+			rend = gtk_cell_renderer_pixbuf_new();
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend,
+							    "pixbuf", STATUS_ICON_COLUMN,
+							    "visible", STATUS_ICON_VISIBLE_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							    NULL);
+			g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
+
+		} else if (text == i) {
+			/* name */
+			gtkblist->text_rend = rend = gtk_cell_renderer_text_new();
+			gtk_tree_view_column_pack_start(column, rend, TRUE);
+			gtk_tree_view_column_set_attributes(column, rend,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							    "markup", NAME_COLUMN,
+							    NULL);
+#if GTK_CHECK_VERSION(2,6,0)
+			g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL);
+			g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list);
+#endif
+			g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list);
+			g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
+#if GTK_CHECK_VERSION(2,6,0)
+			g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+#endif
+
+			/* idle */
+			rend = gtk_cell_renderer_text_new();
+			g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend,
+							    "markup", IDLE_COLUMN,
+							    "visible", IDLE_VISIBLE_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							    NULL);
+		} else if (emblem == i) {
+			/* emblem */
+			rend = gtk_cell_renderer_pixbuf_new();
+			g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL);
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+									  "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+									  "visible", EMBLEM_VISIBLE_COLUMN, NULL);
+
+		} else if (protocol_icon == i) {
+			/* protocol icon */
+			rend = gtk_cell_renderer_pixbuf_new();
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend,
+							   "pixbuf", PROTOCOL_ICON_COLUMN,
+							   "visible", PROTOCOL_ICON_VISIBLE_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							   "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							  NULL);
+			g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
+
+		} else if (buddy_icon == i) {
+			/* buddy icon */
+			rend = gtk_cell_renderer_pixbuf_new();
+			g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							    "visible", BUDDY_ICON_VISIBLE_COLUMN,
+							    NULL);
+		}
+
+	}/* end for loop */
+
+}
+
 static void pidgin_blist_show(PurpleBuddyList *list)
 {
 	PidginBuddyListPrivate *priv;
 	void *handle;
-	GtkCellRenderer *rend;
 	GtkTreeViewColumn *column;
 	GtkWidget *menu;
 	GtkWidget *ebox;
@@ -5291,6 +5490,8 @@
 	gtkblist = PIDGIN_BLIST(list);
 	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
 
+	priv->current_theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), "blist"));
+
 	gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
 	gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000);
 
@@ -5323,8 +5524,8 @@
 	gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu),
 								  blist_menu, NULL);
 	pidgin_load_accels();
-	g_signal_connect(G_OBJECT(accel_group), "accel-changed",
-														G_CALLBACK(pidgin_save_accels_cb), NULL);
+	g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(pidgin_save_accels_cb), NULL);
+
 	menu = gtk_item_factory_get_widget(gtkblist->ift, "<PurpleMain>");
 	gtkblist->menutray = pidgin_menu_tray_new();
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtkblist->menutray);
@@ -5468,105 +5669,16 @@
 
 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
 
+	/* expander columns */
 	column = gtk_tree_view_column_new();
 	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
 	gtk_tree_view_column_set_visible(column, FALSE);
 	gtk_tree_view_set_expander_column(GTK_TREE_VIEW(gtkblist->treeview), column);
 
-	gtkblist->text_column = column = gtk_tree_view_column_new ();
-	rend = pidgin_cell_renderer_expander_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "visible", GROUP_EXPANDER_VISIBLE_COLUMN,
-					    "expander-visible", GROUP_EXPANDER_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "sensitive", GROUP_EXPANDER_COLUMN,
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    NULL);
-
-	rend = pidgin_cell_renderer_expander_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "expander-visible", CONTACT_EXPANDER_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "sensitive", CONTACT_EXPANDER_COLUMN,
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    "visible", CONTACT_EXPANDER_VISIBLE_COLUMN,
-					    NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "pixbuf", STATUS_ICON_COLUMN,
-					    "visible", STATUS_ICON_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    NULL);
-	g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
-
-	gtkblist->text_rend = rend = gtk_cell_renderer_text_new();
-	gtk_tree_view_column_pack_start (column, rend, TRUE);
-	gtk_tree_view_column_set_attributes(column, rend,
-#if GTK_CHECK_VERSION(2,6,0)
-					    	    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-										"markup", NAME_COLUMN,
-										NULL);
-#if GTK_CHECK_VERSION(2,6,0)
-	g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL);
-	g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list);
-#endif
-	g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list);
-	g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
-#if GTK_CHECK_VERSION(2,6,0)
-	g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
-	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
-
-	rend = gtk_cell_renderer_text_new();
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "markup", IDLE_COLUMN,
-					    "visible", IDLE_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-							  "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-							  "visible", EMBLEM_VISIBLE_COLUMN, NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					   "pixbuf", PROTOCOL_ICON_COLUMN,
-					   "visible", PROTOCOL_ICON_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					   "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					  NULL);
-	g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    "visible", BUDDY_ICON_VISIBLE_COLUMN,
-					    NULL);
-
+	/* everything else column */
+	gtkblist->text_column = gtk_tree_view_column_new ();
+	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->text_column);
+	pidgin_blist_build_layout(list);
 
 	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL);
 	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL);
@@ -5959,7 +6071,7 @@
 
 	if (editing_blist)
 		return;
-	
+
 	if (PURPLE_BLIST_NODE_IS_GROUP(node))
 		gnode = node;
 	else if (PURPLE_BLIST_NODE_IS_BUDDY(node))
@@ -5993,13 +6105,19 @@
 		GtkTreeIter iter;
 		GtkTreePath *path;
 		gboolean expanded;
-		GdkColor bgcolor;
+		GdkColor *bgcolor = NULL;
 		GdkPixbuf *avatar = NULL;
+		PidginBlistTheme *theme = NULL;
 
 		if(!insert_node(list, gnode, &iter))
 			return;
 
-		bgcolor = gtkblist->treeview->style->bg[GTK_STATE_ACTIVE];
+		if ((theme = pidgin_blist_get_theme()) == NULL)
+			bgcolor = NULL;
+		else if (purple_blist_node_get_bool(gnode, "collapsed") || count <= 0)
+			bgcolor = pidgin_blist_theme_get_collapsed_background_color(theme);
+		else
+			bgcolor = pidgin_blist_theme_get_expanded_background_color(theme);
 
 		path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
 		expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtkblist->treeview), path);
@@ -6017,7 +6135,7 @@
 				   STATUS_ICON_COLUMN, NULL,
 				   NAME_COLUMN, title,
 				   NODE_COLUMN, gnode,
-	/*			   BGCOLOR_COLUMN, &bgcolor,     */
+				   BGCOLOR_COLUMN, bgcolor,
 				   GROUP_EXPANDER_COLUMN, TRUE,
 				   GROUP_EXPANDER_VISIBLE_COLUMN, TRUE,
 				   CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE,
@@ -6040,6 +6158,9 @@
 	char *mark, *esc;
 	PurpleBlistNode *selected_node = NULL;
 	GtkTreeIter iter;
+	FontColorPair *pair;
+	gchar *text_color, *text_font;
+	PidginBlistTheme *theme;
 
 	group = (PurpleGroup*)gnode;
 
@@ -6055,8 +6176,26 @@
 		           purple_blist_get_group_size(group, FALSE));
 	}
 
+	theme = pidgin_blist_get_theme();
+	if (theme == NULL)
+		pair = NULL;
+	else if (expanded)
+		pair = pidgin_blist_theme_get_expanded_text_info(theme);
+	else
+		pair = pidgin_blist_theme_get_collapsed_text_info(theme);
+
+
+	text_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color;
+	text_font = (pair == NULL || pair->font == NULL) ? "" : pair->font;
+
 	esc = g_markup_escape_text(group->name, -1);
-	mark = g_strdup_printf("<span weight='bold'>%s</span>%s", esc ? esc : "", group_count);
+	if (text_color) {
+		mark = g_strdup_printf("<span foreground='%s' font_desc='%s'><b>%s</b>%s</span>",
+							text_color, text_font, esc ? esc : "", group_count);
+	} else {
+		mark = g_strdup_printf("<span font_desc='%s'><b>%s</b>%s</span>",
+							text_font, esc ? esc : "", group_count);
+	}
 
 	g_free(esc);
 	return mark;
@@ -6064,14 +6203,15 @@
 
 static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node)
 {
-	PurplePresence *presence;
+	PurplePresence *presence = purple_buddy_get_presence(buddy);
 	GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
+	GdkColor *color = NULL;
 	char *mark;
 	char *idle = NULL;
 	gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded;
 	gboolean selected = (gtkblist->selected_node == node);
 	gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
-	presence = purple_buddy_get_presence(buddy);
+	PidginBlistTheme *theme;
 
 	if (editing_blist)
 		return;
@@ -6095,35 +6235,46 @@
 	emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy);
 	mark = pidgin_blist_get_name_markup(buddy, selected, TRUE);
 
+	theme = pidgin_blist_get_theme();
+
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time") &&
-		purple_presence_is_idle(presence) &&
-		!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"))
+		purple_presence_is_idle(presence) && !biglist)
 	{
 		time_t idle_secs = purple_presence_get_idle_time(presence);
 
 		if (idle_secs > 0)
 		{
+			FontColorPair *pair = NULL;
+			const gchar *textcolor;
 			time_t t;
 			int ihrs, imin;
 			time(&t);
+
 			ihrs = (t - idle_secs) / 3600;
 			imin = ((t - idle_secs) / 60) % 60;
-			idle = g_strdup_printf("%d:%02d", ihrs, imin);
-		}
-	}
-
-	if (purple_presence_is_idle(presence))
-	{
-		if (idle && !selected) {
-			char *i2 = g_strdup_printf("<span color='%s'>%s</span>",
-						   dim_grey(), idle);
-			g_free(idle);
-			idle = i2;
+
+			if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL && pair->color != NULL)
+				textcolor = pair->color;
+			else
+				textcolor = NULL;
+
+			if (textcolor) {
+				idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>",
+					textcolor, (pair == NULL || pair->font == NULL) ? "" : pair->font, 
+					ihrs, imin);
+			} else {
+				idle = g_strdup_printf("<span font_desc='%s'>%d:%02d</span>",
+					(pair == NULL || pair->font == NULL) ? "" : pair->font, 
+					ihrs, imin);
+			}
 		}
 	}
 
 	prpl_icon = pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL);
 
+	if (theme != NULL)
+		color = pidgin_blist_theme_get_contact_color(theme);
+
 	gtk_tree_store_set(gtkblist->treemodel, iter,
 			   STATUS_ICON_COLUMN, status,
 			   STATUS_ICON_VISIBLE_COLUMN, TRUE,
@@ -6136,7 +6287,7 @@
 			   EMBLEM_VISIBLE_COLUMN, (emblem != NULL),
 			   PROTOCOL_ICON_COLUMN, prpl_icon,
 			   PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
-			   BGCOLOR_COLUMN, NULL,
+			   BGCOLOR_COLUMN, color,
 			   CONTACT_EXPANDER_COLUMN, NULL,
 			   CONTACT_EXPANDER_VISIBLE_COLUMN, expanded,
 			   GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
@@ -6194,19 +6345,46 @@
 
 		if(gtknode->contact_expanded) {
 			GdkPixbuf *status;
-			char *mark;
+			gchar *mark, *tmp;
+			const gchar *fg_color, *font;
+			GdkColor *color = NULL;
+			PidginBlistTheme *theme = pidgin_blist_get_theme();
+			FontColorPair *pair;
+			gboolean selected = (gtkblist->selected_node == cnode);
+
+			mark = g_markup_escape_text(purple_contact_get_alias(contact), -1);
+
+			theme = pidgin_blist_get_theme();
+			if (theme == NULL)
+				pair = NULL;
+			else {
+				pair = pidgin_blist_theme_get_contact_text_info(theme);
+				color = pidgin_blist_theme_get_contact_color(theme);
+			}
+
+			font = (pair == NULL || pair->font == NULL) ? "" : pair->font;
+			fg_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color;
+
+			if (fg_color) {
+				tmp = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>",
+						font, fg_color, mark);
+			} else {
+				tmp = g_strdup_printf("<span font_desc='%s'>%s</span>", font, 
+					mark);
+			}
+			g_free(mark);
+			mark = tmp;
 
 			status = pidgin_blist_get_status_icon(cnode,
 					 biglist? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL);
 
-			mark = g_markup_escape_text(purple_contact_get_alias(contact), -1);
 			gtk_tree_store_set(gtkblist->treemodel, &iter,
 					   STATUS_ICON_COLUMN, status,
 					   STATUS_ICON_VISIBLE_COLUMN, TRUE,
 					   NAME_COLUMN, mark,
 					   IDLE_COLUMN, NULL,
 					   IDLE_VISIBLE_COLUMN, FALSE,
-					   BGCOLOR_COLUMN, NULL,
+					   BGCOLOR_COLUMN, color,
 					   BUDDY_ICON_COLUMN, NULL,
 					   CONTACT_EXPANDER_COLUMN, TRUE,
 					   CONTACT_EXPANDER_VISIBLE_COLUMN, TRUE,
@@ -6274,20 +6452,28 @@
 	if(purple_account_is_connected(chat->account)) {
 		GtkTreeIter iter;
 		GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
-		char *mark;
+		const gchar *color, *font;
+		gchar *mark, *tmp;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
 		gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
 		PidginBlistNode *ui;
 		PurpleConversation *conv;
 		gboolean hidden;
+		GdkColor *bgcolor = NULL;
+		FontColorPair *pair;
+		PidginBlistTheme *theme;
+		gboolean selected = (gtkblist->selected_node == node);
+		gboolean nick_said = FALSE;
 
 		if (!insert_node(list, node, &iter))
 			return;
 
 		ui = node->ui_data;
 		conv = ui->conv.conv;
-		hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE) &&
-				pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv)));
+		if (conv && pidgin_conv_is_hidden(PIDGIN_CONVERSATION(conv))) {
+			hidden = (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE);
+			nick_said = (ui->conv.flags & PIDGIN_BLIST_CHAT_HAS_PENDING_MESSAGE_WITH_NICK);
+		}
 
 		status = pidgin_blist_get_status_icon(node,
 				 biglist ? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL);
@@ -6300,14 +6486,40 @@
 			avatar = NULL;
 
 		mark = g_markup_escape_text(purple_chat_get_name(chat), -1);
-		if (hidden) {
-			char *bold = g_strdup_printf("<b>%s</b>", mark);
-			g_free(mark);
-			mark = bold;
-		}
+
+		theme = pidgin_blist_get_theme();
+
+		if (theme == NULL)
+			pair = NULL;
+		else if (nick_said)
+			pair = pidgin_blist_theme_get_unread_message_nick_said_text_info(theme);
+		else if (hidden)
+			pair = pidgin_blist_theme_get_unread_message_text_info(theme);
+		else pair = pidgin_blist_theme_get_online_text_info(theme);
+
+
+		font = (pair == NULL || pair->font == NULL) ? "" : pair->font;
+		if (selected || pair == NULL || pair->color == NULL)
+			/* nick_said color is the same as gtkconv:tab-label-attention */
+			color = (nick_said ? "#006aff" : NULL);
+		else
+			color = pair->color;
+
+		if (color) {
+			tmp = g_strdup_printf("<span font_desc='%s' color='%s' weight='%s'>%s</span>",
+				  	  font, color, hidden ? "bold" : "normal", mark);
+		} else {
+			tmp = g_strdup_printf("<span font_desc='%s' weight='%s'>%s</span>",
+				  	  font, hidden ? "bold" : "normal", mark);
+		}
+		g_free(mark);
+		mark = tmp;
 
 		prpl_icon = pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL);
 
+		if (theme != NULL)
+			bgcolor = pidgin_blist_theme_get_contact_color(theme);
+
 		gtk_tree_store_set(gtkblist->treemodel, &iter,
 				STATUS_ICON_COLUMN, status,
 				STATUS_ICON_VISIBLE_COLUMN, TRUE,
@@ -6318,6 +6530,7 @@
 				PROTOCOL_ICON_COLUMN, prpl_icon,
 				PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
 				NAME_COLUMN, mark,
+				BGCOLOR_COLUMN, bgcolor,
 				GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
 				-1);
 
@@ -6330,6 +6543,7 @@
 			g_object_unref(avatar);
 		if(prpl_icon)
 			g_object_unref(prpl_icon);
+
 	} else {
 		pidgin_blist_hide_node(list, node, TRUE);
 	}
@@ -6987,7 +7201,7 @@
 
 	data->group_combo = pidgin_text_combo_box_entry_new(group ? group->name : NULL, groups_tree());
 	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(_("_Remain in chat after window is closed."));
 	gtk_box_pack_start(GTK_BOX(vbox), data->autojoin, FALSE, FALSE, 0);
@@ -7191,6 +7405,34 @@
 			(GSourceFunc)buddy_signonoff_timeout_cb, buddy);
 }
 
+void
+pidgin_blist_set_theme(PidginBlistTheme *theme)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	PurpleBuddyList *list = purple_get_blist();
+
+	if (theme != NULL)
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme",
+				purple_theme_get_name(PURPLE_THEME(theme)));
+	else
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme", "");
+
+	priv->current_theme = theme;
+
+	pidgin_blist_build_layout(list);
+
+	pidgin_blist_refresh(list);
+}
+
+
+PidginBlistTheme *
+pidgin_blist_get_theme()
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+
+	return priv->current_theme;
+}
+
 void pidgin_blist_init(void)
 {
 	void *gtk_blist_handle = pidgin_blist_get_handle();
@@ -7219,6 +7461,9 @@
 	/* This pref is used in pidgintooltip.c. */
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay", 500);
 #endif
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/theme", "");
+
+	purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_BLIST_THEME_LOADER, "type", "blist", NULL));
 
 	/* Register our signals */
 	purple_signal_register(gtk_blist_handle, "gtkblist-hiding",
@@ -7493,11 +7738,11 @@
 	}
 }
 
-static void sort_method_log(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter)
+static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter)
 {
 	GtkTreeIter more_z;
 
-	int log_size = 0, this_log_size = 0;
+	int activity_score = 0, this_log_activity_score = 0;
 	const char *buddy_name, *this_buddy_name;
 
 	if(cur && (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter) == 1)) {
@@ -7507,8 +7752,11 @@
 
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		PurpleBlistNode *n;
-		for (n = node->child; n; n = n->next)
-			log_size += purple_log_get_total_size(PURPLE_LOG_IM, ((PurpleBuddy*)(n))->name, ((PurpleBuddy*)(n))->account);
+		PurpleBuddy *buddy;
+		for (n = node->child; n; n = n->next) {
+			buddy = (PurpleBuddy*)n;
+			activity_score += purple_log_get_activity_score(PURPLE_LOG_IM, buddy->name, buddy->account);
+		}
 		buddy_name = purple_contact_get_alias((PurpleContact*)node);
 	} else if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
 		/* we don't have a reliable way of getting the log filename
@@ -7535,16 +7783,19 @@
 		GValue val;
 		PurpleBlistNode *n;
 		PurpleBlistNode *n2;
+		PurpleBuddy *buddy;
 		int cmp;
 
 		val.g_type = 0;
 		gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &more_z, NODE_COLUMN, &val);
 		n = g_value_get_pointer(&val);
-		this_log_size = 0;
+		this_log_activity_score = 0;
 
 		if(PURPLE_BLIST_NODE_IS_CONTACT(n)) {
-			for (n2 = n->child; n2; n2 = n2->next)
-				this_log_size += purple_log_get_total_size(PURPLE_LOG_IM, ((PurpleBuddy*)(n2))->name, ((PurpleBuddy*)(n2))->account);
+			for (n2 = n->child; n2; n2 = n2->next) {
+                        	buddy = (PurpleBuddy*)n2;
+				this_log_activity_score += purple_log_get_activity_score(PURPLE_LOG_IM, buddy->name, buddy->account);
+			}
 			this_buddy_name = purple_contact_get_alias((PurpleContact*)n);
 		} else {
 			this_buddy_name = NULL;
@@ -7552,8 +7803,8 @@
 
 		cmp = purple_utf8_strcasecmp(buddy_name, this_buddy_name);
 
-		if (!PURPLE_BLIST_NODE_IS_CONTACT(n) || log_size > this_log_size ||
-				((log_size == this_log_size) &&
+		if (!PURPLE_BLIST_NODE_IS_CONTACT(n) || activity_score > this_log_activity_score ||
+				((activity_score == this_log_activity_score) &&
 				 (cmp < 0 || (cmp == 0 && node < n)))) {
 			if (cur != NULL) {
 				gtk_tree_store_move_before(gtkblist->treemodel, cur, &more_z);
--- a/pidgin/gtkblist.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkblist.h	Sat Mar 07 01:59:40 2009 +0000
@@ -60,6 +60,7 @@
 
 #include "pidgin.h"
 #include "blist.h"
+#include "gtkblist-theme.h"
 
 /**************************************************************************
  * @name Structures
@@ -251,6 +252,23 @@
  */
 void pidgin_blist_add_alert(GtkWidget *widget);
 
+/**
+ * Sets the current theme for Pidgin to use
+ *
+ * @param theme	the new theme to use
+ *
+ * @since 2.6.0
+ */
+void pidgin_blist_set_theme(PidginBlistTheme *theme);
+
+/**
+ * Gets Pidgin's current buddy list theme
+ *
+ * @returns	the current theme
+ *
+ * @since 2.6.0
+ */
+PidginBlistTheme *pidgin_blist_get_theme(void);
 
 /**************************************************************************
  * @name GTK+ Buddy List sorting functions
@@ -382,7 +400,7 @@
  *
  * @param buddy The buddy to return markup from
  * @param selected  Whether this buddy is selected. If TRUE, the markup will not change the color.
- * @param aliased  TRUE to return the appropriate alias of this buddy, FALSE to return its screenname and status information
+ * @param aliased  TRUE to return the appropriate alias of this buddy, FALSE to return its username and status information
  * @return The markup for this buddy
  *
  * @since 2.1.0
--- a/pidgin/gtkcellrendererexpander.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkcellrendererexpander.c	Sat Mar 07 01:59:40 2009 +0000
@@ -18,14 +18,14 @@
  * 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
  *
  */
 
-/* This is taken largely from GtkCellRenderer[Text|Pixbuf|Toggle] by 
+/* This is taken largely from GtkCellRenderer[Text|Pixbuf|Toggle] by
  * Jonathon Blandford <jrb@redhat.com> for RedHat, Inc.
  */
 
@@ -74,14 +74,14 @@
 	PROP_0,
 	PROP_IS_EXPANDER
 };
-     
+
 static gpointer parent_class;
 /* static guint expander_cell_renderer_signals [LAST_SIGNAL]; */
 
 GType  pidgin_cell_renderer_expander_get_type (void)
 {
 	static GType cell_expander_type = 0;
-	
+
 	if (!cell_expander_type)
 		{
 			static const GTypeInfo cell_expander_info =
@@ -97,13 +97,13 @@
 					(GInstanceInitFunc) pidgin_cell_renderer_expander_init,
 					NULL		/* value_table */
 				};
-			
+
 			cell_expander_type =
 				g_type_register_static (GTK_TYPE_CELL_RENDERER,
 										"PidginCellRendererExpander",
 										&cell_expander_info, 0);
 		}
-	
+
 	return cell_expander_type;
 }
 
@@ -118,17 +118,17 @@
 {
 	GObjectClass *object_class = G_OBJECT_CLASS(class);
 	GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);
-	
+
 	parent_class = g_type_class_peek_parent (class);
 	object_class->finalize = pidgin_cell_renderer_expander_finalize;
 
 	object_class->get_property = pidgin_cell_renderer_expander_get_property;
 	object_class->set_property = pidgin_cell_renderer_expander_set_property;
-	
+
 	cell_class->get_size = pidgin_cell_renderer_expander_get_size;
 	cell_class->render   = pidgin_cell_renderer_expander_render;
 	cell_class->activate = pidgin_cell_renderer_expander_activate;
-	
+
 	g_object_class_install_property (object_class,
 					 PROP_IS_EXPANDER,
 					 g_param_spec_boolean ("expander-visible",
@@ -162,7 +162,7 @@
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, psec);
 			break;
-			
+
 		}
 }
 
@@ -199,19 +199,19 @@
 {
 	gint calc_width;
 	gint calc_height;
-	gint expander_size; 
-	
+	gint expander_size;
+
 	gtk_widget_style_get(widget, "expander-size", &expander_size, NULL);
-	
+
 	calc_width = (gint) cell->xpad * 2 + expander_size;
 	calc_height = (gint) cell->ypad * 2 + expander_size;
-	
+
 	if (width)
 		*width = calc_width;
-	
+
 	if (height)
 		*height = calc_height;
-	
+
 	if (cell_area)
 		{
 			if (x_offset)
@@ -228,7 +228,7 @@
 }
 
 
-static void pidgin_cell_renderer_expander_render (GtkCellRenderer *cell,
+static void pidgin_cell_renderer_expander_render(GtkCellRenderer *cell,
 					       GdkWindow       *window,
 					       GtkWidget       *widget,
 					       GdkRectangle    *background_area,
@@ -237,7 +237,7 @@
 					       guint            flags)
 {
 	PidginCellRendererExpander *cellexpander = (PidginCellRendererExpander *) cell;
-	
+	gboolean set;
 	gint width, height;
 	GtkStateType state;
 
@@ -246,7 +246,7 @@
 
 	width = cell_area->width;
 	height = cell_area->height;
-	
+
 #if GTK_CHECK_VERSION(2,6,0)
 	if (!cell->sensitive)
 		state = GTK_STATE_INSENSITIVE;
@@ -270,8 +270,11 @@
 			    cell_area->x + cell->xpad + (width / 2),
 			    cell_area->y + cell->ypad + (height / 2),
 			    cell->is_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED);
-	if (cell->is_expanded)
-		gtk_paint_hline (widget->style, window, state, NULL, widget, NULL, 0, 
+
+	/* only draw the line if the color isn't set - this prevents a bug where the hline appears only under the expander */
+	g_object_get(cellexpander, "cell-background-set", &set, NULL);
+	if (cell->is_expanded && !set)
+		gtk_paint_hline (widget->style, window, state, NULL, widget, NULL, 0,
 				 widget->allocation.width, cell_area->y + cell_area->height);
 }
 
--- a/pidgin/gtkcellrendererexpander.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkcellrendererexpander.h	Sat Mar 07 01:59:40 2009 +0000
@@ -12,7 +12,7 @@
  * 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
--- a/pidgin/gtkcellrendererprogress.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkcellrendererprogress.c	Sat Mar 07 01:59:40 2009 +0000
@@ -18,14 +18,14 @@
  * 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
  *
  */
 
-/* This is taken largely from GtkCellRenderer[Text|Pixbuf|Toggle] by 
+/* This is taken largely from GtkCellRenderer[Text|Pixbuf|Toggle] by
  * Jonathon Blandford <jrb@redhat.com> for RedHat, Inc.
  */
 
@@ -76,14 +76,14 @@
 	PROP_TEXT,
 	PROP_SHOW_TEXT
 };
-     
+
 static gpointer parent_class;
 /* static guint progress_cell_renderer_signals [LAST_SIGNAL]; */
 
 GType  pidgin_cell_renderer_progress_get_type (void)
 {
 	static GType cell_progress_type = 0;
-	
+
 	if (!cell_progress_type)
 		{
 			static const GTypeInfo cell_progress_info =
@@ -99,13 +99,13 @@
 					(GInstanceInitFunc) pidgin_cell_renderer_progress_init,
 					NULL		/* value_table */
 				};
-			
+
 			cell_progress_type =
 				g_type_register_static (GTK_TYPE_CELL_RENDERER,
 										"PidginCellRendererProgress",
 										&cell_progress_info, 0);
 		}
-	
+
 	return cell_progress_type;
 }
 
@@ -120,16 +120,16 @@
 {
 	GObjectClass *object_class = G_OBJECT_CLASS(class);
 	GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);
-	
+
 	parent_class = g_type_class_peek_parent (class);
 	object_class->finalize = pidgin_cell_renderer_progress_finalize;
 
 	object_class->get_property = pidgin_cell_renderer_progress_get_property;
 	object_class->set_property = pidgin_cell_renderer_progress_set_property;
-	
+
 	cell_class->get_size = pidgin_cell_renderer_progress_get_size;
 	cell_class->render   = pidgin_cell_renderer_progress_render;
-	
+
 	g_object_class_install_property (object_class,
 					 PROP_PERCENTAGE,
 					 g_param_spec_double ("percentage",
@@ -228,16 +228,16 @@
 {
 	gint calc_width;
 	gint calc_height;
-	
+
 	calc_width = (gint) cell->xpad * 2 + 50;
 	calc_height = (gint) cell->ypad * 2 + 12;
-	
+
 	if (width)
 		*width = calc_width;
-	
+
 	if (height)
 		*height = calc_height;
-	
+
 	if (cell_area)
 		{
 			if (x_offset)
@@ -263,13 +263,13 @@
 					       guint            flags)
 {
 	PidginCellRendererProgress *cellprogress = (PidginCellRendererProgress *) cell;
-	
+
 	gint width, height;
 	GtkStateType state;
 
 	width = cell_area->width;
 	height = cell_area->height;
-	
+
 	if (GTK_WIDGET_HAS_FOCUS (widget))
 		state = GTK_STATE_ACTIVE;
 	else
@@ -280,7 +280,7 @@
 
 	gtk_paint_box (widget->style,
 		       window,
-		       GTK_STATE_NORMAL, GTK_SHADOW_IN, 
+		       GTK_STATE_NORMAL, GTK_SHADOW_IN,
 		       NULL, widget, "trough",
 		       cell_area->x + cell->xpad,
 		       cell_area->y + cell->ypad,
--- a/pidgin/gtkcellrendererprogress.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkcellrendererprogress.h	Sat Mar 07 01:59:40 2009 +0000
@@ -12,7 +12,7 @@
  * 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
--- a/pidgin/gtkcellview.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkcellview.c	Sat Mar 07 01:59:40 2009 +0000
@@ -463,14 +463,14 @@
   area = widget->allocation;
 
   /* we draw on our very own window, initialize x and y to zero */
-  area.x = widget->allocation.x + (rtl ? widget->allocation.width : 0); 
+  area.x = widget->allocation.x + (rtl ? widget->allocation.width : 0);
   area.y = widget->allocation.y;
 
   if (GTK_WIDGET_STATE (widget) == GTK_STATE_PRELIGHT)
     state = GTK_CELL_RENDERER_PRELIT;
   else
     state = 0;
-      
+
   /* PACK_START */
   for (i = cellview->priv->cell_list; i; i = i->next)
     {
@@ -483,7 +483,7 @@
         continue;
 
       area.width = info->real_width;
-      if (rtl)                                             
+      if (rtl)
          area.x -= area.width;
 
       gtk_cell_renderer_render (info->cell,
@@ -492,11 +492,11 @@
                                 /* FIXME! */
                                 &area, &area, &event->area, state);
 
-      if (!rtl)                                           
+      if (!rtl)
          area.x += info->real_width;
     }
 
-   area.x = rtl ? widget->allocation.x : (widget->allocation.x + widget->allocation.width);  
+   area.x = rtl ? widget->allocation.x : (widget->allocation.x + widget->allocation.width);
 
   /* PACK_END */
   for (i = cellview->priv->cell_list; i; i = i->next)
@@ -511,7 +511,7 @@
 
       area.width = info->real_width;
       if (!rtl)
-         area.x -= area.width;   
+         area.x -= area.width;
 
       gtk_cell_renderer_render (info->cell,
                                 widget->window,
@@ -550,7 +550,7 @@
   GtkTreePath *path;
 
   g_return_if_fail (cellview->priv->displayed_row != NULL);
-  
+
   path = gtk_tree_row_reference_get_path (cellview->priv->displayed_row);
   gtk_tree_model_get_iter (cellview->priv->model, &iter, path);
   gtk_tree_path_free (path);
@@ -666,7 +666,7 @@
       gtk_cell_view_cell_layout_clear_attributes (layout, info->cell);
       g_object_unref (G_OBJECT (info->cell));
       g_free (info);
-      cellview->priv->cell_list = g_list_delete_link (cellview->priv->cell_list, 
+      cellview->priv->cell_list = g_list_delete_link (cellview->priv->cell_list,
 						      cellview->priv->cell_list);
     }
 }
@@ -719,7 +719,7 @@
 	  g_free (list->data);
 	  list = list->next->next;
 	}
-      
+
       g_slist_free (info->attributes);
       info->attributes = NULL;
     }
@@ -905,7 +905,7 @@
  * gtk_cell_view_set_displayed_row:
  * @cell_view: a #GtkCellView
  * @path: a #GtkTreePath or %NULL to unset.
- * 
+ *
  * Sets the row of the model that is currently displayed
  * by the #GtkCellView. If the path is unset, then the
  * contents of the cellview "stick" at their last value;
--- a/pidgin/gtkconv.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkconv.c	Sat Mar 07 01:59:40 2009 +0000
@@ -201,11 +201,11 @@
 
 	switch (purple_conversation_get_type(conv)) {
 		case PURPLE_CONV_TYPE_IM:
-			node = (PurpleBlistNode*)purple_find_buddy(conv->account, conv->name);
+			node = PURPLE_BLIST_NODE(purple_find_buddy(conv->account, conv->name));
 			node = node ? node->parent : NULL;
 			break;
 		case PURPLE_CONV_TYPE_CHAT:
-			node = (PurpleBlistNode*)purple_blist_find_chat(conv->account, conv->name);
+			node = PURPLE_BLIST_NODE(purple_blist_find_chat(conv->account, conv->name));
 			break;
 		default:
 			break;
@@ -1395,7 +1395,7 @@
 
 	if (logging == purple_conversation_is_logging(conv))
 		return;
-	
+
 	node = get_conversation_blist_node(conv);
 
 	if (logging)
@@ -1835,7 +1835,7 @@
 	gtk_tree_selection_select_path(GTK_TREE_SELECTION(
 			gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path);
 	gtk_tree_view_set_cursor(GTK_TREE_VIEW(gtkchat->list),
-							 path, NULL, FALSE); 
+							 path, NULL, FALSE);
 	gtk_widget_grab_focus(GTK_WIDGET(gtkchat->list));
 
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
@@ -1867,7 +1867,7 @@
 	GtkTreeIter iter;
 	GtkTreeModel *model;
 	gchar *who;
-	
+
 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
 
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path);
@@ -2061,7 +2061,7 @@
 	gtkconv  = (PidginConversation *)data;
 	conv     = gtkconv->active_conv;
 	win      = gtkconv->win;
-	
+
 	if (conv_keypress_common(gtkconv, event))
 		return TRUE;
 
@@ -2519,7 +2519,7 @@
 			if (ops && ops->update)
 				ops->update(NULL, (PurpleBlistNode*)b);
 
-			/* XXX Seanegan: We really need a util function to return a pixbuf for a Presence to avoid all this switching */	
+			/* XXX Seanegan: We really need a util function to return a pixbuf for a Presence to avoid all this switching */
 			if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY))
 				status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, parent, icon_size);
 			else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY))
@@ -2545,7 +2545,7 @@
 			status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_CHAT,
 					size, "GtkWidget");
 		}
-	}	
+	}
 	return status;
 }
 
@@ -2588,11 +2588,11 @@
 	gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->icon), status);
 	gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status);
 
-	gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model), 
+	gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model),
 			&(gtkconv->infopane_iter),
 			CONV_ICON_COLUMN, infopane_status, -1);
 
-	gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model), 
+	gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model),
 			&(gtkconv->infopane_iter),
 			CONV_EMBLEM_COLUMN, emblem, -1);
 	if (emblem)
@@ -3853,7 +3853,7 @@
 			{
 				PurpleBlistNode *node;
 
-				node = (PurpleBlistNode *) purple_buddy_get_contact((PurpleBuddy *)l->data);
+				node = PURPLE_BLIST_NODE(purple_buddy_get_contact(PURPLE_BUDDY(l->data)));
 
 				for (node = node->child; node != NULL; node = node->next)
 				{
@@ -4802,7 +4802,7 @@
 
 	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);
-	gtk_cell_view_set_model(GTK_CELL_VIEW(gtkconv->infopane), 
+	gtk_cell_view_set_model(GTK_CELL_VIEW(gtkconv->infopane),
 				GTK_TREE_MODEL(gtkconv->infopane_model));
 	g_object_unref(gtkconv->infopane_model);
 	gtk_list_store_append(gtkconv->infopane_model, &(gtkconv->infopane_iter));
@@ -5036,9 +5036,9 @@
 static PidginConversation *
 pidgin_conv_find_gtkconv(PurpleConversation * conv)
 {
-	PurpleBuddy *bud = purple_find_buddy(conv->account, conv->name), *b;
+	PurpleBuddy *bud = purple_find_buddy(conv->account, conv->name);
 	PurpleContact *c;
-	PurpleBlistNode *cn;
+	PurpleBlistNode *cn, *bn;
 
 	if (!bud)
 		return NULL;
@@ -5046,8 +5046,9 @@
 	if (!(c = purple_buddy_get_contact(bud)))
 		return NULL;
 
-	cn = (PurpleBlistNode *)c;
-	for (b = (PurpleBuddy *)cn->child; b; b = (PurpleBuddy *) ((PurpleBlistNode *)b)->next) {
+	cn = PURPLE_BLIST_NODE(c);
+	for (bn = purple_blist_node_get_first_child(cn); bn; bn = purple_blist_node_get_sibling_next(bn)) {
+		PurpleBuddy *b = PURPLE_BUDDY(bn);
 		PurpleConversation *conv;
 		if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, b->name, b->account))) {
 			if (conv->ui_data)
@@ -5497,7 +5498,7 @@
 	gtkconv->newday = mktime(tm);
 }
 
-/* Detect string direction and encapsulate the string in RLE/LRE/PDF unicode characters 
+/* Detect string direction and encapsulate the string in RLE/LRE/PDF unicode characters
    str - pointer to string (string is re-allocated and the pointer updated) */
 static void
 str_embed_direction_chars(char **str)
@@ -5534,14 +5535,14 @@
 }
 
 /* Returns true if the given HTML contains RTL text */
-static gboolean 
+static gboolean
 html_is_rtl(const char *html)
 {
 	GData *attributes;
 	const gchar *start, *end;
 	gboolean res = FALSE;
 
-	if (purple_markup_find_tag("span", html, &start, &end, &attributes)) 
+	if (purple_markup_find_tag("span", html, &start, &end, &attributes))
 	{
 		/* tmp is a member of attributes and is free with g_datalist_clear call */
 		const char *tmp = g_datalist_get_data(&attributes, "dir");
@@ -6465,10 +6466,10 @@
 			PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name);
 			window_icon =
 				gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim);
-		
+
 			if (buddy &&  !PURPLE_BUDDY_IS_ONLINE(buddy))
 				gdk_pixbuf_saturate_and_pixelate(window_icon, window_icon, 0.0, FALSE);
-			
+
 			g_object_ref(window_icon);
 			l = g_list_append(l, window_icon);
 		} else {
@@ -6661,7 +6662,7 @@
 			pango_attr_list_unref(list);
 		} else
 			gtk_label_set_attributes(GTK_LABEL(gtkconv->tab_label), NULL);
-		
+
 		if (pidgin_conv_window_is_active_conversation(conv))
 			update_typing_icon(gtkconv);
 
@@ -7233,7 +7234,7 @@
 		PurpleConversation *conv = l->data;
 		if (!PIDGIN_CONVERSATION(conv))
 			continue;
-		if (GPOINTER_TO_INT(value)) 
+		if (GPOINTER_TO_INT(value))
 			gtk_widget_show(PIDGIN_CONVERSATION(conv)->infopane_hbox);
 		else
 			gtk_widget_hide(PIDGIN_CONVERSATION(conv)->infopane_hbox);
@@ -7643,7 +7644,7 @@
 	list = purple_conversation_get_message_history(conv);
 	if (list) {
 		switch (purple_conversation_get_type(conv)) {
-			case PURPLE_CONV_TYPE_IM: 
+			case PURPLE_CONV_TYPE_IM:
 			{
 				GList *convs;
 				list = g_list_copy(list);
@@ -8409,7 +8410,7 @@
 								    G_CALLBACK(notebook_leave_cb), gtkconv->win);
 		return FALSE;
 	}
-	
+
 	if (e->button == 3) {
 		/* Right click was pressed. Popup the context menu. */
 		GtkWidget *menu = gtk_menu_new(), *sub;
@@ -8435,7 +8436,7 @@
 	}
 	return FALSE;
 }
- 
+
 static gboolean
 notebook_press_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win)
 {
@@ -9015,7 +9016,7 @@
 
 static gboolean gtk_conv_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) {
 	int x, y;
-	
+
 	if (GTK_WIDGET_VISIBLE(w))
 		gtk_window_get_position(GTK_WINDOW(w), &x, &y);
 	else
@@ -9025,7 +9026,7 @@
 	* when the window is being maximized */
 	if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED)
 		return FALSE;
-	
+
 	/* don't save off-screen positioning */
 	if (x + event->width < 0 ||
 	    y + event->height < 0 ||
@@ -9041,7 +9042,7 @@
 
 	/* continue to handle event normally */
 	return FALSE;
-						
+
 }
 
 static void
@@ -9122,7 +9123,7 @@
 	/* Intercept keystrokes from the menu items */
 	g_signal_connect(G_OBJECT(win->window), "key_press_event",
 					 G_CALLBACK(window_keypress_cb), win);
-	
+
 
 	/* Create the notebook. */
 	win->notebook = gtk_notebook_new();
@@ -9284,7 +9285,7 @@
 	win->gtkconvs = g_list_append(win->gtkconvs, gtkconv);
 	gtkconv->win = win;
 
-	if (win->gtkconvs && win->gtkconvs->next && win->gtkconvs->next->next == NULL) 
+	if (win->gtkconvs && win->gtkconvs->next && win->gtkconvs->next->next == NULL)
 		pidgin_conv_tab_pack(win, ((PidginConversation*)win->gtkconvs->data));
 
 
@@ -9443,11 +9444,11 @@
 		gtk_notebook_set_tab_label(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, ebox);
 	}
 
-	gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, 
-					   !tabs_side && !angle, 
+	gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont,
+					   !tabs_side && !angle,
 					   TRUE, GTK_PACK_START);
 
-	if (pidgin_conv_window_get_gtkconv_count(win) == 1) 
+	if (pidgin_conv_window_get_gtkconv_count(win) == 1)
 		gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook),
 					   purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs") &&
                                            (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") ||
@@ -9658,7 +9659,7 @@
 	if (win == NULL) {
 		win = pidgin_conv_window_new();
 
-		g_signal_connect(G_OBJECT(win->window), "configure_event", 
+		g_signal_connect(G_OBJECT(win->window), "configure_event",
 				G_CALLBACK(gtk_conv_configure_cb), NULL);
 
 		pidgin_conv_window_add_gtkconv(win, conv);
@@ -9673,7 +9674,7 @@
 conv_placement_last_created_win_type_configured_cb(GtkWidget *w,
 		GdkEventConfigure *event, PidginConversation *conv)
 {
-	int x, y;	
+	int x, y;
 	PurpleConversationType type = purple_conversation_get_type(conv->active_conv);
 	GList *all;
 
@@ -9686,7 +9687,7 @@
 	* when the window is being maximized */
 	if (gdk_window_get_state(w->window) & GDK_WINDOW_STATE_MAXIMIZED)
 		return FALSE;
-	
+
 	/* don't save off-screen positioning */
 	if (x + event->width < 0 ||
 	    y + event->height < 0 ||
@@ -9740,11 +9741,11 @@
 				purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/width"),
 				purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/height"));
 		}
-				
+
 		pidgin_conv_window_add_gtkconv(win, conv);
 		pidgin_conv_window_show(win);
 
-		g_signal_connect(G_OBJECT(win->window), "configure_event", 
+		g_signal_connect(G_OBJECT(win->window), "configure_event",
 				G_CALLBACK(conv_placement_last_created_win_type_configured_cb), conv);
 	} else
 		pidgin_conv_window_add_gtkconv(win, conv);
@@ -9758,7 +9759,7 @@
 
 	win = pidgin_conv_window_new();
 
-	g_signal_connect(G_OBJECT(win->window), "configure_event", 
+	g_signal_connect(G_OBJECT(win->window), "configure_event",
 			G_CALLBACK(gtk_conv_configure_cb), NULL);
 
 	pidgin_conv_window_add_gtkconv(win, conv);
--- a/pidgin/gtkdebug.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkdebug.c	Sat Mar 07 01:59:40 2009 +0000
@@ -769,7 +769,7 @@
 		win->filter =
 			gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
 									   GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
-									   NULL, _("Filter"), _("Filter"), 
+									   NULL, _("Filter"), _("Filter"),
 									   NULL, NULL,
 									   G_CALLBACK(regex_filter_toggled_cb),
 									   win);
@@ -821,7 +821,7 @@
 		                           GTK_TOOLBAR_CHILD_WIDGET, gtk_label_new(_("Level ")),
 		                           NULL, _("Select the debug filter level."),
 		                           NULL, NULL, NULL, NULL);
-		
+
 		win->filterlevel = gtk_combo_box_new_text();
 		gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
 		                           GTK_TOOLBAR_CHILD_WIDGET, win->filterlevel,
--- a/pidgin/gtkdialogs.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkdialogs.c	Sat Mar 07 01:59:40 2009 +0000
@@ -350,7 +350,7 @@
 }
 
 #if 0
-/* This function puts the version number onto the pixmap we use in the 'about' 
+/* This function puts the version number onto the pixmap we use in the 'about'
  * screen in Pidgin. */
 static void
 pidgin_logo_versionize(GdkPixbuf **original, GtkWidget *widget) {
@@ -493,7 +493,7 @@
 						   _("Retired Crazy Patch Writers"));
 	add_developers(str, retired_patch_writers);
 	g_string_append(str, "<BR/>");
-			
+
 	/* Current Translators */
 	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
 						   _("Current Translators"));
@@ -1067,8 +1067,8 @@
 	g_return_if_fail(contact != NULL);
 	g_return_if_fail(buddy != NULL);
 
-	if (((PurpleBlistNode*)contact)->child == (PurpleBlistNode*)buddy &&
-			!((PurpleBlistNode*)buddy)->next) {
+	if (PURPLE_BLIST_NODE(contact)->child == PURPLE_BLIST_NODE(buddy) &&
+	    PURPLE_BLIST_NODE(buddy)->next == NULL) {
 		pidgin_dialogs_remove_buddy(buddy);
 	} else {
 		gchar *text;
@@ -1122,7 +1122,7 @@
 	ggp = g_new(struct _PidginGroupMergeObject, 1);
 	ggp->parent = source;
 	ggp->new_name = g_strdup(new_name);
-	
+
 	purple_request_action(source, NULL, _("Merge Groups"), text, 0,
 			NULL, NULL, NULL,
 			ggp, 2,
--- a/pidgin/gtkdnd-hints.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkdnd-hints.c	Sat Mar 07 01:59:40 2009 +0000
@@ -46,7 +46,7 @@
 /**
  * Info about each hint widget. See DndHintWindowId enum.
  */
-static HintWindowInfo hint_windows[] = { 
+static HintWindowInfo hint_windows[] = {
 	{ NULL, "arrow-up.xpm",   -13/2,     0 },
 	{ NULL, "arrow-down.xpm", -13/2,   -16 },
 	{ NULL, "arrow-left.xpm",     0, -13/2 },
@@ -139,7 +139,7 @@
 		dnd_hints_hide(i);
 }
 
-void 
+void
 dnd_hints_hide(DndHintWindowId i)
 {
 	GtkWidget *w = hint_windows[i].widget;
@@ -148,7 +148,7 @@
 		gtk_widget_hide(w);
 }
 
-void 
+void
 dnd_hints_show(DndHintWindowId id, gint x, gint y)
 {
 	GtkWidget *w;
@@ -165,7 +165,7 @@
 	}
 }
 
-void 
+void
 dnd_hints_show_relative(DndHintWindowId id, GtkWidget *widget,
 						DndHintPosition horiz, DndHintPosition vert)
 {
--- a/pidgin/gtkdocklet-x11.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkdocklet-x11.c	Sat Mar 07 01:59:40 2009 +0000
@@ -5,7 +5,7 @@
  * Copyright (C) 2003 Herman Bloggs <hermanator12002@yahoo.com>
  * Inspired by a similar plugin by:
  *  John (J5) Palmieri <johnp@martianrock.com>
- * 
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * published by the Free Software Foundation; either version 2 of the
@@ -15,7 +15,7 @@
  * 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
@@ -59,7 +59,7 @@
 docklet_x11_embedded_cb(GtkWidget *widget, void *data)
 {
 	purple_debug(PURPLE_DEBUG_INFO, "docklet", "embedded\n");
-	
+
 	g_source_remove(embed_timeout);
 	embed_timeout = 0;
 	pidgin_docklet_embedded();
@@ -211,9 +211,9 @@
 
 	if (embed_timeout)
 		g_source_remove(embed_timeout);
-	
+
 	pidgin_docklet_remove();
-	
+
 	g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_x11_destroyed_cb), NULL);
 	gtk_widget_destroy(GTK_WIDGET(docklet));
 
@@ -239,7 +239,7 @@
 	 */
 	purple_debug_info("docklet", "failed to embed within timeout\n");
 	pidgin_docklet_remove();
-	
+
 	return FALSE;
 }
 
--- a/pidgin/gtkdocklet.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkdocklet.h	Sat Mar 07 01:59:40 2009 +0000
@@ -1,11 +1,11 @@
-/* 
+/*
  * System tray icon (aka docklet) plugin for Purple
- * 
+ *
  * Copyright (C) 2002-3 Robert McQueen <robot101@debian.org>
  * Copyright (C) 2003 Herman Bloggs <hermanator12002@yahoo.com>
  * Inspired by a similar plugin by:
  *  John (J5) Palmieri <johnp@martianrock.com>
- * 
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * published by the Free Software Foundation; either version 2 of the
@@ -15,7 +15,7 @@
  * 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
--- a/pidgin/gtkexpander.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkexpander.c	Sat Mar 07 01:59:40 2009 +0000
@@ -63,7 +63,7 @@
 
   guint             expanded : 1;
   guint             use_underline : 1;
-  guint             use_markup : 1; 
+  guint             use_markup : 1;
   guint             button_down : 1;
   guint             prelight : 1;
 };
@@ -129,7 +129,7 @@
 gtk_expander_get_type (void)
 {
   static GType expander_type = 0;
-  
+
   if (!expander_type)
     {
       static const GTypeInfo expander_info =
@@ -144,12 +144,12 @@
 	0,		/* n_preallocs */
 	(GInstanceInitFunc) gtk_expander_init,
       };
-      
+
       expander_type = g_type_register_static (GTK_TYPE_BIN,
 					      "GtkExpander",
 					      &expander_info, 0);
     }
-  
+
   return expander_type;
 }
 
@@ -314,7 +314,7 @@
 			   GParamSpec   *pspec)
 {
   GtkExpander *expander = GTK_EXPANDER (object);
-                                                                                                             
+
   switch (prop_id)
     {
     case PROP_EXPANDED:
@@ -382,7 +382,7 @@
 gtk_expander_destroy (GtkObject *object)
 {
   GtkExpanderPrivate *priv = GTK_EXPANDER (object)->priv;
-  
+
   if (priv->animation_timeout)
     {
       g_source_remove (priv->animation_timeout);
@@ -407,7 +407,7 @@
   border_width = GTK_CONTAINER (widget)->border_width;
 
   get_expander_bounds (GTK_EXPANDER (widget), &expander_rect);
-  
+
   attributes.window_type = GDK_WINDOW_CHILD;
   attributes.x = widget->allocation.x + border_width;
   attributes.y = expander_rect.y;
@@ -831,7 +831,7 @@
 			NULL);
 
   ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-  
+
   x = widget->allocation.x + border_width;
   y = widget->allocation.y + border_width;
 
@@ -853,7 +853,7 @@
       width += expander_size + 2 * expander_spacing;
       height = MAX (height, expander_size + 2 * expander_spacing);
     }
-      
+
   width  += 2 * focus_pad + 2 * focus_width;
   height += 2 * focus_pad + 2 * focus_width;
 
@@ -1115,16 +1115,16 @@
 		    GtkDirectionType  direction)
 {
   GtkExpander *expander = GTK_EXPANDER (widget);
-  
+
   if (!focus_current_site (expander, direction))
     {
       GtkWidget *old_focus_child;
       gboolean widget_is_focus;
       FocusSite site = FOCUS_NONE;
-      
+
       widget_is_focus = gtk_widget_is_focus (widget);
       old_focus_child = GTK_CONTAINER (widget)->focus_child;
-      
+
       if (old_focus_child && old_focus_child == expander->priv->label_widget)
 	site = FOCUS_LABEL;
       else if (old_focus_child)
@@ -1192,9 +1192,9 @@
 /**
  * gtk_expander_new:
  * @label: the text of the label
- * 
+ *
  * Creates a new expander using @label as the text of the label.
- * 
+ *
  * Return value: a new #GtkExpander widget.
  *
  * Since: 2.4
@@ -1209,14 +1209,14 @@
  * gtk_expander_new_with_mnemonic:
  * @label: the text of the label with an underscore in front of the
  *         mnemonic character
- * 
+ *
  * Creates a new expander using @label as the text of the label.
  * If characters in @label are preceded by an underscore, they are underlined.
- * If you need a literal underscore character in a label, use '__' (two 
- * underscores). The first underlined character represents a keyboard 
+ * If you need a literal underscore character in a label, use '__' (two
+ * underscores). The first underlined character represents a keyboard
  * accelerator called a mnemonic.
  * Pressing Alt and that key activates the button.
- * 
+ *
  * Return value: a new #GtkExpander widget.
  *
  * Since: 2.4
@@ -1328,7 +1328,7 @@
 	{
 	  gtk_expander_start_animation (expander);
 	}
-      else 
+      else
 	{
 	  priv->expander_style = expanded ? GTK_EXPANDER_EXPANDED :
 					    GTK_EXPANDER_COLLAPSED;
@@ -1643,7 +1643,7 @@
  * gtk_expander_set_label_widget().
  *
  * Return value: the label widget, or %NULL if there is none.
- * 
+ *
  * Since: 2.4
  **/
 GtkWidget *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkicon-theme-loader.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,111 @@
+/*
+ * PidginIconThemeLoader for 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 "gtkicon-theme-loader.h"
+#include "gtkstatus-icon-theme.h"
+
+#include "xmlnode.h"
+
+/*****************************************************************************
+ * Icon Theme Builder
+ *****************************************************************************/
+
+static PurpleTheme *
+pidgin_icon_loader_build(const gchar *dir)
+{
+	xmlnode *root_node = NULL, *sub_node;
+	gchar *filename_full, *data;
+	PidginIconTheme *theme = NULL;
+
+	/* Find the theme file */
+	g_return_val_if_fail(dir != NULL, NULL);
+	filename_full = g_build_filename(dir, "theme.xml", NULL);
+
+	if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR))
+		root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-theme-loader");
+
+	g_free(filename_full);
+	g_return_val_if_fail(root_node != NULL, NULL);
+
+	/* Parse the tree */
+	sub_node = xmlnode_get_child(root_node, "description");
+	data = xmlnode_get_data(sub_node);
+
+	if (xmlnode_get_attrib(root_node, "name") != NULL) {
+		theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME,
+				"type", "status-icon",
+				"name", xmlnode_get_attrib(root_node, "name"),
+				"author", xmlnode_get_attrib(root_node, "author"),
+				"image", xmlnode_get_attrib(root_node, "image"),
+				"directory", dir,
+				"description", data, NULL);
+
+		sub_node = xmlnode_get_child(root_node, "icon");
+
+		while (sub_node) {
+			pidgin_icon_theme_set_icon(theme,
+					xmlnode_get_attrib(sub_node, "id"),
+					xmlnode_get_attrib(sub_node, "file"));
+			sub_node = xmlnode_get_next_twin(sub_node);
+		}
+	}
+
+	xmlnode_free(root_node);
+	g_free(data);
+	return PURPLE_THEME(theme);
+}
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+pidgin_icon_theme_loader_class_init (PidginIconThemeLoaderClass *klass)
+{
+	PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass);
+
+	loader_klass->purple_theme_loader_build = pidgin_icon_loader_build;
+}
+
+
+GType
+pidgin_icon_theme_loader_get_type (void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PidginIconThemeLoaderClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_icon_theme_loader_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (PidginIconThemeLoader),
+			0, /* n_preallocs */
+			NULL, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static (PURPLE_TYPE_THEME_LOADER,
+				"PidginIconThemeLoader", &info, 0);
+	}
+	return type;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkicon-theme-loader.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,71 @@
+/**
+ * @file gtkicon-loader.h  Pidgin Icon Theme Loader Class API
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * 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_ICON_THEME_LOADER_H
+#define PIDGIN_ICON_THEME_LOADER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme-loader.h"
+
+/**
+ * A pidgin icon theme loader. Extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build icon themes
+ *
+ * PidginIconThemeLoader is a GObject.
+ */
+typedef struct _PidginIconThemeLoader       PidginIconThemeLoader;
+typedef struct _PidginIconThemeLoaderClass  PidginIconThemeLoaderClass;
+
+#define PIDGIN_TYPE_ICON_THEME_LOADER            (pidgin_icon_theme_loader_get_type ())
+#define PIDGIN_ICON_THEME_LOADER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoader))
+#define PIDGIN_ICON_THEME_LOADER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoaderClass))
+#define PIDGIN_IS_ICON_THEME_LOADER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_ICON_THEME_LOADER))
+#define PIDGIN_IS_ICON_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME_LOADER))
+#define PIDGIN_ICON_THEME_LOADER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME_LOADER, PidginIconThemeLoaderClass))
+
+struct _PidginIconThemeLoader
+{
+	PurpleThemeLoader parent;
+};
+
+struct _PidginIconThemeLoaderClass
+{
+	PurpleThemeLoaderClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Pidgin Icon Theme-Loader API                                    */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_icon_theme_loader_get_type(void);
+
+G_END_DECLS
+#endif /* PIDGIN_ICON_THEME_LOADER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkicon-theme.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,142 @@
+/*
+ * Icon Themes for 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 "gtkicon-theme.h"
+#include "pidginstock.h"
+
+#include <gtk/gtk.h>
+
+#define PIDGIN_ICON_THEME_GET_PRIVATE(Gobject) \
+	((PidginIconThemePrivate *) ((PIDGIN_ICON_THEME(Gobject))->priv))
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+typedef struct {
+	/* used to store filenames of diffrent icons */
+	GHashTable *icon_files;
+} PidginIconThemePrivate;
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+pidgin_icon_theme_init(GTypeInstance *instance,
+		gpointer klass)
+{
+	PidginIconThemePrivate *priv;
+
+	(PIDGIN_ICON_THEME(instance))->priv = g_new0(PidginIconThemePrivate, 1);
+
+	priv = PIDGIN_ICON_THEME_GET_PRIVATE(instance);
+
+	priv->icon_files = g_hash_table_new_full(g_str_hash,
+			g_str_equal, g_free, g_free);
+}
+
+static void
+pidgin_icon_theme_finalize(GObject *obj)
+{
+	PidginIconThemePrivate *priv;
+
+	priv = PIDGIN_ICON_THEME_GET_PRIVATE(obj);
+
+	g_hash_table_destroy(priv->icon_files);
+	g_free(priv);
+
+	parent_class->finalize(obj);
+}
+
+static void
+pidgin_icon_theme_class_init(PidginIconThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	obj_class->finalize = pidgin_icon_theme_finalize;
+}
+
+GType
+pidgin_icon_theme_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PidginIconThemeClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_icon_theme_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PidginIconTheme),
+			0, /* n_preallocs */
+			pidgin_icon_theme_init, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PURPLE_TYPE_THEME,
+				"PidginIconTheme", &info, G_TYPE_FLAG_ABSTRACT);
+	}
+	return type;
+}
+
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+const gchar *
+pidgin_icon_theme_get_icon(PidginIconTheme *theme,
+		const gchar *id)
+{
+	PidginIconThemePrivate *priv;
+
+	g_return_val_if_fail(PIDGIN_IS_ICON_THEME(theme), NULL);
+
+	priv = PIDGIN_ICON_THEME_GET_PRIVATE(theme);
+
+	return g_hash_table_lookup(priv->icon_files, id);
+}
+
+void
+pidgin_icon_theme_set_icon(PidginIconTheme *theme,
+		const gchar *id,
+		const gchar *filename)
+{
+	PidginIconThemePrivate *priv;
+	g_return_if_fail(PIDGIN_IS_ICON_THEME(theme));
+
+	priv = PIDGIN_ICON_THEME_GET_PRIVATE(theme);
+
+	if (filename != NULL)
+		g_hash_table_replace(priv->icon_files,
+				g_strdup(id), g_strdup(filename));
+	else
+		g_hash_table_remove(priv->icon_files, id);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkicon-theme.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,93 @@
+/**
+ * @file icon-theme.h  Pidgin Icon Theme  Class API
+ */
+
+/* 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_ICON_THEME_H
+#define PIDGIN_ICON_THEME_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme.h"
+
+/**
+ * extends PurpleTheme (theme.h)
+ * A pidgin icon theme.
+ * This object represents a Pidgin icon theme.
+ *
+ * PidginIconTheme is a PurpleTheme Object.
+ */
+typedef struct _PidginIconTheme        PidginIconTheme;
+typedef struct _PidginIconThemeClass   PidginIconThemeClass;
+
+#define PIDGIN_TYPE_ICON_THEME            (pidgin_icon_theme_get_type ())
+#define PIDGIN_ICON_THEME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_ICON_THEME, PidginIconTheme))
+#define PIDGIN_ICON_THEME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_ICON_THEME, PidginIconThemeClass))
+#define PIDGIN_IS_ICON_THEME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_ICON_THEME))
+#define PIDGIN_IS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_ICON_THEME))
+#define PIDGIN_ICON_THEME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_ICON_THEME, PidginIconThemeClass))
+
+struct _PidginIconTheme
+{
+	PurpleTheme parent;
+	gpointer priv;
+};
+
+struct _PidginIconThemeClass
+{
+	PurpleThemeClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Pidgin Icon Theme API                                          */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_icon_theme_get_type(void);
+
+/**
+ * Returns a copy of the filename for the icon event or NULL if it is not set
+ *
+ * @param event		the pidgin icon event to look up
+ *
+ * @returns the filename of the icon event
+ */
+const gchar *pidgin_icon_theme_get_icon(PidginIconTheme *theme,
+		const gchar *event);
+
+/**
+ * Sets the filename for a given icon id, setting the icon to NULL will remove the icon from the theme
+ *
+ * @param icon_id		a string representing what the icon is to be used for
+ * @param filename		the name of the file to be used for the given id
+ */
+void pidgin_icon_theme_set_icon(PidginIconTheme *theme,
+		const gchar *icon_id,
+		const gchar *filename);
+
+G_END_DECLS
+#endif /* PIDGIN_ICON_THEME_H */
--- a/pidgin/gtkimhtml.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkimhtml.c	Sat Mar 07 01:59:40 2009 +0000
@@ -88,6 +88,22 @@
 	GtkTextMark *mark;
 };
 
+struct _GtkIMHtmlLink
+{
+	GtkIMHtml *imhtml;
+	gchar *url;
+	GtkTextTag *tag;
+};
+
+typedef struct _GtkIMHtmlProtocol
+{
+	char *name;
+	int length;
+
+	gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link);
+	gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu);
+} GtkIMHtmlProtocol;
+
 static gboolean
 gtk_text_view_drag_motion (GtkWidget        *widget,
                            GdkDragContext   *context,
@@ -115,6 +131,9 @@
 static void imhtml_font_grow(GtkIMHtml *imhtml);
 static void imhtml_font_shrink(GtkIMHtml *imhtml);
 static void imhtml_clear_formatting(GtkIMHtml *imhtml);
+static int gtk_imhtml_is_protocol(const char *text);
+static void gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag);
+static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link);
 
 /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */
 #define MAX_FONT_SIZE 7
@@ -349,7 +368,7 @@
 			g_string_free (t->values, TRUE);
 			g_free (t->children);
 		}
-		
+
 		g_free (t);
 	}
 }
@@ -819,7 +838,7 @@
 			                                      &tag_area.x,
 			                                      &tag_area.y);
 
-		
+
 			rect.height = tag_area.y + tag_area.height - rect.y
 				+ gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(widget))
 				+ gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(widget));
@@ -1106,8 +1125,8 @@
 	GtkTextIter iter;
 	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);
+	/* Delete any currently selected text */
+	gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
 
 	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, gtk_text_buffer_get_insert(imhtml->text_buffer));
 	if (!imhtml->wbfo && !plaintext)
@@ -1391,6 +1410,38 @@
 
 }
 
+static GtkIMHtmlProtocol *
+imhtml_find_protocol(const char *url, gboolean reverse)
+{
+	GtkIMHtmlClass *klass;
+	GList *iter;
+	GtkIMHtmlProtocol *proto = NULL;
+	int length = reverse ? strlen(url) : -1;
+
+	klass = g_type_class_ref(GTK_TYPE_IMHTML);
+	for (iter = klass->protocols; iter; iter = iter->next) {
+		proto = iter->data;
+		if (g_ascii_strncasecmp(url, proto->name, reverse ? MIN(length, proto->length) : proto->length) == 0) {
+			return proto;
+		}
+	}
+	return NULL;
+}
+
+static void
+imhtml_url_clicked(GtkIMHtml *imhtml, const char *url)
+{
+	GtkIMHtmlProtocol *proto = imhtml_find_protocol(url, FALSE);
+	GtkIMHtmlLink *link;
+	if (!proto)
+		return;
+	link = g_new0(GtkIMHtmlLink, 1);
+	link->imhtml = g_object_ref(imhtml);
+	link->url = g_strdup(url);
+	proto->activate(imhtml, link);   /* XXX: Do something with the return value? */
+	gtk_imhtml_link_destroy(link);
+}
+
 /* Boring GTK+ stuff */
 static void gtk_imhtml_class_init (GtkIMHtmlClass *klass)
 {
@@ -1475,6 +1526,7 @@
 	klass->toggle_format = imhtml_toggle_format;
 	klass->message_send = imhtml_message_send;
 	klass->clear_format = imhtml_clear_formatting;
+	klass->url_clicked = imhtml_url_clicked;
 	klass->undo = gtk_imhtml_undo;
 	klass->redo = gtk_imhtml_redo;
 
@@ -1688,37 +1740,14 @@
 	return imhtml_type;
 }
 
-struct url_data {
-	GObject *object;
-	gchar *url;
-	GtkTextTag *tag;
-};
-
-static void url_data_destroy(gpointer mydata)
-{
-	struct url_data *data = mydata;
-	g_object_unref(data->object);
-	g_object_unref(data->tag);
-	g_free(data->url);
-	g_free(data);
-}
-
-static void url_open(GtkWidget *w, struct url_data *data)
-{
-	if(!data) return;
-	g_signal_emit(data->object, signals[URL_CLICKED], 0, data->url);
-	g_object_set_data(G_OBJECT(data->tag), "visited", GINT_TO_POINTER(TRUE));
-	gtk_imhtml_set_link_color(GTK_IMHTML(data->object), data->tag);
-}
-
-static void url_copy(GtkWidget *w, gchar *url) {
-	GtkClipboard *clipboard;
-
-	clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY);
-	gtk_clipboard_set_text(clipboard, url, -1);
-
-	clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_CLIPBOARD);
-	gtk_clipboard_set_text(clipboard, url, -1);
+static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link)
+{
+	if (link->imhtml)
+		g_object_unref(link->imhtml);
+	if (link->tag)
+		g_object_unref(link->tag);
+	g_free(link->url);
+	g_free(link);
 }
 
 /* The callback for an event on a link tag. */
@@ -1734,21 +1763,16 @@
 			if (gtk_text_buffer_get_selection_bounds(
 						gtk_text_iter_get_buffer(arg2),	&start, &end))
 				return FALSE;
-
-			/* A link was clicked--we emit the "url_clicked" signal
-			 * with the URL as the argument */
-			g_object_ref(G_OBJECT(tag));
-			g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url"));
-			g_object_unref(G_OBJECT(tag));
-			g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE));
-			gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag);
+			gtk_imhtml_activate_tag(GTK_IMHTML(imhtml), tag);
 			return FALSE;
 		} else if(event_button->button == 3) {
-			GtkWidget *img, *item, *menu;
-			struct url_data *tempdata = g_new(struct url_data, 1);
-			tempdata->object = g_object_ref(imhtml);
-			tempdata->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url"));
-			tempdata->tag = g_object_ref(tag);
+			GList *children;
+			GtkWidget *menu;
+			GtkIMHtmlProtocol *proto;
+			GtkIMHtmlLink *link = g_new(GtkIMHtmlLink, 1);
+			link->imhtml = g_object_ref(imhtml);
+			link->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url"));
+			link->tag = g_object_ref(tag);
 
 			/* Don't want the tooltip around if user right-clicked on link */
 			if (GTK_IMHTML(imhtml)->tip_window) {
@@ -1764,43 +1788,23 @@
 			else
 				gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor);
 			menu = gtk_menu_new();
-			g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", tempdata, url_data_destroy);
-
-			/* buttons and such */
-
-			if (!strncmp(tempdata->url, "mailto:", 7))
-			{
-				/* Copy Email Address */
-				img = gtk_image_new_from_stock(GTK_STOCK_COPY,
-											   GTK_ICON_SIZE_MENU);
-				item = gtk_image_menu_item_new_with_mnemonic(
-					_("_Copy Email Address"));
-				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
-				g_signal_connect(G_OBJECT(item), "activate",
-								 G_CALLBACK(url_copy), tempdata->url + 7);
-				gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+			g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", link,
+					(GDestroyNotify)gtk_imhtml_link_destroy);
+
+			proto = imhtml_find_protocol(link->url, FALSE);
+
+			if (proto && proto->context_menu) {
+				proto->context_menu(GTK_IMHTML(link->imhtml), link, menu);
 			}
-			else
-			{
-				/* Open Link in Browser */
-				img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO,
-											   GTK_ICON_SIZE_MENU);
-				item = gtk_image_menu_item_new_with_mnemonic(
-					_("_Open Link in Browser"));
-				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
-				g_signal_connect(G_OBJECT(item), "activate",
-								 G_CALLBACK(url_open), tempdata);
+
+			children = gtk_container_get_children(GTK_CONTAINER(menu));
+			if (!children) {
+				GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available"));
+				gtk_widget_show(item);
+				gtk_widget_set_sensitive(item, FALSE);
 				gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-
-				/* Copy Link Location */
-				img = gtk_image_new_from_stock(GTK_STOCK_COPY,
-											   GTK_ICON_SIZE_MENU);
-				item = gtk_image_menu_item_new_with_mnemonic(
-					_("_Copy Link Location"));
-				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
-				g_signal_connect(G_OBJECT(item), "activate",
-								 G_CALLBACK(url_copy), tempdata->url);
-				gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+			} else {
+				g_list_free(children);
 			}
 
 
@@ -1884,10 +1888,7 @@
 
 			links = g_strsplit((char *)sd->data, "\n", 0);
 			while((link = links[i]) != NULL){
-				if(purple_str_has_prefix(link, "http://") ||
-				   purple_str_has_prefix(link, "https://") ||
-				   purple_str_has_prefix(link, "ftp://"))
-				{
+				if (gtk_imhtml_is_protocol(link)) {
 					gchar *label;
 
 					if(links[i + 1])
@@ -1896,7 +1897,7 @@
 					label = links[i];
 
 					gtk_imhtml_insert_link(imhtml, mark, link, label);
-				} else if (link=='\0') {
+				} else if (*link == '\0') {
 					/* Ignore blank lines */
 				} else {
 					/* Special reasons, aka images being put in via other tag, etc. */
@@ -2067,7 +2068,7 @@
 {
 	if (smiley->imhtml) {
 		gtk_smiley_tree_remove(smiley->imhtml->default_smilies, smiley);
-		g_hash_table_foreach(smiley->imhtml->smiley_data, 
+		g_hash_table_foreach(smiley->imhtml->smiley_data,
 			gtk_imhtml_disassociate_smiley_foreach, smiley);
 		g_signal_handlers_disconnect_matched(smiley->imhtml, G_SIGNAL_MATCH_DATA,
 			0, 0, NULL, NULL, smiley);
@@ -2096,13 +2097,13 @@
 		g_signal_handlers_disconnect_matched(smiley->imhtml, G_SIGNAL_MATCH_DATA,
 			0, 0, NULL, NULL, smiley);
 	}
-	
+
 	smiley->imhtml = imhtml;
 
 	gtk_smiley_tree_insert (tree, smiley);
-	
+
 	/* connect destroy signal for the imhtml */
-	g_signal_connect(imhtml, "destroy", G_CALLBACK(gtk_imhtml_disconnect_smiley), 
+	g_signal_connect(imhtml, "destroy", G_CALLBACK(gtk_imhtml_disconnect_smiley),
 		smiley);
 }
 
@@ -2198,14 +2199,17 @@
 	return gtk_smiley_get_image(smiley);
 }
 
-#define VALID_TAG(x)	if (!g_ascii_strncasecmp (string, x ">", strlen (x ">"))) {	\
-				*tag = g_strndup (string, strlen (x));		\
-				*len = strlen (x) + 1;				\
+#define VALID_TAG(x)	do { \
+			if (!g_ascii_strncasecmp (string, x ">", strlen (x ">"))) {	\
+				if (tag) *tag = g_strndup (string, strlen (x));		\
+				if (len) *len = strlen (x) + 1;				\
 				return TRUE;					\
 			}							\
-			(*type)++
-
-#define VALID_OPT_TAG(x)	if (!g_ascii_strncasecmp (string, x " ", strlen (x " "))) {	\
+			if (type) (*type)++; \
+		} while (0)
+
+#define VALID_OPT_TAG(x)	do { \
+				if (!g_ascii_strncasecmp (string, x " ", strlen (x " "))) {	\
 					const gchar *c = string + strlen (x " ");	\
 					gchar e = '"';					\
 					gboolean quote = FALSE;				\
@@ -2222,12 +2226,13 @@
 						c++;					\
 					}						\
 					if (*c) {					\
-						*tag = g_strndup (string, c - string);	\
-						*len = c - string + 1;			\
+						if (tag) *tag = g_strndup (string, c - string);	\
+						if (len) *len = c - string + 1;			\
 						return TRUE;				\
 					}						\
 				}							\
-				(*type)++
+				if (type) (*type)++; \
+			} while (0)
 
 
 static gboolean
@@ -2237,8 +2242,8 @@
 		   gint        *type)
 {
 	char *close;
-	*type = 1;
-
+	if (type)
+		*type = 1;
 
 	if (!(close = strchr (string, '>')))
 		return FALSE;
@@ -2311,15 +2316,20 @@
 	if (!g_ascii_strncasecmp(string, "!--", strlen ("!--"))) {
 		gchar *e = strstr (string + strlen("!--"), "-->");
 		if (e) {
-			*len = e - string + strlen ("-->");
-			*tag = g_strndup (string + strlen ("!--"), *len - strlen ("!---->"));
+			if (len)
+				*len = e - string + strlen ("-->");
+			if (tag)
+				*tag = g_strndup (string + strlen ("!--"), *len - strlen ("!---->"));
 			return TRUE;
 		}
 	}
 
-	*type = -1;
-	*len = close - string + 1;
-	*tag = g_strndup(string, *len - 1);
+	if (type)
+		*type = -1;
+	if (len)
+		*len = close - string + 1;
+	if (tag)
+		*tag = g_strndup(string, *len - 1);
 	return TRUE;
 }
 
@@ -2382,26 +2392,12 @@
 	return g_string_free(ret, FALSE);
 }
 
-static const char *accepted_protocols[] = {
-	"http://",
-	"https://",
-	"ftp://"
-};
-
-static const int accepted_protocols_size = 3;
-
 /* returns if the beginning of the text is a protocol. If it is the protocol, returns the length so
    the caller knows how long the protocol string is. */
 static int gtk_imhtml_is_protocol(const char *text)
 {
-	gint i;
-
-	for(i=0; i<accepted_protocols_size; i++){
-		if( g_ascii_strncasecmp(text, accepted_protocols[i], strlen(accepted_protocols[i])) == 0  ){
-			return strlen(accepted_protocols[i]);
-		}
-	}
-	return 0;
+	GtkIMHtmlProtocol *proto = imhtml_find_protocol(text, FALSE);
+	return proto ? proto->length : 0;
 }
 
 /*
@@ -2606,7 +2602,7 @@
 
 			count++;
 		}
-		
+
 		g_free(in_color);
 		return g_strdup_printf("#%02X%02X%02X", rgbval[0], rgbval[1], rgbval[2]);
 	}
@@ -2655,7 +2651,7 @@
 	c = text;
 	len = strlen(text);
 	ws = g_malloc(len + 1);
-	ws[0] = 0;
+	ws[0] = '\0';
 
 	gtk_text_buffer_begin_user_action(imhtml->text_buffer);
 	while (pos < len) {
@@ -3307,8 +3303,7 @@
 				ws[wpos] = '\n';
 				wpos++;
 				gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-				ws[0] = '\0';
-				wpos = 0;
+				ws[0] = '\0'; wpos = 0;
 				/* NEW_BIT (NEW_TEXT_BIT); */
 			} else if (!br) {  /* Don't insert a space immediately after an HTML break */
 				/* A newline is defined by HTML as whitespace, which means we have to replace it with a word boundary.
@@ -3319,19 +3314,43 @@
 				ws[wpos] = ' ';
 				wpos++;
 				gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-				ws[0] = '\0';
-				wpos = 0;
+				ws[0] = '\0'; wpos = 0;
 			}
 			c++;
 			pos++;
-		} else if ((len_protocol = gtk_imhtml_is_protocol(c)) > 0){
+		} else if ((pos == 0 || wpos == 0 || isspace(*(c - 1))) &&
+		           (len_protocol = gtk_imhtml_is_protocol(c)) > 0 &&
+				   c[len_protocol] && !isspace(c[len_protocol])) {
 			br = FALSE;
-			while(len_protocol--){
-				/* Skip the next len_protocol characters, but make sure they're
-				   copied into the ws array.
-				*/
-				 ws [wpos++] = *c++;
-				 pos++;
+			if (wpos > 0) {
+				gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
+				ws[0] = '\0'; wpos = 0;
+			}
+			while (len_protocol--) {
+				/* Skip the next len_protocol characters, but
+				 * make sure they're copied into the ws array.
+				 */
+				ws [wpos++] = *c++;
+				pos++;
+			}
+			if (!imhtml->edit.link && (imhtml->format_functions & GTK_IMHTML_LINK)) {
+				while (*c && !isspace((int)*c) &&
+						(*c != '<' || !gtk_imhtml_is_tag(c + 1, NULL, NULL, NULL))) {
+					if (*c == '&' && (amp = purple_markup_unescape_entity(c, &tlen))) {
+						while (*amp)
+							ws[wpos++] = *amp++;
+						c += tlen;
+						pos += tlen;
+					} else {
+						ws [wpos++] = *c++;
+						pos++;
+					}
+				}
+				ws[wpos] = '\0';
+				gtk_imhtml_toggle_link(imhtml, ws);
+				gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
+				ws[0] = '\0'; wpos = 0;
+				gtk_imhtml_toggle_link(imhtml, NULL);
 			}
 		} else if (*c) {
 			br = FALSE;
@@ -3355,7 +3374,7 @@
 		ws[wpos++]  = 0xE2;
 		ws[wpos++]  = 0x80;
 		ws[wpos++]  = 0x8F;
-    
+
 		if (!rtl_direction)
 		{
 			/* insert LRM character to set direction */
@@ -3368,8 +3387,7 @@
 		ws[wpos]  = '\0';
 		gtk_text_buffer_insert(imhtml->text_buffer, &line_iter, ws, wpos);
 		gtk_text_buffer_get_end_iter(gtk_text_iter_get_buffer(&line_iter), iter);
-		ws[0] = '\0';
-		wpos = 0;
+		ws[0] = '\0'; wpos = 0;
 	}
 
 	while (fonts) {
@@ -4885,8 +4903,8 @@
 {
 	GtkTextIter iter;
 
-	if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL))
-		gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
+	/* Delete any currently selected text */
+	gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
 
 	gtk_imhtml_toggle_link(imhtml, url);
 	gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
@@ -4899,8 +4917,8 @@
 	GtkTextMark *mark;
 	GtkTextIter iter;
 
-	if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL))
-		gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
+	/* Delete any currently selected text */
+	gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
 
 	mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
 
@@ -5753,3 +5771,111 @@
 	g_free(smiley);
 }
 
+gboolean gtk_imhtml_class_register_protocol(const char *name,
+		gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link),
+		gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu))
+{
+	GtkIMHtmlClass *klass;
+	GtkIMHtmlProtocol *proto;
+
+	g_return_val_if_fail(name, FALSE);
+
+	klass = g_type_class_ref(GTK_TYPE_IMHTML);
+	g_return_val_if_fail(klass, FALSE);
+
+	if ((proto = imhtml_find_protocol(name, TRUE))) {
+		if (activate) {
+			return FALSE;
+		}
+		g_free(proto->name);
+		g_free(proto);
+		klass->protocols = g_list_remove(klass->protocols, proto);
+		return TRUE;
+	} else if (!activate) {
+		return FALSE;
+	}
+
+	proto = g_new0(GtkIMHtmlProtocol, 1);
+	proto->name = g_strdup(name);
+	proto->length = strlen(name);
+	proto->activate = activate;
+	proto->context_menu = context_menu;
+	klass->protocols = g_list_prepend(klass->protocols, proto);
+
+	return TRUE;
+}
+
+static void
+gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag)
+{
+	/* A link was clicked--we emit the "url_clicked" signal
+	 * with the URL as the argument */
+	g_object_ref(G_OBJECT(tag));
+	g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url"));
+	g_object_unref(G_OBJECT(tag));
+	g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE));
+	gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag);
+}
+
+gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link)
+{
+	g_return_val_if_fail(link, FALSE);
+
+	if (link->tag) {
+		gtk_imhtml_activate_tag(link->imhtml, link->tag);
+	} else if (link->url) {
+		g_signal_emit(link->imhtml, signals[URL_CLICKED], 0, link->url);
+	} else
+		return FALSE;
+	return TRUE;
+}
+
+const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link)
+{
+	return link->url;
+}
+
+const GtkTextTag * gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link)
+{
+	return link->tag;
+}
+
+static gboolean return_add_newline_cb(GtkWidget *widget, gpointer data)
+{
+	GtkTextBuffer *buffer;
+	GtkTextMark *mark;
+	GtkTextIter iter;
+
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
+
+	/* Delete any currently selected text */
+	gtk_text_buffer_delete_selection(buffer, TRUE, TRUE);
+
+	/* Insert a newline at the current cursor position */
+	mark = gtk_text_buffer_get_insert(buffer);
+	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
+	gtk_imhtml_insert_html_at_iter(GTK_IMHTML(widget), "\n", 0, &iter);
+
+	/*
+	 * If we just newlined ourselves past the end of the visible area
+	 * then scroll down so the cursor is in view.
+	 */
+	gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(widget),
+			gtk_text_buffer_get_insert(buffer),
+			0, FALSE, 0.0, 0.0);
+
+	return TRUE;
+}
+
+/*
+ * It's kind of a pain that we need this function and the above just
+ * to reinstate the default GtkTextView behavior.  It might be better
+ * if GtkIMHtml didn't intercept the enter key and just required the
+ * application to deal with it--it's really not much more work than it
+ * is to connect to the current "message_send" signal.
+ */
+void gtk_imhtml_set_return_inserts_newline(GtkIMHtml *imhtml)
+{
+	g_signal_connect(G_OBJECT(imhtml), "message_send",
+		G_CALLBACK(return_add_newline_cb), NULL);
+}
--- a/pidgin/gtkimhtml.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkimhtml.h	Sat Mar 07 01:59:40 2009 +0000
@@ -61,6 +61,11 @@
 typedef struct _GtkIMHtmlHr			GtkIMHtmlHr;
 typedef struct _GtkIMHtmlFuncs		GtkIMHtmlFuncs;
 
+/**
+ * @since 2.6.0
+ */
+typedef struct _GtkIMHtmlLink       GtkIMHtmlLink;
+
 typedef enum {
 	GTK_IMHTML_BOLD =       1 << 0,
 	GTK_IMHTML_ITALIC =     1 << 1,
@@ -156,6 +161,7 @@
 	gboolean (*message_send)(GtkIMHtml *);
 	void (*undo)(GtkIMHtml *);
 	void (*redo)(GtkIMHtml *);
+	GList *protocols; /* List of GtkIMHtmlProtocol's */
 };
 
 struct _GtkIMHtmlFontDetail {
@@ -885,6 +891,74 @@
  * @since 2.5.0
  */
 void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley);
+
+/**
+ * Register a protocol with the GtkIMHtml widget. Registering a protocol would
+ * allow certain text to be clickable.
+ *
+ * @param name      The name of the protocol (e.g. http://)
+ * @param activate  The callback to trigger when the protocol text is clicked.
+ *                  Removes any current protocol definition if @c NULL. The
+ *                  callback should return @c TRUE if the link was activated
+ *                  properly, @c FALSE otherwise.
+ * @param context_menu  The callback to trigger when the context menu is popped
+ *                      up on the protocol text. The callback should return
+ *                      @c TRUE if the request for context menu was processed
+ *                      successfully, @c FALSE otherwise.
+ *
+ * @return  @c TRUE if the protocol was successfully registered (or unregistered, when #activate is @c NULL)
+ *
+ * @since 2.6.0
+ */
+gboolean gtk_imhtml_class_register_protocol(const char *name,
+		gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link),
+		gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu));
+
+/**
+ * Get the URL associated with a link. This should be used by the IMHtml protocol-callbacks.
+ *
+ * @param link   The GtkIMHtmlLink object sent to the callback functions
+ *
+ * @return  The URL
+ *
+ * @since 2.6.0
+ */
+const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link);
+
+/**
+ * Get the GtkTextTag object (if any) associated with a particular link.
+ *
+ * @param link   The GtkIMHtmlLink object sent to the callback functions
+ *
+ * @return  The GtkTextTag object, or @c NULL
+ *
+ * @since 2.6.0
+ */
+const GtkTextTag *gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link);
+
+/**
+ * Activates a GtkIMHtmlLink object. This triggers the 'url-clicked' signal, marks the
+ * link as visited (when possible).
+ *
+ * @param link   The GtkIMHtmlLink object sent to the callback functions
+ *
+ * @return  @c TRUE if 'url-clicked' signal was emitted, @c FALSE otherwise.
+ *
+ * @since 2.6.0
+ */
+gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link);
+
+/**
+ * By default this widget intercepts presses of the "return" key and
+ * emits the "message_send" signal instead.  If you don't want this
+ * behavior, and you want the standard GtkTextView behavior of
+ * inserting a newline into the buffer, then call this function.
+ *
+ * @param imhtml The GtkIMHtml where you want the "return" key to add
+ *        newline and not emit the "message_send" signal.
+ */
+void gtk_imhtml_set_return_inserts_newline(GtkIMHtml *imhtml);
+
 /*@}*/
 
 #ifdef __cplusplus
--- a/pidgin/gtkimhtmltoolbar.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Sat Mar 07 01:59:40 2009 +0000
@@ -680,7 +680,7 @@
 	  is custom smiley-enabled */
 	if (supports_custom && psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
 		gchar tip[128];
-		g_snprintf(tip, sizeof(tip), 
+		g_snprintf(tip, sizeof(tip),
 			_("This smiley is disabled because a custom smiley exists for this shortcut:\n %s"),
 			face);
 		gtk_tooltips_set_tip(toolbar->tooltips, button, tip, NULL);
@@ -808,7 +808,7 @@
 			unique_smileys = g_slist_prepend(unique_smileys, smiley);
 		}
 	}
-	
+
 	/* we need to reverse the list to get the smileys in the correct order */
 	unique_smileys = g_slist_reverse(unique_smileys);
 
@@ -1413,13 +1413,13 @@
 	menuitem = gtk_menu_item_new_with_mnemonic(_("_Horizontal rule"));
 	g_signal_connect(G_OBJECT(menuitem), "activate"	, G_CALLBACK(insert_hr_cb), toolbar);
 	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
-	toolbar->insert_hr = menuitem;	
+	toolbar->insert_hr = menuitem;
 
 	g_signal_connect_swapped(G_OBJECT(insert_button), "button-press-event", G_CALLBACK(gtk_widget_activate), insert_button);
 	g_signal_connect(G_OBJECT(insert_button), "activate", G_CALLBACK(pidgin_menu_clicked), insert_menu);
 	g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button);
 	toolbar->sml = NULL;
-	
+
 	/* Sep */
 	sep = gtk_vseparator_new();
 	gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
--- a/pidgin/gtklog.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtklog.c	Sat Mar 07 01:59:40 2009 +0000
@@ -45,7 +45,7 @@
 
 struct log_viewer_hash_t {
 	PurpleLogType type;
-	char *screenname;
+	char *buddyname;
 	PurpleAccount *account;
 	PurpleContact *contact;
 };
@@ -57,7 +57,7 @@
 	if (viewer->contact != NULL)
 		return g_direct_hash(viewer->contact);
 
-	return g_str_hash(viewer->screenname) +
+	return g_str_hash(viewer->buddyname) +
 		g_str_hash(purple_account_get_username(viewer->account));
 }
 
@@ -80,9 +80,9 @@
 			return FALSE;
 	}
 
-	normal = g_strdup(purple_normalize(a->account, a->screenname));
+	normal = g_strdup(purple_normalize(a->account, a->buddyname));
 	ret = (a->account == b->account) &&
-		!strcmp(normal, purple_normalize(b->account, b->screenname));
+		!strcmp(normal, purple_normalize(b->account, b->buddyname));
 	g_free(normal);
 
 	return ret;
@@ -209,7 +209,7 @@
 		lv = g_hash_table_lookup(log_viewers, ht);
 		g_hash_table_remove(log_viewers, ht);
 
-		g_free(ht->screenname);
+		g_free(ht->buddyname);
 		g_free(ht);
 	} else
 		syslog_viewer = NULL;
@@ -556,7 +556,7 @@
 				if (!purple_prefs_get_bool("/purple/logging/log_chats"))
 					log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled.");
 			}
-			g_free(ht->screenname);
+			g_free(ht->buddyname);
 			g_free(ht);
 		}
 
@@ -681,27 +681,27 @@
 	return lv;
 }
 
-void pidgin_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account) {
+void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *account) {
 	struct log_viewer_hash_t *ht;
 	PidginLogViewer *lv = NULL;
-	const char *name = screenname;
+	const char *name = buddyname;
 	char *title;
 	GdkPixbuf *prpl_icon;
 
 	g_return_if_fail(account != NULL);
-	g_return_if_fail(screenname != NULL);
+	g_return_if_fail(buddyname != NULL);
 
 	ht = g_new0(struct log_viewer_hash_t, 1);
 
 	ht->type = type;
-	ht->screenname = g_strdup(screenname);
+	ht->buddyname = g_strdup(buddyname);
 	ht->account = account;
 
 	if (log_viewers == NULL) {
 		log_viewers = g_hash_table_new(log_viewer_hash, log_viewer_equal);
 	} else if ((lv = g_hash_table_lookup(log_viewers, ht))) {
 		gtk_window_present(GTK_WINDOW(lv->window));
-		g_free(ht->screenname);
+		g_free(ht->buddyname);
 		g_free(ht);
 		return;
 	}
@@ -709,7 +709,7 @@
 	if (type == PURPLE_LOG_CHAT) {
 		PurpleChat *chat;
 
-		chat = purple_blist_find_chat(account, screenname);
+		chat = purple_blist_find_chat(account, buddyname);
 		if (chat != NULL)
 			name = purple_chat_get_name(chat);
 
@@ -717,7 +717,7 @@
 	} else {
 		PurpleBuddy *buddy;
 
-		buddy = purple_find_buddy(account, screenname);
+		buddy = purple_find_buddy(account, buddyname);
 		if (buddy != NULL)
 			name = purple_buddy_get_contact_alias(buddy);
 
@@ -726,9 +726,9 @@
 
 	prpl_icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 
-	display_log_viewer(ht, purple_log_get_logs(type, screenname, account),
+	display_log_viewer(ht, purple_log_get_logs(type, buddyname, account),
 			title, gtk_image_new_from_pixbuf(prpl_icon),
-			purple_log_get_total_size(type, screenname, account));
+			purple_log_get_total_size(type, buddyname, account));
 
 	if (prpl_icon)
 		g_object_unref(prpl_icon);
@@ -760,13 +760,19 @@
 		return;
 	}
 
-	for (child = contact->node.child ; child ; child = child->next) {
+	for (child = purple_blist_node_get_first_child((PurpleBlistNode*)contact) ;
+	     child != NULL ;
+	     child = purple_blist_node_get_sibling_next(child)) {
+		const char *buddy_name;
+		PurpleAccount *account;
+
 		if (!PURPLE_BLIST_NODE_IS_BUDDY(child))
 			continue;
 
-		logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, ((PurpleBuddy *)child)->name,
-						((PurpleBuddy *)child)->account), logs);
-		total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, ((PurpleBuddy *)child)->name, ((PurpleBuddy *)child)->account);
+		buddy_name = purple_buddy_get_name((PurpleBuddy *)child);
+		account = purple_buddy_get_account((PurpleBuddy *)child);
+		logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, buddy_name, account), logs);
+		total_log_size += purple_log_get_total_size(PURPLE_LOG_IM, buddy_name, account);
 	}
 	logs = g_list_sort(logs, purple_log_compare);
 
--- a/pidgin/gtklog.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtklog.h	Sat Mar 07 01:59:40 2009 +0000
@@ -9,7 +9,7 @@
  * 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
@@ -53,7 +53,7 @@
 
 
 
-void pidgin_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account);
+void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *account);
 void pidgin_log_show_contact(PurpleContact *contact);
 
 void pidgin_syslog_show(void);
--- a/pidgin/gtkmain.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkmain.c	Sat Mar 07 01:59:40 2009 +0000
@@ -310,6 +310,7 @@
 	pidgin_log_init();
 	pidgin_docklet_init();
 	pidgin_smileys_init();
+	pidgin_utils_init();
 }
 
 static GHashTable *ui_info = NULL;
@@ -323,6 +324,7 @@
 #endif
 
 	/* Uninit */
+	pidgin_utils_uninit();
 	pidgin_smileys_uninit();
 	pidgin_conversations_uninit();
 	pidgin_status_uninit();
@@ -385,6 +387,7 @@
 		       "Usage: %s [OPTION]...\n\n"
 		       "  -c, --config=DIR    use DIR for config files\n"
 		       "  -d, --debug         print debugging messages to stdout\n"
+		       "  -f, --force-online  force online, regardless of network status\n"
 		       "  -h, --help          display this help and exit\n"
 		       "  -m, --multiple      do not ensure single instance\n"
 		       "  -n, --nologin       don't automatically login\n"
@@ -398,6 +401,7 @@
 		       "Usage: %s [OPTION]...\n\n"
 		       "  -c, --config=DIR    use DIR for config files\n"
 		       "  -d, --debug         print debugging messages to stdout\n"
+		       "  -f, --force-online  force online, regardless of network status\n"
 		       "  -h, --help          display this help and exit\n"
 		       "  -m, --multiple      do not ensure single instance\n"
 		       "  -n, --nologin       don't automatically login\n"
@@ -457,10 +461,10 @@
 int main(int argc, char *argv[])
 #endif
 {
+	gboolean opt_force_online = FALSE;
 	gboolean opt_help = FALSE;
 	gboolean opt_login = FALSE;
 	gboolean opt_nologin = FALSE;
-	gboolean opt_nocrash = FALSE;
 	gboolean opt_version = FALSE;
 	gboolean opt_si = TRUE;     /* Check for single instance? */
 	char *opt_config_dir_arg = NULL;
@@ -485,17 +489,17 @@
 	GList *active_accounts;
 
 	struct option long_options[] = {
-		{"config",   required_argument, NULL, 'c'},
-		{"debug",    no_argument,       NULL, 'd'},
-		{"help",     no_argument,       NULL, 'h'},
-		{"login",    optional_argument, NULL, 'l'},
-		{"multiple", no_argument,       NULL, 'm'},
-		{"nologin",  no_argument,       NULL, 'n'},
-		{"nocrash",  no_argument,       NULL, 'x'},
-		{"session",  required_argument, NULL, 's'},
-		{"version",  no_argument,       NULL, 'v'},
-		{"display",  required_argument, NULL, 'D'},
-		{"sync",     no_argument,       NULL, 'S'},
+		{"config",       required_argument, NULL, 'c'},
+		{"debug",        no_argument,       NULL, 'd'},
+		{"force-online", no_argument,       NULL, 'd'},
+		{"help",         no_argument,       NULL, 'h'},
+		{"login",        optional_argument, NULL, 'l'},
+		{"multiple",     no_argument,       NULL, 'm'},
+		{"nologin",      no_argument,       NULL, 'n'},
+		{"session",      required_argument, NULL, 's'},
+		{"version",      no_argument,       NULL, 'v'},
+		{"display",      required_argument, NULL, 'D'},
+		{"sync",         no_argument,       NULL, 'S'},
 		{0, 0, 0, 0}
 	};
 
@@ -602,9 +606,9 @@
 	opterr = 1;
 	while ((opt = getopt_long(argc, argv,
 #ifndef _WIN32
-				  "c:dhmnl::s:v",
+				  "c:dfhmnl::s:v",
 #else
-				  "c:dhmnl::v",
+				  "c:dfhmnl::v",
 #endif
 				  long_options, NULL)) != -1) {
 		switch (opt) {
@@ -615,6 +619,9 @@
 		case 'd':	/* debug */
 			debug_enabled = TRUE;
 			break;
+		case 'f':	/* force-online */
+			opt_force_online = TRUE;
+			break;
 		case 'h':	/* help */
 			opt_help = TRUE;
 			break;
@@ -637,9 +644,6 @@
 		case 'm':   /* do not ensure single instance. */
 			opt_si = FALSE;
 			break;
-		case 'x':   /* --nocrash */
-			opt_nocrash = TRUE;
-			break;
 		case 'D':   /* --display */
 		case 'S':   /* --sync */
 			/* handled by gtk_init_check below */
@@ -816,6 +820,11 @@
 		opt_config_dir_arg = NULL;
 	}
 
+	/* This needs to be before purple_blist_show() so the
+	 * statusbox gets the forced online status. */
+	if (opt_force_online)
+		purple_network_force_online();
+
 	/*
 	 * We want to show the blist early in the init process so the
 	 * user feels warm and fuzzy (not cold and prickley).
--- a/pidgin/gtkpluginpref.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkpluginpref.c	Sat Mar 07 01:59:40 2009 +0000
@@ -93,7 +93,7 @@
 		case PURPLE_PLUGIN_PREF_NONE:
 		default:
 			if (format == PURPLE_STRING_FORMAT_TYPE_NONE)
-			{				
+			{
 				entry = gtk_entry_new();
 				gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_string(pref_name));
 				gtk_entry_set_max_length(GTK_ENTRY(entry),
--- a/pidgin/gtkpounce.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkpounce.c	Sat Mar 07 01:59:40 2009 +0000
@@ -535,7 +535,7 @@
 
 	/* Create the window. */
 	dialog->window = window = gtk_dialog_new();
-	gtk_window_set_title(GTK_WINDOW(window), (cur_pounce == NULL ? _("New Buddy Pounce") : _("Edit Buddy Pounce")));
+	gtk_window_set_title(GTK_WINDOW(window), (cur_pounce == NULL ? _("Add Buddy Pounce") : _("Modify Buddy Pounce")));
 	gtk_window_set_role(GTK_WINDOW(window), "buddy_pounce");
 	gtk_container_set_border_width(GTK_CONTAINER(dialog->window), PIDGIN_HIG_BORDER);
 
@@ -637,19 +637,19 @@
 					 GTK_FILL, 0, 0, 0);
 	gtk_table_attach(GTK_TABLE(table), dialog->signoff,      0, 1, 2, 3,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->away,         0, 1, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->away,         1, 2, 0, 1,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->away_return,  0, 1, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->away_return,  1, 2, 1, 2,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->idle,         1, 2, 0, 1,
+	gtk_table_attach(GTK_TABLE(table), dialog->idle,         1, 2, 2, 3,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->idle_return,  1, 2, 1, 2,
+	gtk_table_attach(GTK_TABLE(table), dialog->idle_return,  2, 3, 0, 1,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->typing,       1, 2, 2, 3,
+	gtk_table_attach(GTK_TABLE(table), dialog->typing,       2, 3, 1, 2,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->typed,        1, 2, 3, 4,
+	gtk_table_attach(GTK_TABLE(table), dialog->typed,        2, 3, 2, 3,
 					 GTK_FILL, 0, 0, 0);
-	gtk_table_attach(GTK_TABLE(table), dialog->stop_typing,  1, 2, 4, 5,
+	gtk_table_attach(GTK_TABLE(table), dialog->stop_typing,  3, 4, 0, 1,
 					 GTK_FILL, 0, 0, 0);
 
 	gtk_widget_show(dialog->signon);
--- a/pidgin/gtkpounce.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkpounce.h	Sat Mar 07 01:59:40 2009 +0000
@@ -8,7 +8,7 @@
  * 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
--- a/pidgin/gtkprefs.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkprefs.c	Sat Mar 07 01:59:40 2009 +0000
@@ -35,6 +35,8 @@
 #include "request.h"
 #include "savedstatuses.h"
 #include "sound.h"
+#include "sound-theme.h"
+#include "theme-manager.h"
 #include "util.h"
 #include "network.h"
 
@@ -47,6 +49,7 @@
 #include "gtkprefs.h"
 #include "gtksavedstatuses.h"
 #include "gtksound.h"
+#include "gtkstatus-icon-theme.h"
 #include "gtkthemes.h"
 #include "gtkutils.h"
 #include "pidginstock.h"
@@ -56,6 +59,8 @@
 #define PROXYUSER 2
 #define PROXYPASS 3
 
+#define PREFS_OPTIMAL_ICON_SIZE 32
+
 static int sound_row_sel = 0;
 static GtkWidget *prefsnotebook;
 
@@ -69,6 +74,12 @@
 static int notebook_page = 0;
 static GtkTreeRowReference *previous_smiley_row = NULL;
 
+static gboolean prefs_themes_unsorted = TRUE;
+static GtkListStore *prefs_sound_themes;
+static GtkListStore *prefs_blist_themes;
+static GtkListStore *prefs_status_icon_themes;
+
+
 /*
  * PROTOTYPES
  */
@@ -546,6 +557,212 @@
 	gtk_drag_finish(dc, FALSE, FALSE, t);
 }
 
+/* Rebuild the markup for the sound theme selection for "(Custom)" themes */
+static void
+pref_sound_generate_markup()
+{
+	gboolean print_custom, customized;
+	const gchar *name, *author, *description, *current_theme;
+	gchar *markup;
+	PurpleSoundTheme *theme;
+	GtkTreeIter iter;
+
+	customized = pidgin_sound_is_customized();
+	current_theme = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme");
+
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(prefs_sound_themes), &iter)) {
+		do {
+			gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &iter, 2, &name, -1);
+
+			print_custom = customized && g_str_equal(current_theme, name);
+
+			if (g_str_equal(name, ""))
+				markup = g_strdup_printf("<b>(Default)</b>%s%s - None\n<span foreground='dim grey'>The default Pidgin sound theme</span>",
+							 print_custom ? " " : "", print_custom ? "(Custom)" : "");
+			else {
+				theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(name, "sound"));
+				author = purple_theme_get_author(PURPLE_THEME(theme));
+				description = purple_theme_get_description(PURPLE_THEME(theme));
+
+				markup = g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>",
+							 name, print_custom ? " " : "", print_custom ? "(Custom)" : "",
+							 author != NULL ? " - " : "", author != NULL ? author : "", description != NULL ? description : "");
+			}
+
+			gtk_list_store_set(prefs_sound_themes, &iter, 1, markup, -1);
+
+			g_free(markup);
+
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(prefs_sound_themes), &iter));
+	}
+}
+
+/* adds the themes to the theme list from the manager so they can be sisplayed in prefs */
+static void
+prefs_themes_sort(PurpleTheme *theme)
+{
+	GdkPixbuf *pixbuf = NULL;
+	GtkTreeIter iter;
+	gchar *image_full = NULL, *markup;
+	const gchar *name, *author, *description;
+
+	if (PURPLE_IS_SOUND_THEME(theme)){
+
+		image_full = purple_theme_get_image_full(theme);
+		if (image_full != NULL){
+			pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
+			g_free(image_full);
+		} else pixbuf = NULL;
+
+		gtk_list_store_append(prefs_sound_themes, &iter);
+		gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, purple_theme_get_name(theme), -1);
+
+		if (pixbuf != NULL)
+			gdk_pixbuf_unref(pixbuf);
+
+	} else if (PIDGIN_IS_BLIST_THEME(theme) || PIDGIN_IS_STATUS_ICON_THEME(theme)){
+		GtkListStore *store;
+
+		if (PIDGIN_IS_BLIST_THEME(theme))
+			store = prefs_blist_themes;
+		else store = prefs_status_icon_themes;
+
+		image_full = purple_theme_get_image_full(theme);
+		if (image_full != NULL){
+			pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
+			g_free(image_full);
+		} else pixbuf = NULL;
+
+		name = purple_theme_get_name(theme);
+		author = purple_theme_get_author(theme);
+		description = purple_theme_get_description(theme);
+
+		markup = g_strdup_printf("<b>%s</b>%s%s\n<span foreground='dim grey'>%s</span>", name, author != NULL ? " - " : "",
+					 author != NULL ? author : "", description != NULL ? description : "");
+
+		gtk_list_store_append(store, &iter);
+		gtk_list_store_set(store, &iter, 0, pixbuf, 1, markup, 2, name, -1);
+
+		g_free(markup);
+		if (pixbuf != NULL)
+			gdk_pixbuf_unref(pixbuf);
+	}
+
+}
+
+/* init all the theme variables so that the themes can be sorted later and used by pref pages */
+static void
+prefs_themes_init()
+{
+	GdkPixbuf *pixbuf = NULL;
+	gchar *filename;
+	GtkTreeIter iter;
+
+	filename = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
+	pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
+	g_free(filename);
+
+	/* sound themes */
+	prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	gtk_list_store_append(prefs_sound_themes, &iter);
+	gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, "", -1);
+
+	/* blist themes */
+	prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	gtk_list_store_append(prefs_blist_themes, &iter);
+	gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>"
+								    "The default Pidgin buddy list theme</span>", 2, "", -1);
+
+	/* status icon themes */
+	prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	gtk_list_store_append(prefs_status_icon_themes, &iter);
+	gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>"
+								    "The default Pidgin status icon theme</span>", 2, "", -1);
+
+	gdk_pixbuf_unref(pixbuf);
+}
+
+/* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
+static GtkWidget *
+prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme)
+{
+	GtkWidget *combo_box;
+	GtkCellRenderer *cell_rend;
+	GtkTreeIter iter;
+	gchar *theme = NULL;
+	gboolean unset = TRUE;
+
+	g_return_val_if_fail(store != NULL && current_theme != NULL, NULL);
+
+	combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
+
+	cell_rend = gtk_cell_renderer_pixbuf_new();
+	gtk_cell_renderer_set_fixed_size(cell_rend, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE);
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL);
+
+	cell_rend = gtk_cell_renderer_text_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL);
+/*#if GTK_CHECK_VERSION(2,6,0)
+			g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+#endif*/
+
+	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
+		do {
+			gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1);
+
+			if (g_str_equal(current_theme, theme)) {
+				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
+				unset = FALSE;
+			}
+
+			g_free(theme);
+		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
+	}
+
+	if (unset)
+		gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
+
+	return combo_box;
+}
+
+/* sets the current sound theme */
+static void
+prefs_set_sound_theme_cb(GtkComboBox *combo_box, gpointer user_data)
+{
+	gint i;
+	gchar *pref;
+	gchar *new_theme;
+	gboolean success;
+	GtkTreeIter new_iter;
+
+	success = gtk_combo_box_get_active_iter(combo_box, &new_iter);
+	g_return_if_fail(success);
+
+	gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1);
+
+	purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme);
+
+	/* New theme removes all customization */
+	for(i=0; i <  PURPLE_NUM_SOUNDS; i++){
+		pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
+					pidgin_sound_get_event_option(i));
+		purple_prefs_set_path(pref, "");
+		g_free(pref);
+	}
+
+	/* gets rid of the "(Custom)" from the last selection */
+	pref_sound_generate_markup();
+
+	gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
+
+	g_free(new_theme);
+}
+
 /* Does same as normal sort, except "none" is sorted first */
 static gint pidgin_sort_smileys (GtkTreeModel	*model,
 						GtkTreeIter		*a,
@@ -922,6 +1139,40 @@
 	gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
 }
 
+/* sets the current buddy list theme */
+static void
+prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data)
+{
+	PidginBlistTheme *theme;
+	GtkTreeIter iter;
+	gchar *name = NULL;
+
+	g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter));
+	gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1);
+
+	theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist"));
+	g_free(name);
+
+	pidgin_blist_set_theme(theme);
+}
+
+/* sets the current icon theme */
+static void
+prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
+{
+	PidginStatusIconTheme *theme;
+	GtkTreeIter iter;
+	gchar *name = NULL;
+
+	g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter));
+	gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1);
+
+	theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon"));
+	g_free(name);
+
+	pidgin_stock_load_status_icon_theme(theme);
+}
+
 static GtkWidget *
 interface_page(void)
 {
@@ -929,6 +1180,7 @@
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
 	GtkWidget *label;
+	GtkWidget *combo_box;
 	GtkSizeGroup *sg;
 	GList *names = NULL;
 
@@ -937,6 +1189,19 @@
 
 	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
+	/* Buddy List Themes */
+	vbox = pidgin_make_frame(ret, _("Buddy List Theme"));
+
+	combo_box = prefs_build_theme_combo_box(prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"));
+	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_blist_theme_cb, NULL);
+
+	/* Status Icon Themes */
+	combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"));
+	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL);
+
+	/* System Tray */
 	vbox = pidgin_make_frame(ret, _("System Tray Icon"));
 	label = pidgin_prefs_dropdown(vbox, _("_Show system tray icon:"), PURPLE_PREF_STRING,
 					PIDGIN_PREFS_ROOT "/docklet/show",
@@ -1742,6 +2007,8 @@
 	g_free(pref);
 
 	gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
+
+	pref_sound_generate_markup();
 }
 
 static void
@@ -1764,6 +2031,8 @@
 	 */
 	if (sound == sound_row_sel)
 		gtk_entry_set_text(GTK_ENTRY(sound_entry), filename);
+
+	pref_sound_generate_markup();
 }
 
 static void select_sound(GtkWidget *button, gpointer being_NULL_is_fun)
@@ -1832,6 +2101,8 @@
 	if (sound_entry)
 		gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)"));
 	g_value_unset (&val);
+
+	pref_sound_generate_markup();
 }
 
 
@@ -1857,7 +2128,7 @@
 sound_page(void)
 {
 	GtkWidget *ret;
-	GtkWidget *vbox, *sw, *button;
+	GtkWidget *vbox, *sw, *button, *combo_box;
 	GtkSizeGroup *sg;
 	GtkTreeIter iter;
 	GtkWidget *event_view;
@@ -1951,7 +2222,6 @@
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
 								sound_changed2_cb, vbox);
 #endif
-
 	vbox = pidgin_make_frame(ret, _("Sound Events"));
 
 	/* The following is an ugly hack to make the frame expand so the
@@ -1963,6 +2233,14 @@
 	gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent->parent),
 			vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START);
 
+	/* SOUND THEMES */
+	combo_box = prefs_build_theme_combo_box(prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
+	pref_sound_generate_markup();
+	gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0);
+
+	g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL);
+
+	/* SOUND SELECTION */
 	sw = gtk_scrolled_window_new(NULL,NULL);
 	gtk_widget_set_size_request(sw, -1, 100);
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
@@ -2196,6 +2474,14 @@
 		return;
 	}
 
+	/* Refresh the list of themes before showing the preferences window */
+	purple_theme_manager_refresh();
+
+	/* add everything in the theme manager before the window is loaded */
+	if (prefs_themes_unsorted) {
+		purple_theme_manager_for_each_theme(prefs_themes_sort);
+		prefs_themes_unsorted = FALSE;
+	}
 	/* copy the preferences to tmp values...
 	 * I liked "take affect immediately" Oh well :-( */
 	/* (that should have been "effect," right?) */
@@ -2290,6 +2576,9 @@
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", "");
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder", "");
 
+	/* Themes */
+	prefs_themes_init();
+
 	/* Smiley Themes */
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys");
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
--- a/pidgin/gtkprefs.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkprefs.h	Sat Mar 07 01:59:40 2009 +0000
@@ -59,7 +59,7 @@
  * @param max   The maximum value of the spin button
  * @param sg    If not NULL, the size group to which the spin button will be added
  * @return      An hbox containing both the label and the spinner.  Can be
- *              used to set the widgets to sensitive or insensitive based on the 
+ *              used to set the widgets to sensitive or insensitive based on the
  *              value of a checkbox.
  */
 GtkWidget *pidgin_prefs_labeled_spin_button(GtkWidget *page,
--- a/pidgin/gtkrequest.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkrequest.c	Sat Mar 07 01:59:40 2009 +0000
@@ -384,6 +384,8 @@
 			gtk_imhtml_append_text(GTK_IMHTML(entry), default_value, GTK_IMHTML_NO_SCROLL);
 		gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 		gtk_widget_show(frame);
+
+		gtk_imhtml_set_return_inserts_newline(GTK_IMHTML(entry));
 	}
 	else {
 		if (multiline) {
@@ -682,15 +684,17 @@
 static void
 req_entry_field_changed_cb(GtkWidget *entry, PurpleRequestField *field)
 {
+	PurpleRequestFieldGroup *group;
 	PidginRequestData *req_data;
 	const char *text = gtk_entry_get_text(GTK_ENTRY(entry));
 
 	purple_request_field_string_set_value(field, (*text == '\0' ? NULL : text));
 
-	req_data = (PidginRequestData *)field->group->fields_list->ui_data;
+	group = purple_request_field_get_group(field);
+	req_data = (PidginRequestData *)group->fields_list->ui_data;
 
 	gtk_widget_set_sensitive(req_data->ok_button,
-		purple_request_fields_all_required_filled(field->group->fields_list));
+		purple_request_fields_all_required_filled(group->fields_list));
 }
 
 static void
@@ -711,7 +715,8 @@
 		if (purple_str_has_prefix(type_hint, "screenname"))
 		{
 			GtkWidget *optmenu = NULL;
-			GList *fields = field->group->fields;
+			PurpleRequestFieldGroup *group = purple_request_field_get_group(field);
+			GList *fields = group->fields;
 			while (fields)
 			{
 				PurpleRequestField *fld = fields->data;
@@ -722,9 +727,11 @@
 					const char *type_hint = purple_request_field_get_type_hint(fld);
 					if (type_hint != NULL && strcmp(type_hint, "account") == 0)
 					{
-						if (fld->ui_data == NULL)
-							fld->ui_data = create_account_field(fld);
-						optmenu = GTK_WIDGET(fld->ui_data);
+						optmenu = GTK_WIDGET(purple_request_field_get_ui_data(fld));
+						if (optmenu == NULL) {
+							optmenu = GTK_WIDGET(create_account_field(fld));
+							purple_request_field_set_ui_data(field, optmenu);
+						}
 						break;
 					}
 				}
@@ -1338,24 +1345,26 @@
 					gtk_widget_show(label);
 				}
 
-				if (field->ui_data != NULL)
-					widget = GTK_WIDGET(field->ui_data);
-				else if (type == PURPLE_REQUEST_FIELD_STRING)
-					widget = create_string_field(field);
-				else if (type == PURPLE_REQUEST_FIELD_INTEGER)
-					widget = create_int_field(field);
-				else if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
-					widget = create_bool_field(field);
-				else if (type == PURPLE_REQUEST_FIELD_CHOICE)
-					widget = create_choice_field(field);
-				else if (type == PURPLE_REQUEST_FIELD_LIST)
-					widget = create_list_field(field);
-				else if (type == PURPLE_REQUEST_FIELD_IMAGE)
-					widget = create_image_field(field);
-				else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
-					widget = create_account_field(field);
-				else
-					continue;
+				widget = GTK_WIDGET(purple_request_field_get_ui_data(field));
+				if (widget == NULL)
+				{
+					if (type == PURPLE_REQUEST_FIELD_STRING)
+						widget = create_string_field(field);
+					else if (type == PURPLE_REQUEST_FIELD_INTEGER)
+						widget = create_int_field(field);
+					else if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
+						widget = create_bool_field(field);
+					else if (type == PURPLE_REQUEST_FIELD_CHOICE)
+						widget = create_choice_field(field);
+					else if (type == PURPLE_REQUEST_FIELD_LIST)
+						widget = create_list_field(field);
+					else if (type == PURPLE_REQUEST_FIELD_IMAGE)
+						widget = create_image_field(field);
+					else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
+						widget = create_account_field(field);
+					else
+						continue;
+				}
 
 				if (label)
 					gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget);
@@ -1400,7 +1409,7 @@
 
 				gtk_widget_show(widget);
 
-				field->ui_data = widget;
+				purple_request_field_set_ui_data(field, widget);
 			}
 		}
 	}
--- a/pidgin/gtksavedstatuses.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksavedstatuses.c	Sat Mar 07 01:59:40 2009 +0000
@@ -76,7 +76,7 @@
 	STATUS_EDITOR_COLUMN_WINDOW,
 	STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS,
 	STATUS_EDITOR_COLUMN_ICON,
-	STATUS_EDITOR_COLUMN_SCREENNAME,
+	STATUS_EDITOR_COLUMN_USERNAME,
 	/** A hidden column containing the ID of this PurpleStatusType. */
 	STATUS_EDITOR_COLUMN_STATUS_ID,
 	STATUS_EDITOR_COLUMN_STATUS_NAME,
@@ -1007,7 +1007,7 @@
 	g_signal_connect(G_OBJECT(renderer), "toggled",
 			 G_CALLBACK(status_editor_substatus_cb), dialog);
 
-	/* Screen Name column */
+	/* Username column */
 	column = gtk_tree_view_column_new();
 	gtk_tree_view_column_set_resizable(column, TRUE);
 	gtk_tree_view_column_set_title(column, _("Username"));
@@ -1020,11 +1020,11 @@
 	gtk_tree_view_column_add_attribute(column, renderer, "pixbuf",
 									   STATUS_EDITOR_COLUMN_ICON);
 
-	/* Screen Name */
+	/* Username */
 	renderer = gtk_cell_renderer_text_new();
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer, "text",
-									   STATUS_EDITOR_COLUMN_SCREENNAME);
+									   STATUS_EDITOR_COLUMN_USERNAME);
 
 	/* Status column */
 	column = gtk_tree_view_column_new();
@@ -1086,7 +1086,7 @@
 			STATUS_EDITOR_COLUMN_ACCOUNT, account,
 			STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, (substatus != NULL),
 			STATUS_EDITOR_COLUMN_ICON, pixbuf,
-			STATUS_EDITOR_COLUMN_SCREENNAME, purple_account_get_username(account),
+			STATUS_EDITOR_COLUMN_USERNAME, purple_account_get_username(account),
 			STATUS_EDITOR_COLUMN_STATUS_ID, id,
 			STATUS_EDITOR_COLUMN_STATUS_NAME, name,
 			STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message,
@@ -1218,6 +1218,8 @@
 	gtk_container_set_focus_chain(GTK_CONTAINER(hbox), focus_chain);
 	g_list_free(focus_chain);
 
+	gtk_imhtml_set_return_inserts_newline(dialog->message);
+
 	if ((saved_status != NULL) && (purple_savedstatus_get_message(saved_status) != NULL))
 		gtk_imhtml_append_text(GTK_IMHTML(text),
 							   purple_savedstatus_get_message(saved_status), 0);
@@ -1547,7 +1549,7 @@
 	/* Seed the input widgets with the current values */
 
 	/* Only look at the saved status if we can't find it in the parent status dialog's substatuses model */
-	gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter, 
+	gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
 		STATUS_EDITOR_COLUMN_ENABLE_SUBSTATUS, &parent_dialog_has_substatus, -1);
 	if (parent_dialog_has_substatus) {
 		gtk_tree_model_get(GTK_TREE_MODEL(status_editor->model), &iter,
@@ -1656,7 +1658,7 @@
 	 * And whether or not that emblem is visible
 	 */
 	SS_MENU_EMBLEM_VISIBLE_COLUMN,
-	
+
 	SS_MENU_NUM_COLUMNS
 };
 
--- a/pidgin/gtkscrollbook.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkscrollbook.h	Sat Mar 07 01:59:40 2009 +0000
@@ -51,7 +51,7 @@
 	GtkWidget *left_arrow;
 	GtkWidget *right_arrow;
 	GList *children;
-	
+
 	/* Padding for future expansion */
 	void (*_gtk_reserved1) (void);
 	void (*_gtk_reserved2) (void);
--- a/pidgin/gtksession.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksession.c	Sat Mar 07 01:59:40 2009 +0000
@@ -108,7 +108,7 @@
 	purple_debug(PURPLE_DEBUG_INFO, NULL, "done.\n");
 }
 
-/* We call any handler installed before (or after) ice_init but 
+/* We call any handler installed before (or after) ice_init but
  * avoid calling the default libICE handler which does an exit().
  *
  * This means we do nothing by default, which is probably correct,
--- a/pidgin/gtksmiley.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksmiley.c	Sat Mar 07 01:59:40 2009 +0000
@@ -225,6 +225,12 @@
 
 	entry = gtk_entry_get_text(GTK_ENTRY(s->smile));
 	if (!entry || !*entry) {
+		/*
+		 * TODO: We should enable/disable the add button based on
+		 *       whether the user has entered all required data.  That
+		 *       would eliminate the need for this check and provide a
+		 *       better user experience.
+		 */
 		purple_notify_error(s->parent, _("Custom Smiley"),
 				_("More Data needed"),
 				_("Please provide a shortcut to associate with the smiley."));
@@ -233,9 +239,12 @@
 
 	emoticon = purple_smileys_find_by_shortcut(entry);
 	if (emoticon && emoticon != s->smiley) {
+		gchar *msg;
+		msg = g_strdup_printf(_("A custom smiley for '%s' already exists.  "
+				"Please use a different shortcut."), entry);
 		purple_notify_error(s->parent, _("Custom Smiley"),
-				_("Duplicate Shortcut"),
-				_("A custom smiley for the selected shortcut already exists. Please specify a different shortcut."));
+				_("Duplicate Shortcut"), msg);
+		g_free(msg);
 		return;
 	}
 
@@ -273,7 +282,7 @@
 			gsize size = 0;
 			gchar *filename;
 			const gchar *dirname = purple_smileys_get_storing_dir();
-			
+
 			/* since this may be called before purple_smiley_new_* has ever been
 			 called, we create the storing dir, if it doesn't exist yet, to be
 			 able to save the pixbuf before adding the smiley */
@@ -286,7 +295,7 @@
 			                   dirname, g_strerror(errno));
 				}
 			}
-			
+
 			gdk_pixbuf_save_to_buffer(s->custom_pixbuf, &buffer, &size,
 				"png", NULL, "compression", "9", NULL, NULL);
 			filename = purple_util_get_image_filename(buffer, size);
@@ -385,7 +394,7 @@
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox);
 
-	label = gtk_label_new_with_mnemonic(_("Smiley _Image"));
+	label = gtk_label_new_with_mnemonic(_("_Image:"));
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 	gtk_widget_show(label);
 
@@ -415,8 +424,8 @@
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)),hbox);
 
-	/* Smiley shortcut */
-	label = gtk_label_new_with_mnemonic(_("Smiley S_hortcut"));
+	/* Shortcut text */
+	label = gtk_label_new_with_mnemonic(_("S_hortcut text:"));
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 	gtk_widget_show(label);
 
@@ -520,9 +529,9 @@
 	gtk_tree_view_column_pack_start(column, rend, FALSE);
 	gtk_tree_view_column_add_attribute(column, rend, "pixbuf", ICON);
 
-	/* Shortcut */
+	/* Shortcut Text */
 	column = gtk_tree_view_column_new();
-	gtk_tree_view_column_set_title(column, _("Shortcut"));
+	gtk_tree_view_column_set_title(column, _("Shortcut Text"));
 	gtk_tree_view_column_set_resizable(column, TRUE);
 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
 
@@ -712,7 +721,7 @@
 			_("Custom Smiley Manager"),
 			NULL,
 			GTK_DIALOG_DESTROY_WITH_PARENT,
-			GTK_STOCK_ADD, GTK_RESPONSE_YES,
+			PIDGIN_STOCK_ADD, GTK_RESPONSE_YES,
 			PIDGIN_STOCK_MODIFY, PIDGIN_RESPONSE_MODIFY,
 			GTK_STOCK_DELETE, GTK_RESPONSE_NO,
 			GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
@@ -740,4 +749,3 @@
 
 	gtk_widget_show(win);
 }
-
--- a/pidgin/gtksmiley.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksmiley.h	Sat Mar 07 01:59:40 2009 +0000
@@ -25,8 +25,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#ifndef _PIDGIN_GTKSMILEY_H_
-#define _PIDGIN_GTKSMILEY_H_
+#ifndef PIDGIN_GTKSMILEY_H
+#define PIDGIN_GTKSMILEY_H
 
 #include "smiley.h"
 
@@ -62,7 +62,7 @@
  *
  * @constreturn A GtkIMHmlSmiley list
  */
-GSList* pidgin_smileys_get_all(void);
+GSList *pidgin_smileys_get_all(void);
 
 /******************************************************************************
  * Smiley Manager
@@ -100,4 +100,4 @@
  */
 void pidgin_smiley_editor_set_image(PidginSmiley *editor, GdkPixbuf *image);
 
-#endif /* _PIDGIN_GTKSMILEY_H_*/
+#endif /* PIDGIN_GTKSMILEY_H */
--- a/pidgin/gtksound.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksound.c	Sat Mar 07 01:59:40 2009 +0000
@@ -40,6 +40,8 @@
 #include "notify.h"
 #include "prefs.h"
 #include "sound.h"
+#include "sound-theme.h"
+#include "theme-manager.h"
 #include "util.h"
 
 #include "gtkconv.h"
@@ -294,6 +296,7 @@
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/nick_said", "");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/pounce_default", TRUE);
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/pounce_default", "");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/sound/theme", "");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/conv_focus", TRUE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE);
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/command", "");
@@ -557,6 +560,8 @@
 {
 	char *enable_pref;
 	char *file_pref;
+	const char *theme_name;
+	PurpleSoundTheme *theme;
 
 	if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds)
 		return;
@@ -573,13 +578,31 @@
 	/* check NULL for sounds that don't have an option, ie buddy pounce */
 	if (purple_prefs_get_bool(enable_pref)) {
 		char *filename = g_strdup(purple_prefs_get_path(file_pref));
-		if(!filename || !strlen(filename)) {
+		theme_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme");
+
+		if (theme_name && *theme_name && (!filename || !*filename)) {
+			/* Use theme */
 			g_free(filename);
+
+			theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(theme_name, "sound"));
+			filename = purple_sound_theme_get_file_full(theme, sounds[event].pref);
+
+			if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)){ /* Use Default sound in this case */
+				purple_debug_error("sound", "The file: (%s) %s\n from theme: %s, was not found or wasn't readable\n",
+							sounds[event].pref, filename, theme_name);
+				g_free(filename);
+			}
+		}
+
+		if (!filename || !strlen(filename)) {			    /* Use Default sounds */
+			g_free(filename);
+
 			/* XXX Consider creating a constant for "sounds/purple" to be shared with Finch */
 			filename = g_build_filename(DATADIR, "sounds", "purple", sounds[event].def, NULL);
 		}
 
 		purple_sound_play_file(filename, NULL);
+
 		g_free(filename);
 	}
 
@@ -587,6 +610,29 @@
 	g_free(file_pref);
 }
 
+gboolean
+pidgin_sound_is_customized(void)
+{
+	gint i;
+	gchar *path, *file;
+
+	for (i = 0; i < PURPLE_NUM_SOUNDS; i++) {
+		path = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[i].pref);
+		file = g_strdup(purple_prefs_get_path(path));
+		g_free(path);
+
+		if (file && file[0] != '\0'){
+			g_free(file);
+			return TRUE;
+		}
+
+		g_free(file);
+	}
+
+	return FALSE;
+
+}
+
 static PurpleSoundUiOps sound_ui_ops =
 {
 	pidgin_sound_init,
--- a/pidgin/gtksound.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksound.h	Sat Mar 07 01:59:40 2009 +0000
@@ -63,6 +63,15 @@
  */
 void *pidgin_sound_get_handle(void);
 
+/**
+ * Returns true Pidgin is using customized sounds
+ *
+ * @return TRUE if non default sounds are used.
+ *
+ * @since 2.6.0
+ */
+gboolean pidgin_sound_is_customized(void);
+
 /*@}*/
 
 #endif /* _PIDGINSOUND_H_ */
--- a/pidgin/gtksourceiter.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksourceiter.c	Sat Mar 07 01:59:40 2009 +0000
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- 
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *  gtksourceiter.c
  *
  *  Pidgin is the legal property of its developers, whose names are too numerous
@@ -7,7 +7,7 @@
  *
  *  The following copyright notice applies to this file:
  *
- *  Copyright (C) 2000 - 2005 Paolo Maggi 
+ *  Copyright (C) 2000 - 2005 Paolo Maggi
  *  Copyright (C) 2002, 2003 Jeroen Zwartepoorte
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -206,7 +206,7 @@
 
 finally_2:
 	g_free (normalized_s1);
-	g_free (normalized_s2);	
+	g_free (normalized_s2);
 
 	return ret;
 }
@@ -247,7 +247,7 @@
 		{
 			/* being UTF8 correct sucks; this accounts for extra
 			   offsets coming from canonical decompositions of
-			   UTF8 characters (e.g. accented characters) which 
+			   UTF8 characters (e.g. accented characters) which
 			   g_utf8_normalize() performs */
 			gchar *normal;
 			gchar buffer[6];
@@ -530,14 +530,14 @@
  * @match_start: return location for start of match, or %%NULL.
  * @match_end: return location for end of match, or %%NULL.
  * @limit: bound for the search, or %%NULL for the end of the buffer.
- * 
- * Searches forward for @str. Any match is returned by setting 
- * @match_start to the first character of the match and @match_end to the 
+ *
+ * Searches forward for @str. Any match is returned by setting
+ * @match_start to the first character of the match and @match_end to the
  * first character after the match. The search will not continue past
  * @limit. Note that a search is a linear or O(n) operation, so you
  * may wish to use @limit to avoid locking up your UI on large
  * buffers.
- * 
+ *
  * If the #GTK_SOURCE_SEARCH_VISIBLE_ONLY flag is present, the match may
  * have invisible text interspersed in @str. i.e. @str will be a
  * possibly-noncontiguous subsequence of the matched range. similarly,
@@ -550,7 +550,7 @@
  *
  * Same as gtk_text_iter_forward_search(), but supports case insensitive
  * searching.
- * 
+ *
  * Return value: whether a match was found.
  **/
 gboolean
@@ -574,7 +574,7 @@
 	if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0)
 		return gtk_text_iter_forward_search (iter, str, flags,
 						     match_start, match_end,
-						     limit); 
+						     limit);
 
 	if (limit && gtk_text_iter_compare (iter, limit) >= 0)
 		return FALSE;
@@ -650,10 +650,10 @@
  * @match_start: return location for start of match, or %%NULL.
  * @match_end: return location for end of match, or %%NULL.
  * @limit: location of last possible @match_start, or %%NULL for start of buffer.
- * 
+ *
  * Same as gtk_text_iter_backward_search(), but supports case insensitive
  * searching.
- * 
+ *
  * Return value: whether a match was found.
  **/
 gboolean
@@ -677,7 +677,7 @@
 	if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0)
 		return gtk_text_iter_backward_search (iter, str, flags,
 						      match_start, match_end,
-						      limit); 
+						      limit);
 
 	if (limit && gtk_text_iter_compare (iter, limit) <= 0)
 		return FALSE;
--- a/pidgin/gtksourceiter.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksourceiter.h	Sat Mar 07 01:59:40 2009 +0000
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- 
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *  gtksourceiter.h
  *
  *  Pidgin is the legal property of its developers, whose names are too numerous
@@ -7,7 +7,7 @@
  *
  *  The following copyright notice applies to this file:
  *
- *  Copyright (C) 2000 - 2005 Paolo Maggi 
+ *  Copyright (C) 2000 - 2005 Paolo Maggi
  *  Copyright (C) 2002, 2003 Jeroen Zwartepoorte
  *
  *  This program is free software; you can redistribute it and/or modify
--- a/pidgin/gtksourceundomanager.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksourceundomanager.c	Sat Mar 07 01:59:40 2009 +0000
@@ -4,8 +4,8 @@
  * This file is part of GtkSourceView
  *
  * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
- * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi 
- * Copyright (C) 2002-2005  Paolo Maggi 
+ * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
+ * Copyright (C) 2002-2005  Paolo Maggi
  *
  * 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
@@ -19,7 +19,7 @@
  *
  * 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, 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02111-1301, USA.
  */
 
@@ -49,14 +49,14 @@
 	GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR,
 } GtkSourceUndoActionType;
 
-/* 
+/*
  * We use offsets instead of GtkTextIters because the last ones
  * require to much memory in this context without giving us any advantage.
- */ 
+ */
 
 struct _GtkSourceUndoInsertAction
 {
-	gint   pos; 
+	gint   pos;
 	gchar *text;
 	gint   length;
 	gint   chars;
@@ -79,7 +79,7 @@
 struct _GtkSourceUndoAction
 {
 	GtkSourceUndoActionType action_type;
-	
+
 	union {
 		GtkSourceUndoInsertAction  insert;
 		GtkSourceUndoDeleteAction  delete;
@@ -92,7 +92,7 @@
 	guint mergeable : 1;
 
 	/* It is TRUE whether the action is marked as "modified".
-	 * An action is marked as "modified" if it changed the 
+	 * An action is marked as "modified" if it changed the
 	 * state of the buffer from "not modified" to "modified". Only the first
 	 * action of a group can be marked as modified.
 	 * There can be a single action marked as "modified" in the actions list.
@@ -106,29 +106,29 @@
 struct _GtkSourceUndoManagerPrivate
 {
 	GtkTextBuffer	*document;
-	
+
 	GList*		 actions;
-	gint 		 next_redo;	
+	gint 		 next_redo;
 
 	gint 		 actions_in_current_group;
-	
+
 	gint		 running_not_undoable_actions;
 
 	gint		 num_of_groups;
 
 	gint		 max_undo_levels;
-	
+
 	guint	 	 can_undo : 1;
 	guint		 can_redo : 1;
-	
+
 	/* It is TRUE whether, while undoing an action of the current group (with order_in_group > 1),
 	 * the state of the buffer changed from "not modified" to "modified".
 	 */
-	guint	 	 modified_undoing_group : 1;	
+	guint	 	 modified_undoing_group : 1;
 
 	/* Pointer to the action (in the action list) marked as "modified".
-	 * It is NULL when no action is marked as "modified". 
-	 * It is INVALID when the action marked as "modified" has been removed 
+	 * It is NULL when no action is marked as "modified".
+	 * It is INVALID when the action marked as "modified" has been removed
 	 * from the action list (freeing the list or resizing it) */
 	GtkSourceUndoAction *modified_action;
 };
@@ -143,33 +143,33 @@
 static void gtk_source_undo_manager_init 			(GtkSourceUndoManager 		*um);
 static void gtk_source_undo_manager_finalize 			(GObject 			*object);
 
-static void gtk_source_undo_manager_insert_text_handler 	(GtkTextBuffer 			*buffer, 
+static void gtk_source_undo_manager_insert_text_handler 	(GtkTextBuffer 			*buffer,
 							 	 GtkTextIter 			*pos,
-		                             		 	 const 	gchar 			*text, 
-							 	 gint 				 length, 
+		                             		 	 const 	gchar 			*text,
+							 	 gint 				 length,
 							 	 GtkSourceUndoManager 		*um);
 static void gtk_source_undo_manager_insert_anchor_handler (GtkTextBuffer *buffer,
 			                   GtkTextIter            *pos,
 			                   GtkTextChildAnchor     *anchor,
 			                   GtkSourceUndoManager   *um);
-static void gtk_source_undo_manager_delete_range_handler 	(GtkTextBuffer 			*buffer, 
+static void gtk_source_undo_manager_delete_range_handler 	(GtkTextBuffer 			*buffer,
 							 	 GtkTextIter 			*start,
                         		      		 	 GtkTextIter 			*end,
 							 	 GtkSourceUndoManager 		*um);
-static void gtk_source_undo_manager_begin_user_action_handler 	(GtkTextBuffer 			*buffer, 
+static void gtk_source_undo_manager_begin_user_action_handler 	(GtkTextBuffer 			*buffer,
 								 GtkSourceUndoManager 		*um);
 static void gtk_source_undo_manager_modified_changed_handler	(GtkTextBuffer                  *buffer,
 								 GtkSourceUndoManager           *um);
 
 static void gtk_source_undo_manager_free_action_list 		(GtkSourceUndoManager 		*um);
 
-static void gtk_source_undo_manager_add_action 			(GtkSourceUndoManager 		*um, 
+static void gtk_source_undo_manager_add_action 			(GtkSourceUndoManager 		*um,
 		                                         	 const GtkSourceUndoAction 	*undo_action);
-static void gtk_source_undo_manager_free_first_n_actions 	(GtkSourceUndoManager 		*um, 
+static void gtk_source_undo_manager_free_first_n_actions 	(GtkSourceUndoManager 		*um,
 								 gint 				 n);
 static void gtk_source_undo_manager_check_list_size 		(GtkSourceUndoManager 		*um);
 
-static gboolean gtk_source_undo_manager_merge_action 		(GtkSourceUndoManager 		*um, 
+static gboolean gtk_source_undo_manager_merge_action 		(GtkSourceUndoManager 		*um,
 		                                        	 const GtkSourceUndoAction 	*undo_action);
 
 static GObjectClass 	*parent_class 				= NULL;
@@ -216,7 +216,7 @@
 
         klass->can_undo 	= NULL;
 	klass->can_redo 	= NULL;
-	
+
 	undo_manager_signals[CAN_UNDO] =
    		g_signal_new ("can_undo",
 			      G_OBJECT_CLASS_TYPE (object_class),
@@ -269,7 +269,7 @@
 
 	g_return_if_fail (object != NULL);
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (object));
-	
+
    	um = GTK_SOURCE_UNDO_MANAGER (object);
 
 	g_return_if_fail (um->priv != NULL);
@@ -280,19 +280,19 @@
 	}
 
 	g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
-			  G_CALLBACK (gtk_source_undo_manager_delete_range_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_delete_range_handler),
 			  um);
 
 	g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
-			  G_CALLBACK (gtk_source_undo_manager_insert_text_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_insert_text_handler),
 			  um);
-	
+
 	g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
-			  G_CALLBACK (gtk_source_undo_manager_insert_anchor_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_insert_anchor_handler),
 			  um);
-	
+
 	g_signal_handlers_disconnect_by_func (G_OBJECT (um->priv->document),
-			  G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler),
 			  um);
 
 	g_free (um->priv);
@@ -311,19 +311,19 @@
   	um->priv->document = buffer;
 
 	g_signal_connect (G_OBJECT (buffer), "insert_text",
-			  G_CALLBACK (gtk_source_undo_manager_insert_text_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_insert_text_handler),
 			  um);
 
 	g_signal_connect (G_OBJECT (buffer), "insert_child_anchor",
-			  G_CALLBACK (gtk_source_undo_manager_insert_anchor_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_insert_anchor_handler),
 			  um);
 
 	g_signal_connect (G_OBJECT (buffer), "delete_range",
-			  G_CALLBACK (gtk_source_undo_manager_delete_range_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_delete_range_handler),
 			  um);
 
 	g_signal_connect (G_OBJECT (buffer), "begin_user_action",
-			  G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler), 
+			  G_CALLBACK (gtk_source_undo_manager_begin_user_action_handler),
 			  um);
 
 	g_signal_connect (G_OBJECT (buffer), "modified_changed",
@@ -332,7 +332,7 @@
 	return um;
 }
 
-void 
+void
 gtk_source_undo_manager_begin_not_undoable_action (GtkSourceUndoManager *um)
 {
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
@@ -341,18 +341,18 @@
 	++um->priv->running_not_undoable_actions;
 }
 
-static void 
+static void
 gtk_source_undo_manager_end_not_undoable_action_internal (GtkSourceUndoManager *um)
 {
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
 	g_return_if_fail (um->priv != NULL);
 
 	g_return_if_fail (um->priv->running_not_undoable_actions > 0);
-	
+
 	--um->priv->running_not_undoable_actions;
 }
 
-void 
+void
 gtk_source_undo_manager_end_not_undoable_action (GtkSourceUndoManager *um)
 {
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
@@ -361,26 +361,26 @@
 	gtk_source_undo_manager_end_not_undoable_action_internal (um);
 
 	if (um->priv->running_not_undoable_actions == 0)
-	{	
+	{
 		gtk_source_undo_manager_free_action_list (um);
-	
-		um->priv->next_redo = -1;	
+
+		um->priv->next_redo = -1;
 
 		if (um->priv->can_undo)
 		{
 			um->priv->can_undo = FALSE;
-			g_signal_emit (G_OBJECT (um), 
-				       undo_manager_signals [CAN_UNDO], 
-				       0, 
+			g_signal_emit (G_OBJECT (um),
+				       undo_manager_signals [CAN_UNDO],
+				       0,
 				       FALSE);
 		}
 
 		if (um->priv->can_redo)
 		{
 			um->priv->can_redo = FALSE;
-			g_signal_emit (G_OBJECT (um), 
-				       undo_manager_signals [CAN_REDO], 
-				       0, 
+			g_signal_emit (G_OBJECT (um),
+				       undo_manager_signals [CAN_REDO],
+				       0,
 				       FALSE);
 		}
 	}
@@ -395,7 +395,7 @@
 	return um->priv->can_undo;
 }
 
-gboolean 
+gboolean
 gtk_source_undo_manager_can_redo (const GtkSourceUndoManager *um)
 {
 	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
@@ -408,31 +408,31 @@
 set_cursor (GtkTextBuffer *buffer, gint cursor)
 {
 	GtkTextIter iter;
-	
+
 	/* Place the cursor at the requested position */
 	gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor);
 	gtk_text_buffer_place_cursor (buffer, &iter);
 }
 
-static void 
+static void
 insert_text (GtkTextBuffer *buffer, gint pos, const gchar *text, gint len)
 {
 	GtkTextIter iter;
-	
+
 	gtk_text_buffer_get_iter_at_offset (buffer, &iter, pos);
 	gtk_text_buffer_insert (buffer, &iter, text, len);
 }
 
-static void 
+static void
 insert_anchor (GtkTextBuffer *buffer, gint pos, GtkTextChildAnchor *anchor)
 {
 	GtkTextIter iter;
-	
+
 	gtk_text_buffer_get_iter_at_offset (buffer, &iter, pos);
 	gtk_text_buffer_insert_child_anchor (buffer, &iter, anchor);
 }
 
-static void 
+static void
 delete_text (GtkTextBuffer *buffer, gint start, gint end)
 {
 	GtkTextIter start_iter;
@@ -464,7 +464,7 @@
 	return gtk_text_buffer_get_slice (buffer, &start_iter, &end_iter, TRUE);
 }
 
-void 
+void
 gtk_source_undo_manager_undo (GtkSourceUndoManager *um)
 {
 	GtkSourceUndoAction *undo_action;
@@ -473,13 +473,13 @@
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
 	g_return_if_fail (um->priv != NULL);
 	g_return_if_fail (um->priv->can_undo);
-	
+
 	um->priv->modified_undoing_group = FALSE;
 
 	gtk_source_undo_manager_begin_not_undoable_action (um);
 
 	do
-	{	
+	{
 		undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo + 1);
 		g_return_if_fail (undo_action != NULL);
 
@@ -490,7 +490,7 @@
 		if (undo_action->order_in_group <= 1)
 		{
 			/* Set modified to TRUE only if the buffer did not change its state from
-			 * "not modified" to "modified" undoing an action (with order_in_group > 1) 
+			 * "not modified" to "modified" undoing an action (with order_in_group > 1)
 			 * in current group. */
 			modified = (undo_action->modified && !um->priv->modified_undoing_group);
 		}
@@ -499,31 +499,31 @@
 		{
 			case GTK_SOURCE_UNDO_ACTION_DELETE:
 				insert_text (
-					um->priv->document, 
-					undo_action->action.delete.start, 
+					um->priv->document,
+					undo_action->action.delete.start,
 					undo_action->action.delete.text,
 					strlen (undo_action->action.delete.text));
 
 				if (undo_action->action.delete.forward)
 					set_cursor (
-						um->priv->document, 
+						um->priv->document,
 						undo_action->action.delete.start);
 				else
 					set_cursor (
-						um->priv->document, 
+						um->priv->document,
 						undo_action->action.delete.end);
 
 				break;
-				
+
 			case GTK_SOURCE_UNDO_ACTION_INSERT:
 				delete_text (
-					um->priv->document, 
-					undo_action->action.insert.pos, 
-					undo_action->action.insert.pos + 
-						undo_action->action.insert.chars); 
+					um->priv->document,
+					undo_action->action.insert.pos,
+					undo_action->action.insert.pos +
+						undo_action->action.insert.chars);
 
 				set_cursor (
-					um->priv->document, 
+					um->priv->document,
 					undo_action->action.insert.pos);
 				break;
 
@@ -551,29 +551,29 @@
 	}
 
 	gtk_source_undo_manager_end_not_undoable_action_internal (um);
-	
+
 	um->priv->modified_undoing_group = FALSE;
 
 	if (!um->priv->can_redo)
 	{
 		um->priv->can_redo = TRUE;
-		g_signal_emit (G_OBJECT (um), 
-			       undo_manager_signals [CAN_REDO], 
-			       0, 
+		g_signal_emit (G_OBJECT (um),
+			       undo_manager_signals [CAN_REDO],
+			       0,
 			       TRUE);
 	}
 
 	if (um->priv->next_redo >= (gint)(g_list_length (um->priv->actions) - 1))
 	{
 		um->priv->can_undo = FALSE;
-		g_signal_emit (G_OBJECT (um), 
-			       undo_manager_signals [CAN_UNDO], 
-			       0, 
+		g_signal_emit (G_OBJECT (um),
+			       undo_manager_signals [CAN_UNDO],
+			       0,
 			       FALSE);
 	}
 }
 
-void 
+void
 gtk_source_undo_manager_redo (GtkSourceUndoManager *um)
 {
 	GtkSourceUndoAction *undo_action;
@@ -582,7 +582,7 @@
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
 	g_return_if_fail (um->priv != NULL);
 	g_return_if_fail (um->priv->can_redo);
-	
+
 	undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo);
 	g_return_if_fail (undo_action != NULL);
 
@@ -597,29 +597,29 @@
 		}
 
 		--um->priv->next_redo;
-	
+
 		switch (undo_action->action_type)
 		{
 			case GTK_SOURCE_UNDO_ACTION_DELETE:
 				delete_text (
-					um->priv->document, 
-					undo_action->action.delete.start, 
-					undo_action->action.delete.end); 
+					um->priv->document,
+					undo_action->action.delete.start,
+					undo_action->action.delete.end);
 
 				set_cursor (
 					um->priv->document,
 					undo_action->action.delete.start);
 
 				break;
-				
+
 			case GTK_SOURCE_UNDO_ACTION_INSERT:
 				set_cursor (
 					um->priv->document,
 					undo_action->action.insert.pos);
 
 				insert_text (
-					um->priv->document, 
-					undo_action->action.insert.pos, 
+					um->priv->document,
+					undo_action->action.insert.pos,
 					undo_action->action.insert.text,
 					undo_action->action.insert.length);
 
@@ -646,7 +646,7 @@
 			undo_action = NULL;
 		else
 			undo_action = g_list_nth_data (um->priv->actions, um->priv->next_redo);
-			
+
 	} while ((undo_action != NULL) && (undo_action->order_in_group > 1));
 
 	if (modified)
@@ -689,7 +689,7 @@
 	g_free (action);
 }
 
-static void 
+static void
 gtk_source_undo_manager_free_action_list (GtkSourceUndoManager *um)
 {
 	GList *l;
@@ -712,18 +712,18 @@
 	}
 
 	g_list_free (um->priv->actions);
-	um->priv->actions = NULL;	
+	um->priv->actions = NULL;
 }
 
-static void 
-gtk_source_undo_manager_insert_text_handler (GtkTextBuffer 		*buffer, 
+static void
+gtk_source_undo_manager_insert_text_handler (GtkTextBuffer 		*buffer,
 					     GtkTextIter 		*pos,
-		                             const gchar 		*text, 
-					     gint 			 length, 
+		                             const gchar 		*text,
+					     gint 			 length,
 					     GtkSourceUndoManager 	*um)
 {
 	GtkSourceUndoAction undo_action;
-	
+
 	if (um->priv->running_not_undoable_actions > 0)
 		return;
 
@@ -766,15 +766,15 @@
 	gtk_source_undo_manager_add_action (um, &undo_action);
 }
 
-static void 
-gtk_source_undo_manager_delete_range_handler (GtkTextBuffer 		*buffer, 
+static void
+gtk_source_undo_manager_delete_range_handler (GtkTextBuffer 		*buffer,
 					      GtkTextIter 		*start,
-                        		      GtkTextIter 		*end, 
+                        		      GtkTextIter 		*end,
 					      GtkSourceUndoManager 	*um)
 {
 	GtkSourceUndoAction undo_action;
 	GtkTextIter insert_iter;
-	
+
 	if (um->priv->running_not_undoable_actions > 0)
 		return;
 
@@ -805,14 +805,14 @@
 		undo_action.mergeable = TRUE;
 
 	undo_action.modified = FALSE;
-	
+
 	gtk_source_undo_manager_add_action (um, &undo_action);
 
 	g_free (undo_action.action.delete.text);
 
 }
 
-static void 
+static void
 gtk_source_undo_manager_begin_user_action_handler (GtkTextBuffer *buffer, GtkSourceUndoManager *um)
 {
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
@@ -825,11 +825,11 @@
 }
 
 static void
-gtk_source_undo_manager_add_action (GtkSourceUndoManager 	*um, 
+gtk_source_undo_manager_add_action (GtkSourceUndoManager 	*um,
 				    const GtkSourceUndoAction 	*undo_action)
 {
 	GtkSourceUndoAction* action;
-	
+
 	if (um->priv->next_redo >= 0)
 	{
 		gtk_source_undo_manager_free_first_n_actions (um, um->priv->next_redo + 1);
@@ -845,7 +845,7 @@
 		if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
 			action->action.insert.text = g_strndup (undo_action->action.insert.text, undo_action->action.insert.length);
 		else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
-			action->action.delete.text = g_strdup (undo_action->action.delete.text); 
+			action->action.delete.text = g_strdup (undo_action->action.delete.text);
 		else if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT_ANCHOR)
 		{
 			/* Nothing needs to be done */
@@ -855,16 +855,16 @@
 			g_free (action);
 			g_return_if_reached ();
 		}
-		
+
 		++um->priv->actions_in_current_group;
 		action->order_in_group = um->priv->actions_in_current_group;
 
 		if (action->order_in_group == 1)
 			++um->priv->num_of_groups;
-	
+
 		um->priv->actions = g_list_prepend (um->priv->actions, action);
 	}
-	
+
 	gtk_source_undo_manager_check_list_size (um);
 
 	if (!um->priv->can_undo)
@@ -880,8 +880,8 @@
 	}
 }
 
-static void 
-gtk_source_undo_manager_free_first_n_actions (GtkSourceUndoManager	*um, 
+static void
+gtk_source_undo_manager_free_first_n_actions (GtkSourceUndoManager	*um,
 					      gint 			 n)
 {
 	gint i;
@@ -904,21 +904,21 @@
 		um->priv->actions = g_list_delete_link (um->priv->actions,
 							um->priv->actions);
 
-		if (um->priv->actions == NULL) 
+		if (um->priv->actions == NULL)
 			return;
 	}
 }
 
-static void 
+static void
 gtk_source_undo_manager_check_list_size (GtkSourceUndoManager *um)
 {
 	gint undo_levels;
-	
+
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
 	g_return_if_fail (um->priv != NULL);
-	
+
 	undo_levels = gtk_source_undo_manager_get_max_undo_levels (um);
-	
+
 	if (undo_levels < 1)
 		return;
 
@@ -926,14 +926,14 @@
 	{
 		GtkSourceUndoAction *undo_action;
 		GList *last;
-		
+
 		last = g_list_last (um->priv->actions);
 		undo_action = (GtkSourceUndoAction*) last->data;
-			
+
 		do
 		{
 			GList *tmp;
-			
+
 			if (undo_action->order_in_group == 1)
 				--um->priv->num_of_groups;
 
@@ -945,32 +945,32 @@
 			tmp = g_list_previous (last);
 			um->priv->actions = g_list_delete_link (um->priv->actions, last);
 			last = tmp;
-			g_return_if_fail (last != NULL); 
+			g_return_if_fail (last != NULL);
 
 			undo_action = (GtkSourceUndoAction*) last->data;
 
-		} while ((undo_action->order_in_group > 1) || 
+		} while ((undo_action->order_in_group > 1) ||
 			 (um->priv->num_of_groups > undo_levels));
-	}	
+	}
 }
 
 /**
  * gtk_source_undo_manager_merge_action:
- * @um: a #GtkSourceUndoManager. 
+ * @um: a #GtkSourceUndoManager.
  * @undo_action: a #GtkSourceUndoAction.
- * 
+ *
  * This function tries to merge the undo action at the top of
  * the stack with a new undo action. So when we undo for example
  * typing, we can undo the whole word and not each letter by itself.
- * 
- * Return Value: %TRUE is merge was sucessful, %FALSE otherwise.²
+ *
+ * Return Value: %TRUE is merge was successful, %FALSE otherwise.²
  **/
-static gboolean 
-gtk_source_undo_manager_merge_action (GtkSourceUndoManager 	*um, 
+static gboolean
+gtk_source_undo_manager_merge_action (GtkSourceUndoManager 	*um,
 				      const GtkSourceUndoAction *undo_action)
 {
 	GtkSourceUndoAction *last_action;
-	
+
 	g_return_val_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um), FALSE);
 	g_return_val_if_fail (um->priv != NULL, FALSE);
 
@@ -990,7 +990,7 @@
 	}
 
 	if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE)
-	{				
+	{
 		if ((last_action->action.delete.forward != undo_action->action.delete.forward) ||
 		    ((last_action->action.delete.start != undo_action->action.delete.start) &&
 		     (last_action->action.delete.start != undo_action->action.delete.end)))
@@ -998,14 +998,14 @@
 			last_action->mergeable = FALSE;
 			return FALSE;
 		}
-		
+
 		if (last_action->action.delete.start == undo_action->action.delete.start)
 		{
 			gchar *str;
-			
+
 #define L  (last_action->action.delete.end - last_action->action.delete.start - 1)
 #define g_utf8_get_char_at(p,i) g_utf8_get_char(g_utf8_offset_to_pointer((p),(i)))
-		
+
 			/* Deleted with the delete key */
 			if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
 			    (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
@@ -1015,19 +1015,19 @@
 				last_action->mergeable = FALSE;
 				return FALSE;
 			}
-			
-			str = g_strdup_printf ("%s%s", last_action->action.delete.text, 
+
+			str = g_strdup_printf ("%s%s", last_action->action.delete.text,
 				undo_action->action.delete.text);
-			
+
 			g_free (last_action->action.delete.text);
-			last_action->action.delete.end += (undo_action->action.delete.end - 
+			last_action->action.delete.end += (undo_action->action.delete.end -
 							   undo_action->action.delete.start);
 			last_action->action.delete.text = str;
 		}
 		else
 		{
 			gchar *str;
-			
+
 			/* Deleted with the backspace key */
 			if ((g_utf8_get_char (undo_action->action.delete.text) != ' ') &&
 			    (g_utf8_get_char (undo_action->action.delete.text) != '\t') &&
@@ -1038,9 +1038,9 @@
 				return FALSE;
 			}
 
-			str = g_strdup_printf ("%s%s", undo_action->action.delete.text, 
+			str = g_strdup_printf ("%s%s", undo_action->action.delete.text,
 				last_action->action.delete.text);
-			
+
 			g_free (last_action->action.delete.text);
 			last_action->action.delete.start = undo_action->action.delete.start;
 			last_action->action.delete.text = str;
@@ -1049,10 +1049,10 @@
 	else if (undo_action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT)
 	{
 		gchar* str;
-		
+
 #define I (last_action->action.insert.chars - 1)
-		
-		if ((undo_action->action.insert.pos != 
+
+		if ((undo_action->action.insert.pos !=
 		     	(last_action->action.insert.pos + last_action->action.insert.chars)) ||
 		    ((g_utf8_get_char (undo_action->action.insert.text) != ' ') &&
 		      (g_utf8_get_char (undo_action->action.insert.text) != '\t') &&
@@ -1064,9 +1064,9 @@
 			return FALSE;
 		}
 
-		str = g_strdup_printf ("%s%s", last_action->action.insert.text, 
+		str = g_strdup_printf ("%s%s", last_action->action.insert.text,
 				undo_action->action.insert.text);
-		
+
 		g_free (last_action->action.insert.text);
 		last_action->action.insert.length += undo_action->action.insert.length;
 		last_action->action.insert.text = str;
@@ -1080,7 +1080,7 @@
 	else
 		/* Unknown action inside undo merge encountered */
 		g_return_val_if_reached (TRUE);
-		
+
 	return TRUE;
 }
 
@@ -1098,7 +1098,7 @@
 				  	     gint			 max_undo_levels)
 {
 	gint old_levels;
-	
+
 	g_return_if_fail (um != NULL);
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
 
@@ -1107,7 +1107,7 @@
 
 	if (max_undo_levels < 1)
 		return;
-		
+
 	if (old_levels > max_undo_levels)
 	{
 		/* strip redo actions first */
@@ -1116,7 +1116,7 @@
 			gtk_source_undo_manager_free_first_n_actions (um, 1);
 			um->priv->next_redo--;
 		}
-		
+
 		/* now remove undo actions if necessary */
 		gtk_source_undo_manager_check_list_size (um);
 
@@ -1142,7 +1142,7 @@
 {
 	GtkSourceUndoAction *action;
 	GList *list;
-	
+
 	g_return_if_fail (GTK_SOURCE_IS_UNDO_MANAGER (um));
 	g_return_if_fail (um->priv != NULL);
 
@@ -1157,7 +1157,7 @@
 		action = NULL;
 
 	if (gtk_text_buffer_get_modified (buffer) == FALSE)
-	{	
+	{
 		if (action != NULL)
 			action->mergeable = FALSE;
 
@@ -1178,7 +1178,7 @@
 
 		return;
 	}
-	
+
 	/* gtk_text_buffer_get_modified (buffer) == TRUE */
 
 	g_return_if_fail (um->priv->modified_action == NULL);
--- a/pidgin/gtksourceundomanager.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtksourceundomanager.h	Sat Mar 07 01:59:40 2009 +0000
@@ -4,8 +4,8 @@
  * This file is part of GtkSourceView
  *
  * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
- * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi 
- * Copyright (C) 2002, 2003 Paolo Maggi 
+ * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
+ * Copyright (C) 2002, 2003 Paolo Maggi
  *
  * 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
@@ -19,10 +19,10 @@
  *
  * 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, 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02111-1301, USA. * *
  */
- 
+
 #ifndef __GTK_SOURCE_UNDO_MANAGER_H__
 #define __GTK_SOURCE_UNDO_MANAGER_H__
 
@@ -44,7 +44,7 @@
 struct _GtkSourceUndoManager
 {
 	GObject base;
-	
+
 	GtkSourceUndoManagerPrivate *priv;
 };
 
@@ -67,14 +67,14 @@
 void			gtk_source_undo_manager_undo 		(GtkSourceUndoManager 	*um);
 void			gtk_source_undo_manager_redo 		(GtkSourceUndoManager 	*um);
 
-void			gtk_source_undo_manager_begin_not_undoable_action 
+void			gtk_source_undo_manager_begin_not_undoable_action
 								(GtkSourceUndoManager	*um);
-void			gtk_source_undo_manager_end_not_undoable_action 
+void			gtk_source_undo_manager_end_not_undoable_action
 								(GtkSourceUndoManager	*um);
 
-gint			gtk_source_undo_manager_get_max_undo_levels 
+gint			gtk_source_undo_manager_get_max_undo_levels
 								(GtkSourceUndoManager 	*um);
-void			gtk_source_undo_manager_set_max_undo_levels 
+void			gtk_source_undo_manager_set_max_undo_levels
 								(GtkSourceUndoManager 	*um,
 				  	     			 gint		 	 undo_levels);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkstatus-icon-theme.c	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,72 @@
+/*
+ * Status Icon Themes for 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 "gtkstatus-icon-theme.h"
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+pidgin_status_icon_theme_finalize(GObject *obj)
+{
+	parent_class->finalize(obj);
+}
+
+static void
+pidgin_status_icon_theme_class_init(PidginStatusIconThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	obj_class->finalize = pidgin_status_icon_theme_finalize;
+}
+
+GType
+pidgin_status_icon_theme_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof (PidginStatusIconThemeClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_status_icon_theme_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (PidginStatusIconTheme),
+			0, /* n_preallocs */
+			NULL,
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PIDGIN_TYPE_ICON_THEME,
+				"PidginStatusIconTheme", &info, 0);
+	}
+	return type;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkstatus-icon-theme.h	Sat Mar 07 01:59:40 2009 +0000
@@ -0,0 +1,71 @@
+/**
+ * @file status_icon-theme.h  Pidgin Icon Theme  Class API
+ */
+
+/* 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_STATUS_ICON_THEME_H
+#define PIDGIN_STATUS_ICON_THEME_H
+
+#include <glib-object.h>
+#include "gtkicon-theme.h"
+
+/**
+ * extends PidginIconTheme (gtkicon-theme.h)
+ * A pidgin status icon theme.
+ * This object represents a Pidgin status icon theme.
+ *
+ * PidginStatusIconTheme is a PidginIconTheme Object.
+ */
+typedef struct _PidginStatusIconTheme        PidginStatusIconTheme;
+typedef struct _PidginStatusIconThemeClass   PidginStatusIconThemeClass;
+
+#define PIDGIN_TYPE_STATUS_ICON_THEME            (pidgin_status_icon_theme_get_type ())
+#define PIDGIN_STATUS_ICON_THEME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconTheme))
+#define PIDGIN_STATUS_ICON_THEME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconThemeClass))
+#define PIDGIN_IS_STATUS_ICON_THEME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_STATUS_ICON_THEME))
+#define PIDGIN_IS_STATUS_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_STATUS_ICON_THEME))
+#define PIDGIN_STATUS_ICON_THEME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_STATUS_ICON_THEME, PidginStatusIconThemeClass))
+
+struct _PidginStatusIconTheme
+{
+	PidginIconTheme parent;
+};
+
+struct _PidginStatusIconThemeClass
+{
+	PidginIconThemeClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Pidgin Status Icon Theme API                                          */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_status_icon_theme_get_type(void);
+
+G_END_DECLS
+#endif /* PIDGIN_STATUS_ICON_THEME_H */
--- a/pidgin/gtkstatusbox.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkstatusbox.c	Sat Mar 07 01:59:40 2009 +0000
@@ -1646,7 +1646,7 @@
 			pidgin_status_box_popdown (status_box);
 			return TRUE;
 		} else if (ewidget == status_box->toggle_button) {
-			status_box->popup_in_progress = TRUE;		
+			status_box->popup_in_progress = TRUE;
 		}
 
 		/* released outside treeview */
@@ -1698,7 +1698,7 @@
 				gtk_tree_path_free (path);
 				return ret;
 			}
-		} 
+		}
 	}
 	return FALSE;
 }
@@ -1777,9 +1777,9 @@
 	status_box->vsep = gtk_vseparator_new();
 	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, 
+	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, 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, 
+	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);
 
 	gtk_cell_view_set_model(GTK_CELL_VIEW(status_box->cell_view), GTK_TREE_MODEL(status_box->store));
@@ -2529,7 +2529,7 @@
 {
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
-	int wrapped_lines;
+	int display_lines;
 	int lines;
 	GdkRectangle oneline;
 	int height;
@@ -2545,28 +2545,44 @@
 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml));
 
 	height = 0;
-	wrapped_lines = 1;
+	display_lines = 1;
 	gtk_text_buffer_get_start_iter(buffer, &iter);
 	do {
 		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
 		height += oneline.height;
-		wrapped_lines++;
-		if (wrapped_lines > 4)
-			break;
-	} while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->imhtml), &iter));
+		display_lines++;
+	} while (display_lines <= 4 &&
+		gtk_text_view_forward_display_line(GTK_TEXT_VIEW(status_box->imhtml), &iter));
+
+	/*
+	 * This check fixes the case where the last character entered is a
+	 * newline (shift+return).  For some reason the
+	 * gtk_text_view_forward_display_line() function doesn't treat this
+	 * like a new line, and so we think the input box only needs to be
+	 * two lines instead of three, for example.  So we check if the
+	 * last character was a newline and add some extra height if so.
+	 */
+	if (display_lines <= 4
+		&& gtk_text_iter_backward_char(&iter)
+		&& gtk_text_iter_get_char(&iter) == '\n')
+	{
+		gtk_text_view_get_iter_location(GTK_TEXT_VIEW(status_box->imhtml), &iter, &oneline);
+		height += oneline.height;
+		display_lines++;
+	}
 
 	lines = gtk_text_buffer_get_line_count(buffer);
 
 	/* Show a maximum of 4 lines */
 	lines = MIN(lines, 4);
-	wrapped_lines = MIN(wrapped_lines, 4);
+	display_lines = MIN(display_lines, 4);
 
 	pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(status_box->imhtml));
 	pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(status_box->imhtml));
 	pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(status_box->imhtml));
 
 	height += (pad_top + pad_bottom) * lines;
-	height += (pad_inside) * (wrapped_lines - lines);
+	height += (pad_inside) * (display_lines - lines);
 
 	gtk_widget_set_size_request(status_box->vbox, -1, height + PIDGIN_HIG_BOX_SPACE);
 }
--- a/pidgin/gtkthemes.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkthemes.c	Sat Mar 07 01:59:40 2009 +0000
@@ -83,7 +83,7 @@
 {
 	char *theme_dir = NULL, *last_slash = NULL;
 	g_return_if_fail(NULL != file);
-	
+
 	if (!g_file_test(file, G_FILE_TEST_EXISTS)) return;
 	if ((theme_dir = g_strdup(file)) == NULL) return ;
 
@@ -169,12 +169,12 @@
 	for (wer = theme->list; wer != NULL; wer = theme->list) {
 		while (wer->smileys) {
 			GtkIMHtmlSmiley *uio = wer->smileys->data;
-			
+
 			if (uio->imhtml) {
 				g_signal_handlers_disconnect_matched(uio->imhtml, G_SIGNAL_MATCH_DATA,
 					0, 0, NULL, NULL, uio);
 			}
-				
+
 			if (uio->icon)
 				g_object_unref(uio->icon);
 			if (g_hash_table_lookup(already_freed, uio->file) == NULL) {
--- a/pidgin/gtkutils.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkutils.c	Sat Mar 07 01:59:40 2009 +0000
@@ -56,6 +56,9 @@
 #include "signals.h"
 #include "util.h"
 
+#include "gtkaccount.h"
+#include "gtkprefs.h"
+
 #include "gtkconv.h"
 #include "gtkdialogs.h"
 #include "gtkimhtml.h"
@@ -71,6 +74,7 @@
 } AopMenu;
 
 static guint accels_save_timer = 0;
+static GList *gnome_url_handlers = NULL;
 
 static gboolean
 url_clicked_idle_cb(gpointer data)
@@ -80,10 +84,12 @@
 	return FALSE;
 }
 
-static void
-url_clicked_cb(GtkWidget *w, const char *uri)
+static gboolean
+url_clicked_cb(GtkIMHtml *unused, GtkIMHtmlLink *link)
 {
+	const char *uri = gtk_imhtml_link_get_url(link);
 	g_idle_add(url_clicked_idle_cb, g_strdup(uri));
+	return TRUE;
 }
 
 static GtkIMHtmlFuncs gtkimhtml_cbs = {
@@ -102,9 +108,6 @@
 	g_return_if_fail(imhtml != NULL);
 	g_return_if_fail(GTK_IS_IMHTML(imhtml));
 
-	g_signal_connect(G_OBJECT(imhtml), "url_clicked",
-					 G_CALLBACK(url_clicked_cb), NULL);
-
 	pidgin_themes_smiley_themeize(imhtml);
 
 	gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), &gtkimhtml_cbs);
@@ -567,7 +570,7 @@
 	gtk_widget_show (label);
 	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-	
+
 	gtk_container_add(GTK_CONTAINER(item), hbox);
 	gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
@@ -1924,7 +1927,7 @@
 #endif /* !NEW_STYLE_COMPLETION */
 
 #ifdef NEW_STYLE_COMPLETION
-static gboolean screenname_completion_match_func(GtkEntryCompletion *completion,
+static gboolean buddyname_completion_match_func(GtkEntryCompletion *completion,
 		const gchar *key, GtkTreeIter *iter, gpointer user_data)
 {
 	GtkTreeModel *model;
@@ -1957,7 +1960,7 @@
 	return FALSE;
 }
 
-static gboolean screenname_completion_match_selected_cb(GtkEntryCompletion *completion,
+static gboolean buddyname_completion_match_selected_cb(GtkEntryCompletion *completion,
 		GtkTreeModel *model, GtkTreeIter *iter, PidginCompletionData *data)
 {
 	GValue val;
@@ -1983,22 +1986,22 @@
 }
 
 static void
-add_screenname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, const char *contact_alias,
-								  const PurpleAccount *account, const char *screenname)
+add_buddyname_autocomplete_entry(GtkListStore *store, const char *buddy_alias, const char *contact_alias,
+								  const PurpleAccount *account, const char *buddyname)
 {
 	GtkTreeIter iter;
 	gboolean completion_added = FALSE;
-	gchar *normalized_screenname;
+	gchar *normalized_buddyname;
 	gchar *tmp;
 
-	tmp = g_utf8_normalize(screenname, -1, G_NORMALIZE_DEFAULT);
-	normalized_screenname = g_utf8_casefold(tmp, -1);
+	tmp = g_utf8_normalize(buddyname, -1, G_NORMALIZE_DEFAULT);
+	normalized_buddyname = g_utf8_casefold(tmp, -1);
 	g_free(tmp);
 
 	/* There's no sense listing things like: 'xxx "xxx"'
-	   when the screenname and buddy alias match. */
-	if (buddy_alias && strcmp(buddy_alias, screenname)) {
-		char *completion_entry = g_strdup_printf("%s \"%s\"", screenname, buddy_alias);
+	   when the name and buddy alias match. */
+	if (buddy_alias && strcmp(buddy_alias, buddyname)) {
+		char *completion_entry = g_strdup_printf("%s \"%s\"", buddyname, buddy_alias);
 		char *tmp2 = g_utf8_normalize(buddy_alias, -1, G_NORMALIZE_DEFAULT);
 
 		tmp = g_utf8_casefold(tmp2, -1);
@@ -2007,8 +2010,8 @@
 		gtk_list_store_append(store, &iter);
 		gtk_list_store_set(store, &iter,
 				0, completion_entry,
-				1, screenname,
-				2, normalized_screenname,
+				1, buddyname,
+				2, normalized_buddyname,
 				3, tmp,
 				4, account,
 				-1);
@@ -2018,12 +2021,12 @@
 	}
 
 	/* There's no sense listing things like: 'xxx "xxx"'
-	   when the screenname and contact alias match. */
-	if (contact_alias && strcmp(contact_alias, screenname)) {
+	   when the name and contact alias match. */
+	if (contact_alias && strcmp(contact_alias, buddyname)) {
 		/* We don't want duplicates when the contact and buddy alias match. */
 		if (!buddy_alias || strcmp(contact_alias, buddy_alias)) {
 			char *completion_entry = g_strdup_printf("%s \"%s\"",
-							screenname, contact_alias);
+							buddyname, contact_alias);
 			char *tmp2 = g_utf8_normalize(contact_alias, -1, G_NORMALIZE_DEFAULT);
 
 			tmp = g_utf8_casefold(tmp2, -1);
@@ -2032,8 +2035,8 @@
 			gtk_list_store_append(store, &iter);
 			gtk_list_store_set(store, &iter,
 					0, completion_entry,
-					1, screenname,
-					2, normalized_screenname,
+					1, buddyname,
+					2, normalized_buddyname,
 					3, tmp,
 					4, account,
 					-1);
@@ -2044,18 +2047,18 @@
 	}
 
 	if (completion_added == FALSE) {
-		/* Add the buddy's screenname. */
+		/* Add the buddy's name. */
 		gtk_list_store_append(store, &iter);
 		gtk_list_store_set(store, &iter,
-				0, screenname,
-				1, screenname,
-				2, normalized_screenname,
+				0, buddyname,
+				1, buddyname,
+				2, normalized_buddyname,
 				3, NULL,
 				4, account,
 				-1);
 	}
 
-	g_free(normalized_screenname);
+	g_free(normalized_buddyname);
 }
 #endif /* NEW_STYLE_COMPLETION */
 
@@ -2064,8 +2067,8 @@
 	PidginFilterBuddyCompletionEntryFunc filter_func = data->filter_func;
 	gpointer user_data = data->filter_func_user_data;
 
- 	/* 1. Don't show buddies because we will have gotten them already.
- 	 * 2. The boxes that use this autocomplete code handle only IMs. */
+	/* 1. Don't show buddies because we will have gotten them already.
+	 * 2. The boxes that use this autocomplete code handle only IMs. */
 	if (!set->buddy && set->type == PURPLE_LOG_IM) {
 		PidginBuddyCompletionEntry entry;
 		entry.is_buddy = FALSE;
@@ -2073,7 +2076,7 @@
 
 		if (filter_func(&entry, user_data)) {
 #ifdef NEW_STYLE_COMPLETION
-			add_screenname_autocomplete_entry(data->store,
+			add_buddyname_autocomplete_entry(data->store,
 												NULL, NULL, set->account, set->name);
 #else
 			/* Steal the name for the GCompletion. */
@@ -2119,7 +2122,7 @@
 
 				if (filter_func(&entry, user_data)) {
 #ifdef NEW_STYLE_COMPLETION
-					add_screenname_autocomplete_entry(data->store,
+					add_buddyname_autocomplete_entry(data->store,
 														((PurpleContact *)cnode)->alias,
 														purple_buddy_get_contact_alias(entry.entry.buddy),
 														entry.entry.buddy->account,
@@ -2150,7 +2153,7 @@
 }
 
 static void
-screenname_autocomplete_destroyed_cb(GtkWidget *widget, gpointer data)
+buddyname_autocomplete_destroyed_cb(GtkWidget *widget, gpointer data)
 {
 	g_free(data);
 	purple_signals_disconnect_by_handle(widget);
@@ -2162,15 +2165,17 @@
 	add_completion_list(data);
 }
 
-
 void
 pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *accountopt, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data)
 {
 	PidginCompletionData *data;
 
 #ifdef NEW_STYLE_COMPLETION
-	/* Store the displayed completion value, the screenname, the UTF-8 normalized & casefolded screenname,
-	 * the UTF-8 normalized & casefolded value for comparison, and the account. */
+	/*
+	 * Store the displayed completion value, the buddy name, the UTF-8
+	 * normalized & casefolded buddy name, the UTF-8 normalized &
+	 * casefolded value for comparison, and the account.
+	 */
 	GtkListStore *store;
 
 	GtkEntryCompletion *completion;
@@ -2191,15 +2196,15 @@
 
 	add_completion_list(data);
 
-	/* Sort the completion list by screenname. */
+	/* Sort the completion list by buddy name */
 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
 	                                     1, GTK_SORT_ASCENDING);
 
 	completion = gtk_entry_completion_new();
-	gtk_entry_completion_set_match_func(completion, screenname_completion_match_func, NULL, NULL);
+	gtk_entry_completion_set_match_func(completion, buddyname_completion_match_func, NULL, NULL);
 
 	g_signal_connect(G_OBJECT(completion), "match-selected",
-		G_CALLBACK(screenname_completion_match_selected_cb), data);
+		G_CALLBACK(buddyname_completion_match_selected_cb), data);
 
 	gtk_entry_set_completion(GTK_ENTRY(entry), completion);
 	g_object_unref(completion);
@@ -2246,7 +2251,7 @@
 	purple_signal_connect(purple_accounts_get_handle(), "account-removed", entry,
 						PURPLE_CALLBACK(repopulate_autocomplete), data);
 
-	g_signal_connect(G_OBJECT(entry), "destroy", G_CALLBACK(screenname_autocomplete_destroyed_cb), data);
+	g_signal_connect(G_OBJECT(entry), "destroy", G_CALLBACK(buddyname_autocomplete_destroyed_cb), data);
 }
 
 gboolean
@@ -3240,7 +3245,7 @@
  	style = gtk_widget_get_style(widget);
 	if (!style)
 		return "dim grey";
-	
+
 	snprintf(dim_grey_string, sizeof(dim_grey_string), "#%02x%02x%02x",
 	style->text_aa[GTK_STATE_NORMAL].red >> 8,
 	style->text_aa[GTK_STATE_NORMAL].green >> 8,
@@ -3480,3 +3485,198 @@
 	return pixbuf;
 }
 
+static void url_copy(GtkWidget *w, gchar *url)
+{
+	GtkClipboard *clipboard;
+
+	clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY);
+	gtk_clipboard_set_text(clipboard, url, -1);
+
+	clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_CLIPBOARD);
+	gtk_clipboard_set_text(clipboard, url, -1);
+}
+
+static gboolean
+link_context_menu(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+{
+	GtkWidget *img, *item;
+	const char *url;
+
+	url = gtk_imhtml_link_get_url(link);
+
+	/* Open Link */
+	img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
+	item = gtk_image_menu_item_new_with_mnemonic(_("_Open Link"));
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
+	g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_link_activate), link);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	/* Copy Link Location */
+	img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
+	item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Link Location"));
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
+	g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_copy), (gpointer)url);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	return TRUE;
+}
+
+static gboolean
+copy_email_address(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+{
+	GtkWidget *img, *item;
+	const char *text;
+	char *address;
+#define MAILTOSIZE  (sizeof("mailto:") - 1)
+
+	text = gtk_imhtml_link_get_url(link);
+	g_return_val_if_fail(text && strlen(text) > MAILTOSIZE, FALSE);
+	address = (char*)text + MAILTOSIZE;
+
+	/* Copy Email Address */
+	img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
+	item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Email Address"));
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
+	g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(url_copy), address);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	return TRUE;
+}
+
+/* XXX: The following two functions are for demonstration purposes only! */
+static gboolean
+open_dialog(GtkIMHtml *imhtml, GtkIMHtmlLink *link)
+{
+	const char *url;
+	const char *str;
+
+	url = gtk_imhtml_link_get_url(link);
+	if (!url || strlen(url) < sizeof("open://"))
+		return FALSE;
+
+	str = url + sizeof("open://") - 1;
+
+	if (strcmp(str, "accounts") == 0)
+		pidgin_accounts_window_show();
+	else if (strcmp(str, "prefs") == 0)
+		pidgin_prefs_show();
+	else
+		return FALSE;
+	return TRUE;
+}
+
+static gboolean
+dummy(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+{
+	return TRUE;
+}
+
+static gboolean
+register_gnome_url_handlers(void)
+{
+	char *tmp;
+	char *err;
+	char *c;
+	char *start;
+
+	tmp = g_find_program_in_path("gconftool-2");
+	if (tmp == NULL)
+		return FALSE;
+
+	tmp = NULL;
+	if (!g_spawn_command_line_sync("gconftool-2 --all-dirs /desktop/gnome/url-handlers",
+	                               &tmp, &err, NULL, NULL))
+	{
+		g_free(tmp);
+		g_free(err);
+		g_return_val_if_reached(FALSE);
+	}
+	g_free(err);
+	err = NULL;
+
+	for (c = start = tmp ; *c ; c++)
+	{
+		/* Skip leading spaces. */
+		if (c == start && *c == ' ')
+			start = c + 1;
+		else if (*c == '\n')
+		{
+			*c = '\0';
+			if (g_str_has_prefix(start, "/desktop/gnome/url-handlers/"))
+			{
+				char *cmd;
+				char *tmp2 = NULL;
+				char *protocol;
+
+				/* If there is an enabled boolean, honor it. */
+				cmd = g_strdup_printf("gconftool-2 -g %s/enabled", start);
+				if (g_spawn_command_line_sync(cmd, &tmp2, &err, NULL, NULL))
+				{
+					g_free(err);
+					err = NULL;
+					if (!strcmp(tmp2, "false\n"))
+					{
+						g_free(tmp2);
+						g_free(cmd);
+						start = c + 1;
+						continue;
+					}
+				}
+				g_free(cmd);
+				g_free(tmp2);
+
+				start += sizeof("/desktop/gnome/url-handlers/") - 1;
+
+				protocol = g_strdup_printf("%s:", start);
+				gnome_url_handlers = g_list_prepend(gnome_url_handlers, protocol);
+				gtk_imhtml_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
+			}
+			start = c + 1;
+		}
+	}
+	g_free(tmp);
+
+	return (gnome_url_handlers != NULL);
+}
+
+void pidgin_utils_init(void)
+{
+	gtk_imhtml_class_register_protocol("http://", url_clicked_cb, link_context_menu);
+	gtk_imhtml_class_register_protocol("https://", url_clicked_cb, link_context_menu);
+	gtk_imhtml_class_register_protocol("ftp://", url_clicked_cb, link_context_menu);
+	gtk_imhtml_class_register_protocol("gopher://", url_clicked_cb, link_context_menu);
+	gtk_imhtml_class_register_protocol("mailto:", url_clicked_cb, copy_email_address);
+
+	/* Example custom URL handler. */
+	gtk_imhtml_class_register_protocol("open://", open_dialog, dummy);
+
+	/* If we're under GNOME, try registering the system URL handlers. */
+	if (purple_running_gnome())
+		register_gnome_url_handlers();
+}
+
+void pidgin_utils_uninit(void)
+{
+	gtk_imhtml_class_register_protocol("open://", NULL, NULL);
+
+	/* If we have GNOME handlers registered, unregister them. */
+	if (gnome_url_handlers)
+	{
+		GList *l;
+		for (l = gnome_url_handlers ; l ; l = l->next)
+		{
+			gtk_imhtml_class_register_protocol((char *)l->data, NULL, NULL);
+			g_free(l->data);
+		}
+		g_list_free(gnome_url_handlers);
+		gnome_url_handlers = NULL;
+		return;
+	}
+
+	gtk_imhtml_class_register_protocol("http://", NULL, NULL);
+	gtk_imhtml_class_register_protocol("https://", NULL, NULL);
+	gtk_imhtml_class_register_protocol("ftp://", NULL, NULL);
+	gtk_imhtml_class_register_protocol("mailto:", NULL, NULL);
+	gtk_imhtml_class_register_protocol("gopher://", NULL, NULL);
+}
+
--- a/pidgin/gtkutils.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkutils.h	Sat Mar 07 01:59:40 2009 +0000
@@ -136,7 +136,7 @@
  * 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 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
@@ -184,8 +184,8 @@
 void pidgin_toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle);
 
 /**
- * Checks if text has been entered into a GtkTextEntry widget.  If 
- * so, the GTK_RESPONSE_OK on the given dialog is set to TRUE.  
+ * Checks if text has been entered into a GtkTextEntry widget.  If
+ * so, the GTK_RESPONSE_OK on the given dialog is set to TRUE.
  * Otherwise GTK_RESPONSE_OK is set to FALSE.
  *
  * @param entry  The text entry widget.
@@ -355,7 +355,7 @@
  *
  * @param entry       The GtkEntry on which to setup autocomplete.
  * @param optmenu     A menu for accounts, returned by gaim_gtk_account_option_menu_new().
- *                    If @a optmenu is not @c NULL, it'll be updated when a screenname is chosen
+ *                    If @a optmenu is not @c NULL, it'll be updated when a username is chosen
  *                    from the autocomplete list.
  * @param filter_func A function for checking if an autocomplete entry
  *                    should be shown. This can be @c NULL.
@@ -364,7 +364,7 @@
 void pidgin_setup_screenname_autocomplete_with_filter(GtkWidget *entry, GtkWidget *optmenu, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data);
 
 /**
- * The default filter function for screenname autocomplete.
+ * The default filter function for username autocomplete.
  *
  * @param completion_entry The completion entry to filter.
  * @param all_accounts  If this is @c FALSE, only the autocompletion entries
@@ -385,9 +385,9 @@
  * @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
+ *                  NULL, it'll be updated when a username is chosen from the
  *                  autocomplete list.
- * @param all       Whether to include screennames from disconnected accounts.
+ * @param all       Whether to include usernames from disconnected accounts.
  */
 void pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *optmenu, gboolean all);
 
@@ -473,7 +473,7 @@
 									 char **ret_alias);
 
 /**
- * Sets an ATK name for a given widget.  Also sets the labelled-by 
+ * Sets an ATK name for a given widget.  Also sets the labelled-by
  * and label-for ATK relationships.
  *
  * @param w The widget that we want to name.
@@ -509,10 +509,10 @@
 										gboolean *push_in, gpointer data);
 
 /**
- * A valid GtkMenuPositionFunc.  This is used to determine where 
- * 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, 
+ * A valid GtkMenuPositionFunc.  This is used to determine where
+ * 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.
  *
  * @param menu The menu we are positioning.
@@ -612,13 +612,13 @@
  * @param parent      The parent window
  * @param callback    The callback to call when the window is closed. If the user chose an icon, the char* argument will point to its path
  * @param data        Data to pass to @a callback
- * @return            The file dialog 
+ * @return            The file dialog
  */
 GtkWidget *pidgin_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data);
 
 /**
  * Converts a buddy icon to the required size and format
- * 
+ *
  * @param plugin     The prpl to convert the icon
  * @param path       The path of a file to convert
  * @param len        If not @c NULL, the length of the returned data will be set here.
@@ -706,7 +706,7 @@
 			const gchar *key, GtkTreeIter *iter, gpointer data);
 
 /**
- * Sets or resets a window to 'urgent,' by setting the URGENT hint in X 
+ * Sets or resets a window to 'urgent,' by setting the URGENT hint in X
  * or blinking in the win32 taskbar
  *
  * @param window  The window to draw attention to
@@ -792,7 +792,8 @@
  *
  * @param window    The window to make transient.
  *
- * @return  Whether the window was made transient or not.
+ * @return Whether the window was made transient or not.
+ *
  * @since 2.4.0
  */
 gboolean pidgin_auto_parent_window(GtkWidget *window);
@@ -818,9 +819,24 @@
  * @param  image   A PurpleStoredImage.
  *
  * @return   A GdkPixbuf created from the stored image.
+ *
  * @since 2.5.0
  */
-GdkPixbuf * pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
+GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
+
+/**
+ * Initialize some utility functions.
+ *
+ * @since 2.6.0
+ */
+void pidgin_utils_init(void);
+
+/**
+ * Uninitialize some utility functions.
+ *
+ * @since 2.6.0
+ */
+void pidgin_utils_uninit(void);
 
 #endif /* _PIDGINUTILS_H_ */
 
--- a/pidgin/gtkwhiteboard.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/gtkwhiteboard.c	Sat Mar 07 01:59:40 2009 +0000
@@ -857,7 +857,7 @@
 {
 	GdkColor color;
 	GtkColorSelectionDialog *dialog;
-	
+
 	dialog = (GtkColorSelectionDialog *)gtk_color_selection_dialog_new(_("Select color"));
 
 	g_signal_connect(G_OBJECT(dialog->colorsel), "color-changed",
--- a/pidgin/pidgincombobox.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/pidgincombobox.c	Sat Mar 07 01:59:40 2009 +0000
@@ -112,7 +112,7 @@
 /* While debugging this evil code, I have learned that
  * there are actually 4 modes to this widget, which can
  * be characterized as follows
- * 
+ *
  * 1) menu mode, no child added
  *
  * tree_view -> NULL
@@ -127,9 +127,9 @@
  * popup_frame -> NULL
  *
  * 2) menu mode, child added
- * 
+ *
  * tree_view -> NULL
- * cell_view -> NULL 
+ * cell_view -> NULL
  * cell_view_frame -> NULL
  * button -> GtkToggleButton set_parent to combo
  * box -> NULL
@@ -140,7 +140,7 @@
  * popup_frame -> NULL
  *
  * 3) list mode, no child added
- * 
+ *
  * tree_view -> GtkTreeView, child of popup_frame
  * cell_view -> GtkCellView, regular child
  * cell_view_frame -> GtkFrame, set parent to combo
@@ -164,7 +164,7 @@
  * popup_widget -> tree_view
  * popup_window -> GtkWindow
  * popup_frame -> GtkFrame, child of popup_window
- * 
+ *
  */
 
 enum {
@@ -285,9 +285,9 @@
 						    gpointer          data);
 
 /* list */
-static void     gtk_combo_box_list_position        (GtkComboBox      *combo_box, 
-						    gint             *x, 
-						    gint             *y, 
+static void     gtk_combo_box_list_position        (GtkComboBox      *combo_box,
+						    gint             *x,
+						    gint             *y,
 						    gint             *width,
 						    gint             *height);
 
@@ -641,7 +641,7 @@
   if (GTK_WIDGET_REALIZED (widget))
     {
       if (combo_box->priv->tree_view && combo_box->priv->cell_view)
-	gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
+	gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
 					    &widget->style->base[GTK_WIDGET_STATE (widget)]);
     }
 
@@ -694,7 +694,7 @@
   gtk_combo_box_check_appearance (combo_box);
 
   if (combo_box->priv->tree_view && combo_box->priv->cell_view)
-    gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
+    gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
 					&widget->style->base[GTK_WIDGET_STATE (widget)]);
 }
 
@@ -725,7 +725,7 @@
       GTK_BIN (container)->child = NULL;
       gtk_widget_queue_resize (GTK_WIDGET (container));
     }
-  
+
   gtk_widget_set_parent (widget, GTK_WIDGET (container));
   GTK_BIN (container)->child = widget;
 
@@ -770,7 +770,7 @@
     appears_as_list = FALSE;
   else
     appears_as_list = TRUE;
-  
+
   if (appears_as_list)
     gtk_combo_box_list_destroy (combo_box);
   else if (GTK_IS_MENU (combo_box->priv->popup_widget))
@@ -785,7 +785,7 @@
       combo_box->priv->cell_view = gtk_cell_view_new ();
       gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (container));
       GTK_BIN (container)->child = combo_box->priv->cell_view;
-      
+
       gtk_widget_show (combo_box->priv->cell_view);
       gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
 			       combo_box->priv->model);
@@ -856,7 +856,7 @@
   g_signal_handlers_disconnect_by_func (menu,
 					gtk_combo_box_menu_hide,
 					combo_box);
-  
+
   combo_box->priv->popup_widget = NULL;
 }
 
@@ -940,12 +940,12 @@
   GdkScreen *screen;
   gint monitor_num;
   GdkRectangle monitor;
-  
+
   /* FIXME: is using the size request here broken? */
    child = GTK_BIN (combo_box)->child;
-   
+
    gdk_window_get_origin (child->window, &sx, &sy);
-   
+
    if (GTK_WIDGET_NO_WINDOW (child))
       {
 	sx += child->allocation.x;
@@ -961,20 +961,20 @@
    *y = sy;
 
   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
-  monitor_num = gdk_screen_get_monitor_at_window (screen, 
+  monitor_num = gdk_screen_get_monitor_at_window (screen,
 						  GTK_WIDGET (combo_box)->window);
   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-  
+
   if (*x < monitor.x)
     *x = monitor.x;
   else if (*x + req.width > monitor.x + monitor.width)
     *x = monitor.x + monitor.width - req.width;
-  
+
   if (monitor.y + monitor.height - *y - child->allocation.height >= req.height)
     *y += child->allocation.height;
   else if (*y - monitor.y >= req.height)
     *y -= req.height;
-  else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y) 
+  else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y)
     *y += child->allocation.height;
   else
     *y -= req.height;
@@ -1001,7 +1001,7 @@
   gint menu_width;
 
   g_return_if_fail (GTK_IS_COMBO_BOX (user_data));
-  
+
   combo_box = GTK_COMBO_BOX (user_data);
   widget = GTK_WIDGET (combo_box);
 
@@ -1042,7 +1042,7 @@
 
   /* Clamp the position on screen */
   screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
-  
+
   if (menu_xpos < 0)
     menu_xpos = 0;
   else if ((menu_xpos + menu_width) > screen_width)
@@ -1066,13 +1066,13 @@
 
   combo_box = GTK_COMBO_BOX (user_data);
 
-  if (combo_box->priv->wrap_width > 0 || combo_box->priv->cell_view == NULL)	
+  if (combo_box->priv->wrap_width > 0 || combo_box->priv->cell_view == NULL)
     gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
   else
     {
       menu_item = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
       if (menu_item)
-	gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget), 
+	gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
 				    menu_item);
 
       gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
@@ -1082,9 +1082,9 @@
 #endif /* Gtk 2.2 */
 
 static void
-gtk_combo_box_list_position (GtkComboBox *combo_box, 
-			     gint        *x, 
-			     gint        *y, 
+gtk_combo_box_list_position (GtkComboBox *combo_box,
+			     gint        *x,
+			     gint        *y,
 			     gint        *width,
 			     gint        *height)
 {
@@ -1095,7 +1095,7 @@
   gint monitor_num;
   GdkRectangle monitor;
 #endif
-  
+
   sample = GTK_BIN (combo_box)->child;
 
   *width = sample->allocation.width;
@@ -1117,30 +1117,30 @@
       *x += sample->allocation.x;
       *y += sample->allocation.y;
     }
-  
+
 #if GTK_CHECK_VERSION(2,2,0)
   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
-  monitor_num = gdk_screen_get_monitor_at_window (screen, 
+  monitor_num = gdk_screen_get_monitor_at_window (screen,
 						  GTK_WIDGET (combo_box)->window);
   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-  
+
   if (*x < monitor.x)
     *x = monitor.x;
   else if (*x + *width > monitor.x + monitor.width)
     *x = monitor.x + monitor.width - *width;
-  
+
   if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
     *y += sample->allocation.height;
   else
     *y -= *height;
 #endif /* Gtk 2.2 */
-} 
+}
 
 /**
  * gtk_combo_box_popup:
  * @combo_box: a #GtkComboBox
- * 
- * Pops up the menu or dropdown list of @combo_box. 
+ *
+ * Pops up the menu or dropdown list of @combo_box.
  *
  * This function is mostly intended for use by accessibility technologies;
  * applications should have little use for it.
@@ -1151,7 +1151,7 @@
 gtk_combo_box_popup (GtkComboBox *combo_box)
 {
   gint x, y, width, height;
-  
+
   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
 
   if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
@@ -1172,7 +1172,7 @@
 	  gtk_widget_set_size_request (combo_box->priv->popup_widget,
                                        MAX (width, requisition.width), -1);
 	}
-      
+
       gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
 		      NULL, NULL,
 #if GTK_CHECK_VERSION(2,2,0)
@@ -1187,7 +1187,7 @@
   gtk_widget_show_all (combo_box->priv->popup_frame);
   gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
 
-  gtk_widget_set_size_request (combo_box->priv->popup_window, width, -1);  
+  gtk_widget_set_size_request (combo_box->priv->popup_window, width, -1);
   gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
 
   /* popup */
@@ -1217,7 +1217,7 @@
 /**
  * gtk_combo_box_popdown:
  * @combo_box: a #GtkComboBox
- * 
+ *
  * Hides the menu or dropdown list of @combo_box.
  *
  * This function is mostly intended for use by accessibility technologies;
@@ -1305,7 +1305,7 @@
       GtkRequisition req;
 
       if (combo_box->priv->cell_view)
-	gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view), 
+	gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
                                        path, &req);
       else
         req.width = 0;
@@ -1335,7 +1335,7 @@
   bin_req.width = MAX (bin_req.width, combo_box->priv->width);
 
   gtk_combo_box_check_appearance (combo_box);
-      
+
   if (!combo_box->priv->tree_view)
     {
       /* menu mode */
@@ -1454,10 +1454,10 @@
           if (is_rtl)
             {
               child.x += req.width;
-              child.width = MAX(1, allocation->x + allocation->width 
+              child.width = MAX(1, allocation->x + allocation->width
                 - (border_width + 1 + xthickness + 2) - child.x);
             }
-          else 
+          else
             {
               child.width = child.x;
               child.x = allocation->x + border_width + 1 + xthickness + 2;
@@ -1623,16 +1623,16 @@
   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
   gint index;
   gint items;
-    
+
   index = gtk_combo_box_get_active (combo_box);
 
   if (index != -1)
     {
       items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
-      
+
       if (event->direction == GDK_SCROLL_UP)
         index--;
-      else 
+      else
         index++;
 
       gtk_combo_box_set_active (combo_box, CLAMP (index, 0, items - 1));
@@ -1694,15 +1694,15 @@
                              GTK_BIN (combo_box)->child->parent);
 
       combo_box->priv->box = gtk_hbox_new (FALSE, 0);
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->button), 
+      gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
 			 combo_box->priv->box);
 
       combo_box->priv->separator = gtk_vseparator_new ();
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->box), 
+      gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
 			 combo_box->priv->separator);
 
       combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->box), 
+      gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
 			 combo_box->priv->arrow);
 
       gtk_widget_show_all (combo_box->priv->button);
@@ -1792,7 +1792,7 @@
 
   /* unparent will remove our latest ref */
   gtk_widget_unparent (combo_box->priv->button);
-  
+
   combo_box->priv->box = NULL;
   combo_box->priv->button = NULL;
   combo_box->priv->arrow = NULL;
@@ -1895,17 +1895,17 @@
   item = nth->data;
   if (nth->prev)
     last = nth->prev->data;
-  else 
+  else
     last = NULL;
   g_list_free (list);
 
   gtk_combo_box_item_get_size (combo_box, index, &cols, &rows);
-      
+
    if (combo_box->priv->col_column == -1 &&
       combo_box->priv->row_column == -1 &&
       last)
     {
-      gtk_container_child_get (GTK_CONTAINER (menu), 
+      gtk_container_child_get (GTK_CONTAINER (menu),
 			       last,
 			       "right_attach", &current_col,
 			       "top_attach", &current_row,
@@ -1926,12 +1926,12 @@
 	      current_col = 0;
 	      current_row++;
 	    }
-	  
+
 	  if (!menu_occupied (GTK_MENU (menu),
 			      current_col, current_col + cols,
 			      current_row, current_row + rows))
 	    break;
-	  
+
 	  current_col++;
 	}
     }
@@ -1949,19 +1949,19 @@
   GtkWidget *menu;
 
   menu = combo_box->priv->popup_widget;
-  
+
   /* do nothing unless we are in menu style and realized */
   if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
     return;
-  
+
   /* get rid of all children */
   list = gtk_container_get_children (GTK_CONTAINER (menu));
-  
+
   for (j = g_list_last (list); j; j = j->prev)
     gtk_container_remove (GTK_CONTAINER (menu), j->data);
-  
+
   g_list_free (list);
-      
+
   /* and relayout */
   gtk_combo_box_menu_fill (combo_box);
 }
@@ -1980,7 +1980,7 @@
   if (event->type == GDK_BUTTON_PRESS && event->button == 1)
     {
       combo_box->priv->popup_in_progress = TRUE;
-      
+
       gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget),
 			   combo_box->priv->active_item);
 
@@ -2053,7 +2053,7 @@
 
   if (!combo_box->priv->tree_view)
     gtk_combo_box_menu_row_deleted (model, path, user_data);
-  
+
   if (index == combo_box->priv->active_item)
     {
       gint items = gtk_tree_model_iter_n_children (model, NULL);
@@ -2090,7 +2090,7 @@
   if (!combo_box->priv->tree_view)
     gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
 }
-						    
+
 static void
 gtk_combo_box_model_row_changed (GtkTreeModel     *model,
 				 GtkTreePath      *path,
@@ -2103,7 +2103,7 @@
   if (index == combo_box->priv->active_item &&
       combo_box->priv->cell_view)
     gtk_widget_queue_resize (GTK_WIDGET (combo_box->priv->cell_view));
-  
+
   if (combo_box->priv->tree_view)
     gtk_combo_box_list_row_changed (model, path, iter, user_data);
   else
@@ -2174,7 +2174,7 @@
 
   gtk_combo_box_relayout (combo_box);
 }
-				    
+
 static void
 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
                                 GtkTreePath  *path,
@@ -2238,12 +2238,12 @@
       gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
                                  GTK_SHADOW_IN);
 
-      gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
+      gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
 					  &GTK_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
 
       combo_box->priv->box = gtk_event_box_new ();
       /*
-      gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box), 
+      gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box),
 					FALSE);
       */
 
@@ -2253,7 +2253,7 @@
       gtk_widget_show_all (combo_box->priv->cell_view_frame);
 
       g_signal_connect (combo_box->priv->box, "button_press_event",
-			G_CALLBACK (gtk_combo_box_list_button_pressed), 
+			G_CALLBACK (gtk_combo_box_list_button_pressed),
 			combo_box);
     }
 
@@ -2269,7 +2269,7 @@
   if (combo_box->priv->model)
     gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
 			     combo_box->priv->model);
-    
+
   g_signal_connect (combo_box->priv->tree_view, "button_press_event",
                     G_CALLBACK (gtk_combo_box_list_button_pressed),
                     combo_box);
@@ -2494,7 +2494,7 @@
   if (combo_box->priv->model)
     items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
 
-  if ((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) && 
+  if ((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) &&
       state == GDK_MOD1_MASK)
     {
       gtk_combo_box_popup (combo_box);
@@ -2502,7 +2502,7 @@
       return TRUE;
     }
 
-  switch (event->keyval) 
+  switch (event->keyval)
     {
     case GDK_Down:
     case GDK_KP_Down:
@@ -2514,20 +2514,20 @@
       break;
     case GDK_Page_Up:
     case GDK_KP_Page_Up:
-    case GDK_Home: 
+    case GDK_Home:
     case GDK_KP_Home:
       new_index = 0;
       break;
     case GDK_Page_Down:
     case GDK_KP_Page_Down:
-    case GDK_End: 
+    case GDK_End:
     case GDK_KP_End:
       new_index = items - 1;
       break;
     default:
       return FALSE;
     }
-      
+
   if (items > 0)
     gtk_combo_box_set_active (combo_box, CLAMP (new_index, 0, items - 1));
 
@@ -2542,14 +2542,14 @@
   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
 
-  if ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) && 
+  if ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
       state == GDK_MOD1_MASK)
     {
       gtk_combo_box_popdown (combo_box);
 
       return TRUE;
     }
-  
+
   return FALSE;
 }
 
@@ -2562,35 +2562,35 @@
   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
 
   if (event->keyval == GDK_Escape ||
-      ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) && 
+      ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
        state == GDK_MOD1_MASK))
     {
       /* reset active item -- this is incredibly lame and ugly */
       gtk_combo_box_set_active (combo_box,
 				gtk_combo_box_get_active (combo_box));
-      
+
       gtk_combo_box_popdown (combo_box);
-      
+
       return TRUE;
     }
-    
+
   if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
-      event->keyval == GDK_space || event->keyval == GDK_KP_Space) 
+      event->keyval == GDK_space || event->keyval == GDK_KP_Space)
   {
     gboolean ret = FALSE;
     GtkTreeIter iter;
     GtkTreeModel *model = NULL;
-    
+
     if (combo_box->priv->model)
       {
 	GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
-    
+
 	ret = gtk_tree_selection_get_selected (sel, &model, &iter);
       }
     if (ret)
       {
 	GtkTreePath *path;
-	
+
 	path = gtk_tree_model_get_path (model, &iter);
 	if (path)
 	  {
@@ -2600,7 +2600,7 @@
       }
 
     gtk_combo_box_popdown (combo_box);
-    
+
     return TRUE;
   }
 
@@ -2620,7 +2620,7 @@
 
   if (width > combo_box->priv->width)
     {
-      if (combo_box->priv->cell_view) 
+      if (combo_box->priv->cell_view)
 	{
 	  gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
 	  gtk_widget_queue_resize (combo_box->priv->cell_view);
@@ -2742,11 +2742,11 @@
   GtkWidget *menu;
   GtkComboBox *combo_box;
   GSList *i;
-  
+
   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
 
   combo_box = GTK_COMBO_BOX (layout);
- 
+
   if (combo_box->priv->cell_view)
     gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
 
@@ -3082,7 +3082,7 @@
 
       gtk_combo_box_check_appearance (combo_box);
       gtk_combo_box_relayout (combo_box);
-      
+
       g_object_notify (G_OBJECT (combo_box), "wrap_width");
     }
 }
@@ -3112,9 +3112,9 @@
   if (row_span != combo_box->priv->row_column)
     {
       combo_box->priv->row_column = row_span;
-      
+
       gtk_combo_box_relayout (combo_box);
- 
+
       g_object_notify (G_OBJECT (combo_box), "row_span_column");
     }
 }
@@ -3144,7 +3144,7 @@
   if (column_span != combo_box->priv->col_column)
     {
       combo_box->priv->col_column = column_span;
-      
+
       gtk_combo_box_relayout (combo_box);
 
       g_object_notify (G_OBJECT (combo_box), "column_span_column");
@@ -3191,7 +3191,7 @@
 
   if (combo_box->priv->active_item == index_)
     return;
-  
+
   gtk_combo_box_set_active_internal (combo_box, index_);
 }
 
@@ -3252,9 +3252,9 @@
  * gtk_combo_box_get_active_iter:
  * @combo_box: A #GtkComboBox
  * @iter: The uninitialized #GtkTreeIter.
- * 
+ *
  * Sets @iter to point to the current active item, if it exists.
- * 
+ *
  * Return value: %TRUE, if @iter was set
  *
  * Since: 2.4
@@ -3293,10 +3293,10 @@
  * gtk_combo_box_set_active_iter:
  * @combo_box: A #GtkComboBox
  * @iter: The #GtkTreeIter.
- * 
- * Sets the current active item to be the one referenced by @iter. 
+ *
+ * Sets the current active item to be the one referenced by @iter.
  * @iter must correspond to a path of depth one.
- * 
+ *
  * Since: 2.4
  **/
 void
@@ -3310,7 +3310,7 @@
   path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
   g_return_if_fail (path != NULL);
   g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
-  
+
   gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
   gtk_tree_path_free (path);
 }
@@ -3320,11 +3320,11 @@
  * @combo_box: A #GtkComboBox.
  * @model: A #GtkTreeModel.
  *
- * Sets the model used by @combo_box to be @model. Will unset a previously set 
+ * Sets the model used by @combo_box to be @model. Will unset a previously set
  * model (if applicable). If @model is %NULL, then it will unset the model.
- * 
- * Note that this function does not clear the cell renderers, you have to 
- * call gtk_combo_box_cell_layout_clear() yourself if you need to set up 
+ *
+ * Note that this function does not clear the cell renderers, you have to
+ * call gtk_combo_box_cell_layout_clear() yourself if you need to set up
  * different cell renderers for the new model.
  *
  * Since: 2.4
@@ -3345,7 +3345,7 @@
 
   if (model == combo_box->priv->model)
     return;
-  
+
   if (combo_box->priv->model)
     gtk_combo_box_unset_model (combo_box);
 
@@ -3368,7 +3368,7 @@
     g_signal_connect (combo_box->priv->model, "row_changed",
 		      G_CALLBACK (gtk_combo_box_model_row_changed),
 		      combo_box);
-      
+
   if (combo_box->priv->tree_view)
     {
       /* list mode */
@@ -3574,7 +3574,7 @@
 {
   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
 
-  gtk_combo_box_popdown (combo_box); 
+  gtk_combo_box_popdown (combo_box);
 
   combo_box->priv->destroying = 1;
 
@@ -3589,14 +3589,14 @@
 {
   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
   GSList *i;
-  
+
   if (GTK_IS_MENU (combo_box->priv->popup_widget))
     {
       gtk_combo_box_menu_destroy (combo_box);
       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
       combo_box->priv->popup_widget = NULL;
     }
-  
+
   if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
     gtk_combo_box_list_destroy (combo_box);
 
@@ -3655,13 +3655,13 @@
 {
   GObject *object = G_OBJECT (child);
   AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);
-                                                                                                   
+
   if (!ai)
     {
       ai = g_new0 (AttachInfo, 1);
       g_object_set_data_full (object, ATTACH_INFO_KEY, ai, g_free);
     }
-                                                                                                   
+
   return ai;
 }
 
@@ -3693,25 +3693,25 @@
                  guint      bottom_attach)
 {
   GtkMenuShell *menu_shell;
-  
+
   g_return_if_fail (GTK_IS_MENU (menu));
   g_return_if_fail (GTK_IS_MENU_ITEM (child));
-  g_return_if_fail (child->parent == NULL || 
+  g_return_if_fail (child->parent == NULL ||
 		    child->parent == GTK_WIDGET (menu));
   g_return_if_fail (left_attach < right_attach);
   g_return_if_fail (top_attach < bottom_attach);
 
   menu_shell = GTK_MENU_SHELL (menu);
-  
+
   if (!child->parent)
     {
       AttachInfo *ai = get_attach_info (child);
-      
+
       ai->left_attach = left_attach;
       ai->right_attach = right_attach;
       ai->top_attach = top_attach;
       ai->bottom_attach = bottom_attach;
-      
+
       menu_shell->children = g_list_append (menu_shell->children, child);
 
       gtk_widget_set_parent (child, GTK_WIDGET (menu));
@@ -3741,7 +3741,7 @@
   /* g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL); */
 
   if (gtk_combo_box_get_active_iter (combo_box, &iter))
-    gtk_tree_model_get (gtk_combo_box_get_model(combo_box), &iter, 
+    gtk_tree_model_get (gtk_combo_box_get_model(combo_box), &iter,
     			0, &text, -1);
   return text;
 }
--- a/pidgin/pidginstock.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/pidginstock.c	Sat Mar 07 01:59:40 2009 +0000
@@ -26,17 +26,32 @@
  */
 #include "internal.h"
 #include "pidgin.h"
+#include "prefs.h"
+
+#include "gtkicon-theme-loader.h"
+#include "theme-manager.h"
 
 #include "pidginstock.h"
 
+/**************************************************************************
+ * Globals
+ **************************************************************************/
+
+static gboolean stock_initted = FALSE;
+static GtkIconSize microscopic, extra_small, small, medium, large, huge;
+
+/**************************************************************************
+ * Structures
+ **************************************************************************/
+
 static struct StockIcon
 {
 	const char *name;
 	const char *dir;
 	const char *filename;
 
-} const stock_icons[] =
-{
+} const stock_icons[] = {
+
 	{ PIDGIN_STOCK_ACTION,          NULL,      GTK_STOCK_EXECUTE          },
 #if GTK_CHECK_VERSION(2,6,0)
 	{ PIDGIN_STOCK_ALIAS,           NULL,      GTK_STOCK_EDIT             },
@@ -98,7 +113,7 @@
 	{ PIDGIN_STOCK_EDIT,                N_("_Edit"),       0, 0, NULL }
 };
 
-static struct SizedStockIcon {
+typedef struct {
   const char *name;
   const char *dir;
   const char *filename;
@@ -110,19 +125,10 @@
   gboolean huge;
   gboolean rtl;
   const char *translucent_name;
-} const sized_stock_icons [] = {
-	{ PIDGIN_STOCK_STATUS_AVAILABLE,   "status", "available.png", 	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AVAILABLE_I },
-	{ PIDGIN_STOCK_STATUS_AWAY, 	   "status", "away.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AWAY_I },
-	{ PIDGIN_STOCK_STATUS_BUSY, 	"status", "busy.png", 		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_BUSY_I },
-	{ PIDGIN_STOCK_STATUS_CHAT, 	"status", "chat.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_STATUS_INVISIBLE,"status", "invisible.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_STATUS_XA, 	"status", "extended-away.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, PIDGIN_STOCK_STATUS_XA_I },
-	{ PIDGIN_STOCK_STATUS_LOGIN, 	"status", "log-in.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
-	{ PIDGIN_STOCK_STATUS_LOGOUT, 	"status", "log-out.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, NULL },
-	{ PIDGIN_STOCK_STATUS_OFFLINE, 	"status", "offline.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_OFFLINE_I  },
-	{ PIDGIN_STOCK_STATUS_PERSON, 	"status", "person.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_STATUS_MESSAGE, 	"toolbar", "message-new.png",   TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	
+} SizedStockIcon;
+
+const SizedStockIcon sized_stock_icons [] = {
+
 	{ PIDGIN_STOCK_STATUS_IGNORED,	"emblems", "blocked.png",	FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_STATUS_FOUNDER,	"emblems", "founder.png",	FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_STATUS_OPERATOR,	"emblems", "operator.png",	FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
@@ -175,39 +181,53 @@
 	{ PIDGIN_STOCK_ANIMATION_TYPING4,  "animations", "typing4.png",FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_ANIMATION_TYPING5,  "animations", "typing5.png",FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 
-	{ PIDGIN_STOCK_TOOLBAR_BGCOLOR, "toolbar", "change-bgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_BLOCK, "emblems", "blocked.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_FGCOLOR, "toolbar", "change-fgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_SMILEY, "toolbar", "emote-select.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_FONT_FACE, "toolbar", "font-face.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, "toolbar", "font-size-down.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, "toolbar", "font-size-up.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_INSERT, "toolbar", "insert.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, "toolbar", "insert-image.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_INSERT_LINK, "toolbar", "insert-link.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, "toolbar", "message-new.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_PENDING, "tray", "tray-new-im.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_PLUGINS, "toolbar", "plugins.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_UNBLOCK, "toolbar", "unblock.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TOOLBAR_TRANSFER, "toolbar", "transfer.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
-
-	{ PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_AWAY, "tray", "tray-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_BUSY, "tray", "tray-busy.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_XA, "tray", "tray-extended-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_OFFLINE, "tray", "tray-offline.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_CONNECT, "tray", "tray-connecting.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_PENDING, "tray", "tray-new-im.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
-	{ PIDGIN_STOCK_TRAY_EMAIL, "tray", "tray-message.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
+	{ PIDGIN_STOCK_TOOLBAR_BGCOLOR,		"toolbar", "change-bgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_BLOCK,		"emblems", "blocked.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_FGCOLOR,		"toolbar", "change-fgcolor.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_SMILEY,		"toolbar", "emote-select.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_FONT_FACE,	"toolbar", "font-face.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER,	"toolbar", "font-size-down.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TEXT_LARGER,	"toolbar", "font-size-up.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_INSERT,		"toolbar", "insert.png", 	 FALSE,	TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE,	"toolbar", "insert-image.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_INSERT_LINK,	"toolbar", "insert-link.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW,	"toolbar", "message-new.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_PENDING,		"toolbar", "message-new.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_PLUGINS,		"toolbar", "plugins.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_UNBLOCK,		"toolbar", "unblock.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR,	"toolbar", "select-avatar.png",	 FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_SEND_FILE,	"toolbar", "send-file.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TRANSFER,	"toolbar", "transfer.png",	 FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  }
 };
 
-static void
-add_sized_icon_common(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
-	       gboolean rtl, const char *size, const char *file,
-		   gboolean translucent);
+const SizedStockIcon sized_status_icons [] = {
+
+	{ PIDGIN_STOCK_STATUS_AVAILABLE, "status", "available.png", 	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AVAILABLE_I },
+	{ PIDGIN_STOCK_STATUS_AWAY, 	 "status", "away.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_AWAY_I },
+	{ PIDGIN_STOCK_STATUS_BUSY, 	 "status", "busy.png", 		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_BUSY_I },
+	{ PIDGIN_STOCK_STATUS_CHAT, 	 "status", "chat.png",		TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
+	{ PIDGIN_STOCK_STATUS_INVISIBLE, "status", "invisible.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
+	{ PIDGIN_STOCK_STATUS_XA, 	 "status", "extended-away.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE,  PIDGIN_STOCK_STATUS_XA_I },
+	{ PIDGIN_STOCK_STATUS_LOGIN, 	 "status", "log-in.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE,  NULL },
+	{ PIDGIN_STOCK_STATUS_LOGOUT, 	 "status", "log-out.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE,  NULL },
+	{ PIDGIN_STOCK_STATUS_OFFLINE, 	 "status", "offline.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, PIDGIN_STOCK_STATUS_OFFLINE_I  },
+	{ PIDGIN_STOCK_STATUS_PERSON, 	 "status", "person.png",	TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_STATUS_MESSAGE, 	 "toolbar", "message-new.png",  TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+
+	{ PIDGIN_STOCK_TRAY_AVAILABLE,	"tray", "tray-online.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_INVISIBLE,	"tray", "tray-invisible.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_AWAY,	"tray", "tray-away.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_BUSY,	"tray", "tray-busy.png", 	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_XA,		"tray", "tray-extended-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_OFFLINE,	"tray", "tray-offline.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_CONNECT,	"tray", "tray-connecting.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_PENDING,	"tray", "tray-new-im.png", 	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TRAY_EMAIL,	"tray", "tray-message.png",	  FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
+};
+
+/*****************************************************************************
+ * Private functions
+ *****************************************************************************/
 
 static gchar *
 find_file_common(const char *name)
@@ -257,16 +277,10 @@
 	return ret;
 }
 
-static void
-add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
-	       gboolean rtl, const char *size, const char *file)
-{
-	add_sized_icon_common(iconset, sizeid, dir, rtl, size, file, FALSE);
-}
 
 /* Altered from do_colorshift in gnome-panel */
 static void
-do_alphashift (GdkPixbuf *dest, GdkPixbuf *src)
+do_alphashift(GdkPixbuf *dest, GdkPixbuf *src)
 {
         gint i, j;
         gint width, height, has_alpha, srcrowstride, destrowstride;
@@ -300,28 +314,48 @@
         }
 }
 
-static void
-add_translucent_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
-	       gboolean rtl, const char *size, const char *file)
+static gchar *
+find_icon_file(PidginStatusIconTheme *theme, const gchar *size, SizedStockIcon sized_icon, gboolean rtl)
 {
-	add_sized_icon_common(iconset, sizeid, dir, rtl, size, file, TRUE);
+	const gchar *file, *dir;
+	gchar *file_full = NULL;
+	gchar *tmp;
+
+	if (theme != NULL) {
+		file = pidgin_icon_theme_get_icon(PIDGIN_ICON_THEME(theme), sized_icon.name);
+		dir = purple_theme_get_dir(PURPLE_THEME(theme));
+
+		if (rtl)
+			file_full = g_build_filename(dir, size, "rtl", file, NULL);
+		else
+			file_full = g_build_filename(dir, size, file, NULL);
+
+		if (g_file_test(file_full, G_FILE_TEST_IS_REGULAR))
+			return file_full;
+
+		g_free(file_full);
+	}
+
+	if (rtl)
+		tmp = g_build_filename("pixmaps", "pidgin", sized_icon.dir, size, "rtl", sized_icon.filename, NULL);
+	else
+		tmp = g_build_filename("pixmaps", "pidgin", sized_icon.dir, size, sized_icon.filename, NULL);
+
+	file_full = find_file_common(tmp);
+	g_free(tmp);
+	return file_full;
 }
 
 static void
-add_sized_icon_common(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
-	       gboolean rtl, const char *size, const char *file,
-		   gboolean translucent)
+add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginStatusIconTheme *theme,
+		const char *size, SizedStockIcon sized_icon, gboolean translucent)
 {
-	char *filename, *subpath;
+	char *filename;
 	GtkIconSource *source;
 	GdkPixbuf *pixbuf;
 
-	subpath = g_build_filename("pixmaps", "pidgin", dir, size, file, NULL);
-	filename = find_file_common(subpath);
-	g_free(subpath);
-	if (!filename)
-		return;
-
+	filename = find_icon_file(theme, size, sized_icon, FALSE);
+	g_return_if_fail(filename != NULL);
 	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 	if (translucent)
 		do_alphashift(pixbuf, pixbuf);
@@ -329,7 +363,7 @@
 	source = gtk_icon_source_new();
 	gtk_icon_source_set_pixbuf(source, pixbuf);
 	gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR);
-	gtk_icon_source_set_direction_wildcarded(source, !rtl);
+	gtk_icon_source_set_direction_wildcarded(source, !sized_icon.rtl);
 	gtk_icon_source_set_size(source, sizeid);
 	gtk_icon_source_set_size_wildcarded(source, FALSE);
 	gtk_icon_source_set_state_wildcarded(source, TRUE);
@@ -349,17 +383,16 @@
 	g_free(filename);
 	g_object_unref(pixbuf);
 
-	if (rtl) {
-		subpath = g_build_filename("pixmaps", "pidgin", dir, size, "rtl", file, NULL);
-		filename = find_file_common(subpath);
-		g_free(subpath);
-		if (!filename)
-			return;
+	if (sized_icon.rtl) {
+		filename = find_icon_file(theme, size, sized_icon, TRUE);
+		g_return_if_fail(filename != NULL);
 		pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 		if (translucent)
 			do_alphashift(pixbuf, pixbuf);
+
 		source = gtk_icon_source_new();
 		gtk_icon_source_set_pixbuf(source, pixbuf);
+		gtk_icon_source_set_filename(source, filename);
 		gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
 		gtk_icon_source_set_size(source, sizeid);
 		gtk_icon_source_set_size_wildcarded(source, FALSE);
@@ -371,20 +404,90 @@
 	}
 }
 
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+void
+pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme)
+{
+	GtkIconFactory *icon_factory;
+	gint i;
+	GtkIconSet *normal;
+	GtkIconSet *translucent = NULL;
+	GtkWidget *win;
+
+	if (theme != NULL) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/status/icon-theme",
+				        purple_theme_get_name(PURPLE_THEME(theme)));
+		purple_prefs_set_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir",
+				      purple_theme_get_dir(PURPLE_THEME(theme)));
+	}
+	else {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/status/icon-theme", "");
+		purple_prefs_set_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", "");
+	}
+
+	icon_factory = gtk_icon_factory_new();
+
+	gtk_icon_factory_add_default(icon_factory);
+
+	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_widget_realize(win);
+
+	for (i = 0; i < G_N_ELEMENTS(sized_status_icons); i++)
+	{
+		normal = gtk_icon_set_new();
+		if (sized_status_icons[i].translucent_name)
+			translucent = gtk_icon_set_new();
+
+#define ADD_SIZED_ICON(name, size) if (sized_status_icons[i].name) { \
+					add_sized_icon(normal, name, theme, size, sized_status_icons[i], FALSE); \
+					if (sized_status_icons[i].translucent_name) \
+						add_sized_icon(translucent, name, theme, size, sized_status_icons[i], TRUE); \
+				   }
+		ADD_SIZED_ICON(microscopic, "11");
+		ADD_SIZED_ICON(extra_small, "16");
+		ADD_SIZED_ICON(small, "22");
+		ADD_SIZED_ICON(medium, "32");
+		ADD_SIZED_ICON(large, "48");
+		ADD_SIZED_ICON(huge, "64");
+#undef ADD_SIZED_ICON
+
+		gtk_icon_factory_add(icon_factory, sized_status_icons[i].name, normal);
+		gtk_icon_set_unref(normal);
+
+		if (sized_status_icons[i].translucent_name) {
+			gtk_icon_factory_add(icon_factory, sized_status_icons[i].translucent_name, translucent);
+			gtk_icon_set_unref(translucent);
+		}
+	}
+
+
+	gtk_widget_destroy(win);
+	g_object_unref(G_OBJECT(icon_factory));
+}
+
 void
 pidgin_stock_init(void)
 {
-	static gboolean stock_initted = FALSE;
 	GtkIconFactory *icon_factory;
 	size_t i;
 	GtkWidget *win;
-	GtkIconSize microscopic, extra_small, small, medium, large, huge;
+	PidginIconThemeLoader *loader;
+	const gchar *path = NULL;
 
 	if (stock_initted)
 		return;
 
 	stock_initted = TRUE;
 
+	/* Setup the status icon theme */
+	loader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "status-icon", NULL);
+	purple_theme_manager_register_type(PURPLE_THEME_LOADER(loader));
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/status/icon-theme", "");
+	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", "");
+
 	/* Setup the icon factory. */
 	icon_factory = gtk_icon_factory_new();
 
@@ -394,6 +497,7 @@
 	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 	gtk_widget_realize(win);
 
+	/* All non-sized icons */
 	for (i = 0; i < G_N_ELEMENTS(stock_icons); i++)
 	{
 		GtkIconSource *source;
@@ -432,7 +536,6 @@
 	}
 
 	/* register custom icon sizes */
-
 	microscopic =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11);
 	extra_small =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16);
 	small =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22);
@@ -440,18 +543,13 @@
 	large =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_LARGE, 48, 48);
 	huge =         gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_HUGE, 64, 64);
 
+	/* All non-status sized icons */
 	for (i = 0; i < G_N_ELEMENTS(sized_stock_icons); i++)
 	{
-		GtkIconSet *iconset;
-
-		iconset = gtk_icon_set_new();
+		GtkIconSet *iconset = gtk_icon_set_new();
 
-#define ADD_SIZED_ICON(name, size) do { \
-		if (sized_stock_icons[i].name)  \
-			add_sized_icon(iconset, name,  \
-					sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \
-					size, sized_stock_icons[i].filename); \
-		} while (0)
+#define ADD_SIZED_ICON(name, size) if (sized_stock_icons[i].name) \
+					add_sized_icon(iconset, name, NULL, size, sized_stock_icons[i], FALSE);
 		ADD_SIZED_ICON(microscopic, "11");
 		ADD_SIZED_ICON(extra_small, "16");
 		ADD_SIZED_ICON(small, "22");
@@ -462,32 +560,21 @@
 
 		gtk_icon_factory_add(icon_factory, sized_stock_icons[i].name, iconset);
 		gtk_icon_set_unref(iconset);
-
-		if (sized_stock_icons[i].translucent_name) {
-			iconset = gtk_icon_set_new();
-
-#define ADD_TRANS_ICON(name, size) do { \
-			if (sized_stock_icons[i].name) \
-				add_translucent_sized_icon(iconset, name, \
-						sized_stock_icons[i].dir, sized_stock_icons[i].rtl, \
-						size, sized_stock_icons[i].filename); \
-			} while (0)
-			ADD_TRANS_ICON(microscopic, "11");
-			ADD_TRANS_ICON(extra_small, "16");
-			ADD_TRANS_ICON(small, "22");
-			ADD_TRANS_ICON(medium, "32");
-			ADD_TRANS_ICON(large, "48");
-			ADD_TRANS_ICON(huge, "64");
-#undef ADD_TRANS_ICON
-
-			gtk_icon_factory_add(icon_factory, sized_stock_icons[i].translucent_name, iconset);
-			gtk_icon_set_unref(iconset);
-		}
 	}
 
 	gtk_widget_destroy(win);
 	g_object_unref(G_OBJECT(icon_factory));
 
+	/* Pre-load Status icon theme - this avoids a bug with displaying the correct icon in the tray, theme is destroyed after*/
+	if (purple_prefs_get_string(PIDGIN_PREFS_ROOT "/icon/status/theme") &&
+	   (path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir"))) {
+
+		PidginStatusIconTheme *theme = PIDGIN_STATUS_ICON_THEME(purple_theme_loader_build(PURPLE_THEME_LOADER(loader), path));
+		pidgin_stock_load_status_icon_theme(theme);
+		g_object_unref(G_OBJECT(theme));
+
+	} else pidgin_stock_load_status_icon_theme(NULL);
+
 	/* Register the stock items. */
 	gtk_stock_add_static(stock_items, G_N_ELEMENTS(stock_items));
 }
--- a/pidgin/pidginstock.h	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/pidginstock.h	Sat Mar 07 01:59:40 2009 +0000
@@ -8,7 +8,7 @@
  * 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
@@ -24,6 +24,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include <gtk/gtkstock.h>
+#include "gtkstatus-icon-theme.h"
 
 #ifndef _PIDGIN_STOCK_H_
 #define _PIDGIN_STOCK_H_
@@ -177,6 +178,14 @@
 #define PIDGIN_ICON_SIZE_TANGO_MEDIUM         "pidgin-icon-size-tango-medium"
 #define PIDGIN_ICON_SIZE_TANGO_LARGE          "pidgin-icon-size-tango-large"
 #define PIDGIN_ICON_SIZE_TANGO_HUGE           "pidgin-icon-size-tango-huge"
+
+/**
+ * Loades all of the icons from the status icon theme into Pidgin stock
+ *
+ * @param theme		the theme to load, or null to load all the default icons
+ */
+void pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme);
+
 /**
  * Sets up the purple stock repository.
  */
--- a/pidgin/pixmaps/Makefile.am	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/pixmaps/Makefile.am	Sat Mar 07 01:59:40 2009 +0000
@@ -488,6 +488,7 @@
 		tray/32/tray-connecting.png \
 		tray/32/tray-extended-away.png \
 		tray/32/tray-invisible.png \
+		tray/32/tray-message.png \
 		tray/32/tray-new-im.png \
 		tray/32/tray-offline.png \
 		tray/32/tray-online.png
@@ -498,6 +499,7 @@
 		tray/48/tray-connecting.png \
 		tray/48/tray-extended-away.png \
 		tray/48/tray-invisible.png \
+		tray/48/tray-message.png \
 		tray/48/tray-new-im.png \
 		tray/48/tray-offline.png \
 		tray/48/tray-online.png
@@ -525,7 +527,6 @@
 		$(PROTOCOLS_16_SCALABLE)	\
 		$(PROTOCOLS_22_SCALABLE)	\
 		$(PROTOCOLS_48_SCALABLE)	\
-		$(TOOLBAR_11)		\
 		$(TOOLBAR_16_SCALABLE)	\
 		$(TOOLBAR_22_SCALABLE)
 
@@ -553,6 +554,7 @@
 		$(STATUS_32_RTL) \
 		$(STATUS_48) \
 		$(STATUS_48_RTL) \
+		$(TOOLBAR_11) \
 		$(TOOLBAR_16) \
 		$(TOOLBAR_22) \
 		$(TRAY_16) \
--- a/pidgin/plugins/gevolution/gevo-util.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/plugins/gevolution/gevo-util.c	Sat Mar 07 01:59:40 2009 +0000
@@ -27,13 +27,13 @@
 
 void
 gevo_add_buddy(PurpleAccount *account, const char *group_name,
-			   const char *screenname, const char *alias)
+			   const char *buddy_name, const char *alias)
 {
 	PurpleConversation *conv = NULL;
 	PurpleBuddy *buddy;
 	PurpleGroup *group;
 
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, screenname, account);
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy_name, account);
 
 	if ((group = purple_find_group(group_name)) == NULL)
 	{
@@ -41,7 +41,7 @@
 		purple_blist_add_group(group, NULL);
 	}
 
-	buddy = purple_buddy_new(account, screenname, alias);
+	buddy = purple_buddy_new(account, buddy_name, alias);
 	purple_blist_add_buddy(buddy, NULL, group, NULL);
 	purple_account_add_buddy(account, buddy);
 
--- a/pidgin/plugins/gevolution/gevolution.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/plugins/gevolution/gevolution.c	Sat Mar 07 01:59:40 2009 +0000
@@ -52,7 +52,7 @@
 {
 	COLUMN_AUTOADD,
 	COLUMN_ICON,
-	COLUMN_SCREENNAME,
+	COLUMN_USERNAME,
 	COLUMN_DATA,
 	NUM_COLUMNS
 };
@@ -463,11 +463,11 @@
 	gtk_tree_view_column_add_attribute(column, renderer,
 									   "pixbuf", COLUMN_ICON);
 
-	/* Screenname */
+	/* Username */
 	renderer = gtk_cell_renderer_text_new();
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer,
-									   "text", COLUMN_SCREENNAME);
+									   "text", COLUMN_USERNAME);
 
 
 	/* Populate */
@@ -489,7 +489,7 @@
 						   purple_account_get_bool(account, "gevo-autoadd",
 												 FALSE),
 						   COLUMN_ICON, pixbuf,
-						   COLUMN_SCREENNAME,
+						   COLUMN_USERNAME,
 						   purple_account_get_username(account),
 						   COLUMN_DATA, account,
 						   -1);
--- a/pidgin/plugins/gevolution/new_person_dialog.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/plugins/gevolution/new_person_dialog.c	Sat Mar 07 01:59:40 2009 +0000
@@ -67,7 +67,7 @@
 }
 
 static void
-screenname_changed_cb(GtkEntry *entry, GevoNewPersonDialog *dialog)
+username_changed_cb(GtkEntry *entry, GevoNewPersonDialog *dialog)
 {
 	gtk_widget_set_sensitive(dialog->add_button,
 							 *gtk_entry_get_text(entry) != '\0');
@@ -85,7 +85,7 @@
 add_cb(GtkWidget *w, GevoNewPersonDialog *dialog)
 {
 	EContact *contact = NULL;
-	const char *screenname;
+	const char *username;
 	const char *firstname;
 	const char *lastname;
 	const char *email;
@@ -96,9 +96,9 @@
 	char *full_name = NULL;
 
 	if (dialog->person_only)
-		screenname = dialog->buddy->name;
+		username = dialog->buddy->name;
 	else
-		screenname = gtk_entry_get_text(GTK_ENTRY(dialog->screenname));
+		username = gtk_entry_get_text(GTK_ENTRY(dialog->username));
 
 	firstname  = gtk_entry_get_text(GTK_ENTRY(dialog->firstname));
 	lastname   = gtk_entry_get_text(GTK_ENTRY(dialog->lastname));
@@ -143,7 +143,7 @@
 
 		if (!strcmp(im_service, "prpl-oscar"))
 		{
-			if (isdigit(*screenname))
+			if (isdigit(*username))
 				field = E_CONTACT_IM_ICQ;
 			else
 				field = E_CONTACT_IM_AIM;
@@ -163,7 +163,7 @@
 
 		if (field > 0)
 		{
-			GList *list = g_list_append(NULL, g_strdup(screenname));
+			GList *list = g_list_append(NULL, g_strdup(username));
 
 			e_contact_set(contact, field, list);
 
@@ -203,7 +203,7 @@
 
 		group_name = pidgin_text_combo_box_entry_get_text(dialog->group_combo);
 
-		gevo_add_buddy(dialog->account, group_name, screenname, full_name);
+		gevo_add_buddy(dialog->account, group_name, username, full_name);
 	}
 
 	if (name != NULL)
@@ -289,15 +289,15 @@
 											 NULL, dialog);
 		add_pref_box(sg, vbox, _("Account type:"), dialog->accounts_menu);
 
-		/* Screen Name */
-		dialog->screenname = gtk_entry_new();
-		add_pref_box(sg, vbox, _("Username:"), dialog->screenname);
+		/* Username */
+		dialog->username = gtk_entry_new();
+		add_pref_box(sg, vbox, _("Username:"), dialog->username);
 
 		if (username != NULL)
-			gtk_entry_set_text(GTK_ENTRY(dialog->screenname), username);
+			gtk_entry_set_text(GTK_ENTRY(dialog->username), username);
 
-		g_signal_connect(G_OBJECT(dialog->screenname), "changed",
-						 G_CALLBACK(screenname_changed_cb), dialog);
+		g_signal_connect(G_OBJECT(dialog->username), "changed",
+						 G_CALLBACK(username_changed_cb), dialog);
 
 		/* Group */
 		dialog->group_combo = pidgin_text_combo_box_entry_new(NULL,
--- a/pidgin/plugins/history.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/plugins/history.c	Sat Mar 07 01:59:40 2009 +0000
@@ -62,36 +62,39 @@
 			return;
 
 		/* Find buddies for this conversation. */
-	        buddies = purple_find_buddies(account, name);
+		buddies = purple_find_buddies(account, name);
 
 		/* If we found at least one buddy, save the first buddy's alias. */
 		if (buddies != NULL)
 			alias = purple_buddy_get_contact_alias((PurpleBuddy *)buddies->data);
 
-	        for (cur = buddies; cur != NULL; cur = cur->next)
-	        {
-	                PurpleBlistNode *node = cur->data;
-	                if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL)))
-	                {
+		for (cur = buddies; cur != NULL; cur = cur->next)
+		{
+			PurpleBlistNode *node = cur->data;
+			PurpleBlistNode *prev = purple_blist_node_get_sibling_prev(node);
+			PurpleBlistNode *next = purple_blist_node_get_sibling_next(node);
+			if ((node != NULL) && ((prev != NULL) || (next != NULL)))
+			{
 				PurpleBlistNode *node2;
+				PurpleBlistNode *parent = purple_blist_node_get_parent(node);
+				PurpleBlistNode *child = purple_blist_node_get_first_child(parent);
 
 				alias = purple_buddy_get_contact_alias((PurpleBuddy *)node);
 
 				/* We've found a buddy that matches this conversation.  It's part of a
 				 * PurpleContact with more than one PurpleBuddy.  Loop through the PurpleBuddies
 				 * in the contact and get all the logs. */
-				for (node2 = node->parent->child ; node2 != NULL ; node2 = node2->next)
+				for (node2 = child ; node2 != NULL ; node2 = purple_blist_node_get_sibling_next(node2))
 				{
-					logs = g_list_concat(
-						purple_log_get_logs(PURPLE_LOG_IM,
+					logs = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM,
 							purple_buddy_get_name((PurpleBuddy *)node2),
 							purple_buddy_get_account((PurpleBuddy *)node2)),
-						logs);
+							logs);
 				}
 				break;
-	                }
-	        }
-	        g_slist_free(buddies);
+			}
+		}
+		g_slist_free(buddies);
 
 		if (logs == NULL)
 			logs = purple_log_get_logs(PURPLE_LOG_IM, name, account);
@@ -118,7 +121,7 @@
 
 	protocol = g_strdup(gtk_imhtml_get_protocol_name(GTK_IMHTML(gtkconv->imhtml)));
 	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
-							      purple_account_get_protocol_name(((PurpleLog*)logs->data)->account));
+			purple_account_get_protocol_name(((PurpleLog*)logs->data)->account));
 
 	if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
 		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", options);
--- a/pidgin/plugins/perl/common/GtkLog.xs	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/plugins/perl/common/GtkLog.xs	Sat Mar 07 01:59:40 2009 +0000
@@ -7,9 +7,9 @@
 pidgin_log_get_handle()
 
 void
-pidgin_log_show(type, screenname, account)
+pidgin_log_show(type, buddyname, account)
 	Purple::LogType type
-	const char * screenname
+	const char * buddyname
 	Purple::Account account
 
 void
--- a/pidgin/plugins/ticker/ticker.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/plugins/ticker/ticker.c	Sat Mar 07 01:59:40 2009 +0000
@@ -91,7 +91,9 @@
 	PurpleContact *contact = user_data;
 	PurpleBuddy *b = purple_contact_get_priority_buddy(contact);
 
-	purple_conversation_new(PURPLE_CONV_TYPE_IM, b->account, b->name);
+	purple_conversation_new(PURPLE_CONV_TYPE_IM,
+	                        purple_buddy_get_account(b),
+							purple_buddy_get_name(b));
 	return TRUE;
 }
 
@@ -217,20 +219,25 @@
 
 static void buddy_ticker_show(void)
 {
-	PurpleBuddyList *list = purple_get_blist();
 	PurpleBlistNode *gnode, *cnode, *bnode;
 	PurpleBuddy *b;
 
-	if(!list)
-		return;
-
-	for(gnode = list->root; gnode; gnode = gnode->next) {
+	for(gnode = purple_blist_get_root();
+	    gnode;
+	    gnode = purple_blist_node_get_sibling_next(gnode))
+	{
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
-		for(cnode = gnode->child; cnode; cnode = cnode->next) {
+		for(cnode = purple_blist_node_get_first_child(gnode);
+		    cnode;
+		    cnode = purple_blist_node_get_sibling_next(cnode))
+		{
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for(bnode = cnode->child; bnode; bnode = bnode->next) {
+			for(bnode = purple_blist_node_get_first_child(cnode);
+			    bnode;
+			    bnode = purple_blist_node_get_sibling_next(bnode))
+			{
 				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 				b = (PurpleBuddy *)bnode;
--- a/pidgin/plugins/win32/winprefs/gtkappbar.c	Sat Mar 07 01:36:57 2009 +0000
+++ b/pidgin/plugins/win32/winprefs/gtkappbar.c	Sat Mar 07 01:59:40 2009 +0000
@@ -376,7 +376,7 @@
 		APPBARDATA abd;
 		MSG *msg = (MSG*)xevent;
 
-		purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_poschanged\n");
+		purple_debug(PURPLE_DEBUG_MISC, "gtkappbar", "wnd_poschanged\n");
 
 		abd.hWnd = msg->hwnd;
 		abd.cbSize = sizeof(APPBARDATA);
@@ -391,7 +391,7 @@
         MSG *msg = (MSG*)xevent;
         WINDOWPOS *wpos = (WINDOWPOS*)msg->lParam;
 
-        purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "wnd_poschanging\n");
+        purple_debug(PURPLE_DEBUG_MISC, "gtkappbar", "wnd_poschanging\n");
 
         if(ab->docked || ab->docking) {
                 wpos->x = ab->docked_rect.left;
@@ -526,7 +526,7 @@
 	        break;
 
         case ABN_FULLSCREENAPP:
-                purple_debug(PURPLE_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback: ABN_FULLSCREENAPP: %d\n", (BOOL)msg->lParam);
+                purple_debug(PURPLE_DEBUG_MISC, "gtkappbar", "gtk_appbar_callback: ABN_FULLSCREENAPP: %d\n", (BOOL)msg->lParam);
 		if (!ab->iconized && ab->docked) {
 		if ((BOOL)msg->lParam) {
 			SetWindowPos(msg->hwnd, HWND_BOTTOM, 0, 0, 0, 0,
--- a/po/ChangeLog	Sat Mar 07 01:36:57 2009 +0000
+++ b/po/ChangeLog	Sat Mar 07 01:59:40 2009 +0000
@@ -1,5 +1,7 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.6.0
+
 version 2.5.5
 	* Afrikaans translation updated (Friedel Wolff)
 	* Bengali translation updated (Israt Jahan)
--- a/po/POTFILES.in	Sat Mar 07 01:36:57 2009 +0000
+++ b/po/POTFILES.in	Sat Mar 07 01:59:40 2009 +0000
@@ -184,6 +184,7 @@
 libpurple/status.c
 libpurple/util.c
 libpurple/win32/libc_interface.c
+libpurple/xmlnode.c
 pidgin.desktop.in
 pidgin/eggtrayicon.c
 pidgin/gtkaccount.c
--- a/po/README	Sat Mar 07 01:36:57 2009 +0000
+++ b/po/README	Sat Mar 07 01:59:40 2009 +0000
@@ -1,2 +1,2 @@
-For information on translating Pidgin, Libpurple, and Finch, please see
+For information on translating Pidgin, libpurple, and Finch, please see
 our wiki page at http://developer.pidgin.im/wiki/TipsForTranslators.
--- a/po/ca.po	Sat Mar 07 01:36:57 2009 +0000
+++ b/po/ca.po	Sat Mar 07 01:59:40 2009 +0000
@@ -33,8 +33,8 @@
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-02-22 10:28+0100\n"
-"PO-Revision-Date: 2009-02-22 15:18+0100\n"
+"POT-Creation-Date: 2009-03-05 22:22+0100\n"
+"PO-Revision-Date: 2009-03-05 22:37+0100\n"
 "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n"
 "Language-Team: Catalan <tradgnome@softcatala.net>\n"
 "MIME-Version: 1.0\n"
@@ -4468,17 +4468,24 @@
 msgstr "No s'ha pogut fer ping a l'usuari %s"
 
 #, c-format
-msgid "Unable to buzz, because there is nothing known about user %s."
+msgid "Unable to buzz, because there is nothing known about %s."
 msgstr "No s'ha pogut botzinar perquè no es coneix res de l'usuari %s."
 
 #, c-format
-msgid "Unable to buzz, because user %s might be offline."
+msgid "Unable to buzz, because %s might be offline."
 msgstr ""
 "No s'ha pogut botzinar possiblement perquè l'usuari %s està desconnectat."
 
 #, c-format
-msgid "Unable to buzz, because the user %s does not support it."
-msgstr "No s'ha pogut botzinar perquè l'usuari %s no ho permet."
+msgid ""
+"Unable to buzz, because %s does not support it or do not wish to receive "
+"buzzes now."
+msgstr ""
+"No s'ha pogut botzinar perquè no és pot, o bé l'usuari %s no ho permet."
+
+#, c-format
+msgid "Buzzing %s..."
+msgstr "S'està botzinant a %s..."
 
 #. Yahoo only supports one attention command: the 'buzz'.
 #. This is index number YAHOO_BUZZ.
@@ -4489,10 +4496,6 @@
 msgid "%s has buzzed you!"
 msgstr "%s us ha botzinat!"
 
-#, c-format
-msgid "Buzzing %s..."
-msgstr "S'està botzinant a %s..."
-
 msgid "config:  Configure a chat room."
 msgstr "config:  configura la sala de xat."
 
@@ -4646,6 +4649,19 @@
 msgid "Error in chat %s"
 msgstr "S'ha produït un error en el xat %s"
 
+#, fuzzy
+msgid "An error occured on the in-band bytestream transfer\n"
+msgstr "S'ha produït un error en obrir el fitxer."
+
+msgid "Transfer was closed."
+msgstr "La transferència s'ha tancat."
+
+msgid "Failed to open the file"
+msgstr "No s'ha pogut obrir el fitxer"
+
+msgid "Failed to open in-band bytestream"
+msgstr ""
+
 #, c-format
 msgid "Unable to send file to %s, user does not support file transfers"
 msgstr ""
@@ -5794,26 +5810,25 @@
 msgid "Zap"
 msgstr ""
 
-#, fuzzy, c-format
+#, c-format
 msgid "%s has zapped you!"
-msgstr "%s us ha afegit [%s]"
-
-#, fuzzy, c-format
+msgstr ""
+
+#, c-format
 msgid "Zapping %s..."
-msgstr "S'està trucant a %s"
+msgstr ""
 
 #. Whack means "to hit or strike someone with a sharp blow"
-#, fuzzy
 msgid "Whack"
 msgstr "bufetejar"
 
-#, fuzzy, c-format
+#, c-format
 msgid "%s has whacked you!"
-msgstr "%s us ha afegit [%s]"
-
-#, fuzzy, c-format
+msgstr "%s us ha bufetejat [%s]"
+
+#, c-format
 msgid "Whacking %s..."
-msgstr "Bufetejant"
+msgstr "S'està bufetejant %s..."
 
 #. Torch means "to set on fire."  Don't worry, this doesn't
 #. * make a whole lot of sense in English, either.  Feel free
@@ -5897,15 +5912,15 @@
 #. * someone to perform a mischievous trick or practical joke.
 #, fuzzy
 msgid "Punk"
-msgstr "Ping"
+msgstr "Enredar"
 
 #, fuzzy, c-format
 msgid "%s has punk'd you!"
-msgstr "%s s'ha connectat."
+msgstr "%s us ha enredat!"
 
 #, fuzzy, c-format
 msgid "Punking %s..."
-msgstr "Ping"
+msgstr "S'està enredant %s..."
 
 #. Raspberry is a slang term for the vibrating sound made
 #. * when you stick your tongue out of your mouth with your
@@ -6513,7 +6528,7 @@
 "començar amb una lletra i contenir només lletres, nombres o espais, o només "
 "nombres."
 
-#. Unregistered screen name
+#. Unregistered username
 #. uid is not exist
 msgid "Invalid username."
 msgstr "El nom d'usuari no és vàlid"
@@ -6530,7 +6545,7 @@
 msgstr ""
 "El servei de missatges instantanis d'AOL no està disponible temporalment."
 
-#. screen name connecting too frequently
+#. username connecting too frequently
 #. IP address connecting too frequently
 msgid ""
 "You have been connecting and disconnecting too frequently. Wait ten minutes "
@@ -6717,7 +6732,7 @@
 msgstr[0] "Heu perdut %hu missatge de %s per motius desconeguts."
 msgstr[1] "Heu perdut %hu missatges de %s per motius desconeguts."
 
-#. Data is assumed to be the destination sn
+#. Data is assumed to be the destination bn
 #, c-format
 msgid "Unable to send message: %s"
 msgstr "No s'ha pogut enviar el missatge: %s"
@@ -7308,6 +7323,35 @@
 msgid "Could not change buddy information."
 msgstr "No s'ha pogut canviar la informació l'amic."
 
+msgid "Mobile"
+msgstr "Mòbil"
+
+msgid "Note"
+msgstr "Nota"
+
+# FIXME: "memo", el qq té una terminologia molt peculiar
+#. callback
+msgid "Buddy Memo"
+msgstr "Memo de l'amic"
+
+msgid "Change his/her memo as you like"
+msgstr "Canvieu-ne el memo"
+
+msgid "_Modify"
+msgstr "_Modifica"
+
+msgid "Memo Modify"
+msgstr "Modifica el memo"
+
+msgid "Server says:"
+msgstr "El servidor diu:"
+
+msgid "Your request was accepted."
+msgstr "S'ha acceptat la vostra sol·licitud."
+
+msgid "Your request was rejected."
+msgstr "S'ha rebutjat la vostra sol·licitud."
+
 #, c-format
 msgid "%u requires verification"
 msgstr "Cal verificació per a %u"
@@ -7620,6 +7664,9 @@
 msgid "<p><b>Acknowledgement</b>:<br>\n"
 msgstr "<p><b>Reconeixement</b>:<br>\n"
 
+msgid "<p><b>Scrupulous Testers</b>:<br>\n"
+msgstr "<p><b>Comprovadors del codi</b>:<br>\n"
+
 # FIXME: ush... traducció lliure... 
 msgid "<p><i>And, all the boys in the backroom...</i><br>\n"
 msgstr "<p><i>I tothom que ho ha fet possible...<i><br>\n"
@@ -7646,6 +7693,9 @@
 msgid "About OpenQ"
 msgstr "Quant a l'OpenQ"
 
+msgid "Modify Buddy Memo"
+msgstr "Modifica el memo de l'amic"
+
 #. *< type
 #. *< ui_requirement
 #. *< flags
@@ -8662,9 +8712,6 @@
 msgid "Unit"
 msgstr "Unitat"
 
-msgid "Note"
-msgstr "Nota"
-
 msgid "Join Chat"
 msgstr "Entra a un xat"
 
@@ -10166,9 +10213,6 @@
 msgid "Extended away"
 msgstr "Absent durant una bona estona"
 
-msgid "Mobile"
-msgstr "Mòbil"
-
 # És un estat, com "fora de línia", etc. (josep)
 msgid "Listening to music"
 msgstr "Escoltant música"
@@ -10211,18 +10255,6 @@
 msgid "%x %X"
 msgstr "%x %X"
 
-#, c-format
-msgid "Error Reading %s"
-msgstr "S'ha produït un error en llegir %s"
-
-#, c-format
-msgid ""
-"An error was encountered reading your %s.  They have not been loaded, and "
-"the old file has been renamed to %s~."
-msgstr ""
-"S'ha produït un error en llegir el vostre %s. No s'han carregat, i s'ha "
-"canviat el nom del fitxer per %s~."
-
 msgid "Calculating..."
 msgstr "S'està calculant..."
 
@@ -10332,6 +10364,18 @@
 msgid "Address already in use."
 msgstr "Aquesta adreça ja s'està fent servir"
 
+#, c-format
+msgid "Error Reading %s"
+msgstr "S'ha produït un error en llegir %s"
+
+#, c-format
+msgid ""
+"An error was encountered reading your %s.  The file has not been loaded, and "
+"the old file has been renamed to %s~."
+msgstr ""
+"S'ha produït un error en llegir el vostre %s. No s'ha carregat el fitxer, i "
+"s'ha canviat el nom per %s~."
+
 msgid "Internet Messenger"
 msgstr "Missatger d'Internet"
 
@@ -10657,6 +10701,9 @@
 msgid "/Tools/_Certificates"
 msgstr "/Eines/C_ertificats"
 
+msgid "/Tools/Custom Smile_ys"
+msgstr "/Eines/Em_oticones personalitzades"
+
 msgid "/Tools/Plu_gins"
 msgstr "/Eines/_Connectors"
 
@@ -10666,9 +10713,6 @@
 msgid "/Tools/Pr_ivacy"
 msgstr "/Eines/_Privadesa"
 
-msgid "/Tools/Smile_y"
-msgstr "/Eines/Em_oticona"
-
 msgid "/Tools/_File Transfers"
 msgstr "/Eines/_Transferència de fitxers"
 
@@ -10786,8 +10830,8 @@
 msgid "By status"
 msgstr "Per estat"
 
-msgid "By log size"
-msgstr "Per la mida del registre"
+msgid "By recent log activity"
+msgstr "Per activitat recent en el registre"
 
 #, c-format
 msgid "%s disconnected"
@@ -11902,15 +11946,6 @@
 msgid "Enable typing notification"
 msgstr "Habilita les notificacions de que s'està escrivint"
 
-msgid "_Copy Email Address"
-msgstr "_Copia l'adreça de correu"
-
-msgid "_Open Link in Browser"
-msgstr "_Obre l'enllaç en el navegador"
-
-msgid "_Copy Link Location"
-msgstr "_Copia la ubicació de l'enllaç"
-
 msgid ""
 "<span size='larger' weight='bold'>Unrecognized file type</span>\n"
 "\n"
@@ -12169,6 +12204,7 @@
 "\n"
 "  -c, --config=DIR    use DIR for config files\n"
 "  -d, --debug         print debugging messages to stdout\n"
+"  -f, --force-online  force online, regardless of network status\n"
 "  -h, --help          display this help and exit\n"
 "  -m, --multiple      do not ensure single instance\n"
 "  -n, --nologin       don't automatically login\n"
@@ -12183,8 +12219,11 @@
 "\n"
 "  -c, --config=DIR    utilitza DIR per als fitxers de configuració\n"
 "  -d, --debug         mostra missatges de depuració a la sortida estàndard\n"
+"  -f, --force-online  força que s'estigui en línia, independent de l'estat "
+"de\n"
+"                      la xarxa\n"
 "  -h, --help          mostra aquesta ajuda i surt\n"
-"  -m, --multiple      permet que hi hagi més d'una instància\n"
+"  -m, --multiple      no controla que només hi hagi una instància\n"
 "  -n, --nologin       no entra automàticament\n"
 "  -l, --login[=NOM]   habilita el compte especificat (l'argument opcional "
 "NOM\n"
@@ -12201,6 +12240,7 @@
 "\n"
 "  -c, --config=DIR    use DIR for config files\n"
 "  -d, --debug         print debugging messages to stdout\n"
+"  -f, --force-online  force online, regardless of network status\n"
 "  -h, --help          display this help and exit\n"
 "  -m, --multiple      do not ensure single instance\n"
 "  -n, --nologin       don't automatically login\n"
@@ -12214,8 +12254,11 @@
 "\n"
 "  -c, --config=DIR    utilitza DIR per als fitxers de configuració\n"
 "  -d, --debug         mostra missatges de depuració a la sortida estàndard\n"
+"  -f, --force-online  força que s'estigui en línia, independent de l'estat "
+"de\n"
+"                      la xarxa\n"
 "  -h, --help          mostra aquesta ajuda i surt\n"
-"  -m, --multiple      permet que hi hagi més d'una instància\n"
+"  -m, --multiple      no controla que només hi hagi una instància\n"
 "  -n, --nologin       no entra automàticament\n"
 "  -l, --login[=NOM]   habilita el compte especificat (l'argument opcional "
 "NOM\n"
@@ -12346,6 +12389,9 @@
 msgid "Select a file"
 msgstr "Seleccioneu un fitxer"
 
+msgid "Modify Buddy Pounce"
+msgstr "Modifica l'avís per a l'amic"
+
 # FIXME
 #. Create the "Pounce on Whom" frame.
 msgid "Pounce on Whom"
@@ -12443,6 +12489,11 @@
 msgid "Cl_ose conversations with the Escape key"
 msgstr "Tanca les converses amb la tecla d'_escapament"
 
+#. Buddy List Themes
+msgid "Buddy List Theme"
+msgstr "Tema de la llista d'amics"
+
+#. System Tray
 msgid "System Tray Icon"
 msgstr "Icona d'estat"
 
@@ -12928,6 +12979,12 @@
 msgid "Status for %s"
 msgstr "Estat per a %s"
 
+#.
+#. * TODO: We should enable/disable the add button based on
+#. *       whether the user has entered all required data.  That
+#. *       would eliminate the need for this check and provide a
+#. *       better user experience.
+#.
 msgid "Custom Smiley"
 msgstr "Emoticona personalitzada"
 
@@ -12937,16 +12994,15 @@
 msgid "Please provide a shortcut to associate with the smiley."
 msgstr "Especifiqueu una drecera associada a l'emoticona."
 
+#, c-format
+msgid ""
+"A custom smiley for '%s' already exists.  Please use a different shortcut."
+msgstr ""
+"Ja hi ha una emoticona personalitzada per a «%s». Indiqueu-ne una de diferent."
+
 msgid "Duplicate Shortcut"
 msgstr "Drecera duplicada"
 
-msgid ""
-"A custom smiley for the selected shortcut already exists. Please specify a "
-"different shortcut."
-msgstr ""
-"Hi ha una emoticona personalitzada per la drecera que heu seleccionat. "
-"Indiqueu-ne una de diferent."
-
 msgid "Please select an image for the smiley."
 msgstr "Seleccioneu una imatge per a l'emoticona."
 
@@ -12956,16 +13012,19 @@
 msgid "Add Smiley"
 msgstr "Afegeix una emoticona"
 
-msgid "Smiley _Image"
-msgstr "_Imatge de l'emoticona"
-
-#. Smiley shortcut
-msgid "Smiley S_hortcut"
-msgstr "_Dreceres de l'emoticona"
+msgid "_Image:"
+msgstr "_Imatge:"
+
+#. Shortcut text
+msgid "S_hortcut text:"
+msgstr "_Drecera:"
 
 msgid "Smiley"
 msgstr "Emoticona"
 
+msgid "Shortcut Text"
+msgstr "Drecera"
+
 msgid "Custom Smiley Manager"
 msgstr "Gestor d'emoticones personalitzades"
 
@@ -13093,6 +13152,15 @@
 "No s'ha pogut carregar la imatge «%s»: no se'n coneix el motiu, possiblement "
 "la imatge estigui corrompuda"
 
+msgid "_Open Link"
+msgstr "_Obre l'enllaç"
+
+msgid "_Copy Link Location"
+msgstr "_Copia l'enllaç"
+
+msgid "_Copy Email Address"
+msgstr "_Copia l'adreça de correu"
+
 msgid "Save File"
 msgstr "Desa un fitxer"
 
@@ -14126,6 +14194,18 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr "Aquest connector és útil per a depurar servidors i clients XMPP."
 
+#~ msgid "By log size"
+#~ msgstr "Per la mida del registre"
+
+#~ msgid "_Open Link in Browser"
+#~ msgstr "_Obre l'enllaç en el navegador"
+
+#~ msgid "Smiley _Image"
+#~ msgstr "_Imatge de l'emoticona"
+
+#~ msgid "Smiley S_hortcut"
+#~ msgstr "_Dreceres de l'emoticona"
+
 #~ msgid "Unable to retrieve MSN Address Book"
 #~ msgstr "No s'ha pogut obtenir la llibreta d'adreces MSN"
 
--- a/po/et.po	Sat Mar 07 01:36:57 2009 +0000
+++ b/po/et.po	Sat Mar 07 01:59:40 2009 +0000
@@ -2258,7 +2258,7 @@
 #. *  summary
 #. *  description
 msgid "Tests the ciphers that ship with libpurple."
-msgstr "Libpurple'ga kaasasolevate Å¡ifrite testimine."
+msgstr "libpurple'ga kaasasolevate Å¡ifrite testimine."
 
 #. *< type
 #. *< ui_requirement