changeset 25399:8958393be615

propagate from branch 'im.pidgin.pidgin' (head f49b0bab604f956eb4f9fce3a103320497cd40e5) to branch 'im.pidgin.pidgin.next.minor' (head 4f8748faa3551f9475017bfe39dd11ff5e560cf0)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Wed, 21 Jan 2009 18:34:32 +0000
parents 716b14deee97 (diff) 7f9b8351a6b4 (current diff)
children 4e9f98939d32
files ChangeLog ChangeLog.API libpurple/account.c libpurple/blist.c libpurple/core.c libpurple/prefs.c libpurple/status.c pidgin/gtkmain.c
diffstat 138 files changed, 7459 insertions(+), 1911 deletions(-) [+]
line wrap: on
line diff
--- a/.mtn-ignore	Tue Jan 20 19:38:53 2009 +0000
+++ b/.mtn-ignore	Wed Jan 21 18:34:32 2009 +0000
@@ -10,6 +10,7 @@
 .*\.def$
 .*\.dll$
 .*\.exe$
+.*\.loT$
 intltool-.*
 Doxyfile(\.mingw)?$
 aclocal.m4
--- a/ChangeLog	Tue Jan 20 19:38:53 2009 +0000
+++ b/ChangeLog	Wed Jan 21 18:34:32 2009 +0000
@@ -1,5 +1,10 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.6.0 (??/??/????):
+	General:
+	* Theme support in libpurple thanks to Justin Rodriguez's summer of code
+	  project.  With some minor additions and clean ups from Paul Aurich.
+
 version 2.5.5 (??/??/????):
 	libpurple:
 	* Fix transfer of buddy icons, custom smileys and files from the
--- a/ChangeLog.API	Tue Jan 20 19:38:53 2009 +0000
+++ b/ChangeLog.API	Wed Jan 21 18:34:32 2009 +0000
@@ -1,5 +1,56 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.6.0 (??/??/????):
+	libpurple:
+		Added:
+		* purple_buddy_get_protocol_data
+		* purple_buddy_set_protocol_data
+		* purple_connection_get_protocol_data
+		* purple_connection_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_BLIST_NODE
+		* PURPLE_GROUP
+		* PURPLE_CONTACT
+		* PURPLE_BUDDY
+		* PURPLE_CHAT
+		* purple_request_field_get_group
+		* purple_request_field_get_ui_data
+		* purple_request_field_set_ui_data
+		* purple_network_force_online
+		* purple_global_proxy_set_info
+		* purple_strequal
+
+		Deprecated:
+		* purple_buddy_get_local_alias
+
+	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.
+		* 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 (??/??/2009):
 	libpurple:
 		Changed:
--- a/Makefile.am	Tue Jan 20 19:38:53 2009 +0000
+++ b/Makefile.am	Wed Jan 21 18:34:32 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/configure.ac	Tue Jan 20 19:38:53 2009 +0000
+++ b/configure.ac	Wed Jan 21 18:34:32 2009 +0000
@@ -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/finch/gntblist.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/finch/gntblist.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/finch/gntconv.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/finch/gntlog.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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/gntrequest.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/finch/gntrequest.c	Wed Jan 21 18:34:32 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;
@@ -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
 			{
--- a/finch/plugins/grouping.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/finch/plugins/grouping.c	Wed Jan 21 18:34:32 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	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/Makefile.am	Wed Jan 21 18:34:32 2009 +0000
@@ -75,8 +75,13 @@
 	stringref.c \
 	stun.c \
 	sound.c \
+	sound-theme-loader.c \
+	sound-theme.c \
 	sslconn.c \
 	upnp.c \
+	theme.c \
+	theme-loader.c \
+	theme-manager.c \
 	util.c \
 	value.c \
 	version.c \
@@ -128,8 +133,13 @@
 	stringref.h \
 	stun.h \
 	sound.h \
+	sound-theme-loader.h \
+	sound-theme.h \
 	sslconn.h \
 	upnp.h \
+	theme.h \
+	theme-loader.h \
+	theme-manager.h \
 	util.h \
 	value.h \
 	xmlnode.h \
--- a/libpurple/Makefile.mingw	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/Makefile.mingw	Wed Jan 21 18:34:32 2009 +0000
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/account.c	Wed Jan 21 18:34:32 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;
 	}
 
@@ -2267,8 +2265,8 @@
 
 		/* 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)
@@ -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/blist.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/blist.c	Wed Jan 21 18:34:32 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;
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/blist.h	Wed Jan 21 18:34:32 2009 +0000
@@ -75,12 +75,19 @@
 
 } PurpleBlistNodeFlags;
 
+#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)
 
+#define PURPLE_GROUP(obj) ((PurpleGroup *)(obj))
+#define PURPLE_CONTACT(obj) ((PurpleContact *)(obj))
+#define PURPLE_BUDDY(obj) ((PurpleBuddy *)(obj))
+#define PURPLE_CHAT(obj) ((PurpleChat *)(obj))
+
 #include "account.h"
 #include "buddyicon.h"
 #include "status.h"
@@ -156,9 +163,6 @@
 	PurpleAccount *account; /**< The account this chat is attached to */
 };
 
