gobjectification: d1809247: Split blist.c into contact.c, group.c, b...

aluink at soc.pidgin.im aluink at soc.pidgin.im
Wed Jun 17 19:05:28 EDT 2009


-----------------------------------------------------------------
Revision: d18092473b4d9d6e95c12c1d4ad0dff3306271c9
Ancestor: 51a28c52860565118364a83cae30a6305c9a3319
Author: aluink at soc.pidgin.im
Date: 2009-06-17T22:47:25
Branch: im.pidgin.gobjectification
URL: http://d.pidgin.im/viewmtn/revision/info/d18092473b4d9d6e95c12c1d4ad0dff3306271c9

Added files:
        libpurple/blist-node.c libpurple/buddy.c libpurple/chat.c
        libpurple/contact.c libpurple/group.c
Modified files:
        libpurple/Makefile.am libpurple/blist.c libpurple/blist.h

ChangeLog: 

Split blist.c into contact.c, group.c, blist-node.c, buddy.c, chat.c
Rough for now, clean up is continuing.

-------------- next part --------------
============================================================
--- libpurple/blist-node.c	f2ad7fe515dcdce498d66d7095e6144be5da8ebf
+++ libpurple/blist-node.c	f2ad7fe515dcdce498d66d7095e6144be5da8ebf
@@ -0,0 +1,635 @@
+/*
+ * 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
+ *
+ */
+#define _PURPLE_BLIST_NODE_C_
+#define _BLIST_HELPERS_
+
+#include "internal.h"
+#include "blist.h"
+#include "conversation.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "privacy.h"
+#include "prpl.h"
+#include "server.h"
+#include "signals.h"
+#include "util.h"
+#include "value.h"
+#include "xmlnode.h"
+
+PurpleBlistNode *
+purple_blist_get_last_sibling(PurpleBlistNode *node)
+{
+	PurpleBlistNode *n = node;
+	if (!n)
+		return NULL;
+	while (n->next)
+		n = n->next;
+	return n;
+}
+
+PurpleBlistNode *
+purple_blist_get_last_child(PurpleBlistNode *node)
+{
+	if (!node)
+		return NULL;
+	return purple_blist_get_last_sibling(node->child);
+}
+  
+void
+parse_setting(PurpleBlistNode *node, xmlnode *setting)
+{
+  const char *name = xmlnode_get_attrib(setting, "name");
+  const char *type = xmlnode_get_attrib(setting, "type");
+  char *value = xmlnode_get_data(setting);
+
+  if (!value)
+    return;
+
+  if (!type || purple_strequal(type, "string"))
+    purple_blist_node_set_string(node, name, value);
+  else if (purple_strequal(type, "bool"))
+    purple_blist_node_set_bool(node, name, atoi(value));
+  else if (purple_strequal(type, "int"))
+    purple_blist_node_set_int(node, name, atoi(value));
+
+  g_free(value);
+}
+
+/*****************************************************************************
+ * Public API functions                                                      *
+ *****************************************************************************/
+
+static PurpleBlistNode *get_next_node(PurpleBlistNode *node, gboolean godeep)
+{
+	if (node == NULL)
+		return NULL;
+
+	if (godeep && node->child)
+		return node->child;
+
+	if (node->next)
+		return node->next;
+
+	return get_next_node(node->parent, FALSE);
+}
+
+PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline)
+{
+	PurpleBlistNode *ret = node;
+
+	if (offline)
+		return get_next_node(ret, TRUE);
+	do
+	{
+		ret = get_next_node(ret, TRUE);
+	} while (ret && PURPLE_BLIST_NODE_IS_BUDDY(ret) &&
+			!purple_account_is_connected(purple_buddy_get_account((PurpleBuddy *)ret)));
+
+	return ret;
+}
+
+PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node)
+{
+	return node ? node->parent : NULL;
+}
+
+PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node)
+{
+	return node ? node->child : NULL;
+}
+
+PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node)
+{
+	return node? node->next : NULL;
+}
+
+PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node)
+{
+	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_node_icon(PurpleBlistNode *node)
+{
+  PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+  g_return_if_fail(node != NULL);
+
+  if (ops && ops->update)
+    ops->update(purple_blist_get_list(), node);
+}
+
+void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node)
+{
+	PurpleBlistNode *cnode = (PurpleBlistNode*)chat;
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+	g_return_if_fail(chat != NULL);
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT((PurpleBlistNode *)chat));
+
+	if (node == NULL) {
+		if (group == NULL)
+			group = purple_group_new(_("Chats"));
+
+		/* Add group to blist if isn't already on it. Fixes #2752. */
+		if (!purple_find_group(group->name)) {
+			purple_blist_add_group(group,
+					purple_blist_get_last_sibling(purple_blist_get_list()->root));
+		}
+	} else {
+		group = (PurpleGroup*)node->parent;
+	}
+
+	/* if we're moving to overtop of ourselves, do nothing */
+	if (cnode == node)
+		return;
+
+	if (cnode->parent) {
+		/* This chat was already in the list and is
+		 * being moved.
+		 */
+		((PurpleGroup *)cnode->parent)->totalsize--;
+		if (purple_account_is_connected(chat->account)) {
+			((PurpleGroup *)cnode->parent)->online--;
+			((PurpleGroup *)cnode->parent)->currentsize--;
+		}
+		if (cnode->next)
+			cnode->next->prev = cnode->prev;
+		if (cnode->prev)
+			cnode->prev->next = cnode->next;
+		if (cnode->parent->child == cnode)
+			cnode->parent->child = cnode->next;
+
+		if (ops && ops->remove)
+			ops->remove(purple_blist_get_list(), cnode);
+		/* ops->remove() cleaned up the cnode's ui_data, so we need to
+		 * reinitialize it */
+		if (ops && ops->new_node)
+			ops->new_node(cnode);
+
+		purple_blist_schedule_save();
+	}
+
+	if (node != NULL) {
+		if (node->next)
+			node->next->prev = cnode;
+		cnode->next = node->next;
+		cnode->prev = node;
+		cnode->parent = node->parent;
+		node->next = cnode;
+		((PurpleGroup *)node->parent)->totalsize++;
+		if (purple_account_is_connected(chat->account)) {
+			((PurpleGroup *)node->parent)->online++;
+			((PurpleGroup *)node->parent)->currentsize++;
+		}
+	} else {
+		if (((PurpleBlistNode *)group)->child)
+			((PurpleBlistNode *)group)->child->prev = cnode;
+		cnode->next = ((PurpleBlistNode *)group)->child;
+		cnode->prev = NULL;
+		((PurpleBlistNode *)group)->child = cnode;
+		cnode->parent = (PurpleBlistNode *)group;
+		group->totalsize++;
+		if (purple_account_is_connected(chat->account)) {
+			group->online++;
+			group->currentsize++;
+		}
+	}
+
+	purple_blist_schedule_save();
+
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode *)cnode);
+
+	purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
+			cnode);
+}
+
+void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
+{
+	PurpleBlistNode *cnode, *bnode;
+	PurpleGroup *g;
+	PurpleContact *c;
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	struct _purple_hbuddy *hb, *hb2;
+	GHashTable *account_buddies;
+
+	g_return_if_fail(buddy != NULL);
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode*)buddy));
+
+	bnode = (PurpleBlistNode *)buddy;
+
+	/* if we're moving to overtop of ourselves, do nothing */
+	if (bnode == node || (!node && bnode->parent &&
+				contact && bnode->parent == (PurpleBlistNode*)contact
+				&& bnode == bnode->parent->child))
+		return;
+
+	if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		c = (PurpleContact*)node->parent;
+		g = (PurpleGroup*)node->parent->parent;
+	} else if (contact) {
+		c = contact;
+		g = PURPLE_GROUP(PURPLE_BLIST_NODE(c)->parent);
+	} else {
+		g = group;
+		if (g == NULL)
+			g = purple_group_new(_("Buddies"));
+		/* Add group to blist if isn't already on it. Fixes #2752. */
+		if (!purple_find_group(g->name)) {
+			purple_blist_add_group(g,
+					purple_blist_get_last_sibling(purple_blist_get_list()->root));
+		}
+		c = purple_contact_new();
+		purple_blist_add_contact(c, g,
+				purple_blist_get_last_child((PurpleBlistNode*)g));
+	}
+
+	cnode = (PurpleBlistNode *)c;
+
+	if (bnode->parent) {
+		if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
+			((PurpleContact*)bnode->parent)->online--;
+			if (((PurpleContact*)bnode->parent)->online == 0)
+				((PurpleGroup*)bnode->parent->parent)->online--;
+		}
+		if (purple_account_is_connected(buddy->account)) {
+			((PurpleContact*)bnode->parent)->currentsize--;
+			if (((PurpleContact*)bnode->parent)->currentsize == 0)
+				((PurpleGroup*)bnode->parent->parent)->currentsize--;
+		}
+		((PurpleContact*)bnode->parent)->totalsize--;
+		/* the group totalsize will be taken care of by remove_contact below */
+
+		if (bnode->parent->parent != (PurpleBlistNode*)g)
+			serv_move_buddy(buddy, (PurpleGroup *)bnode->parent->parent, g);
+
+		if (bnode->next)
+			bnode->next->prev = bnode->prev;
+		if (bnode->prev)
+			bnode->prev->next = bnode->next;
+		if (bnode->parent->child == bnode)
+			bnode->parent->child = bnode->next;
+
+		if (ops && ops->remove)
+			ops->remove(purple_blist_get_list(), bnode);
+
+		purple_blist_schedule_save();
+
+		if (bnode->parent->parent != (PurpleBlistNode*)g) {
+			hb = g_new(struct _purple_hbuddy, 1);
+			hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
+			hb->account = buddy->account;
+			hb->group = bnode->parent->parent;
+			g_hash_table_remove(purple_blist_get_list()->buddies, hb);
+
+			account_buddies = g_hash_table_lookup(purple_blist_get_buddies_cache(), buddy->account);
+			g_hash_table_remove(account_buddies, hb);
+
+			g_free(hb->name);
+			g_free(hb);
+		}
+
+		if (!bnode->parent->child) {
+			purple_blist_remove_contact((PurpleContact*)bnode->parent);
+		} else {
+			purple_contact_invalidate_priority_buddy((PurpleContact*)bnode->parent);
+			if (ops && ops->update)
+				ops->update(purple_blist_get_list(), bnode->parent);
+		}
+	}
+
+	if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		if (node->next)
+			node->next->prev = bnode;
+		bnode->next = node->next;
+		bnode->prev = node;
+		bnode->parent = node->parent;
+		node->next = bnode;
+	} else {
+		if (cnode->child)
+			cnode->child->prev = bnode;
+		bnode->prev = NULL;
+		bnode->next = cnode->child;
+		cnode->child = bnode;
+		bnode->parent = cnode;
+	}
+
+	if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
+		if (++(PURPLE_CONTACT(bnode->parent)->online) == 1)
+			PURPLE_GROUP(bnode->parent->parent)->online++;
+	}
+	if (purple_account_is_connected(buddy->account)) {
+		if (++(PURPLE_CONTACT(bnode->parent)->currentsize) == 1)
+			PURPLE_GROUP(bnode->parent->parent)->currentsize++;
+	}
+	PURPLE_CONTACT(bnode->parent)->totalsize++;
+
+	hb = g_new(struct _purple_hbuddy, 1);
+	hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
+	hb->account = buddy->account;
+	hb->group = ((PurpleBlistNode*)buddy)->parent->parent;
+
+	g_hash_table_replace(purple_blist_get_list()->buddies, hb, buddy);
+
+	account_buddies = g_hash_table_lookup(purple_blist_get_buddies_cache(), buddy->account);
+
+	hb2 = g_new(struct _purple_hbuddy, 1);
+	hb2->name = g_strdup(hb->name);
+	hb2->account = buddy->account;
+	hb2->group = ((PurpleBlistNode*)buddy)->parent->parent;
+
+	g_hash_table_replace(account_buddies, hb2, buddy);
+
+	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
+
+	purple_blist_schedule_save();
+
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode*)buddy);
+
+	/* Signal that the buddy has been added */
+	purple_signal_emit(purple_blist_get_handle(), "buddy-added", buddy);
+
+	purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
+			PURPLE_BLIST_NODE(buddy));
+}
+
+void purple_blist_merge_contact(PurpleContact *source, PurpleBlistNode *node)
+{
+	PurpleBlistNode *sourcenode = (PurpleBlistNode*)source;
+	PurpleBlistNode *targetnode;
+	PurpleBlistNode *prev, *cur, *next;
+	PurpleContact *target;
+g_return_if_fail(source != NULL);
+	g_return_if_fail(node != NULL);
+
+	if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
+		target = (PurpleContact *)node;
+		prev = purple_blist_get_last_child(node);
+	} else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		target = (PurpleContact *)node->parent;
+		prev = node;
+	} else {
+		return;
+	}
+
+	if (source == target || !target)
+		return;
+
+	targetnode = (PurpleBlistNode *)target;
+	next = sourcenode->child;
+
+	while (next) {
+		cur = next;
+		next = cur->next;
+		if (PURPLE_BLIST_NODE_IS_BUDDY(cur)) {
+			purple_blist_add_buddy((PurpleBuddy *)cur, target, NULL, prev);
+			prev = cur;
+		}
+	}
+}
+
+void
+purple_blist_node_destroy(PurpleBlistNode *node)
+{
+	PurpleBlistUiOps *ui_ops;
+	PurpleBlistNode *child, *next_child;
+
+	ui_ops = purple_blist_get_ui_ops();
+	child = node->child;
+	while (child) {
+		next_child = child->next;
+		purple_blist_node_destroy(child);
+		child = next_child;
+	}
+
+	/* Allow the UI to free data */
+	node->parent = NULL;
+	node->child  = NULL;
+	node->next   = NULL;
+	node->prev   = NULL;
+	if (ui_ops && ui_ops->remove)
+		ui_ops->remove(purple_blist_get_list(), node);
+
+	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
+		purple_buddy_destroy((PurpleBuddy*)node);
+	else if (PURPLE_BLIST_NODE_IS_CHAT(node))
+		purple_chat_destroy((PurpleChat*)node);
+	else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
+		purple_contact_destroy((PurpleContact*)node);
+	else if (PURPLE_BLIST_NODE_IS_GROUP(node))
+		purple_group_destroy((PurpleGroup*)node);
+}
+
+void purple_blist_node_initialize_settings(PurpleBlistNode *node)
+{
+	if (node->settings)
+		return;
+
+	node->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+			(GDestroyNotify)purple_g_value_slice_free);
+}
+
+void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key)
+{
+	g_return_if_fail(node != NULL);
+	g_return_if_fail(node->settings != NULL);
+	g_return_if_fail(key != NULL);
+
+	g_hash_table_remove(node->settings, key);
+
+	purple_blist_schedule_save();
+}
+
+void
+purple_blist_node_set_flags(PurpleBlistNode *node, PurpleBlistNodeFlags flags)
+{
+	g_return_if_fail(node != NULL);
+
+	node->flags = flags;
+}
+
+PurpleBlistNodeFlags
+purple_blist_node_get_flags(PurpleBlistNode *node)
+{
+	g_return_val_if_fail(node != NULL, 0);
+
+	return node->flags;
+}
+
+PurpleBlistNodeType
+purple_blist_node_get_type(PurpleBlistNode *node)
+{
+	g_return_val_if_fail(node != NULL, PURPLE_BLIST_OTHER_NODE);
+	return node->type;
+}
+
+gboolean
+purple_blist_node_has_setting(PurpleBlistNode *node,
+                              const char *key)
+{
+	g_return_val_if_fail(node != NULL, FALSE);
+	g_return_val_if_fail(node->settings != NULL, FALSE);
+	g_return_val_if_fail(key != NULL, FALSE);
+
+	return (g_hash_table_lookup(node->settings, key) != NULL);
+}
+
+void
+purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
+{
+	GValue *value;
+
+	g_return_if_fail(node != NULL);
+	g_return_if_fail(node->settings != NULL);
+	g_return_if_fail(key != NULL);
+
+	value = purple_g_value_slice_new(G_TYPE_BOOLEAN);
+	g_value_set_boolean(value, data);
+
+	g_hash_table_replace(node->settings, g_strdup(key), value);
+
+	purple_blist_schedule_save();
+}
+
+gboolean
+purple_blist_node_get_bool(PurpleBlistNode* node, const char *key)
+{
+	GValue *value;
+
+	g_return_val_if_fail(node != NULL, FALSE);
+	g_return_val_if_fail(node->settings != NULL, FALSE);
+	g_return_val_if_fail(key != NULL, FALSE);
+
+	value = g_hash_table_lookup(node->settings, key);
+
+	if (value == NULL)
+		return FALSE;
+
+	g_return_val_if_fail(G_VALUE_HOLDS_BOOLEAN(value), FALSE);
+
+	return g_value_get_boolean(value);
+}
+
+void
+purple_blist_node_set_int(PurpleBlistNode* node, const char *key, int data)
+{
+	GValue *value;
+
+	g_return_if_fail(node != NULL);
+	g_return_if_fail(node->settings != NULL);
+	g_return_if_fail(key != NULL);
+
+	value = purple_g_value_slice_new(G_TYPE_INT);
+	g_value_set_int(value, data);
+
+	g_hash_table_replace(node->settings, g_strdup(key), value);
+
+	purple_blist_schedule_save();
+}
+
+int
+purple_blist_node_get_int(PurpleBlistNode* node, const char *key)
+{
+	GValue *value;
+
+	g_return_val_if_fail(node != NULL, 0);
+	g_return_val_if_fail(node->settings != NULL, 0);
+	g_return_val_if_fail(key != NULL, 0);
+
+	value = g_hash_table_lookup(node->settings, key);
+
+	if (value == NULL)
+		return 0;
+
+	g_return_val_if_fail(G_VALUE_HOLDS_INT(value), 0);
+
+	return g_value_get_int(value);
+}
+
+void
+purple_blist_node_set_string(PurpleBlistNode* node, const char *key, const char *data)
+{
+	GValue *value;
+
+	g_return_if_fail(node != NULL);
+	g_return_if_fail(node->settings != NULL);
+	g_return_if_fail(key != NULL);
+
+	value = purple_g_value_slice_new(G_TYPE_STRING);
+	g_value_set_string(value, data);
+
+	g_hash_table_replace(node->settings, g_strdup(key), value);
+
+	purple_blist_schedule_save();
+}
+
+const char *
+purple_blist_node_get_string(PurpleBlistNode* node, const char *key)
+{
+	GValue *value;
+
+	g_return_val_if_fail(node != NULL, NULL);
+	g_return_val_if_fail(node->settings != NULL, NULL);
+	g_return_val_if_fail(key != NULL, NULL);
+
+	value = g_hash_table_lookup(node->settings, key);
+
+	if (value == NULL)
+		return NULL;
+
+	g_return_val_if_fail(G_VALUE_HOLDS_STRING(value), NULL);
+
+	return g_value_get_string(value);
+}
+
+GList *
+purple_blist_node_get_extended_menu(PurpleBlistNode *n)
+{
+	GList *menu = NULL;
+
+	g_return_val_if_fail(n != NULL, NULL);
+
+	purple_signal_emit(purple_blist_get_handle(),
+			"blist-node-extended-menu",
+			n, &menu);
+	return menu;
+}
============================================================
--- libpurple/buddy.c	cb402e709299a42ecc929e46abfc9d6bcd031df7
+++ libpurple/buddy.c	cb402e709299a42ecc929e46abfc9d6bcd031df7
@@ -0,0 +1,565 @@
+/*
+ * 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
+ *
+ */
+#define _PURPLE_BUDDY_C_
+#define _BLIST_HELPERS_
+
+#include "internal.h"
+#include "blist.h"
+#include "conversation.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "privacy.h"
+#include "prpl.h"
+#include "server.h"
+#include "signals.h"
+#include "util.h"
+#include "value.h"
+#include "xmlnode.h"
+
+void
+parse_buddy(PurpleGroup *group, PurpleContact *contact, xmlnode *bnode)
+{
+	PurpleAccount *account;
+	PurpleBuddy *buddy;
+	char *name = NULL, *alias = NULL;
+	const char *acct_name, *proto, *protocol;
+	xmlnode *x;
+
+	acct_name = xmlnode_get_attrib(bnode, "account");
+	protocol = xmlnode_get_attrib(bnode, "protocol");
+	protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
+	proto = xmlnode_get_attrib(bnode, "proto");
+	proto = _purple_oscar_convert(acct_name, proto); /* XXX: Remove */
+
+	if (!acct_name || (!proto && !protocol))
+		return;
+
+	account = purple_accounts_find(acct_name, proto ? proto : protocol);
+
+	if (!account)
+		return;
+
+	if ((x = xmlnode_get_child(bnode, "name")))
+		name = xmlnode_get_data(x);
+
+	if (!name)
+		return;
+
+	if ((x = xmlnode_get_child(bnode, "alias")))
+		alias = xmlnode_get_data(x);
+
+	buddy = purple_buddy_new(account, name, alias);
+	purple_blist_add_buddy(buddy, contact, group,
+	purple_blist_get_last_child((PurpleBlistNode*)contact));
+
+	for (x = xmlnode_get_child(bnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
+		parse_setting((PurpleBlistNode*)buddy, x);
+	}
+
+	g_free(name);
+	g_free(alias);
+}
+
+static void
+append_buddy(gpointer key, gpointer value, gpointer user_data)
+{
+	GSList **list = user_data;
+	*list = g_slist_prepend(*list, value);
+}
+
+GSList *
+purple_blist_get_buddies()
+{
+	GSList *buddies = NULL;
+
+	if (!purple_blist_get_list())
+		return NULL;
+
+	g_hash_table_foreach(purple_blist_get_list()->buddies, append_buddy, &buddies);
+	return buddies;
+}
+
+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);
+
+	presence = purple_buddy_get_presence(buddy);
+	status = purple_presence_get_active_status(presence);
+
+	purple_debug_info("blist", "Updating buddy status for %s (%s)\n",
+			buddy->name, purple_account_get_protocol_name(buddy->account));
+
+	if (purple_status_is_online(status) &&
+		!purple_status_is_online(old_status)) {
+
+		purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy);
+
+		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);
+
+		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,
+		                 status);
+	}
+
+	/*
+	 * This function used to only call the following two functions if one of
+	 * the above signals had been triggered, but that's not good, because
+	 * if someone's away message changes and they don't go from away to back
+	 * to away then no signal is triggered.
+	 *
+	 * It's a safe assumption that SOMETHING called this function.  PROBABLY
+	 * because something, somewhere changed.  Calling the stuff below
+	 * certainly won't hurt anything.  Unless you're on a K6-2 300.
+	 */
+	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode *)buddy);
+}
+
+void
+purple_blist_update_buddy_icon(PurpleBuddy *buddy)
+{
+	purple_blist_update_node_icon((PurpleBlistNode *)buddy);
+}
+
+/*
+ * TODO: Maybe remove the call to this from server.c and call it
+ * from oscar.c and toc.c instead?
+ */
+void purple_blist_rename_buddy(PurpleBuddy *buddy, const char *name)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	struct _purple_hbuddy *hb, *hb2;
+	GHashTable *account_buddies;
+
+	g_return_if_fail(buddy != NULL);
+
+	hb = g_new(struct _purple_hbuddy, 1);
+	hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
+	hb->account = buddy->account;
+	hb->group = ((PurpleBlistNode *)buddy)->parent->parent;
+	g_hash_table_remove(purple_blist_get_list()->buddies, hb);
+	
+	account_buddies = g_hash_table_lookup(purple_blist_get_buddies_cache(), buddy->account);
+	g_hash_table_remove(account_buddies, hb);
+
+	g_free(hb->name);
+	hb->name = g_strdup(purple_normalize(buddy->account, name));
+	g_hash_table_replace(purple_blist_get_list()->buddies, hb, buddy);
+
+	hb2 = g_new(struct _purple_hbuddy, 1);
+	hb2->name = g_strdup(hb->name);
+	hb2->account = buddy->account;
+	hb2->group = ((PurpleBlistNode *)buddy)->parent->parent;
+
+	g_hash_table_replace(account_buddies, hb2, buddy);
+
+	g_free(buddy->name);
+	buddy->name = g_strdup(name);
+
+	purple_blist_schedule_save();
+
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode *)buddy);
+}
+
+void purple_blist_alias_buddy(PurpleBuddy *buddy, const char *alias)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleConversation *conv;
+	char *old_alias;
+	char *new_alias = NULL;
+
+	g_return_if_fail(buddy != NULL);
+
+	if ((alias != NULL) && (*alias != '\0'))
+		new_alias = purple_utf8_strip_unprintables(alias);
+
+	if (!purple_strings_are_different(buddy->alias, new_alias)) {
+		g_free(new_alias);
+		return;
+	}
+
+	old_alias = buddy->alias;
+
+	if ((new_alias != NULL) && (*new_alias != '\0'))
+		buddy->alias = g_strdup(alias);
+	else {
+		buddy->alias = NULL;
+		g_free(new_alias); /* could be "\0" */
+	}
+
+	purple_blist_schedule_save();
+
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode *)buddy);
+
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
+											   buddy->account);
+	if (conv)
+		purple_conversation_autoset_title(conv);
+
+	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+					 buddy, old_alias);
+	g_free(old_alias);
+}
+
+void purple_blist_server_alias_buddy(PurpleBuddy *buddy, const char *alias)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleConversation *conv;
+	char *old_alias;
+	char *new_alias = NULL;
+
+	g_return_if_fail(buddy != NULL);
+
+	if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
+		new_alias = purple_utf8_strip_unprintables(alias);
+
+	if (!purple_strings_are_different(buddy->server_alias, new_alias)) {
+		g_free(new_alias);
+		return;
+	}
+
+	old_alias = buddy->server_alias;
+
+	if ((new_alias != NULL) && (*new_alias != '\0'))
+		buddy->server_alias = new_alias;
+	else {
+		buddy->server_alias = NULL;
+		g_free(new_alias); /* could be "\0"; */
+	}
+
+	purple_blist_schedule_save();
+
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode *)buddy);
+
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
+											   buddy->account);
+	if (conv)
+		purple_conversation_autoset_title(conv);
+
+	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+					 buddy, old_alias);
+	g_free(old_alias);
+}
+
+PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleBuddy *buddy;
+
+	g_return_val_if_fail(account != NULL, NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	buddy = g_new0(PurpleBuddy, 1);
+	buddy->account  = account;
+	buddy->name     = purple_utf8_strip_unprintables(name);
+	buddy->alias    = purple_utf8_strip_unprintables(alias);
+	buddy->presence = purple_presence_new_for_buddy(buddy);
+	((PurpleBlistNode *)buddy)->type = PURPLE_BLIST_BUDDY_NODE;
+
+	purple_presence_set_status_active(buddy->presence, "offline", TRUE);
+
+	purple_blist_node_initialize_settings((PurpleBlistNode *)buddy);
+
+	if (ops && ops->new_node)
+		ops->new_node((PurpleBlistNode *)buddy);
+
+	PURPLE_DBUS_REGISTER_POINTER(buddy, PurpleBuddy);
+	return buddy;
+}
+
+void
+purple_buddy_destroy(PurpleBuddy *buddy)
+{
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
+
+	/*
+	 * Tell the owner PRPL that we're about to free the buddy so it
+	 * can free proto_data
+	 */
+	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
+	if (prpl) {
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+		if (prpl_info && prpl_info->buddy_free)
+			prpl_info->buddy_free(buddy);
+	}
+
+	/* Delete the node */
+	purple_buddy_icon_unref(buddy->icon);
+	g_hash_table_destroy(buddy->node.settings);
+	purple_presence_destroy(buddy->presence);
+	g_free(buddy->name);
+	g_free(buddy->alias);
+	g_free(buddy->server_alias);
+
+	PURPLE_DBUS_UNREGISTER_POINTER(buddy);
+	g_free(buddy);
+
+	/* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can
+	 * g_object_ref() it when connecting the callback and
+	 * g_object_unref() it in the handler.  That way, it won't
+	 * get freed while the timeout is pending and this line can
+	 * be removed. */
+	while (g_source_remove_by_user_data((gpointer *)buddy));
+}
+
+void
+purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
+{
+	g_return_if_fail(buddy != NULL);
+
+	if (buddy->icon != icon)
+	{
+		purple_buddy_icon_unref(buddy->icon);
+		buddy->icon = (icon != NULL ? purple_buddy_icon_ref(icon) : NULL);
+	}
+
+	purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy);
+
+	purple_blist_update_node_icon((PurpleBlistNode*)buddy);
+}
+
+PurpleAccount *
+purple_buddy_get_account(const PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	return buddy->account;
+}
+
+const char *
+purple_buddy_get_name(const PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	return buddy->name;
+}
+
+PurpleBuddyIcon *
+purple_buddy_get_icon(const PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	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;
+}
+
+const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	if ((buddy->alias != NULL) && (*buddy->alias != '\0')) {
+		return buddy->alias;
+	} else if ((purple_buddy_get_server_alias(buddy) != NULL) &&
+		   (*purple_buddy_get_server_alias(buddy) != '\0')) {
+
+		return purple_buddy_get_server_alias(buddy);
+	}
+
+	return NULL;
+}
+
+const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy)
+{
+	PurpleContact *c;
+
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	/* Search for an alias for the buddy. In order of precedence: */
+	/* The buddy alias */
+	if (buddy->alias != NULL)
+		return buddy->alias;
+
+	/* The contact alias */
+	c = purple_buddy_get_contact(buddy);
+	if ((c != NULL) && (c->alias != NULL))
+		return c->alias;
+
+	/* The server alias */
+	if ((purple_buddy_get_server_alias(buddy)) && (*purple_buddy_get_server_alias(buddy)))
+		return purple_buddy_get_server_alias(buddy);
+
+	/* The buddy's user name (i.e. no alias) */
+	return buddy->name;
+}
+
+const char *purple_buddy_get_alias(PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	/* Search for an alias for the buddy. In order of precedence: */
+	/* The buddy alias */
+	if (buddy->alias != NULL)
+		return buddy->alias;
+
+	/* The server alias */
+	if ((purple_buddy_get_server_alias(buddy)) && (*purple_buddy_get_server_alias(buddy)))
+		return purple_buddy_get_server_alias(buddy);
+
+	/* The buddy's user name (i.e. no alias) */
+	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);
+
+	if ((buddy->server_alias) && (*buddy->server_alias))
+	    return buddy->server_alias;
+
+	return NULL;
+}
+
+const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
+{
+	PurpleContact *c;
+
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	/* Search for an alias for the buddy. In order of precedence: */
+	/* The buddy alias */
+	if (buddy->alias != NULL)
+		return buddy->alias;
+
+	/* The contact alias */
+	c = purple_buddy_get_contact(buddy);
+	if ((c != NULL) && (c->alias != NULL))
+		return c->alias;
+
+	/* The buddy's user name (i.e. no alias) */
+	return buddy->name;
+}
+
+PurpleBuddy *purple_find_buddy_in_group(PurpleAccount *account, const char *name,
+		PurpleGroup *group)
+{
+	struct _purple_hbuddy hb;
+	PurpleBuddy *ret;
+
+	g_return_val_if_fail(purple_blist_get_list() != NULL, NULL);
+	g_return_val_if_fail(account != NULL, NULL);
+	g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
+
+	hb.name = g_strdup(purple_normalize(account, name));
+	hb.account = account;
+	hb.group = (PurpleBlistNode*)group;
+
+	ret = g_hash_table_lookup(purple_blist_get_list()->buddies, &hb);
+	g_free(hb.name);
+
+	return ret;
+}
+
+PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy)->parent);
+}
+
+PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+	return buddy->presence;
+}
+
+PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	if (((PurpleBlistNode *)buddy)->parent == NULL)
+		return NULL;
+
+	return (PurpleGroup *)(((PurpleBlistNode*)buddy)->parent->parent);
+}
+
+xmlnode *
+buddy_to_xmlnode(PurpleBlistNode *bnode)
+{
+	xmlnode *node, *child;
+	PurpleBuddy *buddy;
+
+	buddy = (PurpleBuddy *)bnode;
+
+	node = xmlnode_new("buddy");
+	xmlnode_set_attrib(node, "account", purple_account_get_username(buddy->account));
+	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(buddy->account));
+
+	child = xmlnode_new_child(node, "name");
+	xmlnode_insert_data(child, buddy->name, -1);
+
+	if (buddy->alias != NULL)
+	{
+		child = xmlnode_new_child(node, "alias");
+		xmlnode_insert_data(child, buddy->alias, -1);
+	}
+
+	/* Write buddy settings */
+	g_hash_table_foreach(buddy->node.settings, value_to_xmlnode, node);
+
+	return node;
+}
============================================================
--- libpurple/chat.c	3183d861ddd88cb7e861d72304bf9b2c8b294aed
+++ libpurple/chat.c	3183d861ddd88cb7e861d72304bf9b2c8b294aed
@@ -0,0 +1,307 @@
+/*
+ * 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
+ *
+ */
+#define _PURPLE_CHAT_C
+#define _BLIST_HELPERS_
+
+#include "internal.h"
+#include "blist.h"
+#include "conversation.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "privacy.h"
+#include "prpl.h"
+#include "server.h"
+#include "signals.h"
+#include "util.h"
+#include "value.h"
+#include "xmlnode.h"
+
+static void
+chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
+{
+	const char *name;
+	const char *data;
+	xmlnode *node, *child;
+
+	name = (const char *)key;
+	data = (const char *)value;
+	node = (xmlnode *)user_data;
+
+	g_return_if_fail(data != NULL);
+
+	child = xmlnode_new_child(node, "component");
+	xmlnode_set_attrib(child, "name", name);
+	xmlnode_insert_data(child, data, -1);
+}
+
+xmlnode *
+chat_to_xmlnode(PurpleBlistNode *cnode)
+{
+	xmlnode *node, *child;
+	PurpleChat *chat;
+
+	chat = (PurpleChat *)cnode;
+
+	node = xmlnode_new("chat");
+	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(chat->account));
+	xmlnode_set_attrib(node, "account", purple_account_get_username(chat->account));
+
+	if (chat->alias != NULL)
+	{
+		child = xmlnode_new_child(node, "alias");
+		xmlnode_insert_data(child, chat->alias, -1);
+	}
+
+	/* Write chat components */
+	g_hash_table_foreach(chat->components, chat_component_to_xmlnode, node);
+
+	/* Write chat settings */
+	g_hash_table_foreach(chat->node.settings, value_to_xmlnode, node);
+
+	return node;
+}
+
+void
+parse_chat(PurpleGroup *group, xmlnode *cnode)
+{
+	PurpleChat *chat;
+	PurpleAccount *account;
+	const char *acct_name, *proto, *protocol;
+	xmlnode *x;
+	char *alias = NULL;
+	GHashTable *components;
+
+	acct_name = xmlnode_get_attrib(cnode, "account");
+	protocol = xmlnode_get_attrib(cnode, "protocol");
+	proto = xmlnode_get_attrib(cnode, "proto");
+
+	if (!acct_name || (!proto && !protocol))
+		return;
+
+	account = purple_accounts_find(acct_name, proto ? proto : protocol);
+
+	if (!account)
+		return;
+
+	if ((x = xmlnode_get_child(cnode, "alias")))
+		alias = xmlnode_get_data(x);
+
+	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+	for (x = xmlnode_get_child(cnode, "component"); x; x = xmlnode_get_next_twin(x)) {
+		const char *name;
+		char *value;
+
+		name = xmlnode_get_attrib(x, "name");
+		value = xmlnode_get_data(x);
+		g_hash_table_replace(components, g_strdup(name), value);
+	}
+
+	chat = purple_chat_new(account, alias, components);
+	purple_blist_add_chat(chat, group,
+			purple_blist_get_last_child((PurpleBlistNode*)group));
+
+	for (x = xmlnode_get_child(cnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
+		parse_setting((PurpleBlistNode*)chat, x);
+	}
+
+	g_free(alias);
+}
+
+void purple_blist_alias_chat(PurpleChat *chat, const char *alias)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	char *old_alias;
+	char *new_alias = NULL;
+
+	g_return_if_fail(chat != NULL);
+
+	if ((alias != NULL) && (*alias != '\0'))
+		new_alias = purple_utf8_strip_unprintables(alias);
+
+	if (!purple_strings_are_different(chat->alias, new_alias)) {
+		g_free(new_alias);
+		return;
+	}
+
+	old_alias = chat->alias;
+
+	if ((new_alias != NULL) && (*new_alias != '\0'))
+		chat->alias = new_alias;
+	else {
+		chat->alias = NULL;
+		g_free(new_alias); /* could be "\0" */
+	}
+
+	purple_blist_schedule_save();
+
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode *)chat);
+
+	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+					 chat, old_alias);
+	g_free(old_alias);
+}
+
+PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleChat *chat;
+
+	g_return_val_if_fail(account != NULL, FALSE);
+	g_return_val_if_fail(components != NULL, FALSE);
+
+	chat = g_new0(PurpleChat, 1);
+	chat->account = account;
+	if ((alias != NULL) && (*alias != '\0'))
+		chat->alias = purple_utf8_strip_unprintables(alias);
+	chat->components = components;
+	purple_blist_node_initialize_settings((PurpleBlistNode *)chat);
+	((PurpleBlistNode *)chat)->type = PURPLE_BLIST_CHAT_NODE;
+
+	if (ops != NULL && ops->new_node != NULL)
+		ops->new_node((PurpleBlistNode *)chat);
+
+	PURPLE_DBUS_REGISTER_POINTER(chat, PurpleChat);
+	return chat;
+}
+
+void
+purple_chat_destroy(PurpleChat *chat)
+{
+	g_hash_table_destroy(chat->components);
+	g_hash_table_destroy(chat->node.settings);
+	g_free(chat->alias);
+	PURPLE_DBUS_UNREGISTER_POINTER(chat);
+	g_free(chat);
+}
+
+const char *purple_chat_get_name(PurpleChat *chat)
+{
+	char *ret = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+
+	g_return_val_if_fail(chat != NULL, NULL);
+
+	if ((chat->alias != NULL) && (*chat->alias != '\0'))
+		return chat->alias;
+
+	prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account));
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info->chat_info) {
+		struct proto_chat_entry *pce;
+		GList *parts = prpl_info->chat_info(purple_account_get_connection(chat->account));
+		pce = parts->data;
+		ret = g_hash_table_lookup(chat->components, pce->identifier);
+		g_list_foreach(parts, (GFunc)g_free, NULL);
+		g_list_free(parts);
+	}
+
+	return ret;
+}
+
+PurpleChat *
+purple_blist_find_chat(PurpleAccount *account, const char *name)
+{
+	char *chat_name;
+	PurpleChat *chat;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	struct proto_chat_entry *pce;
+	PurpleBlistNode *node, *group;
+	GList *parts;
+	char *normname;
+
+	g_return_val_if_fail(purple_blist_get_list() != NULL, NULL);
+	g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
+
+	if (!purple_account_is_connected(account))
+		return NULL;
+
+	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info->find_blist_chat != NULL)
+		return prpl_info->find_blist_chat(account, name);
+
+	normname = g_strdup(purple_normalize(account, name));
+	for (group = purple_blist_get_list()->root; group != NULL; group = group->next) {
+		for (node = group->child; node != NULL; node = node->next) {
+			if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
+
+				chat = (PurpleChat*)node;
+
+				if (account != chat->account)
+					continue;
+
+				parts = prpl_info->chat_info(
+					purple_account_get_connection(chat->account));
+
+				pce = parts->data;
+				chat_name = g_hash_table_lookup(chat->components,
+												pce->identifier);
+				g_list_foreach(parts, (GFunc)g_free, NULL);
+				g_list_free(parts);
+
+				if (chat->account == account && chat_name != NULL &&
+					normname != NULL && !strcmp(purple_normalize(account, chat_name), normname)) {
+					g_free(normname);
+					return chat;
+				}
+			}
+		}
+	}
+
+	g_free(normname);
+	return NULL;
+}
+
+PurpleGroup *
+purple_chat_get_group(PurpleChat *chat)
+{
+	g_return_val_if_fail(chat != NULL, NULL);
+
+	return (PurpleGroup *)(((PurpleBlistNode *)chat)->parent);
+}
+
+PurpleAccount *
+purple_chat_get_account(PurpleChat *chat)
+{
+	g_return_val_if_fail(chat != NULL, NULL);
+
+	return chat->account;
+}
+
+GHashTable *
+purple_chat_get_components(PurpleChat *chat)
+{
+	g_return_val_if_fail(chat != NULL, NULL);
+
+	return chat->components;
+}
+
+
============================================================
--- libpurple/contact.c	aee5a71756fb1cf9f13a88939a5bf111aceeacd3
+++ libpurple/contact.c	aee5a71756fb1cf9f13a88939a5bf111aceeacd3
@@ -0,0 +1,302 @@
+/*
+ * 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
+ *
+ */
+#define _PURPLE_CHAT_C_
+#define _BLIST_HELPERS_
+
+#include "internal.h"
+#include "blist.h"
+#include "conversation.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "privacy.h"
+#include "prpl.h"
+#include "server.h"
+#include "signals.h"
+#include "util.h"
+#include "value.h"
+#include "xmlnode.h"
+
+xmlnode *
+contact_to_xmlnode(PurpleBlistNode *cnode)
+{
+	xmlnode *node, *child;
+	PurpleContact *contact;
+	PurpleBlistNode *bnode;
+
+	contact = (PurpleContact *)cnode;
+
+	node = xmlnode_new("contact");
+
+	if (contact->alias != NULL)
+	{
+		xmlnode_set_attrib(node, "alias", contact->alias);
+	}
+
+	/* Write buddies */
+	for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
+	{
+		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(bnode))
+			continue;
+		if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+		{
+			child = buddy_to_xmlnode(bnode);
+			xmlnode_insert_child(node, child);
+		}
+	}
+
+	/* Write contact settings */
+	g_hash_table_foreach(cnode->settings, value_to_xmlnode, node);
+
+	return node;
+}
+
+void
+parse_contact(PurpleGroup *group, xmlnode *cnode)
+{
+	PurpleContact *contact = purple_contact_new();
+	xmlnode *x;
+	const char *alias;
+
+	purple_blist_add_contact(contact, group,
+			purple_blist_get_last_child((PurpleBlistNode*)group));
+
+	if ((alias = xmlnode_get_attrib(cnode, "alias"))) {
+		purple_blist_alias_contact(contact, alias);
+	}
+
+	for (x = cnode->child; x; x = x->next) {
+		if (x->type != XMLNODE_TYPE_TAG)
+			continue;
+		if (purple_strequal(x->name, "buddy"))
+			parse_buddy(group, contact, x);
+		else if (purple_strequal(x->name, "setting"))
+			parse_setting((PurpleBlistNode*)contact, x);
+	}
+
+	/* if the contact is empty, don't keep it around.  it causes problems */
+	if (!((PurpleBlistNode*)contact)->child)
+		purple_blist_remove_contact(contact);
+}
+
+void
+purple_contact_compute_priority_buddy(PurpleContact *contact)
+{
+	PurpleBlistNode *bnode;
+	PurpleBuddy *new_priority = NULL;
+
+	g_return_if_fail(contact != NULL);
+
+	contact->priority = NULL;
+	for (bnode = ((PurpleBlistNode*)contact)->child;
+			bnode != NULL;
+			bnode = bnode->next)
+	{
+		PurpleBuddy *buddy;
+
+		if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+			continue;
+
+		buddy = (PurpleBuddy*)bnode;
+		if (new_priority == NULL)
+		{
+			new_priority = buddy;
+			continue;
+		}
+
+		if (purple_account_is_connected(buddy->account))
+		{
+			int cmp = 1;
+			if (purple_account_is_connected(new_priority->account))
+				cmp = purple_presence_compare(purple_buddy_get_presence(new_priority),
+						purple_buddy_get_presence(buddy));
+
+			if (cmp > 0 || (cmp == 0 &&
+			                purple_prefs_get_bool("/purple/contact/last_match")))
+			{
+				new_priority = buddy;
+			}
+		}
+	}
+
+	contact->priority = new_priority;
+	contact->priority_valid = TRUE;
+}
+
+/*****************************************************************************
+ * Public API functions                                                      *
+ *****************************************************************************/
+
+void purple_blist_alias_contact(PurpleContact *contact, const char *alias)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleConversation *conv;
+	PurpleBlistNode *bnode;
+	char *old_alias;
+	char *new_alias = NULL;
+
+	g_return_if_fail(contact != NULL);
+
+	if ((alias != NULL) && (*alias != '\0'))
+		new_alias = purple_utf8_strip_unprintables(alias);
+
+	if (!purple_strings_are_different(contact->alias, new_alias)) {
+		g_free(new_alias);
+		return;
+	}
+
+	old_alias = contact->alias;
+
+	if ((new_alias != NULL) && (*new_alias != '\0'))
+		contact->alias = new_alias;
+	else {
+		contact->alias = NULL;
+		g_free(new_alias); /* could be "\0" */
+	}
+
+	purple_blist_schedule_save();
+
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode *)contact);
+
+	for(bnode = ((PurpleBlistNode *)contact)->child; bnode != NULL; bnode = bnode->next)
+	{
+		PurpleBuddy *buddy = (PurpleBuddy *)bnode;
+
+		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
+												   buddy->account);
+		if (conv)
+			purple_conversation_autoset_title(conv);
+	}
+
+	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
+					 contact, old_alias);
+	g_free(old_alias);
+}
+
+PurpleContact *purple_contact_new()
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+
+	PurpleContact *contact = g_new0(PurpleContact, 1);
+	contact->totalsize = 0;
+	contact->currentsize = 0;
+	contact->online = 0;
+	purple_blist_node_initialize_settings((PurpleBlistNode *)contact);
+	((PurpleBlistNode *)contact)->type = PURPLE_BLIST_CONTACT_NODE;
+
+	if (ops && ops->new_node)
+		ops->new_node((PurpleBlistNode *)contact);
+
+	PURPLE_DBUS_REGISTER_POINTER(contact, PurpleContact);
+	return contact;
+}
+
+void
+purple_contact_destroy(PurpleContact *contact)
+{
+	g_hash_table_destroy(contact->node.settings);
+	g_free(contact->alias);
+	PURPLE_DBUS_UNREGISTER_POINTER(contact);
+	g_free(contact);
+}
+
+void purple_contact_set_alias(PurpleContact *contact, const char *alias)
+{
+	purple_blist_alias_contact(contact,alias);
+}
+
+const char *purple_contact_get_alias(PurpleContact* contact)
+{
+	g_return_val_if_fail(contact != NULL, NULL);
+
+	if (contact->alias)
+		return contact->alias;
+
+	return purple_buddy_get_alias(purple_contact_get_priority_buddy(contact));
+}
+
+gboolean purple_contact_on_account(PurpleContact *c, PurpleAccount *account)
+{
+	PurpleBlistNode *bnode, *cnode = (PurpleBlistNode *) c;
+
+	g_return_val_if_fail(c != NULL, FALSE);
+	g_return_val_if_fail(account != NULL, FALSE);
+
+	for (bnode = cnode->child; bnode; bnode = bnode->next) {
+		PurpleBuddy *buddy;
+
+		if (! PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+			continue;
+
+		buddy = (PurpleBuddy *)bnode;
+		if (buddy->account == account)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+void purple_contact_invalidate_priority_buddy(PurpleContact *contact)
+{
+	g_return_if_fail(contact != NULL);
+
+	contact->priority_valid = FALSE;
+}
+
+PurpleGroup *purple_group_new(const char *name)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleGroup *group;
+
+	g_return_val_if_fail(name  != NULL, NULL);
+	g_return_val_if_fail(*name != '\0', NULL);
+
+	group = purple_find_group(name);
+	if (group != NULL)
+		return group;
+
+	group = g_new0(PurpleGroup, 1);
+	group->name = purple_utf8_strip_unprintables(name);
+	group->totalsize = 0;
+	group->currentsize = 0;
+	group->online = 0;
+	purple_blist_node_initialize_settings((PurpleBlistNode *)group);
+	((PurpleBlistNode *)group)->type = PURPLE_BLIST_GROUP_NODE;
+
+	if (ops && ops->new_node)
+		ops->new_node((PurpleBlistNode *)group);
+
+	PURPLE_DBUS_REGISTER_POINTER(group, PurpleGroup);
+	return group;
+}
+
+PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact)
+{
+	g_return_val_if_fail(contact != NULL, NULL);
+
+	if (!contact->priority_valid)
+		purple_contact_compute_priority_buddy(contact);
+
+	return contact->priority;
+}
============================================================
--- libpurple/group.c	a00a480ba06e038f085790a88d89dacfb0d333fe
+++ libpurple/group.c	a00a480ba06e038f085790a88d89dacfb0d333fe
@@ -0,0 +1,284 @@
+/*
+ * 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
+ *
+ */
+#define _PURPLE_GROUP_C_
+#define _BLIST_HELPERS_
+
+#include "internal.h"
+#include "blist.h"
+#include "conversation.h"
+#include "dbus-maybe.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "privacy.h"
+#include "prpl.h"
+#include "server.h"
+#include "signals.h"
+#include "util.h"
+#include "value.h"
+#include "xmlnode.h"
+
+xmlnode *
+group_to_xmlnode(PurpleBlistNode *gnode)
+{
+	xmlnode *node, *child;
+	PurpleGroup *group;
+	PurpleBlistNode *cnode;
+
+	group = (PurpleGroup *)gnode;
+
+	node = xmlnode_new("group");
+	xmlnode_set_attrib(node, "name", group->name);
+
+	/* Write settings */
+	g_hash_table_foreach(group->node.settings, value_to_xmlnode, node);
+
+	/* Write contacts and chats */
+	for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
+	{
+		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode))
+			continue;
+		if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+		{
+			child = contact_to_xmlnode(cnode);
+			xmlnode_insert_child(node, child);
+		}
+		else if (PURPLE_BLIST_NODE_IS_CHAT(cnode))
+		{
+			child = chat_to_xmlnode(cnode);
+			xmlnode_insert_child(node, child);
+		}
+	}
+
+	return node;
+}
+
+void
+parse_group(xmlnode *groupnode)
+{
+	const char *name = xmlnode_get_attrib(groupnode, "name");
+	PurpleGroup *group;
+	xmlnode *cnode;
+
+	if (!name)
+		name = _("Buddies");
+
+	group = purple_group_new(name);
+	purple_blist_add_group(group,
+			purple_blist_get_last_sibling(purple_blist_get_list()->root));
+
+	for (cnode = groupnode->child; cnode; cnode = cnode->next) {
+		if (cnode->type != XMLNODE_TYPE_TAG)
+			continue;
+		if (purple_strequal(cnode->name, "setting"))
+			parse_setting((PurpleBlistNode*)group, cnode);
+		else if (purple_strequal(cnode->name, "contact") ||
+				purple_strequal(cnode->name, "person"))
+			parse_contact(group, cnode);
+		else if (purple_strequal(cnode->name, "chat"))
+			parse_chat(group, cnode);
+	}
+}
+
+/*
+ * TODO: If merging, prompt the user if they want to merge.
+ */
+void purple_blist_rename_group(PurpleGroup *source, const char *name)
+{
+	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
+	PurpleGroup *dest;
+	gchar *old_name;
+	gchar *new_name;
+	GList *moved_buddies = NULL;
+	GSList *accts;
+
+	g_return_if_fail(source != NULL);
+	g_return_if_fail(name != NULL);
+
+	new_name = purple_utf8_strip_unprintables(name);
+
+	if (*new_name == '\0' || purple_strequal(new_name, source->name)) {
+		g_free(new_name);
+		return;
+	}
+
+	dest = purple_find_group(new_name);
+	if (dest != NULL && purple_utf8_strcasecmp(source->name, dest->name) != 0) {
+		/* We're merging two groups */
+		PurpleBlistNode *prev, *child, *next;
+
+		prev = purple_blist_get_last_child((PurpleBlistNode*)dest);
+		child = ((PurpleBlistNode*)source)->child;
+
+		/*
+		 * TODO: This seems like a dumb way to do this... why not just
+		 * append all children from the old group to the end of the new
+		 * one?  PRPLs might be expecting to receive an add_buddy() for
+		 * each moved buddy...
+		 */
+		while (child)
+		{
+			next = child->next;
+			if (PURPLE_BLIST_NODE_IS_CONTACT(child)) {
+				PurpleBlistNode *bnode;
+				purple_blist_add_contact((PurpleContact *)child, dest, prev);
+				for (bnode = child->child; bnode != NULL; bnode = bnode->next) {
+					purple_blist_add_buddy((PurpleBuddy *)bnode, (PurpleContact *)child,
+							NULL, bnode->prev);
+					moved_buddies = g_list_append(moved_buddies, bnode);
+				}
+				prev = child;
+			} else if (PURPLE_BLIST_NODE_IS_CHAT(child)) {
+				purple_blist_add_chat((PurpleChat *)child, dest, prev);
+				prev = child;
+			} else {
+				purple_debug(PURPLE_DEBUG_ERROR, "blist",
+						"Unknown child type in group %s\n", source->name);
+			}
+			child = next;
+		}
+
+		/* Make a copy of the old group name and then delete the old group */
+		old_name = g_strdup(source->name);
+		purple_blist_remove_group(source);
+		source = dest;
+		g_free(new_name);
+	} else {
+		/* A simple rename */
+		PurpleBlistNode *cnode, *bnode;
+
+		/* Build a GList of all buddies in this group */
+		for (cnode = ((PurpleBlistNode *)source)->child; cnode != NULL; cnode = cnode->next) {
+			if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+				for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
+					moved_buddies = g_list_append(moved_buddies, bnode);
+		}
+
+		old_name = source->name;
+		source->name = new_name;
+	}
+
+	/* Save our changes */
+	purple_blist_schedule_save();
+
+	/* Update the UI */
+	if (ops && ops->update)
+		ops->update(purple_blist_get_list(), (PurpleBlistNode*)source);
+
+	/* Notify all PRPLs */
+	/* TODO: Is this condition needed?  Seems like it would always be TRUE */
+	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;
+			PurplePlugin *prpl = NULL;
+			PurplePluginProtocolInfo *prpl_info = NULL;
+			GList *l = NULL, *buddies = NULL;
+
+			gc = purple_account_get_connection(account);
+
+			if(gc)
+				prpl = purple_connection_get_prpl(gc);
+
+			if(gc && prpl)
+				prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+			if(!prpl_info)
+				continue;
+
+			for(l = moved_buddies; l; l = l->next) {
+				PurpleBuddy *buddy = (PurpleBuddy *)l->data;
+
+				if(buddy && buddy->account == account)
+					buddies = g_list_append(buddies, (PurpleBlistNode *)buddy);
+			}
+
+			if(prpl_info->rename_group) {
+				prpl_info->rename_group(gc, old_name, source, buddies);
+			} else {
+				GList *cur, *groups = NULL;
+
+				/* Make a list of what the groups each buddy is in */
+				for(cur = buddies; cur; cur = cur->next) {
+					PurpleBlistNode *node = (PurpleBlistNode *)cur->data;
+					groups = g_list_prepend(groups, node->parent->parent);
+				}
+
+				purple_account_remove_buddies(account, buddies, groups);
+				g_list_free(groups);
+				purple_account_add_buddies(account, buddies);
+			}
+
+			g_list_free(buddies);
+		}
+	}
+	g_list_free(moved_buddies);
+	g_free(old_name);
+}
+
+void
+purple_group_destroy(PurpleGroup *group)
+{
+	g_hash_table_destroy(group->node.settings);
+	g_free(group->name);
+	PURPLE_DBUS_UNREGISTER_POINTER(group);
+	g_free(group);
+}
+
+PurpleGroup *purple_find_group(const char *name)
+{
+	PurpleBlistNode *node;
+
+	g_return_val_if_fail(purple_blist_get_list() != NULL, NULL);
+	g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
+
+	for (node = purple_blist_get_list()->root; node != NULL; node = node->next) {
+		if (!purple_utf8_strcasecmp(((PurpleGroup *)node)->name, name))
+			return (PurpleGroup *)node;
+	}
+
+	return NULL;
+}
+
+const char *purple_group_get_name(PurpleGroup *group)
+{
+	g_return_val_if_fail(group != NULL, NULL);
+
+	return group->name;
+}
+
+int purple_blist_get_group_size(PurpleGroup *group, gboolean offline)
+{
+	if (!group)
+		return 0;
+
+	return offline ? group->totalsize : group->currentsize;
+}
+
+int purple_blist_get_group_online_count(PurpleGroup *group)
+{
+	if (!group)
+		return 0;
+
+	return group->online;
+}
============================================================
--- libpurple/Makefile.am	a056a5348cd7d3350a919a8f3e8e87b7ad5df858
+++ libpurple/Makefile.am	8185e184741f4727c96ec17e30e3abe6fe35f3fe
@@ -38,13 +38,17 @@ purple_coresources = \
 	accountmanager.c \
 	accountopt.c \
 	blist.c \
