# HG changeset patch
# Node ID b3583ba25191
# Parent  9e78b5799658
diff --git a/libpurple/account.c b/libpurple/account.c
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -29,6 +29,7 @@
 #include "network.h"
 #include "notify.h"
 #include "prefs.h"
+#include "purpleaccountmanager.h"
 #include "purpleaccountpresence.h"
 #include "purpleconversationmanager.h"
 #include "purplecredentialmanager.h"
@@ -1152,22 +1153,24 @@
  * Public API
  *****************************************************************************/
 PurpleAccount *
-purple_account_new(const char *username, const char *protocol_id)
-{
+purple_account_new(const gchar *username, const gchar *protocol_id) {
 	PurpleAccount *account;
+	PurpleAccountManager *manager = NULL;
 
 	g_return_val_if_fail(username != NULL, NULL);
 	g_return_val_if_fail(protocol_id != NULL, NULL);
 
-	account = purple_accounts_find(username, protocol_id);
-
-	if (account != NULL)
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find(manager, username, protocol_id);
+	if(account != NULL) {
 		return account;
-
-	account = g_object_new(PURPLE_TYPE_ACCOUNT,
-					"username",    username,
-					"protocol-id", protocol_id,
-					NULL);
+	}
+
+	account = g_object_new(
+		PURPLE_TYPE_ACCOUNT,
+		"username", username,
+		"protocol-id", protocol_id,
+		NULL);
 
 	return account;
 }
diff --git a/libpurple/accounts.h b/libpurple/accounts.h
--- a/libpurple/accounts.h
+++ b/libpurple/accounts.h
@@ -119,6 +119,7 @@
  *
  * Adds an account to the list of accounts.
  */
+G_DEPRECATED_FOR(purple_account_manager_add)
 void purple_accounts_add(PurpleAccount *account);
 
 /**
@@ -127,6 +128,7 @@
  *
  * Removes an account from the list of accounts.
  */
+G_DEPRECATED_FOR(purple_account_manager_remove)
 void purple_accounts_remove(PurpleAccount *account);
 
 /**
@@ -148,6 +150,7 @@
  *
  * Reorders an account.
  */
+G_DEPRECATED_FOR(purple_account_manager_reorder)
 void purple_accounts_reorder(PurpleAccount *account, guint new_index);
 
 /**
@@ -157,6 +160,7 @@
  *
  * Returns: (element-type PurpleAccount) (transfer none): A list of all accounts.
  */
+G_DEPRECATED_FOR(purple_account_manager_get_all)
 GList *purple_accounts_get_all(void);
 
 /**
@@ -167,6 +171,7 @@
  * Returns: (element-type PurpleAccount) (transfer container): A list of all
  *          enabled accounts.
  */
+G_DEPRECATED_FOR(purple_account_manager_get_active)
 GList *purple_accounts_get_all_active(void);
 
 /**
@@ -178,6 +183,7 @@
  *
  * Returns: (transfer none): The account, if found, or %NULL otherwise.
  */
+G_DEPRECATED_FOR(purple_account_manager_find)
 PurpleAccount *purple_accounts_find(const char *name, const char *protocol);
 
 /**
diff --git a/libpurple/accounts.c b/libpurple/accounts.c
--- a/libpurple/accounts.c
+++ b/libpurple/accounts.c
@@ -26,6 +26,7 @@
 #include "core.h"
 #include "debug.h"
 #include "network.h"
+#include "purpleaccountmanager.h"
 #include "purpleconversationmanager.h"
 #include "purplecredentialmanager.h"
 #include "purpleenums.h"
@@ -33,7 +34,6 @@
 
 static PurpleAccountUiOps *account_ui_ops = NULL;
 
-static GList   *accounts = NULL;
 static guint    save_timer = 0;
 static gboolean accounts_loaded = FALSE;
 
@@ -49,20 +49,24 @@
 /*********************************************************************
  * Writing to disk                                                   *
  *********************************************************************/
+static void
+accounts_to_xmlnode_helper(PurpleAccount *account, gpointer data) {
+	PurpleXmlNode *node = data, *child = NULL;
+
+	child = _purple_account_to_xmlnode(account);
+	purple_xmlnode_insert_child(node, child);
+}
+
 static PurpleXmlNode *
 accounts_to_xmlnode(void)
 {
-	PurpleXmlNode *node, *child;
-	GList *cur;
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+	PurpleXmlNode *node = NULL;
 
 	node = purple_xmlnode_new("account");
 	purple_xmlnode_set_attrib(node, "version", "1.0");
 
-	for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
-	{
-		child = _purple_account_to_xmlnode(cur->data);
-		purple_xmlnode_insert_child(node, child);
-	}
+	purple_account_manager_foreach(manager, accounts_to_xmlnode_helper, node);
 
 	return node;
 }
@@ -505,23 +509,27 @@
 }
 
 static void