-#endif /* PURPLE_HIDE_STRUCTS && PURPLE_BLIST_STRUCTS */
-
-
 /**
  * The Buddy List
  */
@@ -168,6 +172,8 @@
 	void *ui_data;                /**< UI-specific data. */
 };
 
+#endif /* PURPLE_HIDE_STRUCTS && PURPLE_BLIST_STRUCTS */
+
 /**
  * Buddy list UI operations.
  *
@@ -236,6 +242,30 @@
 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 +332,24 @@
 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);
@@ -470,6 +518,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,6 +733,7 @@
  */
 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
@@ -666,8 +741,10 @@
  * 
  * @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 +757,15 @@
 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.
--- a/libpurple/buddyicon.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/buddyicon.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/certificate.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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;
 		}
 	}
--- a/libpurple/cipher.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/cipher.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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;
 	}
 
@@ -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,7 +1942,7 @@
 
 	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);
 	}
 }
@@ -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/cmds.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/cmds.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/connection.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/connection.h	Wed Jan 21 18:34:32 2009 +0000
@@ -354,6 +354,15 @@
 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.
@@ -408,6 +417,16 @@
 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.
--- a/libpurple/conversation.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/conversation.c	Wed Jan 21 18:34:32 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/core.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/core.c	Wed Jan 21 18:34:32 2009 +0000
@@ -46,10 +46,12 @@
 #include "signals.h"
 #include "smiley.h"
 #include "sound.h"
+#include "sound-theme-loader.h"
 #include "sslconn.h"
 #include "status.h"
 #include "stun.h"
 #include "util.h"
+#include "theme-manager.h"
 
 #ifdef HAVE_DBUS
 #  ifndef DBUS_API_SUBJECT_TO_CHANGE
@@ -143,6 +145,7 @@
 
 	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 +174,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 +184,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;
 }
 
@@ -226,6 +232,7 @@
 	purple_savedstatuses_uninit();
 	purple_status_uninit();
 	purple_sound_uninit();
+	purple_theme_manager_uninit();
 	purple_xfers_uninit();
 	purple_proxy_uninit();
 	purple_dnsquery_uninit();
@@ -343,15 +350,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);
 		}
 	}
@@ -482,7 +481,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)
@@ -525,7 +524,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,
@@ -570,7 +570,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
@@ -639,7 +639,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/desktopitem.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/desktopitem.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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;
 	}
 
@@ -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);
 }
 
@@ -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 {
@@ -697,7 +697,7 @@
 		 * 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,8 +791,7 @@
 						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), 
@@ -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) {
@@ -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 {
--- a/libpurple/idle.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/idle.c	Wed Jan 21 18:34:32 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;
--- a/libpurple/log.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/log.c	Wed Jan 21 18:34:32 2009 +0000
@@ -200,7 +200,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)
@@ -324,7 +324,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 +406,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 +588,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,
@@ -1019,7 +1019,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 +1039,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 +1068,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 +1773,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 +1930,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 +1943,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 +1952,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/network.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/network.c	Wed Jan 21 18:34:32 2009 +0000
@@ -92,6 +92,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)
 {
@@ -648,6 +652,9 @@
 purple_network_is_available(void)
 {
 #ifdef HAVE_NETWORKMANAGER
+	if (force_online)
+		return TRUE;
+
 	if (!have_nm_state)
 	{
 		have_nm_state = TRUE;
@@ -662,12 +669,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/network.h	Wed Jan 21 18:34:32 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/plugin.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/plugin.c	Wed Jan 21 18:34:32 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/plugins/autoaccept.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/plugins/autoaccept.c	Wed Jan 21 18:34:32 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)
 	{
--- a/libpurple/plugins/perl/common/Request.xs	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/plugins/perl/common/Request.xs	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/plugins/statenotify.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/plugins/tcl/tcl_cmds.c	Wed Jan 21 18:34:32 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;
--- a/libpurple/pounce.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/pounce.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/prefs.c	Wed Jan 21 18:34:32 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/privacy.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/privacy.c	Wed Jan 21 18:34:32 2009 +0000
@@ -224,8 +224,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);
 	}
 }
@@ -259,7 +261,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);
 				}
 			}
@@ -303,7 +305,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/protocols/bonjour/bonjour.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Wed Jan 21 18:34:32 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");
@@ -743,17 +757,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."),
@@ -766,12 +783,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."),
@@ -791,14 +811,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;
 