+	blist-node.c \
+	buddy.c \
 	buddyicon.c \
 	certificate.c \
+	chat.c \
 	cipher.c \
 	circbuffer.c \
 	cmds.c \
 	connection.c \
 	conversation.c \
+	contact.c \
 	core.c \
 	debug.c \
 	descipher.c \
@@ -52,6 +56,7 @@ purple_coresources = \
 	desktopitem.c \
 	eventloop.c \
 	ft.c \
+	group.c \
 	hmaccipher.c \
 	idle.c \
 	imgstore.c \
============================================================
--- libpurple/blist.c	e38e665fd6379dc890709cd58b31f4741cc51855
+++ libpurple/blist.c	334e745de44806a8c54287ca85b1e06f4f72ca0b
@@ -21,6 +21,7 @@
  *
  */
 #define _PURPLE_BLIST_C_
+#define _BLIST_HELPERS_
 
 #include "internal.h"
 #include "blist.h"
@@ -55,34 +56,6 @@ static gboolean       blist_loaded = FAL
  * Private utility functions                                         *
  *********************************************************************/
 
-static PurpleBlistNode *purple_blist_get_last_sibling(PurpleBlistNode *node)
-{
-	PurpleBlistNode *n = node;
-	if (!n)
-		return NULL;
-	while (n->next)
-		n = n->next;
-	return n;
-}
-
-static PurpleBlistNode *purple_blist_get_last_child(PurpleBlistNode *node)
-{
-	if (!node)
-		return NULL;
-	return purple_blist_get_last_sibling(node->child);
-}
-
-struct _list_account_buddies {
-	GSList *list;
-	PurpleAccount *account;
-};
-
-struct _purple_hbuddy {
-	char *name;
-	PurpleAccount *account;
-	PurpleBlistNode *group;
-};
-
 static guint _purple_blist_hbuddy_hash(struct _purple_hbuddy *hb)
 {
 	return g_str_hash(hb->name);
@@ -114,12 +87,35 @@ purple_blist_buddies_cache_remove_accoun
 	g_hash_table_remove(buddies_cache, account);
 }
 