-load_accounts(void)
-{
+load_accounts(void) {
+	PurpleAccountManager *manager = NULL;
 	PurpleXmlNode *node, *child;
 
 	accounts_loaded = TRUE;
 
 	node = purple_util_read_xml_from_config_file("accounts.xml", _("accounts"));
 
-	if (node == NULL)
+	if(node == NULL) {
 		return;
+	}
 
-	for (child = purple_xmlnode_get_child(node, "account"); child != NULL;
-			child = purple_xmlnode_get_next_twin(child))
+	manager = purple_account_manager_get_default();
+
+	for(child = purple_xmlnode_get_child(node, "account"); child != NULL;
+	    child = purple_xmlnode_get_next_twin(child))
 	{
 		PurpleAccount *new_acct;
 		new_acct = parse_account(child);
-		purple_accounts_add(new_acct);
+
+		purple_account_manager_add(manager, new_acct);
 	}
 
 	purple_xmlnode_free(node);
@@ -529,38 +537,6 @@
 	_purple_buddy_icons_account_loaded_cb();
 }
 
-void
-purple_accounts_add(PurpleAccount *account)
-{
-	g_return_if_fail(account != NULL);
-
-	if (g_list_find(accounts, account) != NULL)
-		return;
-
-	accounts = g_list_append(accounts, account);
-
-	purple_accounts_schedule_save();
-
-	purple_signal_emit(purple_accounts_get_handle(), "account-added", account);
-}
-
-void
-purple_accounts_remove(PurpleAccount *account)
-{
-	g_return_if_fail(account != NULL);
-
-	accounts = g_list_remove(accounts, account);
-
-	purple_accounts_schedule_save();
-
-	/* Clearing the error ensures that account-error-changed is emitted,
-	 * which is the end of the guarantee that the the error's pointer is
-	 * valid.
-	 */
-	purple_account_clear_current_error(account);
-	purple_signal_emit(purple_accounts_get_handle(), "account-removed", account);
-}
-
 static void
 purple_accounts_delete_set(GObject *obj, GAsyncResult *res, gpointer d) {
 	PurpleCredentialManager *manager = PURPLE_CREDENTIAL_MANAGER(obj);
@@ -584,10 +560,11 @@
 void
 purple_accounts_delete(PurpleAccount *account)
 {
-	PurpleBlistNode *gnode, *cnode, *bnode;
+	PurpleAccountManager *manager = NULL;
+	PurpleBlistNode *gnode = NULL, *cnode = NULL, *bnode = NULL;
 	PurpleConversationManager *conv_manager = NULL;
 	PurpleCredentialManager *cred_manager = NULL;
-	GList *iter;
+	GList *iter = NULL;
 
 	g_return_if_fail(account != NULL);
 
@@ -602,36 +579,41 @@
 	purple_notify_close_with_handle(account);
 	purple_request_close_with_handle(account);
 
-	purple_accounts_remove(account);
+	manager = purple_account_manager_get_default();
+	purple_account_manager_remove(manager, account);
 
 	/* Remove this account's buddies */
-	for (gnode = purple_blist_get_default_root(); gnode != NULL;
-	     gnode = purple_blist_node_get_sibling_next(gnode)) {
-		if (!PURPLE_IS_GROUP(gnode))
+	for(gnode = purple_blist_get_default_root(); gnode != NULL;
+	    gnode = purple_blist_node_get_sibling_next(gnode))
+	{
+		if(!PURPLE_IS_GROUP(gnode)) {
 			continue;
+		}
 
 		cnode = purple_blist_node_get_first_child(gnode);
-		while (cnode) {
+		while(cnode) {
 			PurpleBlistNode *cnode_next = purple_blist_node_get_sibling_next(cnode);
 
 			if(PURPLE_IS_CONTACT(cnode)) {
 				bnode = purple_blist_node_get_first_child(cnode);
-				while (bnode) {
+				while(bnode) {
 					PurpleBlistNode *bnode_next = purple_blist_node_get_sibling_next(bnode);
 
 					if (PURPLE_IS_BUDDY(bnode)) {
 						PurpleBuddy *b = (PurpleBuddy *)bnode;
 
-						if (purple_buddy_get_account(b) == account)
+						if(purple_buddy_get_account(b) == account) {
 							purple_blist_remove_buddy(b);
+						}
 					}
 					bnode = bnode_next;
 				}
-			} else if (PURPLE_IS_CHAT(cnode)) {
+			} else if(PURPLE_IS_CHAT(cnode)) {
 				PurpleChat *c = (PurpleChat *)cnode;
 
-				if (purple_chat_get_account(c) == account)
+				if(purple_chat_get_account(c) == account) {
 					purple_blist_remove_chat(c);
+				}
 			}
 			cnode = cnode_next;
 		}
@@ -662,112 +644,34 @@
 	                                               NULL);
 }
 
-void
-purple_accounts_reorder(PurpleAccount *account, guint new_index)
-{
-	gint index;
-	GList *l;
+static void
+purple_accounts_restore_current_status(PurpleAccount *account,
+                                       G_GNUC_UNUSED gpointer data) {
+	gboolean enabled = FALSE, online = FALSE;
+
+	enabled = purple_account_get_enabled(account, purple_core_get_ui());
+	online = purple_presence_is_online(purple_account_get_presence(account));
 
-	g_return_if_fail(account != NULL);
-	g_return_if_fail(new_index <= g_list_length(accounts));
-
-	index = g_list_index(accounts, account);
+	if(enabled && online) {
+		purple_account_connect(account);
+	}
+}
 
-	if (index < 0) {
-		purple_debug_error("accounts",
-				   "Unregistered account (%s) discovered during reorder!\n",
-				   purple_account_get_username(account));
+void
+purple_accounts_restore_current_statuses() {
+	PurpleAccountManager *manager = NULL;
+
+	/* If we're not connected to the Internet right now, we bail on this */
+	if (!purple_network_is_available()) {
+		g_warning("Network not connected; skipping reconnect");
+
 		return;
 	}
 
-	l = g_list_nth(accounts, index);
-
-	if (new_index > (guint)index)
-		new_index--;
-
-	/* Remove the old one. */
-	accounts = g_list_delete_link(accounts, l);
-
-	/* Insert it where it should go. */
-	accounts = g_list_insert(accounts, account, new_index);
-
-	purple_accounts_schedule_save();
-}
-
-GList *
-purple_accounts_get_all(void)
-{
-	return accounts;
-}
-
-GList *
-purple_accounts_get_all_active(void)
-{
-	GList *list = NULL;
-	GList *all = purple_accounts_get_all();
-
-	while (all != NULL) {
-		PurpleAccount *account = all->data;
-
-		if (purple_account_get_enabled(account, purple_core_get_ui()))
-			list = g_list_append(list, account);
-
-		all = all->next;
-	}
-
-	return list;
-}
-
-PurpleAccount *
-purple_accounts_find(const char *name, const char *protocol_id)
-{
-	PurpleAccount *account = NULL;
-	GList *l;
-	char *who;
-
-	g_return_val_if_fail(name != NULL, NULL);
-	g_return_val_if_fail(protocol_id != NULL, NULL);
-
-	for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
-		account = (PurpleAccount *)l->data;
-
-		if (!purple_strequal(purple_account_get_protocol_id(account), protocol_id))
-			continue;
-
-		who = g_strdup(purple_normalize(account, name));
-		if (purple_strequal(purple_normalize(account, purple_account_get_username(account)), who)) {
-			g_free(who);
-			return account;
-		}
-		g_free(who);
-	}
-
-	return NULL;
-}
-
-void
-purple_accounts_restore_current_statuses()
-{
-	GList *l;
-	PurpleAccount *account;
-
-	/* If we're not connected to the Internet right now, we bail on this */
-	if (!purple_network_is_available())
-	{
-		purple_debug_warning("accounts", "Network not connected; skipping reconnect\n");
-		return;
-	}
-
-	for (l = purple_accounts_get_all(); l != NULL; l = l->next)
-	{
-		account = (PurpleAccount *)l->data;
-
-		if (purple_account_get_enabled(account, purple_core_get_ui()) &&
-			(purple_presence_is_online(purple_account_get_presence(account))))
-		{
-			purple_account_connect(account);
-		}
-	}
+	manager = purple_account_manager_get_default();
+	purple_account_manager_foreach(manager,
+	                               purple_accounts_restore_current_status,
+	                               NULL);
 }
 
 static PurpleAccountUiOps *
@@ -979,9 +883,52 @@
 		sync_accounts();
 	}
 
-	for (; accounts; accounts = g_list_delete_link(accounts, accounts))
-		g_object_unref(G_OBJECT(accounts->data));
-
 	purple_signals_disconnect_by_handle(handle);
 	purple_signals_unregister_by_instance(handle);
 }
+
+/******************************************************************************
+ * Deprecated API
+ *****************************************************************************/
+void
+purple_accounts_add(PurpleAccount *account) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+
+	purple_account_manager_add(manager, account);
+}
+
+void
+purple_accounts_remove(PurpleAccount *account) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+
+	purple_account_manager_remove(manager, account);
+}
+
+GList *
+purple_accounts_get_all(void) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+
+	return purple_account_manager_get_all(manager);
+}
+
+GList *
+purple_accounts_get_all_active(void) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+
+	return purple_account_manager_get_active(manager);
+}
+
+void
+purple_accounts_reorder(PurpleAccount *account, guint new_index) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+
+	purple_account_manager_reorder(manager, account, new_index);
+}
+
+PurpleAccount *
+purple_accounts_find(const char *name, const char *protocol_id) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+
+	return purple_account_manager_find(manager, name, protocol_id);
+}
+
diff --git a/libpurple/buddyicon.c b/libpurple/buddyicon.c
--- a/libpurple/buddyicon.c
+++ b/libpurple/buddyicon.c
@@ -23,6 +23,7 @@
 #include "buddyicon.h"
 #include "debug.h"
 #include "image.h"