@@ -848,7 +868,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) {
@@ -856,7 +876,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);
@@ -896,12 +916,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)
 	{
@@ -948,7 +966,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;
@@ -956,8 +974,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));
@@ -1007,7 +1023,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;
 	}
@@ -1036,7 +1052,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);
@@ -1095,7 +1111,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;
@@ -1158,15 +1174,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;
 		}
@@ -1178,16 +1199,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/gg/buddylist.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/gg/gg.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/irc/irc.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/irc/msgs.c	Wed Jan 21 18:34:32 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/adhoccommands.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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);
 	}
 }
--- a/libpurple/protocols/jabber/buddy.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Wed Jan 21 18:34:32 2009 +0000
@@ -422,7 +422,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;
 
@@ -496,7 +496,7 @@
 	PurplePresence *gpresence;
 	PurpleStatus *status;
 	
-	if(((JabberStream*)gc->proto_data)->pep) {
+	if(((JabberStream*)purple_connection_get_protocol_data(gc))->pep) {
 		/* XEP-0084: User Avatars */
 		if(img) {
 			/*
@@ -558,7 +558,7 @@
 				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");
@@ -584,7 +584,7 @@
 				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 {
@@ -1780,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)
@@ -1840,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)
@@ -1855,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,
@@ -1871,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)
@@ -1887,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");
 }
 
 
@@ -1903,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);
@@ -1928,7 +1928,7 @@
 		
 		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);
@@ -1939,12 +1939,13 @@
 	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);
@@ -1953,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;
@@ -2010,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);
@@ -2478,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/google.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Wed Jan 21 18:34:32 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");
 
--- a/libpurple/protocols/jabber/jabber.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed Jan 21 18:34:32 2009 +0000
@@ -1620,13 +1620,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 ||
@@ -1640,9 +1641,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"));
@@ -1671,14 +1674,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;
@@ -2014,19 +2022,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 &&
--- a/libpurple/protocols/jabber/roster.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/roster.c	Wed Jan 21 18:34:32 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);
@@ -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);
 		}
@@ -273,6 +276,7 @@
 	GSList *groups = NULL, *l;
 	JabberIq *iq;
 	xmlnode *query, *item, *group;
+	const char *balias;
 
 	if (js->currently_parsing_roster_push)
 		return;
@@ -289,7 +293,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);
 		}
 	}
@@ -301,7 +305,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");
@@ -327,14 +332,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);
 
@@ -375,6 +382,7 @@
 	GSList *buddies, *groups = NULL;
 	PurpleBuddy *b;
 	PurpleGroup *g;
+	const char *gname;
 
 	if(!old_group || !new_group || !strcmp(old_group, new_group))
 		return;
@@ -383,10 +391,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);
@@ -397,15 +406,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) {
@@ -416,11 +427,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,
@@ -428,7 +439,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/msn/directconn.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/msn/directconn.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/msn/msn.c	Wed Jan 21 18:34:32 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),
@@ -1419,14 +1425,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
@@ -1453,10 +1460,10 @@
 	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 {
 		/* 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);
 		msn_notification_send_fqy(session, who);
 	}
 }
@@ -1474,7 +1481,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
@@ -1727,20 +1734,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);
 	}
 }
 
@@ -1800,20 +1809,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);
 }
 
 /**
@@ -1830,17 +1841,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/session.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/msn/session.c	Wed Jan 21 18:34:32 2009 +0000
@@ -269,16 +269,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/msn/user.c	Wed Jan 21 18:34:32 2009 +0000
@@ -289,9 +289,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/msn/userlist.c	Wed Jan 21 18:34:32 2009 +0000
@@ -930,31 +930,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/myspace.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Wed Jan 21 18:34:32 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. */
@@ -1376,7 +1383,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 {
@@ -2630,10 +2637,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,
@@ -2642,7 +2653,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;
@@ -2654,7 +2665,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(""),
@@ -2675,7 +2686,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);
@@ -2684,7 +2695,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);
 }
 
 /**
@@ -2696,8 +2714,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,
@@ -2705,7 +2725,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;
@@ -2724,7 +2744,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;
@@ -2736,15 +2756,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);
 }
 
 /**
@@ -2834,13 +2851,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/myspace/user.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/myspace/zap.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/novell/novell.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/null/README	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Wed Jan 21 18:34:32 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) {
@@ -305,20 +287,20 @@
                     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"),
+                                NULL, TRUE);
+  purple_status_type_add_attr(type, "message", _("Message"),
                               purple_value_new(PURPLE_TYPE_STRING));
   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"),
+                                NULL, TRUE);
+  purple_status_type_add_attr(type, "message", _("Message"),
                               purple_value_new(PURPLE_TYPE_STRING));
   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"),
+                                NULL, TRUE);
+  purple_status_type_add_attr(type, "message", _("Message"),
                               purple_value_new(PURPLE_TYPE_STRING));
   types = g_list_prepend(types, type);
 
@@ -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/oscar.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed Jan 21 18:34:32 2009 +0000
@@ -807,11 +807,13 @@
 	PurpleStatus *status = NULL;
 	gchar *message = NULL, *itmsurl = NULL, *tmp;
 	gboolean is_away;
+	const char *bname;
 
 	od = gc->proto_data;
 
+	bname = purple_buddy_get_name(b);
 	if (userinfo == NULL)
-		userinfo = aim_locate_finduserinfo(od, b->name);
+		userinfo = aim_locate_finduserinfo(od, bname);
 
 	if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
 		return;
@@ -884,7 +886,7 @@
 
 	if (b) {
 		if (purple_presence_is_online(presence)) {
-			if (aim_snvalid_icq(b->name) || is_away || !message || !(*message)) {
+			if (aim_snvalid_icq(bname) || 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);
@@ -901,8 +903,8 @@
 
 		} else {
 			if (aim_ssi_waitingforauth(od->ssi.local,
-									   aim_ssi_itemlist_findparentname(od->ssi.local, b->name),
-									   b->name)) {
+									   aim_ssi_itemlist_findparentname(od->ssi.local, bname),
+									   bname)) {
 				/* Note if an offline buddy is not authorized */
 				tmp = g_strdup_printf("%s%s%s",
 									  _("Not Authorized"),
@@ -931,6 +933,7 @@
 	PurpleGroup *g = NULL;
 	struct buddyinfo *bi = NULL;
 	char *tmp;
+	const char *bname, *gname = NULL;
 
 	od = gc->proto_data;
 	account = purple_connection_get_account(gc);
@@ -938,14 +941,16 @@
 	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);
 
 	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);
 	}