+PurpleBuddyList*
+purple_blist_get_list(void)
+{
+  return purplebuddylist;
+}
 
+GHashTable* 
+purple_blist_get_buddies_cache(void)
+{
+  return buddies_cache;
+}
+
+guint 
+purple_blist_get_save_timer(void)
+{
+  return save_timer;
+}
+
+gboolean 
+purple_blist_get_blist_loaded(void)
+{
+  return blist_loaded;
+}
+
 /*********************************************************************
  * Writing to disk                                                   *
  *********************************************************************/
 
-static void
+void
 value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data)
 {
 	const char *name;
@@ -162,148 +158,7 @@ value_to_xmlnode(gpointer key, gpointer 
 	}
 }
 
-static void
-chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
-{
-	const char *name;
-	const char *data;
-	xmlnode *node, *child;
-
-	name = (const char *)key;
-	data = (const char *)value;
-	node = (xmlnode *)user_data;
-
-	g_return_if_fail(data != NULL);
-
-	child = xmlnode_new_child(node, "component");
-	xmlnode_set_attrib(child, "name", name);
-	xmlnode_insert_data(child, data, -1);
-}
-
 static xmlnode *
-buddy_to_xmlnode(PurpleBlistNode *bnode)
-{
-	xmlnode *node, *child;
-	PurpleBuddy *buddy;
-
-	buddy = (PurpleBuddy *)bnode;
-
-	node = xmlnode_new("buddy");
-	xmlnode_set_attrib(node, "account", purple_account_get_username(buddy->account));
-	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(buddy->account));
-
-	child = xmlnode_new_child(node, "name");
-	xmlnode_insert_data(child, buddy->name, -1);
-
-	if (buddy->alias != NULL)
-	{
-		child = xmlnode_new_child(node, "alias");
-		xmlnode_insert_data(child, buddy->alias, -1);
-	}
-
-	/* Write buddy settings */
-	g_hash_table_foreach(buddy->node.settings, value_to_xmlnode, node);
-
-	return node;
-}
-
-static xmlnode *
-contact_to_xmlnode(PurpleBlistNode *cnode)
-{
-	xmlnode *node, *child;
-	PurpleContact *contact;
-	PurpleBlistNode *bnode;
-
-	contact = (PurpleContact *)cnode;
-
-	node = xmlnode_new("contact");
-
-	if (contact->alias != NULL)
-	{
-		xmlnode_set_attrib(node, "alias", contact->alias);
-	}
-
-	/* Write buddies */
-	for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
-	{
-		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(bnode))
-			continue;
-		if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
-		{
-			child = buddy_to_xmlnode(bnode);
-			xmlnode_insert_child(node, child);
-		}
-	}
-
-	/* Write contact settings */
-	g_hash_table_foreach(cnode->settings, value_to_xmlnode, node);
-
-	return node;
-}
-
-static xmlnode *
-chat_to_xmlnode(PurpleBlistNode *cnode)
-{
-	xmlnode *node, *child;
-	PurpleChat *chat;
-
-	chat = (PurpleChat *)cnode;
-
-	node = xmlnode_new("chat");
-	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(chat->account));
-	xmlnode_set_attrib(node, "account", purple_account_get_username(chat->account));
-
-	if (chat->alias != NULL)
-	{
-		child = xmlnode_new_child(node, "alias");
-		xmlnode_insert_data(child, chat->alias, -1);
-	}
-
-	/* Write chat components */
-	g_hash_table_foreach(chat->components, chat_component_to_xmlnode, node);
-
-	/* Write chat settings */
-	g_hash_table_foreach(chat->node.settings, value_to_xmlnode, node);
-
-	return node;
-}
-
-static xmlnode *
-group_to_xmlnode(PurpleBlistNode *gnode)
-{
-	xmlnode *node, *child;
-	PurpleGroup *group;
-	PurpleBlistNode *cnode;
-
-	group = (PurpleGroup *)gnode;
-
-	node = xmlnode_new("group");
-	xmlnode_set_attrib(node, "name", group->name);
-
-	/* Write settings */
-	g_hash_table_foreach(group->node.settings, value_to_xmlnode, node);
-
-	/* Write contacts and chats */
-	for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
-	{
-		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode))
-			continue;
-		if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
-		{
-			child = contact_to_xmlnode(cnode);
-			xmlnode_insert_child(node, child);
-		}
-		else if (PURPLE_BLIST_NODE_IS_CHAT(cnode))
-		{
-			child = chat_to_xmlnode(cnode);
-			xmlnode_insert_child(node, child);
-		}
-	}
-
-	return node;
-}
-
-static xmlnode *
 accountprivacy_to_xmlnode(PurpleAccount *account)
 {
 	xmlnode *node, *child;
@@ -405,172 +260,6 @@ purple_blist_schedule_save()
  * Reading from disk                                                 *
  *********************************************************************/
 
-static void
-parse_setting(PurpleBlistNode *node, xmlnode *setting)
-{
-	const char *name = xmlnode_get_attrib(setting, "name");
-	const char *type = xmlnode_get_attrib(setting, "type");
-	char *value = xmlnode_get_data(setting);
-
-	if (!value)
-		return;
-
-	if (!type || purple_strequal(type, "string"))
-		purple_blist_node_set_string(node, name, value);
-	else if (purple_strequal(type, "bool"))
-		purple_blist_node_set_bool(node, name, atoi(value));
-	else if (purple_strequal(type, "int"))
-		purple_blist_node_set_int(node, name, atoi(value));
-
-	g_free(value);
-}
-
-static void
-parse_buddy(PurpleGroup *group, PurpleContact *contact, xmlnode *bnode)
-{
-	PurpleAccount *account;
-	PurpleBuddy *buddy;
-	char *name = NULL, *alias = NULL;
-	const char *acct_name, *proto, *protocol;
-	xmlnode *x;
-
-	acct_name = xmlnode_get_attrib(bnode, "account");
-	protocol = xmlnode_get_attrib(bnode, "protocol");
-	protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
-	proto = xmlnode_get_attrib(bnode, "proto");
-	proto = _purple_oscar_convert(acct_name, proto); /* XXX: Remove */
-
-	if (!acct_name || (!proto && !protocol))
-		return;
-
-	account = purple_accounts_find(acct_name, proto ? proto : protocol);
-
-	if (!account)
-		return;
-
-	if ((x = xmlnode_get_child(bnode, "name")))
-		name = xmlnode_get_data(x);
-
-	if (!name)
-		return;
-
-	if ((x = xmlnode_get_child(bnode, "alias")))
-		alias = xmlnode_get_data(x);
-
-	buddy = purple_buddy_new(account, name, alias);
-	purple_blist_add_buddy(buddy, contact, group,
-			purple_blist_get_last_child((PurpleBlistNode*)contact));
-
-	for (x = xmlnode_get_child(bnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
-		parse_setting((PurpleBlistNode*)buddy, x);
-	}
-
-	g_free(name);
-	g_free(alias);
-}
-
-static void
-parse_contact(PurpleGroup *group, xmlnode *cnode)
-{
-	PurpleContact *contact = purple_contact_new();
-	xmlnode *x;
-	const char *alias;
-
-	purple_blist_add_contact(contact, group,
-			purple_blist_get_last_child((PurpleBlistNode*)group));
-
-	if ((alias = xmlnode_get_attrib(cnode, "alias"))) {
-		purple_blist_alias_contact(contact, alias);
-	}
-
-	for (x = cnode->child; x; x = x->next) {
-		if (x->type != XMLNODE_TYPE_TAG)
-			continue;
-		if (purple_strequal(x->name, "buddy"))
-			parse_buddy(group, contact, x);
-		else if (purple_strequal(x->name, "setting"))
-			parse_setting((PurpleBlistNode*)contact, x);
-	}
-
-	/* if the contact is empty, don't keep it around.  it causes problems */
-	if (!((PurpleBlistNode*)contact)->child)
-		purple_blist_remove_contact(contact);
-}
-
-static void
-parse_chat(PurpleGroup *group, xmlnode *cnode)
-{
-	PurpleChat *chat;
-	PurpleAccount *account;
-	const char *acct_name, *proto, *protocol;
-	xmlnode *x;
-	char *alias = NULL;
-	GHashTable *components;
-
-	acct_name = xmlnode_get_attrib(cnode, "account");
-	protocol = xmlnode_get_attrib(cnode, "protocol");
-	proto = xmlnode_get_attrib(cnode, "proto");
-
-	if (!acct_name || (!proto && !protocol))
-		return;
-
-	account = purple_accounts_find(acct_name, proto ? proto : protocol);
-
-	if (!account)
-		return;
-
-	if ((x = xmlnode_get_child(cnode, "alias")))
-		alias = xmlnode_get_data(x);
-
-	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-
-	for (x = xmlnode_get_child(cnode, "component"); x; x = xmlnode_get_next_twin(x)) {
-		const char *name;
-		char *value;
-
-		name = xmlnode_get_attrib(x, "name");
-		value = xmlnode_get_data(x);
-		g_hash_table_replace(components, g_strdup(name), value);
-	}
-
-	chat = purple_chat_new(account, alias, components);
-	purple_blist_add_chat(chat, group,
-			purple_blist_get_last_child((PurpleBlistNode*)group));
-
-	for (x = xmlnode_get_child(cnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
-		parse_setting((PurpleBlistNode*)chat, x);
-	}
-
-	g_free(alias);
-}
-
-static void
-parse_group(xmlnode *groupnode)
-{
-	const char *name = xmlnode_get_attrib(groupnode, "name");
-	PurpleGroup *group;
-	xmlnode *cnode;
-
-	if (!name)
-		name = _("Buddies");
-
-	group = purple_group_new(name);
-	purple_blist_add_group(group,
-			purple_blist_get_last_sibling(purplebuddylist->root));
-
-	for (cnode = groupnode->child; cnode; cnode = cnode->next) {
-		if (cnode->type != XMLNODE_TYPE_TAG)
-			continue;
-		if (purple_strequal(cnode->name, "setting"))
-			parse_setting((PurpleBlistNode*)group, cnode);
-		else if (purple_strequal(cnode->name, "contact") ||
-				purple_strequal(cnode->name, "person"))
-			parse_contact(group, cnode);
-		else if (purple_strequal(cnode->name, "chat"))
-			parse_chat(group, cnode);
-	}
-}
-
 /* TODO: Make static and rename to load_blist */
 void
 purple_blist_load()
@@ -647,51 +336,7 @@ purple_blist_load()
  * Stuff                                                             *
  *********************************************************************/
 
-static void
-purple_contact_compute_priority_buddy(PurpleContact *contact)
-{
-	PurpleBlistNode *bnode;
-	PurpleBuddy *new_priority = NULL;
 
-	g_return_if_fail(contact != NULL);
-
-	contact->priority = NULL;
-	for (bnode = ((PurpleBlistNode*)contact)->child;
-			bnode != NULL;
-			bnode = bnode->next)
-	{
-		PurpleBuddy *buddy;
-
-		if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
-			continue;
-
-		buddy = (PurpleBuddy*)bnode;
-		if (new_priority == NULL)
-		{
-			new_priority = buddy;
-			continue;
-		}
-
-		if (purple_account_is_connected(buddy->account))
-		{
-			int cmp = 1;
-			if (purple_account_is_connected(new_priority->account))
-				cmp = purple_presence_compare(purple_buddy_get_presence(new_priority),
-						purple_buddy_get_presence(buddy));
-
-			if (cmp > 0 || (cmp == 0 &&
-			                purple_prefs_get_bool("/purple/contact/last_match")))
-			{
-				new_priority = buddy;
-			}
-		}
-	}
-
-	contact->priority = new_priority;
-	contact->priority_valid = TRUE;
-}
-
-
 /*****************************************************************************
  * Public API functions                                                      *
  *****************************************************************************/
@@ -741,25 +386,6 @@ purple_blist_get_root()
 	return purplebuddylist ? purplebuddylist->root : NULL;
 }
 
-static void
-append_buddy(gpointer key, gpointer value, gpointer user_data)
-{
-	GSList **list = user_data;
-	*list = g_slist_prepend(*list, value);
-}
-
-GSList *
-purple_blist_get_buddies()
-{
-	GSList *buddies = NULL;
-
-	if (!purplebuddylist)
-		return NULL;
-
-	g_hash_table_foreach(purplebuddylist->buddies, append_buddy, &buddies);
-	return buddies;
-}
-
 void *
 purple_blist_get_ui_data()
 {
@@ -798,984 +424,15 @@ void purple_blist_set_visible(gboolean s
 		ops->set_visible(purplebuddylist, show);
 }
 
-static PurpleBlistNode *get_next_node(PurpleBlistNode *node, gboolean godeep)
-{
-	if (node == NULL)
-		return NULL;
 
-	if (godeep && node->child)
-		return node->child;
 
-	if (node->next)
-		return node->next;
-
-	return get_next_node(node->parent, FALSE);
-}
-
-PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline)
-{
-	PurpleBlistNode *ret = node;
-
-	if (offline)
-		return get_next_node(ret, TRUE);
-	do
-	{
-		ret = get_next_node(ret, TRUE);
-	} while (ret && PURPLE_BLIST_NODE_IS_BUDDY(ret) &&
-			!purple_account_is_connected(purple_buddy_get_account((PurpleBuddy *)ret)));
-
-	return ret;
-}
-
-PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node)
-{
-	return node ? node->parent : NULL;
-}
-
-PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node)
-{
-	return node ? node->child : NULL;
-}
-
-PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node)
-{
-	return node? node->next : NULL;
-}
-
-PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node)
-{
-	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);
-
-	presence = purple_buddy_get_presence(buddy);
-	status = purple_presence_get_active_status(presence);
-
-	purple_debug_info("blist", "Updating buddy status for %s (%s)\n",
-			buddy->name, purple_account_get_protocol_name(buddy->account));
-
-	if (purple_status_is_online(status) &&
-		!purple_status_is_online(old_status)) {
-
-		purple_signal_emit(purple_blist_get_handle(), "buddy-signed-on", buddy);
-
-		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);
-
-		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,
-		                 status);
-	}
-
-	/*
-	 * This function used to only call the following two functions if one of
-	 * the above signals had been triggered, but that's not good, because
-	 * if someone's away message changes and they don't go from away to back
-	 * to away then no signal is triggered.
-	 *
-	 * It's a safe assumption that SOMETHING called this function.  PROBABLY
-	 * because something, somewhere changed.  Calling the stuff below
-	 * certainly won't hurt anything.  Unless you're on a K6-2 300.
-	 */
-	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-}
-
-void
-purple_blist_update_node_icon(PurpleBlistNode *node)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
-	g_return_if_fail(node != NULL);
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, node);
-}
-
-void
-purple_blist_update_buddy_icon(PurpleBuddy *buddy)
-{
-	purple_blist_update_node_icon((PurpleBlistNode *)buddy);
-}
-
-/*
- * TODO: Maybe remove the call to this from server.c and call it
- * from oscar.c and toc.c instead?
- */
-void purple_blist_rename_buddy(PurpleBuddy *buddy, const char *name)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	struct _purple_hbuddy *hb, *hb2;
-	GHashTable *account_buddies;
-
-	g_return_if_fail(buddy != NULL);
-
-	hb = g_new(struct _purple_hbuddy, 1);
-	hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
-	hb->account = buddy->account;
-	hb->group = ((PurpleBlistNode *)buddy)->parent->parent;
-	g_hash_table_remove(purplebuddylist->buddies, hb);
-	
-	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
-	g_hash_table_remove(account_buddies, hb);
-
-	g_free(hb->name);
-	hb->name = g_strdup(purple_normalize(buddy->account, name));
-	g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
-
-	hb2 = g_new(struct _purple_hbuddy, 1);
-	hb2->name = g_strdup(hb->name);
-	hb2->account = buddy->account;
-	hb2->group = ((PurpleBlistNode *)buddy)->parent->parent;
-
-	g_hash_table_replace(account_buddies, hb2, buddy);
-
-	g_free(buddy->name);
-	buddy->name = g_strdup(name);
-
-	purple_blist_schedule_save();
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-}
-
-static gboolean
+gboolean
 purple_strings_are_different(const char *one, const char *two)
 {
 	return !((one && two && g_utf8_collate(one, two) == 0) ||
 			((one == NULL || *one == '\0') && (two == NULL || *two == '\0')));
 }
 