+#include "purpleaccountmanager.h"
 #include "purpleconversation.h"
 #include "purpleconversationmanager.h"
 #include "purpleprivate.h"
@@ -1003,33 +1004,39 @@
 	}
 }
 
-void
-_purple_buddy_icons_account_loaded_cb()
+static void
+_purple_buddy_icons_account_loaded_cb_helper(PurpleAccount *account,
+                                             gpointer data)
 {
-	const char *dirname = purple_buddy_icons_get_cache_dir();
-	GList *cur;
-
-	for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
-	{
-		PurpleAccount *account = cur->data;
-		const char *account_icon_file = purple_account_get_string(account, "buddy_icon", NULL);
+	const gchar *dirname = (const gchar *)data;
+	const gchar *filename = NULL;
 
-		if (account_icon_file != NULL)
-		{
-			char *path = g_build_filename(dirname, account_icon_file, NULL);
-			if (!g_file_test(path, G_FILE_TEST_EXISTS))
-			{
-				purple_account_set_string(account, "buddy_icon", NULL);
-			} else {
-				ref_filename(account_icon_file);
-			}
-			g_free(path);
+	filename = purple_account_get_string(account, "buddy_icon", NULL);
+	if(filename != NULL) {
+		gchar *path = g_build_filename(dirname, filename, NULL);
+
+		if(!g_file_test(path, G_FILE_TEST_EXISTS)) {
+			purple_account_set_string(account, "buddy_icon", NULL);
+		} else {
+			ref_filename(filename);
 		}
+		g_free(path);
 	}
 }
 
 void
-_purple_buddy_icons_blist_loaded_cb()
+_purple_buddy_icons_account_loaded_cb(void)
+{
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+	const char *dirname = purple_buddy_icons_get_cache_dir();
+
+	purple_account_manager_foreach(manager,
+	                               _purple_buddy_icons_account_loaded_cb_helper,
+	                               (gpointer)dirname);
+}
+
+void
+_purple_buddy_icons_blist_loaded_cb(void)
 {
 	PurpleBlistNode *node = purple_blist_get_default_root();
 	const char *dirname = purple_buddy_icons_get_cache_dir();
diff --git a/libpurple/buddylist.c b/libpurple/buddylist.c
--- a/libpurple/buddylist.c
+++ b/libpurple/buddylist.c
@@ -29,6 +29,7 @@
 #include "debug.h"
 #include "notify.h"
 #include "prefs.h"
+#include "purpleaccountmanager.h"
 #include "purpleprivate.h"
 #include "purpleprotocol.h"
 #include "purpleprotocolchat.h"
@@ -360,8 +361,8 @@
 }
 
 static PurpleXmlNode *
-blist_to_xmlnode(void)
-{
+blist_to_xmlnode(void) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
 	PurpleXmlNode *node, *child, *grandchild;
 	PurpleBlistNode *gnode;
 	GList *cur;
@@ -394,7 +395,8 @@
 
 	/* Write privacy settings */
 	child = purple_xmlnode_new_child(node, "privacy");
-	for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
+	for(cur = purple_account_manager_get_all(manager); cur != NULL;
+	    cur = cur->next)
 	{
 		grandchild = accountprivacy_to_xmlnode(cur->data);
 		purple_xmlnode_insert_child(child, grandchild);
@@ -497,9 +499,9 @@
 }
 
 static void
-parse_buddy(PurpleGroup *group, PurpleContact *contact, PurpleXmlNode *bnode)
-{
+parse_buddy(PurpleGroup *group, PurpleContact *contact, PurpleXmlNode *bnode) {
 	PurpleAccount *account;
+	PurpleAccountManager *manager = purple_account_manager_get_default();
 	PurpleBuddy *buddy;
 	char *name = NULL, *alias = NULL;
 	const char *acct_name, *proto;
@@ -508,28 +510,33 @@
 	acct_name = purple_xmlnode_get_attrib(bnode, "account");
 	proto = purple_xmlnode_get_attrib(bnode, "proto");
 
-	if (!acct_name || !proto)
+	if(!acct_name || !proto) {
 		return;
+	}
 
-	account = purple_accounts_find(acct_name, proto);
+	account = purple_account_manager_find(manager, acct_name, proto);
 
-	if (!account)
+	if(!account) {
 		return;
+	}
 
-	if ((x = purple_xmlnode_get_child(bnode, "name")))
+	if((x = purple_xmlnode_get_child(bnode, "name"))) {
 		name = purple_xmlnode_get_data(x);
+	}
 
-	if (!name)
+	if(!name) {
 		return;
+	}
 
-	if ((x = purple_xmlnode_get_child(bnode, "alias")))
+	if((x = purple_xmlnode_get_child(bnode, "alias"))) {
 		alias = purple_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 = purple_xmlnode_get_child(bnode, "setting"); x; x = purple_xmlnode_get_next_twin(x)) {
+	for(x = purple_xmlnode_get_child(bnode, "setting"); x; x = purple_xmlnode_get_next_twin(x)) {
 		parse_setting((PurpleBlistNode*)buddy, x);
 	}
 
@@ -568,8 +575,9 @@
 static void
 parse_chat(PurpleGroup *group, PurpleXmlNode *cnode)
 {
+	PurpleAccount *account;
+	PurpleAccountManager *manager = purple_account_manager_get_default();
 	PurpleChat *chat;
-	PurpleAccount *account;
 	const char *acct_name, *proto;
 	PurpleXmlNode *x;
 	char *alias = NULL;
@@ -578,20 +586,23 @@
 	acct_name = purple_xmlnode_get_attrib(cnode, "account");
 	proto = purple_xmlnode_get_attrib(cnode, "proto");
 
-	if (!acct_name || !proto)
+	if(!acct_name || !proto) {
 		return;
+	}
 
-	account = purple_accounts_find(acct_name, proto);
+	account = purple_account_manager_find(manager, acct_name, proto);
 
-	if (!account)
+	if(!account) {
 		return;
+	}
 
-	if ((x = purple_xmlnode_get_child(cnode, "alias")))
+	if((x = purple_xmlnode_get_child(cnode, "alias"))) {
 		alias = purple_xmlnode_get_data(x);
+	}
 
 	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 
-	for (x = purple_xmlnode_get_child(cnode, "component"); x; x = purple_xmlnode_get_next_twin(x)) {
+	for(x = purple_xmlnode_get_child(cnode, "component"); x; x = purple_xmlnode_get_next_twin(x)) {
 		const char *name;
 		char *value;
 
@@ -604,7 +615,7 @@
 	purple_blist_add_chat(chat, group,
 			_purple_blist_get_last_child((PurpleBlistNode*)group));
 
-	for (x = purple_xmlnode_get_child(cnode, "setting"); x; x = purple_xmlnode_get_next_twin(x)) {
+	for(x = purple_xmlnode_get_child(cnode, "setting"); x; x = purple_xmlnode_get_next_twin(x)) {
 		parse_setting((PurpleBlistNode*)chat, x);
 	}
 
@@ -638,24 +649,28 @@
 static void
 load_blist(void)
 {
+	PurpleAccountManager *manager = NULL;
 	PurpleXmlNode *purple, *blist, *privacy;
 
 	blist_loaded = TRUE;
 
 	purple = purple_util_read_xml_from_config_file("blist.xml", _("buddy list"));
 
-	if (purple == NULL)
+	if(purple == NULL) {
 		return;
+	}
+
+	manager = purple_account_manager_get_default();
 
 	blist = purple_xmlnode_get_child(purple, "blist");
-	if (blist) {
+	if(blist) {
 		PurpleXmlNode *groupnode;
 
 		localized_default_group_name = g_strdup(
 			purple_xmlnode_get_attrib(blist,
 				"localized-default-group"));
 
-		for (groupnode = purple_xmlnode_get_child(blist, "group"); groupnode != NULL;
+		for(groupnode = purple_xmlnode_get_child(blist, "group"); groupnode != NULL;
 				groupnode = purple_xmlnode_get_next_twin(groupnode)) {
 			parse_group(groupnode);
 		}
@@ -665,11 +680,11 @@
 	}
 
 	privacy = purple_xmlnode_get_child(purple, "privacy");
-	if (privacy) {
+	if(privacy) {
 		PurpleXmlNode *anode;
-		for (anode = privacy->child; anode; anode = anode->next) {
+		for(anode = privacy->child; anode; anode = anode->next) {
+			PurpleAccount *account;
 			PurpleXmlNode *x;
-			PurpleAccount *account;
 			int imode;
 			const char *acct_name, *proto, *mode;
 
@@ -677,27 +692,30 @@
 			proto = purple_xmlnode_get_attrib(anode, "proto");
 			mode = purple_xmlnode_get_attrib(anode, "mode");
 
-			if (!acct_name || !proto || !mode)
+			if(!acct_name || !proto || !mode) {
 				continue;
+			}
 
-			account = purple_accounts_find(acct_name, proto);
+			account = purple_account_manager_find(manager, acct_name, proto);
 
-			if (!account)
+			if(!account) {
 				continue;
+			}
 
 			imode = atoi(mode);
 			purple_account_set_privacy_type(account, (imode != 0 ? imode : PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL));
 
-			for (x = anode->child; x; x = x->next) {
+			for(x = anode->child; x; x = x->next) {
 				char *name;
-				if (x->type != PURPLE_XMLNODE_TYPE_TAG)
+				if(x->type != PURPLE_XMLNODE_TYPE_TAG) {
 					continue;
+				}
 
-				if (purple_strequal(x->name, "permit")) {
+				if(purple_strequal(x->name, "permit")) {
 					name = purple_xmlnode_get_data(x);
 					purple_account_privacy_permit_add(account, name, TRUE);
 					g_free(name);
-				} else if (purple_strequal(x->name, "block")) {
+				} else if(purple_strequal(x->name, "block")) {
 					name = purple_xmlnode_get_data(x);
 					purple_account_privacy_deny_add(account, name, TRUE);
 					g_free(name);
@@ -727,17 +745,18 @@
 void
 purple_blist_boot(void)
 {
-	GList *account;
+	PurpleAccountManager *manager = NULL;
 	PurpleBuddyList *gbl = g_object_new(buddy_list_type, NULL);
+	GList *l;
 
 	buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
 					 NULL, (GDestroyNotify)g_hash_table_destroy);
 
 	groups_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 
-	for (account = purple_accounts_get_all(); account != NULL; account = account->next)
-	{
-		purple_blist_buddies_cache_add_account(account->data);
+	manager = purple_account_manager_get_default();
+	for(l = purple_account_manager_get_all(manager); l != NULL; l = l->next) {
+		purple_blist_buddies_cache_add_account(PURPLE_ACCOUNT(l->data));
 	}
 
 	purplebuddylist = gbl;
diff --git a/libpurple/core.c b/libpurple/core.c
--- a/libpurple/core.c
+++ b/libpurple/core.c
@@ -161,6 +161,7 @@
 	purple_buddy_icons_init();
 	purple_connections_init();
 
+	purple_account_manager_startup();
 	purple_accounts_init();
 	purple_savedstatuses_init();
 	purple_notify_init();
@@ -228,6 +229,7 @@
 	purple_savedstatuses_uninit();
 	purple_statuses_uninit();
 	purple_accounts_uninit();
+	purple_account_manager_shutdown();
 	purple_xfers_uninit();
 	purple_proxy_uninit();
 	_purple_image_store_uninit();
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -37,6 +37,7 @@
 	'proxy.c',
 	'protocols.c',
 	'purple-gio.c',
+	'purpleaccountmanager.c',
 	'purpleaccountoption.c',
 	'purpleaccountpresence.c',
 	'purpleaccountusersplit.c',
@@ -128,6 +129,7 @@
 	'proxy.h',
 	'protocols.h',
 	'purple-gio.h',
+	'purpleaccountmanager.h',
 	'purpleaccountoption.h',
 	'purpleaccountpresence.h',
 	'purpleaccountusersplit.h',
diff --git a/libpurple/purpleaccountmanager.h b/libpurple/purpleaccountmanager.h
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleaccountmanager.h
@@ -0,0 +1,155 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <pidgin.h> may be included directly"
+#endif
+
+#ifndef PURPLE_ACCOUNT_MANAGER_H
+#define PURPLE_ACCOUNT_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libpurple/account.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_ACCOUNT_MANAGER (purple_account_manager_get_type())
+G_DECLARE_FINAL_TYPE(PurpleAccountManager, purple_account_manager, PURPLE, ACCOUNT_MANAGER, GObject)
+
+/**
+ * PurpleAccountManagerForeachFunc:
+ * @account: The account instance.
+ * @data: User specified data.
+ *
+ * A function used as a callback with purple_account_manager_foreach().
+ *
+ * Since: 3.0.0
+ */
+typedef void (*PurpleAccountManagerForeachFunc)(PurpleAccount *account, gpointer data);
+
+/**
+ * PurpleAccountManager:
+ * @get_idle_time: vfunc to get the time that the user interface has been idle.
+ *
+ * An interface that a user interface can implement to let the core determine
+ * idle times.
+ *
+ * Since: 3.0.0
+ */
+
+/**
+ * purple_account_manager_get_default:
+ *
+ * Gets the default account manager for libpurple.
+ *
+ * Returns: (transfer none): The default account manager for libpurple.
+ *
+ * Since: 3.0.0
+ */
+PurpleAccountManager *purple_account_manager_get_default(void);
+
+/** purple_account_manager_add:
+ * @manager: The account manager instance.
+ * @account: (transfer full): The account to add.
+ *
+ * Adds @account to @manager.
+ *
+ * Since: 3.0.0
+ */
+void purple_account_manager_add(PurpleAccountManager *manager, PurpleAccount *account);
+
+/** purple_account_manager_remove:
+ * @manager: The account manager instance.
+ * @account: The account to remove.
+ *
+ * Removes @account from @manager.
+ *
+ * Since: 3.0.0
+ */
+void purple_account_manager_remove(PurpleAccountManager *manager, PurpleAccount *account);
+
+/**
+ * purple_account_manager_reorder:
+ * @manager: The account manager instance.
+ * @account: The account instance.
+ * @new_index: The numerical position to move @account to.
+ *
+ * Moves @account to @new_index in @manager.
+ *
+ * Since: 3.0.0
+ */
+void purple_account_manager_reorder(PurpleAccountManager *manager, PurpleAccount *account, guint new_index);
+
+/**
+ * purple_account_manager_get_all:
+ * @manager: The account manager instance.
+ *
+ * Gets the list of all accounts.
+ *
+ * Returns: (transfer none) (element-type PurpleAccount): The list of all
+ *          accounts.
+ *
+ * Since: 3.0.0
+ */
+GList *purple_account_manager_get_all(PurpleAccountManager *manager);
+
+/**
+ * purple_account_manager_get_active:
+ * @manager: The account manager instance.
+ *
+ * Gets the list of all active accounts.
+ *
+ * Returns: (transfer container) (element-type PurpleAccount): The list of all
+ *          active accounts.
+ *
+ * Since: 3.0.0
+ */
+GList *purple_account_manager_get_active(PurpleAccountManager *manager);
+
+/**
+ * purple_account_manager_find:
+ * @manager: The account manager instance.
+ * @username: The username of the account.
+ * @protocol_id: The id of the protocol of the account.
+ *
+ * Attempts to find an account in @manager with the matching @username and
+ * @protocol_id.
+ *
+ * Returns: (transfer none): The account if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleAccount *purple_account_manager_find(PurpleAccountManager *manager, const gchar *username, const gchar *protocol_id);
+
+/**
+ * purple_account_manager_foreach:
+ * @manager: The account manager instance.
+ * @callback: (scope callback): The function to call.
+ * @data: User data to pass to @callback.
+ *
+ * Calls @callback with @data for each account that @manager knows about.
+ *
+ * Since: 3.0.0
+ */
+void purple_account_manager_foreach(PurpleAccountManager *manager, PurpleAccountManagerForeachFunc callback, gpointer data);
+
+G_END_DECLS
+
+#endif /* PURPLE_ACCOUNT_MANAGER_H */
diff --git a/libpurple/purpleaccountmanager.c b/libpurple/purpleaccountmanager.c
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleaccountmanager.c
@@ -0,0 +1,305 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "purpleaccountmanager.h"
+#include "purpleprivate.h"
+
+#include "account.h"
+#include "accounts.h"
+#include "core.h"
+
+enum {
+	SIG_ADDED,
+	SIG_REMOVED,
+	N_SIGNALS,
+};
+static guint signals[N_SIGNALS] = {0, };
+
+struct _PurpleAccountManager {
+	GObject parent;
+
+	GList *accounts;
+};
+
+static PurpleAccountManager *default_manager = NULL;
+
+G_DEFINE_TYPE(PurpleAccountManager, purple_account_manager, G_TYPE_OBJECT)
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_account_manager_finalize(GObject *obj) {
+	PurpleAccountManager *manager = PURPLE_ACCOUNT_MANAGER(obj);
+	GList *l = NULL;
+
+	for(l = manager->accounts; l != NULL; l = l->next) {
+		g_object_unref(l->data);
+	}
+
+	G_OBJECT_CLASS(purple_account_manager_parent_class)->finalize(obj);
+}
+
+static void
+purple_account_manager_init(PurpleAccountManager *manager) {
+}
+
+static void
+purple_account_manager_class_init(PurpleAccountManagerClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->finalize = purple_account_manager_finalize;
+
+	/**
+	 * PurpleAccountManager::added:
+	 * @manager: The account manager instance.
+	 * @account: The account that was added.
+	 *
+	 * Emitted after @account was added to @manager.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_ADDED] = g_signal_new_class_handler(
+		"added",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		1,
+		PURPLE_TYPE_ACCOUNT);
+
+	/**
+	 * PurpleAccountManager::removed:
+	 * @manager: The account manager instance.
+	 * @account: The account that was removed.
+	 *
+	 * Emitted after @account was removed from @manager.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_REMOVED] = g_signal_new_class_handler(
+		"removed",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		1,
+		PURPLE_TYPE_ACCOUNT);
+}
+
+/******************************************************************************
+ * Private API
+ *****************************************************************************/
+void
+purple_account_manager_startup(void) {
+	if(!PURPLE_IS_ACCOUNT_MANAGER(default_manager)) {
+		default_manager = g_object_new(PURPLE_TYPE_ACCOUNT_MANAGER, NULL);
+	}
+}
+
+void
+purple_account_manager_shutdown(void) {
+	g_clear_object(&default_manager);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleAccountManager *
+purple_account_manager_get_default(void) {
+	return default_manager;
+}
+
+void
+purple_account_manager_add(PurpleAccountManager *manager,
+                           PurpleAccount *account)
+{
+	g_return_if_fail(PURPLE_IS_ACCOUNT_MANAGER(manager));
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+	/* If the manager already knows about the account, we do nothing. */
+	if(g_list_find(manager->accounts, account) != NULL) {
+		return;
+	}
+
+	/* Since the manager doesn't know about the account, put the new account
+	 * at the start of the list as that's likely to be the first one in user
+	 * interfaces and the most likely to have configuration issues as it's a
+	 * new account.
+	 */
+	manager->accounts = g_list_prepend(manager->accounts, account);
+
+	purple_accounts_schedule_save();
+
+	g_signal_emit(manager, signals[SIG_ADDED], 0, account);
+
+	/* Finally emit the old purple signal that will eventually be removed. */
+	purple_signal_emit(purple_accounts_get_handle(), "account-added", account);
+}
+
+void
+purple_account_manager_remove(PurpleAccountManager *manager,
+                              PurpleAccount *account)
+{
+	g_return_if_fail(PURPLE_IS_ACCOUNT_MANAGER(manager));
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+	manager->accounts = g_list_remove(manager->accounts, account);
+
+	purple_accounts_schedule_save();
+
+	/* Clearing the error ensures that account-error-changed is emitted,
+	 * which is the end of the guarantee that the error's pointer is valid.
+	 */
+	purple_account_clear_current_error(account);
+
+	g_signal_emit(manager, signals[SIG_REMOVED], 0, account);
+
+	/* Finally emit the old purple signal that will eventually be removed. */
+	purple_signal_emit(purple_accounts_get_handle(), "account-removed",
+	                   account);
+}
+
+GList *
+purple_account_manager_get_all(PurpleAccountManager *manager) {
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT_MANAGER(manager), NULL);
+
+	return manager->accounts;
+}
+
+GList *
+purple_account_manager_get_active(PurpleAccountManager *manager) {
+	GList *active = NULL, *l = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT_MANAGER(manager), NULL);
+
+	for(l = manager->accounts; l != NULL; l = l->next) {
+		PurpleAccount *account = PURPLE_ACCOUNT(l->data);
+
+		if(purple_account_get_enabled(account, purple_core_get_ui())) {
+			active = g_list_append(active, account);
+		}
+	}
+
+	return active;
+}
+
+void
+purple_account_manager_reorder(PurpleAccountManager *manager,
+                               PurpleAccount *account,
+                               guint new_index)
+{
+	GList *l = NULL;
+	gboolean found = FALSE;
+	guint index = 0;
+
+	g_return_if_fail(PURPLE_IS_ACCOUNT_MANAGER(manager));
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+	/* Iterate over the known accounts until we have found a matching account
+	 * or exhausted the list. For each iteration increment idx.
+	 */
+	for(l = manager->accounts; l != NULL; l = l->next, index++) {
+		if(PURPLE_ACCOUNT(l->data) == account) {
+			manager->accounts = g_list_delete_link(manager->accounts, l);
+
+			found = TRUE;
+
+			/* If new_index is greater than the current index, we need to
+			 * decrement new_index by 1 to account for the move as we'll be
+			 * inserting into a list with one less item.
+			 */
+			if(new_index > index) {
+				new_index--;
+			}
+
+			break;
+		}
+	}
+
+	if(!found) {
+		g_critical("Unregistered account (%s) found during reorder!",
+		           purple_account_get_username(account));
+		return;
+	}
+
+	/* Insert the account into its new position. */
+	manager->accounts = g_list_insert(manager->accounts, account, new_index);
+
+	purple_accounts_schedule_save();
+}
+
+PurpleAccount *
+purple_account_manager_find(PurpleAccountManager *manager,
+                            const gchar *username, const gchar *protocol_id)
+{
+	GList *l;
+
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT_MANAGER(manager), NULL);
+	g_return_val_if_fail(username != NULL, NULL);
+	g_return_val_if_fail(protocol_id != NULL, NULL);
+
+	for(l = manager->accounts; l != NULL; l = l->next) {
+		PurpleAccount *account = PURPLE_ACCOUNT(l->data);
+		gchar *normalized = NULL;
+		const gchar *existing_protocol_id = NULL;
+		const gchar *existing_username = NULL;
+		const gchar *existing_normalized = NULL;
+
+		/* Check if the protocol id matches what the user asked for. */
+		existing_protocol_id = purple_account_get_protocol_id(account);
+		if(!purple_strequal(existing_protocol_id, protocol_id)) {
+			continue;
+		}
+
+		/* Finally verify the username. */
+		existing_username = purple_account_get_username(account);
+		normalized = g_strdup(purple_normalize(account, username));
+		existing_normalized = purple_normalize(account, existing_username);
+
+		if(purple_strequal(existing_normalized, normalized)) {
+			g_free(normalized);
+
+			return account;
+		}
+		g_free(normalized);
+	}
+
+	return NULL;
+}
+
+void
+purple_account_manager_foreach(PurpleAccountManager *manager,
+                               PurpleAccountManagerForeachFunc callback,
+                               gpointer data)
+{
+	GList *l = NULL;
+
+	g_return_if_fail(PURPLE_IS_ACCOUNT_MANAGER(manager));
+	g_return_if_fail(callback != NULL);
+
+	for(l = manager->accounts; l != NULL; l = l->next) {
+		callback(PURPLE_ACCOUNT(l->data), data);
+	}
+}
diff --git a/libpurple/purpleprivate.h b/libpurple/purpleprivate.h
--- a/libpurple/purpleprivate.h
+++ b/libpurple/purpleprivate.h
@@ -182,6 +182,24 @@
 _purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *msg);
 
 /**
+ * purple_account_manager_startup:
+ *
+ * Starts up the account manager by creating the default instance.
+ *
+ * Since: 3.0.0
+ */
+void purple_account_manager_startup(void);
+
+/**
+ * purple_account_manager_shutdown:
+ *
+ * Shuts down the account manager by destroying the default instance.
+ *
+ * Since: 3.0.0
+ */
+void purple_account_manager_shutdown(void);
+
+/**
  * purple_conversation_manager_startup:
  *
  * Starts up the conversation manager by creating the default instance.
diff --git a/libpurple/purpleprotocol.c b/libpurple/purpleprotocol.c
--- a/libpurple/purpleprotocol.c
+++ b/libpurple/purpleprotocol.c
@@ -22,7 +22,7 @@
 
 #include "purpleprotocol.h"
 
-#include "accounts.h"
+#include "purpleaccountmanager.h"
 #include "purpleenums.h"
 
 enum {
@@ -145,6 +145,25 @@
 	g_object_notify_by_pspec(G_OBJECT(protocol), properties[PROP_OPTIONS]);
 }
 
+static void
+purple_protocol_foreach_account_disconnect(PurpleAccount *account,
+                                           gpointer data)
+{
+	const gchar *protocol_id = (const gchar *)data;
+
+	/* I'm not sure that we can finalize a protocol plugin if an account is
+	 * still using it..  Right now accounts don't ref protocols, but maybe
+	 * they should?
+	 */
+	if(purple_account_is_disconnected(account)) {
+		return;
+	}
+
+	if(purple_strequal(protocol_id, purple_account_get_protocol_id(account))) {
+		purple_account_disconnect(account);
+	}
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -228,12 +247,16 @@
 
 static void
 purple_protocol_finalize(GObject *object) {
+	PurpleAccountManager *manager = purple_account_manager_get_default();
 	PurpleProtocol *protocol = PURPLE_PROTOCOL(object);
 	PurpleProtocolPrivate *priv = NULL;
-	GList *accounts, *l;
 
 	priv = purple_protocol_get_instance_private(protocol);
 
+	purple_account_manager_foreach(manager,
+	                               purple_protocol_foreach_account_disconnect,
+	                               priv->id);
+
 	g_clear_pointer(&priv->id, g_free);
 	g_clear_pointer(&priv->name, g_free);
 	g_clear_pointer(&priv->description, g_free);
@@ -241,22 +264,6 @@
 	g_clear_pointer(&priv->icon_search_path, g_free);
 	g_clear_pointer(&priv->icon_resource_path, g_free);
 
-	/* I'm not sure that we can finalize a protocol plugin if an account is
-	 * still using it..  Right now accounts don't ref protocols, but maybe
-	 * they should?
-	 */
-	accounts = purple_accounts_get_all_active();
-	for (l = accounts; l != NULL; l = l->next) {
-		PurpleAccount *account = PURPLE_ACCOUNT(l->data);
-		if (purple_account_is_disconnected(account))
-			continue;
-
-		if (purple_strequal(priv->id, purple_account_get_protocol_id(account)))
-			purple_account_disconnect(account);
-	}
-
-	g_list_free(accounts);
-
 	/* these seem to be fallbacks if the subclass protocol doesn't do it's own
 	 * clean up?  I kind of want to delete them... - gk 2021-03-03
 	 */
diff --git a/libpurple/savedstatuses.c b/libpurple/savedstatuses.c
--- a/libpurple/savedstatuses.c
+++ b/libpurple/savedstatuses.c
@@ -27,6 +27,7 @@
 #include "debug.h"
 #include "idle.h"
 #include "notify.h"
+#include "purpleaccountmanager.h"
 #include "purplemarkup.h"
 #include "savedstatuses.h"
 #include "request.h"
@@ -374,42 +375,46 @@
 
 	/* Read the account */
 	node = purple_xmlnode_get_child(substatus, "account");
-	if (node != NULL)
-	{
-		char *acct_name;
+	if(node != NULL) {
+		gchar *acct_name;
 		const char *protocol;
+
 		acct_name = purple_xmlnode_get_data(node);
 		protocol = purple_xmlnode_get_attrib(node, "protocol");
-		if ((acct_name != NULL) && (protocol != NULL))
-			ret->account = purple_accounts_find(acct_name, protocol);
+
+		if(acct_name != NULL && protocol != NULL) {
+			PurpleAccountManager *manager = NULL;
+
+			manager = purple_account_manager_get_default();
+
+			ret->account = purple_account_manager_find(manager, acct_name,
+			                                           protocol);
+		}
+
 		g_free(acct_name);
 	}
 
-	if (ret->account == NULL)
-	{
+	if(ret->account == NULL) {
 		g_free(ret);
 		return NULL;
 	}
 
 	/* Read the state */
 	node = purple_xmlnode_get_child(substatus, "state");
-	if ((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL))
-	{
+	if((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL)) {
 		ret->type = purple_status_type_find_with_id(
 							purple_account_get_status_types(ret->account), data);
 		g_free(data);
 	}
 
-	if (ret->type == NULL)
-	{
+	if(ret->type == NULL) {
 		g_free(ret);
 		return NULL;
 	}
 
 	/* Read the message */
 	node = purple_xmlnode_get_child(substatus, "message");
-	if ((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL))
-	{
+	if((node != NULL) && ((data = purple_xmlnode_get_data(node)) != NULL)) {
 		ret->message = data;
 	}
 
@@ -846,14 +851,15 @@
 }
 
 void
-purple_savedstatus_set_idleaway(gboolean idleaway)
-{
+purple_savedstatus_set_idleaway(gboolean idleaway) {
+	PurpleAccountManager *manager = NULL;
+	PurpleSavedStatus *old, *saved_status;
 	GList *accounts, *node;
-	PurpleSavedStatus *old, *saved_status;
 
-	if (purple_savedstatus_is_idleaway() == idleaway)
+	if(purple_savedstatus_is_idleaway() == idleaway) {
 		/* Don't need to do anything */
 		return;
+	}
 
 	old = purple_savedstatus_get_current();
 	saved_status = idleaway ? purple_savedstatus_get_idleaway()
@@ -861,16 +867,19 @@
 	purple_prefs_set_bool("/purple/savedstatus/isidleaway", idleaway);
 
 	/* Changing our status makes us un-idle */
-	if (!idleaway)
+	if(!idleaway) {
 		purple_idle_touch();
+	}
 
-	if (idleaway && (purple_savedstatus_get_primitive_type(old) != PURPLE_STATUS_AVAILABLE))
+	if(idleaway && (purple_savedstatus_get_primitive_type(old) != PURPLE_STATUS_AVAILABLE))
+	{
 		/* Our global status is already "away," so don't change anything */
 		return;
+	}
 
-	accounts = purple_accounts_get_all_active();
-	for (node = accounts; node != NULL; node = node->next)
-	{
+	manager = purple_account_manager_get_default();
+	accounts = purple_account_manager_get_active(manager);
+	for (node = accounts; node != NULL; node = node->next) {
 		PurpleAccount *account;
 		PurplePresence *presence;
 		PurpleStatus *status;
@@ -1085,10 +1094,10 @@
 }
 
 void
-purple_savedstatus_activate(PurpleSavedStatus *saved_status)
-{
+purple_savedstatus_activate(PurpleSavedStatus *saved_status) {
+	PurpleAccountManager *manager = NULL;
+	PurpleSavedStatus *old = purple_savedstatus_get_current();
 	GList *accounts, *node;
-	PurpleSavedStatus *old = purple_savedstatus_get_current();
 
 	g_return_if_fail(saved_status != NULL);
 
@@ -1096,13 +1105,14 @@
 	saved_status->lastused = time(NULL);
 	saved_status->usage_count++;
 	saved_statuses = g_list_remove(saved_statuses, saved_status);
-	saved_statuses = g_list_insert_sorted(saved_statuses, saved_status, saved_statuses_sort_func);
+	saved_statuses = g_list_insert_sorted(saved_statuses, saved_status,
+	                                      saved_statuses_sort_func);
 	purple_prefs_set_int("/purple/savedstatus/default",
-					   purple_savedstatus_get_creation_time(saved_status));
+	                     purple_savedstatus_get_creation_time(saved_status));
 
-	accounts = purple_accounts_get_all_active();
-	for (node = accounts; node != NULL; node = node->next)
-	{
+	manager = purple_account_manager_get_default();
+	accounts = purple_account_manager_get_active(manager);
+	for(node = accounts; node != NULL; node = node->next) {
 		PurpleAccount *account;
 
 		account = node->data;
@@ -1112,11 +1122,11 @@
 
 	g_list_free(accounts);
 
-	if (purple_savedstatus_is_idleaway()) {
+	if(purple_savedstatus_is_idleaway()) {
 		purple_savedstatus_set_idleaway(FALSE);
 	} else {
-		purple_signal_emit(purple_savedstatuses_get_handle(), "savedstatus-changed",
-					 	   saved_status, old);
+		purple_signal_emit(purple_savedstatuses_get_handle(),
+		                   "savedstatus-changed", saved_status, old);
 	}
 }
 
diff --git a/libpurple/tests/meson.build b/libpurple/tests/meson.build
--- a/libpurple/tests/meson.build
+++ b/libpurple/tests/meson.build
@@ -1,5 +1,6 @@
 PROGS = [
     'account_option',
+    'account_manager',
     'circular_buffer',
     'credential_manager',
     'credential_provider',
diff --git a/libpurple/tests/test_account_manager.c b/libpurple/tests/test_account_manager.c
new file mode 100644
--- /dev/null
+++ b/libpurple/tests/test_account_manager.c
@@ -0,0 +1,210 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <purple.h>
+
+#include "test_ui.h"
+
+#define PURPLE_GLOBAL_HEADER_INSIDE
+#include "../purpleprivate.h"
+#undef PURPLE_GLOBAL_HEADER_INSIDE
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_purple_account_manager_get_default(void) {
+	PurpleAccountManager *manager1 = NULL, *manager2 = NULL;
+
+	manager1 = purple_account_manager_get_default();
+	g_assert_true(PURPLE_IS_ACCOUNT_MANAGER(manager1));
+
+	manager2 = purple_account_manager_get_default();
+	g_assert_true(PURPLE_IS_ACCOUNT_MANAGER(manager2));
+
+	g_assert_true(manager1 == manager2);
+}
+
+/******************************************************************************
+ * Add/Remove Test
+ *****************************************************************************/
+static void
+test_purple_account_manager_signal_called(G_GNUC_UNUSED PurpleAccountManager *manager,
+                                          G_GNUC_UNUSED PurpleAccount *account,
+                                          gpointer data)
+{
+	gboolean *called = (data);
+
+	*called = TRUE;
+}
+
+static void
+test_purple_account_manager_add_remove(void) {
+	PurpleAccount *account = NULL;
+	PurpleAccountManager *manager = NULL;
+	GList *accounts = NULL;
+	gboolean signal_called = FALSE;
+
+	account = purple_account_new("test", "test");
+	manager = g_object_new(PURPLE_TYPE_ACCOUNT_MANAGER, NULL);
+
+	g_signal_connect(manager, "added",
+	                 G_CALLBACK(test_purple_account_manager_signal_called),
+	                 &signal_called);
+	g_signal_connect(manager, "removed",
+	                 G_CALLBACK(test_purple_account_manager_signal_called),
+	                 &signal_called);
+
+	accounts = purple_account_manager_get_all(manager);
+	g_assert_true(g_list_length(accounts) == 0);
+
+	/* Add the account and verify that it was added and that the signal was
+	 * emitted.
+	 */
+	purple_account_manager_add(manager, account);
+	accounts = purple_account_manager_get_all(manager);
+	g_assert_true(g_list_length(accounts) == 1);
+	g_assert_true(signal_called);
+
+	signal_called = FALSE;
+
+	/* Remove the account and verify that it was removed and that the signal
+	 * was emitted.
+	 */
+	purple_account_manager_remove(manager, account);
+	accounts = purple_account_manager_get_all(manager);
+	g_assert_true(g_list_length(accounts) == 0);
+	g_assert_true(signal_called);
+
+	/* Cleanup */
+	g_clear_object(&account);
+	g_clear_object(&manager);
+}
+
+/******************************************************************************
+ * Find Tests
+ *****************************************************************************/
+static void
+test_purple_account_manager_find(void) {
+	PurpleAccount *account = NULL, *found = NULL;
+	PurpleAccountManager *manager = NULL;
+
+	manager = g_object_new(PURPLE_TYPE_ACCOUNT_MANAGER, NULL);
+
+	/* Try to find an account that doesn't exist. */
+	found = purple_account_manager_find(manager, "test", "test");
+	g_assert_null(found);
+
+	/* Create the account that will be used in the rest of the test. */
+	account = purple_account_new("test", "test");
+
+	/* Now add an account and verify that we can find it. */
+	purple_account_manager_add(manager, account);
+	found = purple_account_manager_find(manager, "test", "test");
+	g_assert_nonnull(found);
+
+	/* Finally remove the account and verify it can't be found. */
+	purple_account_manager_remove(manager, account);
+	found = purple_account_manager_find(manager, "test", "test");
+	g_assert_null(found);
+
+	/* Cleanup */
+	g_clear_object(&account);
+	g_clear_object(&manager);
+}
+
+/******************************************************************************
+ * Foreach Tests
+ *****************************************************************************/
+static void
+test_purple_account_manager_foreach_func(G_GNUC_UNUSED PurpleAccount *account,
+                                         gpointer data)
+{
+	guint *count = (guint *)data;
+
+	/* We have to use (*count)++ because *count++ doesn't work as the ++
+	 * happens after the statement which is no longer the dereferenced pointer.
+	 */
+	(*count)++;
+}
+
+static void
+test_purple_account_manager_foreach(void) {
+	PurpleAccount *accounts[3];
+	PurpleAccountManager *manager = NULL;
+	guint count = 0;
+
+	manager = g_object_new(PURPLE_TYPE_ACCOUNT_MANAGER, NULL);
+
+	accounts[0] = purple_account_new("test0", "test");
+	accounts[1] = purple_account_new("test1", "test");
+	accounts[2] = purple_account_new("test2", "test");
+
+	purple_account_manager_add(manager, accounts[0]);
+	purple_account_manager_add(manager, accounts[1]);
+	purple_account_manager_add(manager, accounts[2]);
+
+	purple_account_manager_foreach(manager,
+	                               test_purple_account_manager_foreach_func,
+	                               &count);
+
+	g_assert_cmpuint(count, ==, 3);
+
+	/* Now remove everything and verify that the foreach callback wasn't
+	 * called.
+	 */
+	purple_account_manager_remove(manager, accounts[0]);
+	purple_account_manager_remove(manager, accounts[1]);
+	purple_account_manager_remove(manager, accounts[2]);
+
+	count = 0;
+	purple_account_manager_foreach(manager,
+	                               test_purple_account_manager_foreach_func,
+	                               &count);
+
+	g_assert_cmpuint(count, ==, 0);
+
+	/* Cleanup */
+	g_clear_object(&accounts[0]);
+	g_clear_object(&accounts[1]);
+	g_clear_object(&accounts[2]);
+	g_clear_object(&manager);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar *argv[]) {
+	g_test_init(&argc, &argv, NULL);
+
+	test_ui_purple_init();
+
+	g_test_add_func("/account-manager/get-default",
+	                test_purple_account_manager_get_default);
+	g_test_add_func("/account-manager/add-remove",
+	                test_purple_account_manager_add_remove);
+	g_test_add_func("/account-manager/find",
+	                test_purple_account_manager_find);
+	g_test_add_func("/account-manager/foreach",
+	                test_purple_account_manager_foreach);
+
+	return g_test_run();
+}
diff --git a/libpurple/util.c b/libpurple/util.c
--- a/libpurple/util.c
+++ b/libpurple/util.c
@@ -27,6 +27,7 @@
 #include "debug.h"
 #include "notify.h"
 #include "prefs.h"
+#include "purpleaccountmanager.h"
 #include "purpleconversation.h"
 #include "purpleprotocol.h"
 #include "purpleprotocolclient.h"
@@ -1895,7 +1896,8 @@
 
 void purple_util_set_current_song(const char *title, const char *artist, const char *album)
 {
-	GList *list = purple_accounts_get_all();
+	PurpleAccountManager *manager = purple_account_manager_get_default();
+	GList *list = purple_account_manager_get_all(manager);
 	for (; list; list = list->next) {
 		PurplePresence *presence;
 		PurpleStatus *tune;