@@ -969,8 +974,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);
@@ -2444,6 +2449,7 @@
 	PurpleAccount *account;
 	PurpleBuddy *buddy;
 	PurpleGroup *group;
+	const char *bname, *gname;
 
 	gc = data->gc;
 	od = gc->proto_data;
@@ -2456,15 +2462,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);
@@ -2504,8 +2512,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 */
@@ -3896,9 +3904,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;
 
@@ -3915,7 +3923,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);
@@ -3924,7 +3932,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);
@@ -3959,7 +3967,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);
@@ -3995,7 +4003,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);
@@ -4029,7 +4037,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);
@@ -4796,14 +4804,17 @@
 oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
 	OscarData *od;
 	PurpleAccount *account;
+	const char *bname, *gname;
 
 	od = (OscarData *)gc->proto_data;
 	account = purple_connection_get_account(gc);
-
-	if (!aim_snvalid(buddy->name)) {
+	bname = purple_buddy_get_name(buddy);
+	gname = purple_group_get_name(group);
+
+	if (!aim_snvalid(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);
 
@@ -4813,34 +4824,34 @@
 		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;
 
 	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);
 	}
 }
 
@@ -4872,7 +4883,8 @@
 	OscarData *od = (OscarData *)gc->proto_data;
 
 	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);
 
@@ -4882,25 +4894,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(gc->proto_data, purple_group_get_name(group));
 }
 
 static gboolean purple_ssi_rerequestdata(gpointer data) {
@@ -5009,33 +5021,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);
 							}
@@ -5131,15 +5154,15 @@
 					} 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)) {
@@ -5152,7 +5175,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);
@@ -5366,13 +5389,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);
 		}
 
 	}
@@ -5704,7 +5725,8 @@
 
 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) || aim_snvalid_sms(name))
 	{
 		if (a == NULL || aim_snvalid_icq(purple_account_get_username(a)))
 			return "icq";
@@ -5712,14 +5734,15 @@
 			return "aim";
 	}
 
-	if (aim_snvalid_icq(b->name))
+	if (aim_snvalid_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) || aim_snvalid_sms(name))
 	{
 		if (a != NULL && aim_snvalid_icq(purple_account_get_username(a)))
 			return "icq";
@@ -5727,7 +5750,7 @@
 			return "aim";
 	}
 
-	if (aim_snvalid_icq(b->name))
+	if (aim_snvalid_icq(name))
 		return "icq";
 	return "aim";
 }
@@ -5741,14 +5764,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;
 	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);
@@ -5756,9 +5781,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";
 		}
 	}
@@ -5781,15 +5806,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;
+	account = purple_buddy_get_account(b);
+	gc = purple_account_get_connection(account);
 	od = gc->proto_data;
-	userinfo = aim_locate_finduserinfo(od, b->name);
+	userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
 
 	oscar_user_info_append_status(gc, user_info, b, userinfo, /* strip_html_tags */ TRUE);
 