-void purple_blist_alias_contact(PurpleContact *contact, const char *alias)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	PurpleConversation *conv;
-	PurpleBlistNode *bnode;
-	char *old_alias;
-	char *new_alias = NULL;
-
-	g_return_if_fail(contact != NULL);
-
-	if ((alias != NULL) && (*alias != '\0'))
-		new_alias = purple_utf8_strip_unprintables(alias);
-
-	if (!purple_strings_are_different(contact->alias, new_alias)) {
-		g_free(new_alias);
-		return;
-	}
-
-	old_alias = contact->alias;
-
-	if ((new_alias != NULL) && (*new_alias != '\0'))
-		contact->alias = new_alias;
-	else {
-		contact->alias = NULL;
-		g_free(new_alias); /* could be "\0" */
-	}
-
-	purple_blist_schedule_save();
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode *)contact);
-
-	for(bnode = ((PurpleBlistNode *)contact)->child; bnode != NULL; bnode = bnode->next)
-	{
-		PurpleBuddy *buddy = (PurpleBuddy *)bnode;
-
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
-												   buddy->account);
-		if (conv)
-			purple_conversation_autoset_title(conv);
-	}
-
-	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
-					 contact, old_alias);
-	g_free(old_alias);
-}
-
-void purple_blist_alias_chat(PurpleChat *chat, const char *alias)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	char *old_alias;
-	char *new_alias = NULL;
-
-	g_return_if_fail(chat != NULL);
-
-	if ((alias != NULL) && (*alias != '\0'))
-		new_alias = purple_utf8_strip_unprintables(alias);
-
-	if (!purple_strings_are_different(chat->alias, new_alias)) {
-		g_free(new_alias);
-		return;
-	}
-
-	old_alias = chat->alias;
-
-	if ((new_alias != NULL) && (*new_alias != '\0'))
-		chat->alias = new_alias;
-	else {
-		chat->alias = NULL;
-		g_free(new_alias); /* could be "\0" */
-	}
-
-	purple_blist_schedule_save();
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode *)chat);
-
-	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
-					 chat, old_alias);
-	g_free(old_alias);
-}
-
-void purple_blist_alias_buddy(PurpleBuddy *buddy, const char *alias)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	PurpleConversation *conv;
-	char *old_alias;
-	char *new_alias = NULL;
-
-	g_return_if_fail(buddy != NULL);
-
-	if ((alias != NULL) && (*alias != '\0'))
-		new_alias = purple_utf8_strip_unprintables(alias);
-
-	if (!purple_strings_are_different(buddy->alias, new_alias)) {
-		g_free(new_alias);
-		return;
-	}
-
-	old_alias = buddy->alias;
-
-	if ((new_alias != NULL) && (*new_alias != '\0'))
-		buddy->alias = g_strdup(alias);
-	else {
-		buddy->alias = NULL;
-		g_free(new_alias); /* could be "\0" */
-	}
-
-	purple_blist_schedule_save();
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
-											   buddy->account);
-	if (conv)
-		purple_conversation_autoset_title(conv);
-
-	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
-					 buddy, old_alias);
-	g_free(old_alias);
-}
-
-void purple_blist_server_alias_buddy(PurpleBuddy *buddy, const char *alias)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	PurpleConversation *conv;
-	char *old_alias;
-	char *new_alias = NULL;
-
-	g_return_if_fail(buddy != NULL);
-
-	if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
-		new_alias = purple_utf8_strip_unprintables(alias);
-
-	if (!purple_strings_are_different(buddy->server_alias, new_alias)) {
-		g_free(new_alias);
-		return;
-	}
-
-	old_alias = buddy->server_alias;
-
-	if ((new_alias != NULL) && (*new_alias != '\0'))
-		buddy->server_alias = new_alias;
-	else {
-		buddy->server_alias = NULL;
-		g_free(new_alias); /* could be "\0"; */
-	}
-
-	purple_blist_schedule_save();
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
-
-	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy->name,
-											   buddy->account);
-	if (conv)
-		purple_conversation_autoset_title(conv);
-
-	purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
-					 buddy, old_alias);
-	g_free(old_alias);
-}
-
-/*
- * TODO: If merging, prompt the user if they want to merge.
- */
-void purple_blist_rename_group(PurpleGroup *source, const char *name)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	PurpleGroup *dest;
-	gchar *old_name;
-	gchar *new_name;
-	GList *moved_buddies = NULL;
-	GSList *accts;
-
-	g_return_if_fail(source != NULL);
-	g_return_if_fail(name != NULL);
-
-	new_name = purple_utf8_strip_unprintables(name);
-
-	if (*new_name == '\0' || purple_strequal(new_name, source->name)) {
-		g_free(new_name);
-		return;
-	}
-
-	dest = purple_find_group(new_name);
-	if (dest != NULL && purple_utf8_strcasecmp(source->name, dest->name) != 0) {
-		/* We're merging two groups */
-		PurpleBlistNode *prev, *child, *next;
-
-		prev = purple_blist_get_last_child((PurpleBlistNode*)dest);
-		child = ((PurpleBlistNode*)source)->child;
-
-		/*
-		 * TODO: This seems like a dumb way to do this... why not just
-		 * append all children from the old group to the end of the new
-		 * one?  PRPLs might be expecting to receive an add_buddy() for
-		 * each moved buddy...
-		 */
-		while (child)
-		{
-			next = child->next;
-			if (PURPLE_BLIST_NODE_IS_CONTACT(child)) {
-				PurpleBlistNode *bnode;
-				purple_blist_add_contact((PurpleContact *)child, dest, prev);
-				for (bnode = child->child; bnode != NULL; bnode = bnode->next) {
-					purple_blist_add_buddy((PurpleBuddy *)bnode, (PurpleContact *)child,
-							NULL, bnode->prev);
-					moved_buddies = g_list_append(moved_buddies, bnode);
-				}
-				prev = child;
-			} else if (PURPLE_BLIST_NODE_IS_CHAT(child)) {
-				purple_blist_add_chat((PurpleChat *)child, dest, prev);
-				prev = child;
-			} else {
-				purple_debug(PURPLE_DEBUG_ERROR, "blist",
-						"Unknown child type in group %s\n", source->name);
-			}
-			child = next;
-		}
-
-		/* Make a copy of the old group name and then delete the old group */
-		old_name = g_strdup(source->name);
-		purple_blist_remove_group(source);
-		source = dest;
-		g_free(new_name);
-	} else {
-		/* A simple rename */
-		PurpleBlistNode *cnode, *bnode;
-
-		/* Build a GList of all buddies in this group */
-		for (cnode = ((PurpleBlistNode *)source)->child; cnode != NULL; cnode = cnode->next) {
-			if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
-				for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
-					moved_buddies = g_list_append(moved_buddies, bnode);
-		}
-
-		old_name = source->name;
-		source->name = new_name;
-	}
-
-	/* Save our changes */
-	purple_blist_schedule_save();
-
-	/* Update the UI */
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode*)source);
-
-	/* Notify all PRPLs */
-	/* TODO: Is this condition needed?  Seems like it would always be TRUE */
-	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;
-			PurplePlugin *prpl = NULL;
-			PurplePluginProtocolInfo *prpl_info = NULL;
-			GList *l = NULL, *buddies = NULL;
-
-			gc = purple_account_get_connection(account);
-
-			if(gc)
-				prpl = purple_connection_get_prpl(gc);
-
-			if(gc && prpl)
-				prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-			if(!prpl_info)
-				continue;
-
-			for(l = moved_buddies; l; l = l->next) {
-				PurpleBuddy *buddy = (PurpleBuddy *)l->data;
-
-				if(buddy && buddy->account == account)
-					buddies = g_list_append(buddies, (PurpleBlistNode *)buddy);
-			}
-
-			if(prpl_info->rename_group) {
-				prpl_info->rename_group(gc, old_name, source, buddies);
-			} else {
-				GList *cur, *groups = NULL;
-
-				/* Make a list of what the groups each buddy is in */
-				for(cur = buddies; cur; cur = cur->next) {
-					PurpleBlistNode *node = (PurpleBlistNode *)cur->data;
-					groups = g_list_prepend(groups, node->parent->parent);
-				}
-
-				purple_account_remove_buddies(account, buddies, groups);
-				g_list_free(groups);
-				purple_account_add_buddies(account, buddies);
-			}
-
-			g_list_free(buddies);
-		}
-	}
-	g_list_free(moved_buddies);
-	g_free(old_name);
-}
-
-static void purple_blist_node_initialize_settings(PurpleBlistNode *node);
-
-PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	PurpleChat *chat;
-
-	g_return_val_if_fail(account != NULL, FALSE);
-	g_return_val_if_fail(components != NULL, FALSE);
-
-	chat = g_new0(PurpleChat, 1);
-	chat->account = account;
-	if ((alias != NULL) && (*alias != '\0'))
-		chat->alias = purple_utf8_strip_unprintables(alias);
-	chat->components = components;
-	purple_blist_node_initialize_settings((PurpleBlistNode *)chat);
-	((PurpleBlistNode *)chat)->type = PURPLE_BLIST_CHAT_NODE;
-
-	if (ops != NULL && ops->new_node != NULL)
-		ops->new_node((PurpleBlistNode *)chat);
-
-	PURPLE_DBUS_REGISTER_POINTER(chat, PurpleChat);
-	return chat;
-}
-
-void
-purple_chat_destroy(PurpleChat *chat)
-{
-	g_hash_table_destroy(chat->components);
-	g_hash_table_destroy(chat->node.settings);
-	g_free(chat->alias);
-	PURPLE_DBUS_UNREGISTER_POINTER(chat);
-	g_free(chat);
-}
-
-PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	PurpleBuddy *buddy;
-
-	g_return_val_if_fail(account != NULL, NULL);
-	g_return_val_if_fail(name != NULL, NULL);
-
-	buddy = g_new0(PurpleBuddy, 1);
-	buddy->account  = account;
-	buddy->name     = purple_utf8_strip_unprintables(name);
-	buddy->alias    = purple_utf8_strip_unprintables(alias);
-	buddy->presence = purple_presence_new_for_buddy(buddy);
-	((PurpleBlistNode *)buddy)->type = PURPLE_BLIST_BUDDY_NODE;
-
-	purple_presence_set_status_active(buddy->presence, "offline", TRUE);
-
-	purple_blist_node_initialize_settings((PurpleBlistNode *)buddy);
-
-	if (ops && ops->new_node)
-		ops->new_node((PurpleBlistNode *)buddy);
-
-	PURPLE_DBUS_REGISTER_POINTER(buddy, PurpleBuddy);
-	return buddy;
-}
-
-void
-purple_buddy_destroy(PurpleBuddy *buddy)
-{
-	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info;
-
-	/*
-	 * Tell the owner PRPL that we're about to free the buddy so it
-	 * can free proto_data
-	 */
-	prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account));
-	if (prpl) {
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-		if (prpl_info && prpl_info->buddy_free)
-			prpl_info->buddy_free(buddy);
-	}
-
-	/* Delete the node */
-	purple_buddy_icon_unref(buddy->icon);
-	g_hash_table_destroy(buddy->node.settings);
-	purple_presence_destroy(buddy->presence);
-	g_free(buddy->name);
-	g_free(buddy->alias);
-	g_free(buddy->server_alias);
-
-	PURPLE_DBUS_UNREGISTER_POINTER(buddy);
-	g_free(buddy);
-
-	/* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can
-	 * g_object_ref() it when connecting the callback and
-	 * g_object_unref() it in the handler.  That way, it won't
-	 * get freed while the timeout is pending and this line can
-	 * be removed. */
-	while (g_source_remove_by_user_data((gpointer *)buddy));
-}
-
-void
-purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
-{
-	g_return_if_fail(buddy != NULL);
-
-	if (buddy->icon != icon)
-	{
-		purple_buddy_icon_unref(buddy->icon);
-		buddy->icon = (icon != NULL ? purple_buddy_icon_ref(icon) : NULL);
-	}
-
-	purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy);
-
-	purple_blist_update_node_icon((PurpleBlistNode*)buddy);
-}
-
-PurpleAccount *
-purple_buddy_get_account(const PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	return buddy->account;
-}
-
-const char *
-purple_buddy_get_name(const PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	return buddy->name;
-}
-
-PurpleBuddyIcon *
-purple_buddy_get_icon(const PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	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;
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
-	g_return_if_fail(chat != NULL);
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT((PurpleBlistNode *)chat));
-
-	if (node == NULL) {
-		if (group == NULL)
-			group = purple_group_new(_("Chats"));
-
-		/* Add group to blist if isn't already on it. Fixes #2752. */
-		if (!purple_find_group(group->name)) {
-			purple_blist_add_group(group,
-					purple_blist_get_last_sibling(purplebuddylist->root));
-		}
-	} else {
-		group = (PurpleGroup*)node->parent;
-	}
-
-	/* if we're moving to overtop of ourselves, do nothing */
-	if (cnode == node)
-		return;
-
-	if (cnode->parent) {
-		/* This chat was already in the list and is
-		 * being moved.
-		 */
-		((PurpleGroup *)cnode->parent)->totalsize--;
-		if (purple_account_is_connected(chat->account)) {
-			((PurpleGroup *)cnode->parent)->online--;
-			((PurpleGroup *)cnode->parent)->currentsize--;
-		}
-		if (cnode->next)
-			cnode->next->prev = cnode->prev;
-		if (cnode->prev)
-			cnode->prev->next = cnode->next;
-		if (cnode->parent->child == cnode)
-			cnode->parent->child = cnode->next;
-
-		if (ops && ops->remove)
-			ops->remove(purplebuddylist, cnode);
-		/* ops->remove() cleaned up the cnode's ui_data, so we need to
-		 * reinitialize it */
-		if (ops && ops->new_node)
-			ops->new_node(cnode);
-
-		purple_blist_schedule_save();
-	}
-
-	if (node != NULL) {
-		if (node->next)
-			node->next->prev = cnode;
-		cnode->next = node->next;
-		cnode->prev = node;
-		cnode->parent = node->parent;
-		node->next = cnode;
-		((PurpleGroup *)node->parent)->totalsize++;
-		if (purple_account_is_connected(chat->account)) {
-			((PurpleGroup *)node->parent)->online++;
-			((PurpleGroup *)node->parent)->currentsize++;
-		}
-	} else {
-		if (((PurpleBlistNode *)group)->child)
-			((PurpleBlistNode *)group)->child->prev = cnode;
-		cnode->next = ((PurpleBlistNode *)group)->child;
-		cnode->prev = NULL;
-		((PurpleBlistNode *)group)->child = cnode;
-		cnode->parent = (PurpleBlistNode *)group;
-		group->totalsize++;
-		if (purple_account_is_connected(chat->account)) {
-			group->online++;
-			group->currentsize++;
-		}
-	}
-
-	purple_blist_schedule_save();
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode *)cnode);
-
-	purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
-			cnode);
-}
-
-void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
-{
-	PurpleBlistNode *cnode, *bnode;
-	PurpleGroup *g;
-	PurpleContact *c;
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	struct _purple_hbuddy *hb, *hb2;
-	GHashTable *account_buddies;
-
-	g_return_if_fail(buddy != NULL);
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode*)buddy));
-
-	bnode = (PurpleBlistNode *)buddy;
-
-	/* if we're moving to overtop of ourselves, do nothing */
-	if (bnode == node || (!node && bnode->parent &&
-				contact && bnode->parent == (PurpleBlistNode*)contact
-				&& bnode == bnode->parent->child))
-		return;
-
-	if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-		c = (PurpleContact*)node->parent;
-		g = (PurpleGroup*)node->parent->parent;
-	} else if (contact) {
-		c = contact;
-		g = PURPLE_GROUP(PURPLE_BLIST_NODE(c)->parent);
-	} else {
-		g = group;
-		if (g == NULL)
-			g = purple_group_new(_("Buddies"));
-		/* Add group to blist if isn't already on it. Fixes #2752. */
-		if (!purple_find_group(g->name)) {
-			purple_blist_add_group(g,
-					purple_blist_get_last_sibling(purplebuddylist->root));
-		}
-		c = purple_contact_new();
-		purple_blist_add_contact(c, g,
-				purple_blist_get_last_child((PurpleBlistNode*)g));
-	}
-
-	cnode = (PurpleBlistNode *)c;
-
-	if (bnode->parent) {
-		if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
-			((PurpleContact*)bnode->parent)->online--;
-			if (((PurpleContact*)bnode->parent)->online == 0)
-				((PurpleGroup*)bnode->parent->parent)->online--;
-		}
-		if (purple_account_is_connected(buddy->account)) {
-			((PurpleContact*)bnode->parent)->currentsize--;
-			if (((PurpleContact*)bnode->parent)->currentsize == 0)
-				((PurpleGroup*)bnode->parent->parent)->currentsize--;
-		}
-		((PurpleContact*)bnode->parent)->totalsize--;
-		/* the group totalsize will be taken care of by remove_contact below */
-
-		if (bnode->parent->parent != (PurpleBlistNode*)g)
-			serv_move_buddy(buddy, (PurpleGroup *)bnode->parent->parent, g);
-
-		if (bnode->next)
-			bnode->next->prev = bnode->prev;
-		if (bnode->prev)
-			bnode->prev->next = bnode->next;
-		if (bnode->parent->child == bnode)
-			bnode->parent->child = bnode->next;
-
-		if (ops && ops->remove)
-			ops->remove(purplebuddylist, bnode);
-
-		purple_blist_schedule_save();
-
-		if (bnode->parent->parent != (PurpleBlistNode*)g) {
-			hb = g_new(struct _purple_hbuddy, 1);
-			hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
-			hb->account = buddy->account;
-			hb->group = bnode->parent->parent;
-			g_hash_table_remove(purplebuddylist->buddies, hb);
-
-			account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
-			g_hash_table_remove(account_buddies, hb);
-
-			g_free(hb->name);
-			g_free(hb);
-		}
-
-		if (!bnode->parent->child) {
-			purple_blist_remove_contact((PurpleContact*)bnode->parent);
-		} else {
-			purple_contact_invalidate_priority_buddy((PurpleContact*)bnode->parent);
-			if (ops && ops->update)
-				ops->update(purplebuddylist, bnode->parent);
-		}
-	}
-
-	if (node && PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-		if (node->next)
-			node->next->prev = bnode;
-		bnode->next = node->next;
-		bnode->prev = node;
-		bnode->parent = node->parent;
-		node->next = bnode;
-	} else {
-		if (cnode->child)
-			cnode->child->prev = bnode;
-		bnode->prev = NULL;
-		bnode->next = cnode->child;
-		cnode->child = bnode;
-		bnode->parent = cnode;
-	}
-
-	if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
-		if (++(PURPLE_CONTACT(bnode->parent)->online) == 1)
-			PURPLE_GROUP(bnode->parent->parent)->online++;
-	}
-	if (purple_account_is_connected(buddy->account)) {
-		if (++(PURPLE_CONTACT(bnode->parent)->currentsize) == 1)
-			PURPLE_GROUP(bnode->parent->parent)->currentsize++;
-	}
-	PURPLE_CONTACT(bnode->parent)->totalsize++;
-
-	hb = g_new(struct _purple_hbuddy, 1);
-	hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
-	hb->account = buddy->account;
-	hb->group = ((PurpleBlistNode*)buddy)->parent->parent;
-
-	g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
-
-	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
-
-	hb2 = g_new(struct _purple_hbuddy, 1);
-	hb2->name = g_strdup(hb->name);
-	hb2->account = buddy->account;
-	hb2->group = ((PurpleBlistNode*)buddy)->parent->parent;
-
-	g_hash_table_replace(account_buddies, hb2, buddy);
-
-	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
-
-	purple_blist_schedule_save();
-
-	if (ops && ops->update)
-		ops->update(purplebuddylist, (PurpleBlistNode*)buddy);
-
-	/* Signal that the buddy has been added */
-	purple_signal_emit(purple_blist_get_handle(), "buddy-added", buddy);
-
-	purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
-			PURPLE_BLIST_NODE(buddy));
-}
-
-PurpleContact *purple_contact_new()
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-
-	PurpleContact *contact = g_new0(PurpleContact, 1);
-	contact->totalsize = 0;
-	contact->currentsize = 0;
-	contact->online = 0;
-	purple_blist_node_initialize_settings((PurpleBlistNode *)contact);
-	((PurpleBlistNode *)contact)->type = PURPLE_BLIST_CONTACT_NODE;
-
-	if (ops && ops->new_node)
-		ops->new_node((PurpleBlistNode *)contact);
-
-	PURPLE_DBUS_REGISTER_POINTER(contact, PurpleContact);
-	return contact;
-}
-
-void
-purple_contact_destroy(PurpleContact *contact)
-{
-	g_hash_table_destroy(contact->node.settings);
-	g_free(contact->alias);
-	PURPLE_DBUS_UNREGISTER_POINTER(contact);
-	g_free(contact);
-}
-
-void purple_contact_set_alias(PurpleContact *contact, const char *alias)
-{
-	purple_blist_alias_contact(contact,alias);
-}
-
-const char *purple_contact_get_alias(PurpleContact* contact)
-{
-	g_return_val_if_fail(contact != NULL, NULL);
-
-	if (contact->alias)
-		return contact->alias;
-
-	return purple_buddy_get_alias(purple_contact_get_priority_buddy(contact));
-}
-
-gboolean purple_contact_on_account(PurpleContact *c, PurpleAccount *account)
-{
-	PurpleBlistNode *bnode, *cnode = (PurpleBlistNode *) c;
-
-	g_return_val_if_fail(c != NULL, FALSE);
-	g_return_val_if_fail(account != NULL, FALSE);
-
-	for (bnode = cnode->child; bnode; bnode = bnode->next) {
-		PurpleBuddy *buddy;
-
-		if (! PURPLE_BLIST_NODE_IS_BUDDY(bnode))
-			continue;
-
-		buddy = (PurpleBuddy *)bnode;
-		if (buddy->account == account)
-			return TRUE;
-	}
-	return FALSE;
-}
-
-void purple_contact_invalidate_priority_buddy(PurpleContact *contact)
-{
-	g_return_if_fail(contact != NULL);
-
-	contact->priority_valid = FALSE;
-}
-
-PurpleGroup *purple_group_new(const char *name)
-{
-	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	PurpleGroup *group;
-
-	g_return_val_if_fail(name  != NULL, NULL);
-	g_return_val_if_fail(*name != '\0', NULL);
-
-	group = purple_find_group(name);
-	if (group != NULL)
-		return group;
-
-	group = g_new0(PurpleGroup, 1);
-	group->name = purple_utf8_strip_unprintables(name);
-	group->totalsize = 0;
-	group->currentsize = 0;
-	group->online = 0;
-	purple_blist_node_initialize_settings((PurpleBlistNode *)group);
-	((PurpleBlistNode *)group)->type = PURPLE_BLIST_GROUP_NODE;
-
-	if (ops && ops->new_node)
-		ops->new_node((PurpleBlistNode *)group);
-
-	PURPLE_DBUS_REGISTER_POINTER(group, PurpleGroup);
-	return group;
-}
-
-void
-purple_group_destroy(PurpleGroup *group)
-{
-	g_hash_table_destroy(group->node.settings);
-	g_free(group->name);
-	PURPLE_DBUS_UNREGISTER_POINTER(group);
-	g_free(group);
-}
-
 void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
@@ -1916,42 +573,6 @@ void purple_blist_add_contact(PurpleCont
 	}
 }
 
-void purple_blist_merge_contact(PurpleContact *source, PurpleBlistNode *node)
-{
-	PurpleBlistNode *sourcenode = (PurpleBlistNode*)source;
-	PurpleBlistNode *targetnode;
-	PurpleBlistNode *prev, *cur, *next;
-	PurpleContact *target;
-
-	g_return_if_fail(source != NULL);
-	g_return_if_fail(node != NULL);
-
-	if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
-		target = (PurpleContact *)node;
-		prev = purple_blist_get_last_child(node);
-	} else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-		target = (PurpleContact *)node->parent;
-		prev = node;
-	} else {
-		return;
-	}
-
-	if (source == target || !target)
-		return;
-
-	targetnode = (PurpleBlistNode *)target;
-	next = sourcenode->child;
-
-	while (next) {
-		cur = next;
-		next = cur->next;
-		if (PURPLE_BLIST_NODE_IS_BUDDY(cur)) {
-			purple_blist_add_buddy((PurpleBuddy *)cur, target, NULL, prev);
-			prev = cur;
-		}
-	}
-}
-
 void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node)
 {
 	PurpleBlistUiOps *ops;
@@ -2223,136 +844,7 @@ void purple_blist_remove_group(PurpleGro
 	purple_group_destroy(group);
 }
 
-PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact)
-{
-	g_return_val_if_fail(contact != NULL, NULL);
 
-	if (!contact->priority_valid)
-		purple_contact_compute_priority_buddy(contact);
-
-	return contact->priority;
-}
-
-const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	if ((buddy->alias != NULL) && (*buddy->alias != '\0')) {
-		return buddy->alias;
-	} else if ((purple_buddy_get_server_alias(buddy) != NULL) &&
-		   (*purple_buddy_get_server_alias(buddy) != '\0')) {
-
-		return purple_buddy_get_server_alias(buddy);
-	}
-
-	return NULL;
-}
-
-
-const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy)
-{
-	PurpleContact *c;
-
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	/* Search for an alias for the buddy. In order of precedence: */
-	/* The buddy alias */
-	if (buddy->alias != NULL)
-		return buddy->alias;
-
-	/* The contact alias */
-	c = purple_buddy_get_contact(buddy);
-	if ((c != NULL) && (c->alias != NULL))
-		return c->alias;
-
-	/* The server alias */
-	if ((purple_buddy_get_server_alias(buddy)) && (*purple_buddy_get_server_alias(buddy)))
-		return purple_buddy_get_server_alias(buddy);
-
-	/* The buddy's user name (i.e. no alias) */
-	return buddy->name;
-}
-
-
-const char *purple_buddy_get_alias(PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	/* Search for an alias for the buddy. In order of precedence: */
-	/* The buddy alias */
-	if (buddy->alias != NULL)
-		return buddy->alias;
-
-	/* The server alias */
-	if ((purple_buddy_get_server_alias(buddy)) && (*purple_buddy_get_server_alias(buddy)))
-		return purple_buddy_get_server_alias(buddy);
-
-	/* The buddy's user name (i.e. no alias) */
-	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);
-
-	if ((buddy->server_alias) && (*buddy->server_alias))
-	    return buddy->server_alias;
-
-	return NULL;
-}
-
-const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
-{
-	PurpleContact *c;
-
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	/* Search for an alias for the buddy. In order of precedence: */
-	/* The buddy alias */
-	if (buddy->alias != NULL)
-		return buddy->alias;
-
-	/* The contact alias */
-	c = purple_buddy_get_contact(buddy);
-	if ((c != NULL) && (c->alias != NULL))
-		return c->alias;
-
-	/* The buddy's user name (i.e. no alias) */
-	return buddy->name;
-}
-
-const char *purple_chat_get_name(PurpleChat *chat)
-{
-	char *ret = NULL;
-	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info = NULL;
-
-	g_return_val_if_fail(chat != NULL, NULL);
-
-	if ((chat->alias != NULL) && (*chat->alias != '\0'))
-		return chat->alias;
-
-	prpl = purple_find_prpl(purple_account_get_protocol_id(chat->account));
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info->chat_info) {
-		struct proto_chat_entry *pce;
-		GList *parts = prpl_info->chat_info(purple_account_get_connection(chat->account));
-		pce = parts->data;
-		ret = g_hash_table_lookup(chat->components, pce->identifier);
-		g_list_foreach(parts, (GFunc)g_free, NULL);
-		g_list_free(parts);
-	}
-
-	return ret;
-}
-
 PurpleBuddy *purple_find_buddy(PurpleAccount *account, const char *name)
 {
 	PurpleBuddy *buddy;
@@ -2378,26 +870,6 @@ PurpleBuddy *purple_find_buddy(PurpleAcc
 	return NULL;
 }
 
-PurpleBuddy *purple_find_buddy_in_group(PurpleAccount *account, const char *name,
-		PurpleGroup *group)
-{
-	struct _purple_hbuddy hb;
-	PurpleBuddy *ret;
-
-	g_return_val_if_fail(purplebuddylist != NULL, NULL);
-	g_return_val_if_fail(account != NULL, NULL);
-	g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
-
-	hb.name = g_strdup(purple_normalize(account, name));
-	hb.account = account;
-	hb.group = (PurpleBlistNode*)group;
-
-	ret = g_hash_table_lookup(purplebuddylist->buddies, &hb);
-	g_free(hb.name);
-
-	return ret;
-}
-
 static void find_acct_buddies(gpointer key, gpointer value, gpointer data)
 {
 	PurpleBuddy *buddy = value;
@@ -2437,124 +909,6 @@ GSList *purple_find_buddies(PurpleAccoun
 	return ret;
 }
 
-PurpleGroup *purple_find_group(const char *name)
-{
-	PurpleBlistNode *node;
-
-	g_return_val_if_fail(purplebuddylist != NULL, NULL);
-	g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
-
-	for (node = purplebuddylist->root; node != NULL; node = node->next) {
-		if (!purple_utf8_strcasecmp(((PurpleGroup *)node)->name, name))
-			return (PurpleGroup *)node;
-	}
-
-	return NULL;
-}
-
-PurpleChat *
-purple_blist_find_chat(PurpleAccount *account, const char *name)
-{
-	char *chat_name;
-	PurpleChat *chat;
-	PurplePlugin *prpl;
-	PurplePluginProtocolInfo *prpl_info = NULL;
-	struct proto_chat_entry *pce;
-	PurpleBlistNode *node, *group;
-	GList *parts;
-	char *normname;
-
-	g_return_val_if_fail(purplebuddylist != NULL, NULL);
-	g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);
-
-	if (!purple_account_is_connected(account))
-		return NULL;
-
-	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info->find_blist_chat != NULL)
-		return prpl_info->find_blist_chat(account, name);
-
-	normname = g_strdup(purple_normalize(account, name));
-	for (group = purplebuddylist->root; group != NULL; group = group->next) {
-		for (node = group->child; node != NULL; node = node->next) {
-			if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
-
-				chat = (PurpleChat*)node;
-
-				if (account != chat->account)
-					continue;
-
-				parts = prpl_info->chat_info(
-					purple_account_get_connection(chat->account));
-
-				pce = parts->data;
-				chat_name = g_hash_table_lookup(chat->components,
-												pce->identifier);
-				g_list_foreach(parts, (GFunc)g_free, NULL);
-				g_list_free(parts);
-
-				if (chat->account == account && chat_name != NULL &&
-					normname != NULL && !strcmp(purple_normalize(account, chat_name), normname)) {
-					g_free(normname);
-					return chat;
-				}
-			}
-		}
-	}
-
-	g_free(normname);
-	return NULL;
-}
-
-PurpleGroup *
-purple_chat_get_group(PurpleChat *chat)
-{
-	g_return_val_if_fail(chat != NULL, NULL);
-
-	return (PurpleGroup *)(((PurpleBlistNode *)chat)->parent);
-}
-
-PurpleAccount *
-purple_chat_get_account(PurpleChat *chat)
-{
-	g_return_val_if_fail(chat != NULL, NULL);
-
-	return chat->account;
-}
-
-GHashTable *
-purple_chat_get_components(PurpleChat *chat)
-{
-	g_return_val_if_fail(chat != NULL, NULL);
-
-	return chat->components;
-}
-
-PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	return PURPLE_CONTACT(PURPLE_BLIST_NODE(buddy)->parent);
-}
-
-PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-	return buddy->presence;
-}
-
-PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy)
-{
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	if (((PurpleBlistNode *)buddy)->parent == NULL)
-		return NULL;
-
-	return (PurpleGroup *)(((PurpleBlistNode*)buddy)->parent->parent);
-}
-
 GSList *purple_group_get_accounts(PurpleGroup *group)
 {
 	GSList *l = NULL;
@@ -2723,13 +1077,6 @@ gboolean purple_group_on_account(PurpleG
 	return FALSE;
 }
 
-const char *purple_group_get_name(PurpleGroup *group)
-{
-	g_return_val_if_fail(group != NULL, NULL);
-
-	return group->name;
-}
-
 void
 purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
 							 const char *group, const char *alias)