@@ -5817,8 +5844,9 @@
 
 	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"));
@@ -6063,7 +6091,7 @@
 		return;
 	}
 
-	aim_ssi_editcomment(od, g->name, data->name, text);
+	aim_ssi_editcomment(od, purple_group_get_name(g), data->name, text);
 
 	if (!aim_sncmp(data->name, gc->account->username))
 		purple_check_comment(od, text);
@@ -6081,11 +6109,15 @@
 	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);
+	name = purple_buddy_get_name(buddy);
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
 	od = gc->proto_data;
 
 	if (!(g = purple_buddy_get_group(buddy)))
@@ -6093,11 +6125,11 @@
 
 	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);
@@ -6105,7 +6137,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);
 
@@ -6137,26 +6169,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->who = g_strdup(purple_buddy_get_name(buddy));
 	data->od = gc->proto_data;
 	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));
@@ -6172,7 +6206,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));
 
 	aim_locate_getinfoshort(gc->proto_data, purple_buddy_get_name(buddy), 0x00000003);
 }
@@ -6185,13 +6219,16 @@
 	GList *menu;
 	PurpleMenuAction *act;
 	aim_userinfo_t *userinfo;
-
-	gc = purple_account_get_connection(buddy->account);
+	PurpleAccount *account;
+	const char *bname = purple_buddy_get_name(buddy);
+
+	account = purple_buddy_get_account(buddy);
+	gc = purple_account_get_connection(account);
 	od = gc->proto_data;
-	userinfo = aim_locate_finduserinfo(od, buddy->name);
+	userinfo = aim_locate_finduserinfo(od, bname);
 	menu = NULL;
 
-	if (od->icq && aim_snvalid_icq(purple_buddy_get_name(buddy)))
+	if (od->icq && aim_snvalid_icq(bname))
 	{
 		act = purple_menu_action_new(_("Get AIM Info"),
 								   PURPLE_CALLBACK(oscar_get_aim_info_cb),
@@ -6219,7 +6256,7 @@
 #endif
 
 	if (userinfo &&
-		aim_sncmp(purple_account_get_username(buddy->account), buddy->name) &&
+		aim_sncmp(purple_account_get_username(account), bname) &&
 		PURPLE_BUDDY_IS_ONLINE(buddy))
 	{
 		if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM)
@@ -6243,8 +6280,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),
@@ -6401,26 +6438,37 @@
 	OscarData *od = gc->proto_data;
 	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;
--- a/libpurple/protocols/qq/buddy_info.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Wed Jan 21 18:34:32 2009 +0000
@@ -612,7 +612,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);
@@ -631,15 +631,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);
@@ -768,8 +769,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Wed Jan 21 18:34:32 2009 +0000
@@ -232,7 +232,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);
@@ -334,7 +334,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;
 		}
@@ -342,7 +342,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);
 		*/
@@ -567,7 +567,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;
@@ -659,9 +659,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;
@@ -681,16 +682,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/qq/group_internal.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/qq/im.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Jan 21 18:34:32 2009 +0000
@@ -246,7 +246,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;
 
@@ -289,7 +289,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;
 
@@ -380,11 +380,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";
 	}
@@ -693,8 +694,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;
 
@@ -712,8 +714,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;
 
@@ -800,7 +803,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);
 }
@@ -813,7 +816,7 @@
 	PurpleConnection *gc = purple_account_get_connection(buddy->account);
 	qq_data *qd = gc->proto_data;
 	*/
-	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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/qq/send_file.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Wed Jan 21 18:34:32 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);
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/silc/buddy.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/silc/chat.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/silc/ops.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/simple/simple.c	Wed Jan 21 18:34:32 2009 +0000
@@ -196,33 +196,41 @@
 {
 	struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
 	struct simple_buddy *b;
-	if(strcmp("sip:", buddy->name)) {
-		gchar *buf = g_strdup_printf("sip:%s", buddy->name);
+	const char *name = purple_buddy_get_name(buddy);
+	if(strcmp("sip:", name)) {
+		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/yahoo/yahoo.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Wed Jan 21 18:34:32 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);
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/proxy.c	Wed Jan 21 18:34:32 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;
@@ -2242,31 +2252,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/proxy.h	Wed Jan 21 18:34:32 2009 +0000
@@ -186,6 +186,13 @@
  */
 PurpleProxyInfo *purple_global_proxy_get_info(void);
 
+/**
+ * Set purple's global proxy information.
+ *
+ * @param info     The proxy information.
+ */
+void purple_global_proxy_set_info(PurpleProxyInfo *info);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/prpl.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/prpl.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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/request.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/request.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/request.h	Wed Jan 21 18:34:32 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,16 @@
 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 +580,28 @@
  */
 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/savedstatuses.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/server.c	Wed Jan 21 18:34:32 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);
@@ -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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/sound-theme-loader.c	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,116 @@
+/*
+ * 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-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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,71 @@
+/**
+ * @file sound-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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,164 @@
+/*
+ * 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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,102 @@
+/**
+ * @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
+ */
+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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/sound.c	Wed Jan 21 18:34:32 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/status.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/status.c	Wed Jan 21 18:34:32 2009 +0000
@@ -203,7 +203,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 +451,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 +477,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 +612,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 +624,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 +782,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;
 		}