@@ -2765,231 +1112,7 @@ purple_blist_request_add_group(void)
 		ui_ops->request_add_group();
 }
 
-static void
-purple_blist_node_destroy(PurpleBlistNode *node)
-{
-	PurpleBlistUiOps *ui_ops;
-	PurpleBlistNode *child, *next_child;
-
-	ui_ops = purple_blist_get_ui_ops();
-	child = node->child;
-	while (child) {
-		next_child = child->next;
-		purple_blist_node_destroy(child);
-		child = next_child;
-	}
-
-	/* Allow the UI to free data */
-	node->parent = NULL;
-	node->child  = NULL;
-	node->next   = NULL;
-	node->prev   = NULL;
-	if (ui_ops && ui_ops->remove)
-		ui_ops->remove(purplebuddylist, node);
-
-	if (PURPLE_BLIST_NODE_IS_BUDDY(node))
-		purple_buddy_destroy((PurpleBuddy*)node);
-	else if (PURPLE_BLIST_NODE_IS_CHAT(node))
-		purple_chat_destroy((PurpleChat*)node);
-	else if (PURPLE_BLIST_NODE_IS_CONTACT(node))
-		purple_contact_destroy((PurpleContact*)node);
-	else if (PURPLE_BLIST_NODE_IS_GROUP(node))
-		purple_group_destroy((PurpleGroup*)node);
-}
-
-static void purple_blist_node_initialize_settings(PurpleBlistNode *node)
-{
-	if (node->settings)
-		return;
-
-	node->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
-			(GDestroyNotify)purple_g_value_slice_free);
-}
-
-void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key)
-{
-	g_return_if_fail(node != NULL);
-	g_return_if_fail(node->settings != NULL);
-	g_return_if_fail(key != NULL);
-
-	g_hash_table_remove(node->settings, key);
-
-	purple_blist_schedule_save();
-}
-
 void
-purple_blist_node_set_flags(PurpleBlistNode *node, PurpleBlistNodeFlags flags)
-{
-	g_return_if_fail(node != NULL);
-
-	node->flags = flags;
-}
-
-PurpleBlistNodeFlags
-purple_blist_node_get_flags(PurpleBlistNode *node)
-{
-	g_return_val_if_fail(node != NULL, 0);
-
-	return node->flags;
-}
-
-PurpleBlistNodeType
-purple_blist_node_get_type(PurpleBlistNode *node)
-{
-	g_return_val_if_fail(node != NULL, PURPLE_BLIST_OTHER_NODE);
-	return node->type;
-}
-
-
-gboolean
-purple_blist_node_has_setting(PurpleBlistNode *node,
-                              const char *key)
-{
-	g_return_val_if_fail(node != NULL, FALSE);
-	g_return_val_if_fail(node->settings != NULL, FALSE);
-	g_return_val_if_fail(key != NULL, FALSE);
-
-	return (g_hash_table_lookup(node->settings, key) != NULL);
-}
-
-void
-purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
-{
-	GValue *value;
-
-	g_return_if_fail(node != NULL);
-	g_return_if_fail(node->settings != NULL);
-	g_return_if_fail(key != NULL);
-
-	value = purple_g_value_slice_new(G_TYPE_BOOLEAN);
-	g_value_set_boolean(value, data);
-
-	g_hash_table_replace(node->settings, g_strdup(key), value);
-
-	purple_blist_schedule_save();
-}
-
-gboolean
-purple_blist_node_get_bool(PurpleBlistNode* node, const char *key)
-{
-	GValue *value;
-
-	g_return_val_if_fail(node != NULL, FALSE);
-	g_return_val_if_fail(node->settings != NULL, FALSE);
-	g_return_val_if_fail(key != NULL, FALSE);
-
-	value = g_hash_table_lookup(node->settings, key);
-
-	if (value == NULL)
-		return FALSE;
-
-	g_return_val_if_fail(G_VALUE_HOLDS_BOOLEAN(value), FALSE);
-
-	return g_value_get_boolean(value);
-}
-
-void
-purple_blist_node_set_int(PurpleBlistNode* node, const char *key, int data)
-{
-	GValue *value;
-
-	g_return_if_fail(node != NULL);
-	g_return_if_fail(node->settings != NULL);
-	g_return_if_fail(key != NULL);
-
-	value = purple_g_value_slice_new(G_TYPE_INT);
-	g_value_set_int(value, data);
-
-	g_hash_table_replace(node->settings, g_strdup(key), value);
-
-	purple_blist_schedule_save();
-}
-
-int
-purple_blist_node_get_int(PurpleBlistNode* node, const char *key)
-{
-	GValue *value;
-
-	g_return_val_if_fail(node != NULL, 0);
-	g_return_val_if_fail(node->settings != NULL, 0);
-	g_return_val_if_fail(key != NULL, 0);
-
-	value = g_hash_table_lookup(node->settings, key);
-
-	if (value == NULL)
-		return 0;
-
-	g_return_val_if_fail(G_VALUE_HOLDS_INT(value), 0);
-
-	return g_value_get_int(value);
-}
-
-void
-purple_blist_node_set_string(PurpleBlistNode* node, const char *key, const char *data)
-{
-	GValue *value;
-
-	g_return_if_fail(node != NULL);
-	g_return_if_fail(node->settings != NULL);
-	g_return_if_fail(key != NULL);
-
-	value = purple_g_value_slice_new(G_TYPE_STRING);
-	g_value_set_string(value, data);
-
-	g_hash_table_replace(node->settings, g_strdup(key), value);
-
-	purple_blist_schedule_save();
-}
-
-const char *
-purple_blist_node_get_string(PurpleBlistNode* node, const char *key)
-{
-	GValue *value;
-
-	g_return_val_if_fail(node != NULL, NULL);
-	g_return_val_if_fail(node->settings != NULL, NULL);
-	g_return_val_if_fail(key != NULL, NULL);
-
-	value = g_hash_table_lookup(node->settings, key);
-
-	if (value == NULL)
-		return NULL;
-
-	g_return_val_if_fail(G_VALUE_HOLDS_STRING(value), NULL);
-
-	return g_value_get_string(value);
-}
-
-GList *
-purple_blist_node_get_extended_menu(PurpleBlistNode *n)
-{
-	GList *menu = NULL;
-
-	g_return_val_if_fail(n != NULL, NULL);
-
-	purple_signal_emit(purple_blist_get_handle(),
-			"blist-node-extended-menu",
-			n, &menu);
-	return menu;
-}
-
-int purple_blist_get_group_size(PurpleGroup *group, gboolean offline)
-{
-	if (!group)
-		return 0;
-
-	return offline ? group->totalsize : group->currentsize;
-}
-
-int purple_blist_get_group_online_count(PurpleGroup *group)
-{
-	if (!group)
-		return 0;
-
-	return group->online;
-}
-
-void
 purple_blist_set_ui_ops(PurpleBlistUiOps *ops)
 {
 	blist_ui_ops = ops;
@@ -3132,4 +1255,4 @@ purple_blist_uninit(void)
 
 	purple_signals_disconnect_by_handle(purple_blist_get_handle());
 	purple_signals_unregister_by_instance(purple_blist_get_handle());
-}
+};
============================================================
--- libpurple/blist.h	5d2b355c508232845cfbdc865e46aeb57260d849
+++ libpurple/blist.h	1e32736797585033d30e01130064724e5e965955
@@ -228,6 +228,44 @@ struct _PurpleBlistUiOps
 	void (*_purple_reserved4)(void);
 };
 