@@ -1132,13 +1131,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;
 
@@ -1234,12 +1233,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)
 			{
@@ -1259,7 +1259,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)
 			{
@@ -1447,7 +1447,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/stun.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/stun.c	Wed Jan 21 18:34:32 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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,186 @@
+/*
+ * 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	Wed Jan 21 18:34:32 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 		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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,304 @@
+/*
+ * 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()
+{
+	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()
+{
+	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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,125 @@
+/**
+ * @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 an 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 exits it 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);
+
+/**
+ * Addes 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);
+
+/**
+ * Calles 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	Wed Jan 21 18:34:32 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, strlen(text));	
+	g_strdelimit(clean_text, "\n", ' ');
+	purple_str_strip_char(clean_text, '\r');
+	return clean_text;
+}
+
+/*****************************************************************************
+ * Public API functions                                                      
+ *****************************************************************************/
+
+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	Wed Jan 21 18:34:32 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/util.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/util.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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 ||
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/util.h	Wed Jan 21 18:34:32 2009 +0000
@@ -775,6 +775,20 @@
 /*@{*/
 
 /**
+ * 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/whiteboard.c	Wed Jan 21 18:34:32 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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/xmlnode.c	Wed Jan 21 18:34:32 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);
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/xmlnode.h	Wed Jan 21 18:34:32 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,20 @@
  */
 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.
+ */
+xmlnode *xmlnode_from_file(const char *dir, const char *filename, 
+			   const char *description, const char *process);
+
 #ifdef __cplusplus
 }
 #endif
--- a/pidgin/Makefile.am	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/Makefile.am	Wed Jan 21 18:34:32 2009 +0000
@@ -78,6 +78,8 @@
 	pidginstock.c \
 	gtkaccount.c \
 	gtkblist.c \
+	gtkblist-theme-loader.c \
+	gtkblist-theme.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-loader.h \
+	gtkblist-theme.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	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/Makefile.mingw	Wed Jan 21 18:34:32 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 \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkblist-theme-loader.c	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,283 @@
+/*
+ * 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	Wed Jan 21 18:34:32 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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,782 @@
+/*
+ * 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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,333 @@
+/**
+ * @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"
+
+/**
+ * extends PurpleTheme (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	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkblist.c	Wed Jan 21 18:34:32 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-loader.h"
+#include "gtkblist-theme.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) \
@@ -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
@@ -1030,7 +1068,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);
@@ -1790,7 +1828,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;
@@ -2494,7 +2533,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 {
@@ -3813,19 +3852,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;
@@ -3839,178 +3881,171 @@
 	}
 
 	/* 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;
+#else	
+			if(tmp) {
+				g_strdelimit(tmp, "\n", ' ');
+				purple_str_strip_char(tmp, '\r');
+			}
+			statustext = tmp;
+#endif	
 		}
-
-#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);
-
-			g_free(tmp);
-		}
-#else
-		if(tmp) {
-			g_strdelimit(tmp, "\n", ' ');
-			purple_str_strip_char(tmp, '\r');
-		}
-		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
-				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 idletime = g_strdup(_("Idle"));
 		}
-	} 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 :  "");
-	}
-
-	g_free(idletime);
-	g_free(statustext);
-	g_free(esc);
+	}
+
+	/* 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 : "black";
+		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 : "black";
+		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 : "black";
+		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) {
+		name_color = "black";
+		status_color = "black";
+	}
+
+	/* Put it all together */
+	if (aliased && biglist && (statustext || idletime)) {
+		/* using <span size='smaller'> breaks the status, so it must be seperated into <small><span>*/
+		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 text = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>", name_font, name_color, nametext); 
+
+	g_free(nametext);
+	if (statustext)
+		g_free(statustext);
+	if (idletime)
+		g_free(idletime);
 
 	if (hidden_conv) {
 		char *tmp = text;
@@ -4329,6 +4364,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);
 }
@@ -4339,7 +4378,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);
 }
 
@@ -5256,11 +5296,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;
@@ -5286,6 +5459,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);
 
@@ -5318,8 +5493,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);
@@ -5462,105 +5637,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);
@@ -5987,13 +6073,18 @@
 		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);
@@ -6011,7 +6102,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,
@@ -6034,6 +6125,9 @@
 	char *mark, *esc;
 	PurpleBlistNode *selected_node = NULL;
 	GtkTreeIter iter;
+	FontColorPair *pair;
+	gchar *text_color, *text_font;
+	PidginBlistTheme *theme;
 
 	group = (PurpleGroup*)gnode;
 
@@ -6049,8 +6143,20 @@
 		           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) ? "black" : 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);
+	mark = g_strdup_printf("<span foreground='%s' font_desc='%s'><b>%s</b>%s</span>",
+							text_color, text_font, esc ? esc : "", group_count);
 
 	g_free(esc);
 	return mark;
@@ -6058,14 +6164,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;
@@ -6089,35 +6196,38 @@
 	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 = "black";
+
+			idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>", textcolor, 
+					      (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,
@@ -6130,7 +6240,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,
@@ -6188,19 +6298,41 @@
 
 		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) ? "black" : pair->color;
+
+			tmp = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>",
+						font, fg_color, 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,
@@ -6268,20 +6400,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);
@@ -6293,14 +6433,36 @@
 		else
 			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;
-		}
+		mark = g_markup_escape_text(purple_chat_get_name(chat), -1);	
+	
+		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" : "black");
+		else
+			color = pair->color;
+
+		tmp = g_strdup_printf("<span font_desc='%s' color='%s' weight='%s'>%s</span>", 
+				      font, color, 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,
@@ -6312,6 +6474,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);
 
@@ -6324,6 +6487,7 @@
 			g_object_unref(avatar);
 		if(prpl_icon)
 			g_object_unref(prpl_icon);
+
 	} else {
 		pidgin_blist_hide_node(list, node, TRUE);
 	}
@@ -7183,6 +7347,33 @@
 			(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();
@@ -7211,6 +7402,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",
--- a/pidgin/gtkblist.h	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkblist.h	Wed Jan 21 18:34:32 2009 +0000
@@ -59,6 +59,7 @@
 
 #include "pidgin.h"
 #include "blist.h"
+#include "gtkblist-theme.h"
 
 /**************************************************************************
  * @name Structures
@@ -250,6 +251,19 @@
  */
 void pidgin_blist_add_alert(GtkWidget *widget);
 
+/**
+ * Sets the current theme for Pidgin to use
+ *
+ * @param theme	the new theme to use
+ */
+void pidgin_blist_set_theme(PidginBlistTheme *theme);
+
+/**
+ * Gets Pidgin's current buddy list theme
+ *
+ * @returns	the current theme 
+ */
+PidginBlistTheme *pidgin_blist_get_theme(void);
 
 /**************************************************************************
  * @name GTK+ Buddy List sorting functions
--- a/pidgin/gtkcellrendererexpander.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkcellrendererexpander.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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;
 
@@ -270,7 +270,10 @@
 			    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)
+
+	/* 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/gtkconv.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkconv.c	Wed Jan 21 18:34:32 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;
@@ -3850,7 +3850,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)
 				{
@@ -5033,9 +5033,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;
@@ -5043,8 +5043,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)
--- a/pidgin/gtkdialogs.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkdialogs.c	Wed Jan 21 18:34:32 2009 +0000
@@ -1061,8 +1061,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;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkicon-theme-loader.c	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,115 @@
+/*
+ * 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-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	Wed Jan 21 18:34:32 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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,147 @@
+/*
+ * 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	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,92 @@
+/**
+ * @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	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkimhtml.c	Wed Jan 21 18:34:32 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
@@ -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. */
@@ -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;
 }
 
 /*
@@ -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,42 @@
 				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) {
 			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;
@@ -3368,8 +3386,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) {
@@ -5751,3 +5768,72 @@
 	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;
+}
+
--- a/pidgin/gtkimhtml.h	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkimhtml.h	Wed Jan 21 18:34:32 2009 +0000
@@ -60,6 +60,7 @@
 typedef struct _GtkIMHtmlAnimation	GtkIMHtmlAnimation;
 typedef struct _GtkIMHtmlHr			GtkIMHtmlHr;
 typedef struct _GtkIMHtmlFuncs		GtkIMHtmlFuncs;
+typedef struct _GtkIMHtmlLink       GtkIMHtmlLink;
 
 typedef enum {
 	GTK_IMHTML_BOLD =       1 << 0,
@@ -156,6 +157,7 @@
 	gboolean (*message_send)(GtkIMHtml *);
 	void (*undo)(GtkIMHtml *);
 	void (*redo)(GtkIMHtml *);
+	GList *protocols; /* List of GtkIMHtmlProtocol's */
 };
 
 struct _GtkIMHtmlFontDetail {
@@ -885,6 +887,59 @@
  * @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);
+
 /*@}*/
 
 #ifdef __cplusplus