+#ifdef _BLIST_HELPERS_
+
+void value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data);
+xmlnode *contact_to_xmlnode(PurpleBlistNode *cnode);
+xmlnode *chat_to_xmlnode(PurpleBlistNode *cnode);
+xmlnode *buddy_to_xmlnode(PurpleBlistNode *bnode);
+xmlnode *group_to_xmlnode(PurpleBlistNode *bnode);
+PurpleBlistNode *purple_blist_get_last_sibling(PurpleBlistNode *node);
+PurpleBlistNode *purple_blist_get_last_child(PurpleBlistNode *node);
+void parse_setting(PurpleBlistNode *node, xmlnode *setting);
+void parse_contact(PurpleGroup *group, xmlnode *cnode);
+void parse_chat(PurpleGroup *group, xmlnode *cnode);
+void parse_buddy(PurpleGroup *group, PurpleContact *contact, xmlnode *bnode);
+void parse_group(xmlnode *groupnode);
+void purple_contact_compute_priority_buddy(PurpleContact *contact);
+gboolean purple_strings_are_different(const char *one, const char *two);
+
+/* The global static members */
+struct _list_account_buddies {
+  GSList *list;
+  PurpleAccount *account;
+};
+
+struct _purple_hbuddy {
+  char *name;
+  PurpleAccount *account;
+  PurpleBlistNode *group;
+};
+
+PurpleBuddyList* purple_blist_get_list(void);
+GHashTable* purple_blist_get_buddies_cache(void);
+guint purple_blist_get_save_timer(void);
+gboolean purple_blist_get_blist_loaded(void);
+void purple_blist_node_initialize_settings(PurpleBlistNode *node);
+void purple_blist_node_destroy(PurpleBlistNode* node);
+
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -418,7 +456,7 @@ void purple_blist_update_node_icon(Purpl
  */
 void purple_blist_update_node_icon(PurpleBlistNode *node);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
+#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BUDDY_C_)
 /**
  * Updates a buddy's icon.
  *


More information about the Commits mailing list