--- a/pidgin/gtklog.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtklog.c	Wed Jan 21 18:34:32 2009 +0000
@@ -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/gtkmain.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkmain.c	Wed Jan 21 18:34:32 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/gtkpounce.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkpounce.c	Wed Jan 21 18:34:32 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);
 
--- a/pidgin/gtkprefs.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkprefs.c	Wed Jan 21 18:34:32 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,213 @@
 	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 sucess;
+	GtkTreeIter new_iter;
+	
+
+	sucess = gtk_combo_box_get_active_iter(combo_box, &new_iter);
+	g_return_if_fail(sucess);
+
+	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 +1140,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 +1181,7 @@
 	GtkWidget *vbox;
 	GtkWidget *vbox2;
 	GtkWidget *label;
+	GtkWidget *combo_box;
 	GtkSizeGroup *sg;
 	GList *names = NULL;
 
@@ -937,6 +1190,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",
@@ -1736,6 +2002,8 @@
 	g_free(pref);
 
 	gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
+
+	pref_sound_generate_markup();
 }
 
 static void
@@ -1758,6 +2026,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)
@@ -1826,6 +2096,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();
 }
 
 
@@ -1851,7 +2123,7 @@
 sound_page(void)
 {
 	GtkWidget *ret;
-	GtkWidget *vbox, *sw, *button;
+	GtkWidget *vbox, *sw, *button, *combo_box;
 	GtkSizeGroup *sg;
 	GtkTreeIter iter;
 	GtkWidget *event_view;
@@ -1945,7 +2217,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
@@ -1957,6 +2228,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);
@@ -2190,6 +2469,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?) */
@@ -2284,6 +2571,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/gtkrequest.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkrequest.c	Wed Jan 21 18:34:32 2009 +0000
@@ -680,15 +680,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
@@ -709,7 +711,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;
@@ -720,9 +723,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;
 					}
 				}
@@ -1334,24 +1339,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);
@@ -1396,7 +1403,7 @@
 
 				gtk_widget_show(widget);
 
-				field->ui_data = widget;
+				purple_request_field_set_ui_data(field, widget);
 			}
 		}
 	}
--- a/pidgin/gtksound.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtksound.c	Wed Jan 21 18:34:32 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;
@@ -570,16 +575,35 @@
 			sounds[event].pref);
 	file_pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[event].pref);
 
+
+
 	/* 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 && strlen(theme_name) && (!filename || !strlen(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 +611,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 && strlen(file)){
+			g_free(file);
+			return TRUE;
+		}
+
+		g_free(file);
+	}
+
+	return FALSE;
+
+}
+
 static PurpleSoundUiOps sound_ui_ops =
 {
 	pidgin_sound_init,
--- a/pidgin/gtksound.h	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtksound.h	Wed Jan 21 18:34:32 2009 +0000
@@ -63,6 +63,13 @@
  */
 void *pidgin_sound_get_handle(void);
 
+/**
+ * Returns true Pidgin is using customized sounds
+ *
+ * @return TRUE if non default sounds are used 
+ */
+gboolean pidgin_sound_is_customized(void);
+
 /*@}*/
 
 #endif /* _PIDGINSOUND_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkstatus-icon-theme.c	Wed Jan 21 18:34:32 2009 +0000
@@ -0,0 +1,73 @@
+/*
+ * 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	Wed Jan 21 18:34:32 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/gtkutils.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkutils.c	Wed Jan 21 18:34:32 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);
@@ -3480,3 +3483,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	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/gtkutils.h	Wed Jan 21 18:34:32 2009 +0000
@@ -822,5 +822,17 @@
  */
 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/pidginstock.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/pidginstock.c	Wed Jan 21 18:34:32 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,18 +125,9 @@
   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  },
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/pidginstock.h	Wed Jan 21 18:34:32 2009 +0000
@@ -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	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/pixmaps/Makefile.am	Wed Jan 21 18:34:32 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/history.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/plugins/history.c	Wed Jan 21 18:34:32 2009 +0000
@@ -71,16 +71,20 @@
 	        for (cur = buddies; cur != NULL; cur = cur->next)
 	        {
 	                PurpleBlistNode *node = cur->data;
-	                if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL)))
+					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,
--- a/pidgin/plugins/ticker/ticker.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/pidgin/plugins/ticker/ticker.c	Wed Jan 21 18:34:32 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/po/POTFILES.in	Tue Jan 20 19:38:53 2009 +0000
+++ b/po/POTFILES.in	Wed Jan 21 18:34:32 2009 +0000
@@ -183,6 +183,7 @@
 libpurple/status.c
 libpurple/util.c
 libpurple/win32/libc_interface.c
+libpurple/xmlnode.c
 pidgin.desktop.in
 pidgin/eggtrayicon.c
 pidgin/gtkaccount.c