diff --git a/doc/reference/libpurple/libpurple-docs.xml b/doc/reference/libpurple/libpurple-docs.xml
--- a/doc/reference/libpurple/libpurple-docs.xml
+++ b/doc/reference/libpurple/libpurple-docs.xml
@@ -72,6 +72,7 @@
       <xi:include href="xml/purplechatconversation.xml" />
       <xi:include href="xml/purplechatuser.xml" />
       <xi:include href="xml/purpleconversation.xml" />
+      <xi:include href="xml/purpleconversationmanager.xml" />
       <xi:include href="xml/purpleconversationuiops.xml" />
       <xi:include href="xml/purplecredentialmanager.xml" />
       <xi:include href="xml/purplecredentialprovider.xml" />
diff --git a/finch/gntconv.c b/finch/gntconv.c
--- a/finch/gntconv.c
+++ b/finch/gntconv.c
@@ -281,55 +281,88 @@
 static void
 account_signed_on_off(PurpleConnection *gc, gpointer null)
 {
-	GList *list = purple_conversations_get_ims();
-	while (list) {
-		PurpleConversation *conv = list->data;
-		PurpleConversation *cc = find_im_with_contact(
-				purple_conversation_get_account(conv), purple_conversation_get_name(conv));
-		if (cc)
+	GList *list, *l;
+
+	list = purple_conversations_get_all();
+
+	for(l = list; l != NULL; l = l->next) {
+		PurpleConversation *conv = NULL;
+		PurpleConversation *cc = NULL;
+
+		if(!PURPLE_IS_IM_CONVERSATION(l->data)) {
+			continue;
+		}
+
+		conv = PURPLE_CONVERSATION(l->data);
+		cc = find_im_with_contact(purple_conversation_get_account(conv),
+		                          purple_conversation_get_name(conv));
+
+		if(cc) {
 			generate_send_to_menu(FINCH_CONV(cc));
-		list = list->next;
+		}
+	}
+
+	/* If we disconnected we're done for now. */
+	if(!PURPLE_CONNECTION_IS_CONNECTED(gc)) {
+		g_list_free(list);
+
+		return;
 	}
 
-	if (PURPLE_CONNECTION_IS_CONNECTED(gc)) {
-		/* We just signed on. Let's see if there's any chat that we have open,
-		 * and hadn't left before the disconnect. */
-		list = purple_conversations_get_chats();
-		while (list) {
-			PurpleConversation *conv = list->data;
-			PurpleChat *chat;
-			GHashTable *comps = NULL;
+	/* Since we just signed on. Let's see if there's any chat that we have open,
+	 * and hadn't left before the disconnect.
+	 */
+	for(l = list; l != NULL; l = l->next) {
+		PurpleConversation *conv = NULL;
+		PurpleChat *chat;
+		GHashTable *comps = NULL;
+
+		conv = PURPLE_CONVERSATION(l->data);
+
+		if(!PURPLE_IS_CHAT_CONVERSATION(conv)) {
+			continue;
+		}
 
-			list = list->next;
-			if (purple_conversation_get_account(conv) != purple_connection_get_account(gc) ||
-					!g_object_get_data(G_OBJECT(conv), "want-to-rejoin"))
-				continue;
+		if (purple_conversation_get_account(conv) != purple_connection_get_account(gc) ||
+				!g_object_get_data(G_OBJECT(conv), "want-to-rejoin"))
+		{
+			continue;
+		}
 
-			chat = find_chat_for_conversation(conv);
-			if (chat == NULL) {
-				PurpleProtocol *protocol = purple_connection_get_protocol(gc);
-				comps = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc,
-						purple_conversation_get_name(conv));
-			} else {
-				comps = purple_chat_get_components(chat);
-			}
-			purple_serv_join_chat(gc, comps);
-			if (chat == NULL && comps != NULL)
-				g_hash_table_destroy(comps);
+		chat = find_chat_for_conversation(conv);
+		if (chat == NULL) {
+			PurpleProtocol *protocol = purple_connection_get_protocol(gc);
+			comps = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc,
+					purple_conversation_get_name(conv));
+		} else {
+			comps = purple_chat_get_components(chat);
+		}
+		purple_serv_join_chat(gc, comps);
+		if(chat == NULL && comps != NULL) {
+			g_hash_table_destroy(comps);
 		}
 	}
+
+	g_list_free(list);
 }
 
 static void
 account_signing_off(PurpleConnection *gc)
 {
-	GList *list = purple_conversations_get_chats();
+	GList *list = purple_conversations_get_all();
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	/* We are about to sign off. See which chats we are currently in, and mark
 	 * them for rejoin on reconnect. */
-	while (list) {
-		PurpleConversation *conv = list->data;
+	while(list != NULL) {
+		PurpleConversation *conv = NULL;
+
+		if(!PURPLE_IS_CHAT_CONVERSATION(list->data)) {
+			continue;
+		}
+
+		conv = PURPLE_CONVERSATION(list->data);
+
 		if (!purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)) &&
 				purple_conversation_get_account(conv) == account) {
 			g_object_set_data(G_OBJECT(conv), "want-to-rejoin", GINT_TO_POINTER(TRUE));
@@ -339,7 +372,8 @@
 				"the account reconnects."),
 				PURPLE_MESSAGE_NO_LOG);
 		}
-		list = list->next;
+
+		list = g_list_delete_link(list, list);
 	}
 }
 
@@ -1001,7 +1035,7 @@
 		/* Print the list of users in the room */
 		GString *string = g_string_new(NULL);
 		GList *iter;
-		int count = g_list_length(users);
+		gint count = g_list_length(users);
 
 		g_string_printf(string,
 				ngettext("List of %d user:\n", "List of %d users:\n", count), count);
diff --git a/libpurple/conversations.h b/libpurple/conversations.h
--- a/libpurple/conversations.h
+++ b/libpurple/conversations.h
@@ -48,6 +48,7 @@
  *
  * Adds a conversation to the list of conversations.
  */
+G_DEPRECATED_FOR(purple_conversation_manager_register)
 void purple_conversations_add(PurpleConversation *conv);
 
 /**
@@ -56,6 +57,7 @@
  *
  * Removes a conversation from the list of conversations.
  */
+G_DEPRECATED_FOR(purple_conversation_manager_unregister)
 void purple_conversations_remove(PurpleConversation *conv);
 
 /**
@@ -67,27 +69,10 @@
  *
  * Returns: (element-type PurpleConversation) (transfer none): A GList of all conversations.
  */
+G_DEPRECATED_FOR(purple_conversation_manager_get_all)
 GList *purple_conversations_get_all(void);
 
 /**
- * purple_conversations_get_ims:
- *
- * Returns a list of all IMs.
- *
- * Returns: (element-type PurpleIMConversation) (transfer none): All IMs.
- */
-GList *purple_conversations_get_ims(void);
-
-/**
- * purple_conversations_get_chats:
- *
- * Returns a list of all chats.
- *
- * Returns: (element-type PurpleChatConversation) (transfer none): All chats.
- */
-GList *purple_conversations_get_chats(void);
-
-/**
  * purple_conversations_find_with_account:
  * @name: The name of the conversation.
  * @account: The account associated with the conversation.
@@ -96,6 +81,7 @@
  *
  * Returns: (transfer none): The conversation if found, or %NULL otherwise.
  */
+G_DEPRECATED_FOR(purple_conversation_manager_find)
 PurpleConversation *purple_conversations_find_with_account(const char *name,
 		PurpleAccount *account);
 
@@ -108,6 +94,7 @@
  *
  * Returns: (transfer none): The conversation if found, or %NULL otherwise.
  */
+G_DEPRECATED_FOR(purple_conversation_manager_find_im)
 PurpleConversation *purple_conversations_find_im_with_account(const char *name, PurpleAccount *account);
 
 /**
@@ -119,6 +106,7 @@
  *
  * Returns: (transfer none): The conversation if found, or %NULL otherwise.
  */
+G_DEPRECATED_FOR(purple_conversation_manager_find_chat)
 PurpleConversation *purple_conversations_find_chat_with_account(const char *name, PurpleAccount *account);
 
 /**
@@ -130,7 +118,8 @@
  *
  * Returns: (transfer none): The chat conversation.
  */
-PurpleConversation *purple_conversations_find_chat(const PurpleConnection *gc, int id);
+G_DEPRECATED_FOR(purple_conversation_manager_find_chat_by_id)
+PurpleConversation *purple_conversations_find_chat(PurpleConnection *gc, int id);
 
 /**
  * purple_conversations_set_ui_ops:
diff --git a/libpurple/conversations.c b/libpurple/conversations.c
--- a/libpurple/conversations.c
+++ b/libpurple/conversations.c
@@ -22,219 +22,91 @@
 #include "internal.h"
 #include "purpleprivate.h"
 
-#include "conversations.h"
+#include "purpleconversationmanager.h"
 
-static GList *conversations = NULL;
-static GList *ims = NULL;
-static GList *chats = NULL;
 static PurpleConversationUiOps *default_ops = NULL;
 
-/*
- * A hash table used for efficient lookups of conversations by name.
- * struct _purple_hconv => PurpleConversation*
- */
-static GHashTable *conversation_cache = NULL;
-
-struct _purple_hconv {
-	gboolean im;
-	char *name;
-	PurpleAccount *account;
-};
-
-static guint
-_purple_conversations_hconv_hash(struct _purple_hconv *hc)
-{
-	return g_str_hash(hc->name) ^ hc->im ^ g_direct_hash(hc->account);
-}
-
-static guint
-_purple_conversations_hconv_equal(struct _purple_hconv *hc1, struct _purple_hconv *hc2)
-{
-	return (hc1->im == hc2->im &&
-	        hc1->account == hc2->account &&
-	        g_str_equal(hc1->name, hc2->name));
-}
-
-static void
-_purple_conversations_hconv_free_key(struct _purple_hconv *hc)
-{
-	g_free(hc->name);
-	g_free(hc);
-}
-
 void
-purple_conversations_add(PurpleConversation *conv)
-{
-	PurpleAccount *account;
-	struct _purple_hconv *hc;
+purple_conversations_add(PurpleConversation *conv) {
+	PurpleConversationManager *manager = NULL;
 
 	g_return_if_fail(conv != NULL);
 
-	if (g_list_find(conversations, conv) != NULL)
-		return;
-
-	conversations = g_list_prepend(conversations, conv);
-
-	if (PURPLE_IS_IM_CONVERSATION(conv))
-		ims = g_list_prepend(ims, conv);
-	else
-		chats = g_list_prepend(chats, conv);
+	manager = purple_conversation_manager_get_default();
 
-	account = purple_conversation_get_account(conv);
-
-	hc = g_new(struct _purple_hconv, 1);
-	hc->name = g_strdup(purple_normalize(account,
-				purple_conversation_get_name(conv)));
-	hc->account = account;
-	hc->im = PURPLE_IS_IM_CONVERSATION(conv);
-
-	g_hash_table_insert(conversation_cache, hc, conv);
+	purple_conversation_manager_register(manager, conv);
 }
 
 void
-purple_conversations_remove(PurpleConversation *conv)
-{
-	PurpleAccount *account;
-	struct _purple_hconv hc;
+purple_conversations_remove(PurpleConversation *conv) {
+	PurpleConversationManager *manager = NULL;
 
 	g_return_if_fail(conv != NULL);
 
-	conversations = g_list_remove(conversations, conv);
-
-	if (PURPLE_IS_IM_CONVERSATION(conv))
-		ims = g_list_remove(ims, conv);
-	else
-		chats = g_list_remove(chats, conv);
-
-	account = purple_conversation_get_account(conv);
-
-	hc.name = (gchar *)purple_normalize(account,
-				purple_conversation_get_name(conv));
-	hc.account = account;
-	hc.im = PURPLE_IS_IM_CONVERSATION(conv);
-
-	g_hash_table_remove(conversation_cache, &hc);
-}
+	manager = purple_conversation_manager_get_default();
 
-void
-_purple_conversations_update_cache(PurpleConversation *conv, const char *name,
-		PurpleAccount *account)
-{
-	PurpleAccount *old_account;
-	struct _purple_hconv *hc;
-
-	g_return_if_fail(conv != NULL);
-	g_return_if_fail(account != NULL || name != NULL);
-
-	old_account = purple_conversation_get_account(conv);
-
-	hc = g_new(struct _purple_hconv, 1);
-	hc->im = PURPLE_IS_IM_CONVERSATION(conv);
-	hc->account = old_account;
-	hc->name = (gchar *)purple_normalize(old_account,
-				purple_conversation_get_name(conv));
-
-	g_hash_table_remove(conversation_cache, hc);
-
-	if (account)
-		hc->account = account;
-	if (name)
-		hc->name = g_strdup(purple_normalize(hc->account, name));
-
-	g_hash_table_insert(conversation_cache, hc, conv);
+	purple_conversation_manager_unregister(manager, conv);
 }
 
 GList *
-purple_conversations_get_all(void)
-{
-	return conversations;
-}
+purple_conversations_get_all(void) {
+	PurpleConversationManager *manager = NULL;
 
-GList *
-purple_conversations_get_ims(void)
-{
-	return ims;
-}
+	manager = purple_conversation_manager_get_default();
 
-GList *
-purple_conversations_get_chats(void)
-{
-	return chats;
+	return purple_conversation_manager_get_all(manager);
 }
 
 PurpleConversation *
 purple_conversations_find_with_account(const char *name,
-		PurpleAccount *account)
+                                       PurpleAccount *account)
 {
-	PurpleConversation *c = NULL;
-	struct _purple_hconv hc;
+	PurpleConversationManager *manager = NULL;
 
 	g_return_val_if_fail(name != NULL, NULL);
 
-	hc.name = (gchar *)purple_normalize(account, name);
-	hc.account = account;
+	manager = purple_conversation_manager_get_default();
 
-	hc.im = TRUE;
-	c = g_hash_table_lookup(conversation_cache, &hc);
-	if (!c) {
-		hc.im = FALSE;
-		c = g_hash_table_lookup(conversation_cache, &hc);
-	}
-
-	return c;
+	return purple_conversation_manager_find(manager, account, name);
 }
 
 PurpleConversation *
-purple_conversations_find_im_with_account(const char *name,
-		PurpleAccount *account)
+purple_conversations_find_im_with_account(const gchar *name,
+                                          PurpleAccount *account)
 {
-	PurpleConversation *im = NULL;
-	struct _purple_hconv hc;
+	PurpleConversationManager *manager = NULL;
 
 	g_return_val_if_fail(name != NULL, NULL);
 
-	hc.name = (gchar *)purple_normalize(account, name);
-	hc.account = account;
-	hc.im = TRUE;
+	manager = purple_conversation_manager_get_default();
 
-	im = g_hash_table_lookup(conversation_cache, &hc);
-
-	return im;
+	return purple_conversation_manager_find_im(manager, account, name);
 }
 
 PurpleConversation *
-purple_conversations_find_chat_with_account(const char *name,
-		PurpleAccount *account)
+purple_conversations_find_chat_with_account(const gchar *name,
+                                            PurpleAccount *account)
 {
-	PurpleConversation *c = NULL;
-	struct _purple_hconv hc;
+	PurpleConversationManager *manager = NULL;
 
 	g_return_val_if_fail(name != NULL, NULL);
 
-	hc.name = (gchar *)purple_normalize(account, name);
-	hc.account = account;
-	hc.im = FALSE;
+	manager = purple_conversation_manager_get_default();
 
-	c = g_hash_table_lookup(conversation_cache, &hc);
-
-	return c;
+	return purple_conversation_manager_find_chat(manager, account, name);
 }
 
 PurpleConversation *
-purple_conversations_find_chat(const PurpleConnection *gc, int id)
-{
-	GList *l;
-	PurpleConversation *chat;
+purple_conversations_find_chat(PurpleConnection *gc, gint id) {
+	PurpleAccount *account = NULL;
+	PurpleConversationManager *manager = NULL;
 
-	for (l = purple_conversations_get_chats(); l != NULL; l = l->next) {
-		chat = (PurpleConversation *)l->data;
+	g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
 
-		if (purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)) == id &&
-				purple_conversation_get_connection(chat) == gc)
-			return chat;
-	}
+	account = purple_connection_get_account(gc);
+	manager = purple_conversation_manager_get_default();
 
-	return NULL;
+	return purple_conversation_manager_find_chat_by_id(manager, account, id);
 }
 
 void
@@ -262,10 +134,6 @@
 {
 	void *handle = purple_conversations_get_handle();
 
-	conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
-						(GEqualFunc)_purple_conversations_hconv_equal,
-						(GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
-
 	/**********************************************************************
 	 * Register preferences
 	 **********************************************************************/
@@ -458,9 +326,5 @@
 void
 purple_conversations_uninit(void)
 {
-	while (conversations)
-		g_object_unref(G_OBJECT(conversations->data));
-
-	g_hash_table_destroy(conversation_cache);
 	purple_signals_unregister_by_instance(purple_conversations_get_handle());
 }
diff --git a/libpurple/core.c b/libpurple/core.c
--- a/libpurple/core.c
+++ b/libpurple/core.c
@@ -172,6 +172,7 @@
 	purple_savedstatuses_init();
 	purple_notify_init();
 	purple_conversations_init();
+	purple_conversation_manager_startup();
 	purple_blist_init();
 	purple_log_init();
 	purple_network_init();
@@ -226,6 +227,7 @@
 	_purple_smiley_custom_uninit();
 	_purple_smiley_parser_uninit();
 	purple_idle_uninit();
+	purple_conversation_manager_shutdown();
 	purple_conversations_uninit();
 	purple_blist_uninit();
 	purple_notify_uninit();
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -48,6 +48,7 @@
 	'purplechatconversation.c',
 	'purplechatuser.c',
 	'purpleconversation.c',
+	'purpleconversationmanager.c',
 	'purpleconversationuiops.c',
 	'purplecredentialmanager.c',
 	'purplecredentialprovider.c',
@@ -139,6 +140,7 @@
 	'purplechatconversation.h',
 	'purplechatuser.h',
 	'purpleconversation.h',
+	'purpleconversationmanager.h',
 	'purpleconversationuiops.h',
 	'purplecredentialmanager.h',
 	'purplecredentialprovider.h',
diff --git a/libpurple/protocols/null/nullprpl.c b/libpurple/protocols/null/nullprpl.c
--- a/libpurple/protocols/null/nullprpl.c
+++ b/libpurple/protocols/null/nullprpl.c
@@ -1036,11 +1036,19 @@
 
   /* add each chat room. the chat ids are cached in seen_ids so that each room
    * is only returned once, even if multiple users are in it. */
-  for (chats  = purple_conversations_get_chats(); chats; chats = g_list_next(chats)) {
-    PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(chats->data);
-    PurpleRoomlistRoom *room;
-    const char *name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
-    int id = purple_chat_conversation_get_id(chat);
+  for (chats  = purple_conversations_get_all(); chats; chats = g_list_next(chats)) {
+    PurpleChatConversation *chat = NULL;
+    PurpleRoomlistRoom *room = NULL;
+    const gchar *name = NULL;
+    gint id = 0;
+
+    if(!PURPLE_IS_CHAT_CONVERSATION(chats->data)) {
+      continue;
+    }
+
+    chat = PURPLE_CHAT_CONVERSATION(chats->data);
+    name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
+    id = purple_chat_conversation_get_id(chat);
 
     /* have we already added this room? */
     if (g_list_find_custom(seen_ids, name, (GCompareFunc)strcmp))
diff --git a/libpurple/purpleconversation.c b/libpurple/purpleconversation.c
--- a/libpurple/purpleconversation.c
+++ b/libpurple/purpleconversation.c
@@ -483,8 +483,6 @@
 	priv = purple_conversation_get_instance_private(conv);
 
 	if(g_set_object(&priv->account, account)) {
-		_purple_conversations_update_cache(conv, NULL, account);
-
 		g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_ACCOUNT]);
 
 		purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ACCOUNT);
@@ -585,8 +583,6 @@
 	g_free(priv->name);
 	priv->name = g_strdup(name);
 
-	_purple_conversations_update_cache(conv, name, NULL);
-
 	g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_NAME]);
 
 	purple_conversation_autoset_title(conv);
diff --git a/libpurple/purpleconversationmanager.h b/libpurple/purpleconversationmanager.h
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleconversationmanager.h
@@ -0,0 +1,177 @@
+/*
+ * 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/>.
+ */
+
+#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <purple.h> may be included directly"
+#endif
+
+#ifndef PURPLE_CONVERSATION_MANAGER_H
+#define PURPLE_CONVERSATION_MANAGER_H
+
+#include <glib.h>
+
+#include <purpleconversation.h>
+
+/**
+ * SECTION:purpleconversationmanager
+ * @section_id: libpurple-purpleconversationmanager
+ * @title: Management of conversations
+ *
+ * #PurpleConversationManager keeps track of all #PurpleConversation's inside of
+ * libpurple and allows searching of them.
+ */
+
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_CONVERSATION_MANAGER (purple_conversation_manager_get_type())
+G_DECLARE_FINAL_TYPE(PurpleConversationManager, purple_conversation_manager,
+                     PURPLE, CONVERSATION_MANAGER, GObject)
+
+/**
+ * purple_conversation_manager_get_default:
+ *
+ * Gets the default instance of #PurpleConversationManager.  This instance
+ * can be used for any of the API including connecting to signals.
+ *
+ * Returns: (transfer none): The default #PurpleConversationManager instance.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversationManager *purple_conversation_manager_get_default(void);
+
+/**
+ * purple_conversation_manager_register:
+ * @manager: The #PurpleConversationManager instance.
+ * @conversation: The #PurpleConversation to register.
+ *
+ * Registers @conversation with @manager.
+ *
+ * Returns: %TRUE if @conversation was not yet registered.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_manager_register(PurpleConversationManager *manager, PurpleConversation *conversation);
+
+/**
+ * purple_conversation_manager_unregister:
+ * @manager: The #PurpleConversationManager instance.
+ * @conversation: The #PurpleConversation to unregister.
+ *
+ * Unregisters @conversation with @manager.
+ *
+ * Returns: %TRUE if @conversation was found and unregistered.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_manager_unregister(PurpleConversationManager *manager, PurpleConversation *conversation);
+
+/**
+ * purple_conversation_manager_is_registered:
+ * @manager: The #PurpleConversationManager instance.
+ * @conversation: The #PurpleConversation instance.
+ *
+ * Checks if @conversation is registered with @manager.
+ *
+ * Returns: %TRUE if @conversation is registered with @manager, %FALSE
+ *          otherwise.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_manager_is_registered(PurpleConversationManager *manager, PurpleConversation *conversation);
+
+/**
+ * purple_conversation_manager_get_all:
+ * @manager: The #PurpleConversationManager instance.
+ *
+ * Gets a list of all conversations that are registered with @manager.
+ *
+ * Returns: (transfer container) (element-type PurpleConversation): A list of
+ *          all of the registered conversations.
+ *
+ * Since: 3.0.0
+ */
+GList *purple_conversation_manager_get_all(PurpleConversationManager *manager);
+
+/**
+ * purple_conversation_manager_find:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @name: The name of the conversation.
+ *
+ * Looks for a registered conversation belonging to @account and named @named.
+ * This function will return the first one matching the given criteria. If you
+ * specifically need an im or chat see purple_conversation_manager_find_im()
+ * or purple_conversation_manager_find_chat().
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find(PurpleConversationManager *manager, PurpleAccount *account, const gchar *name);
+
+/**
+ * purple_conversation_manager_find_im:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @name: The name of the conversation.
+ *
+ * Looks for a registered im conversation belonging to @account and named
+ * @name.
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find_im(PurpleConversationManager *manager, PurpleAccount *account, const gchar *name);
+
+/**
+ * purple_conversation_manager_find_chat:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @name: The name of the conversation.
+ *
+ * Looks for a registered chat conversation belonging to @account and named
+ * @name.
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find_chat(PurpleConversationManager *manager, PurpleAccount *account, const gchar *name);
+
+/**
+ * purple_conversation_manager_find_chat_by_id:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @id: The id of the conversation.
+ *
+ * Looks for a registered chat conversation belonging to @account with an id of
+ * @id.
+ *
+ * This is typically only called by protocols.
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find_chat_by_id(PurpleConversationManager *manager, PurpleAccount *account, gint id);
+
+G_END_DECLS
+
+#endif /* PURPLE_CONVERSATION_MANAGER_H */
diff --git a/libpurple/purpleconversationmanager.c b/libpurple/purpleconversationmanager.c
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleconversationmanager.c
@@ -0,0 +1,241 @@
+/*
+ * 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 <purpleconversationmanager.h>
+
+#include <purplechatconversation.h>
+#include <purpleimconversation.h>
+#include <purpleprivate.h>
+
+struct _PurpleConversationManager {
+	GObject parent;
+
+	GHashTable *conversations;
+};
+
+static PurpleConversationManager *default_manager = NULL;
+
+G_DEFINE_TYPE(PurpleConversationManager, purple_conversation_manager,
+              G_TYPE_OBJECT)
+
+typedef gboolean (*PurpleConversationManagerCompareFunc)(PurpleConversation *conversation, gpointer userdata);
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static gboolean
+purple_conversation_is_im(PurpleConversation *conversation,
+                          G_GNUC_UNUSED gpointer userdata)
+{
+	return PURPLE_IS_IM_CONVERSATION(conversation);
+}
+
+static gboolean
+purple_conversation_is_chat(PurpleConversation *conversation,
+                            G_GNUC_UNUSED gpointer userdata)
+{
+	return PURPLE_IS_CHAT_CONVERSATION(conversation);
+}
+
+static gboolean
+purple_conversation_chat_has_id(PurpleConversation *conversation,
+                                gpointer userdata)
+{
+	PurpleChatConversation *chat = NULL;
+	gint id = GPOINTER_TO_INT(userdata);
+
+
+	if(!PURPLE_IS_CHAT_CONVERSATION(conversation)) {
+		return FALSE;
+	}
+
+	chat = PURPLE_CHAT_CONVERSATION(conversation);
+
+	return (purple_chat_conversation_get_id(chat) == id);
+}
+
+static PurpleConversation *
+purple_conversation_manager_find_internal(PurpleConversationManager *manager,
+                                          PurpleAccount *account,
+                                          const gchar *name,
+                                          PurpleConversationManagerCompareFunc func,
+                                          gpointer userdata)
+{
+	GHashTableIter iter;
+	gpointer key;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+	g_hash_table_iter_init(&iter, manager->conversations);
+	while(g_hash_table_iter_next(&iter, &key, NULL)) {
+		PurpleConversation *conversation = PURPLE_CONVERSATION(key);
+
+		if(purple_strequal(purple_conversation_get_name(conversation), name)) {
+			if(purple_conversation_get_account(conversation) == account) {
+				if(func != NULL && !func(conversation, userdata)) {
+					continue;
+				}
+
+				return conversation;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_conversation_manager_init(PurpleConversationManager *manager) {
+	manager->conversations = g_hash_table_new_full(g_direct_hash,
+	                                               g_direct_equal,
+	                                               g_object_unref, NULL);
+}
+
+static void
+purple_conversation_manager_finalize(GObject *obj) {
+	PurpleConversationManager *manager = PURPLE_CONVERSATION_MANAGER(obj);
+
+	g_hash_table_destroy(manager->conversations);
+
+	G_OBJECT_CLASS(purple_conversation_manager_parent_class)->finalize(obj);
+}
+
+static void
+purple_conversation_manager_class_init(PurpleConversationManagerClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->finalize = purple_conversation_manager_finalize;
+}
+
+/******************************************************************************
+ * Private API
+ *****************************************************************************/
+void
+purple_conversation_manager_startup(void) {
+	if(default_manager == NULL) {
+		default_manager = g_object_new(PURPLE_TYPE_CONVERSATION_MANAGER, NULL);
+	}
+}
+
+void
+purple_conversation_manager_shutdown(void) {
+	g_clear_object(&default_manager);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleConversationManager *
+purple_conversation_manager_get_default(void) {
+	return default_manager;
+}
+
+gboolean
+purple_conversation_manager_register(PurpleConversationManager *manager,
+                                     PurpleConversation *conversation)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE);
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return g_hash_table_insert(manager->conversations,
+	                           g_object_ref(conversation), NULL);
+}
+
+gboolean
+purple_conversation_manager_unregister(PurpleConversationManager *manager,
+                                       PurpleConversation *conversation)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE);
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return g_hash_table_remove(manager->conversations, conversation);
+}
+
+gboolean
+purple_conversation_manager_is_registered(PurpleConversationManager *manager,
+                                          PurpleConversation *conversation)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE);
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return g_hash_table_lookup_extended(manager->conversations, conversation,
+	                                    NULL, NULL);
+}
+
+GList *
+purple_conversation_manager_get_all(PurpleConversationManager *manager) {
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+
+	return g_hash_table_get_keys(manager->conversations);
+}
+
+
+PurpleConversation *
+purple_conversation_manager_find(PurpleConversationManager *manager,
+                                 PurpleAccount *account, const gchar *name)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	return purple_conversation_manager_find_internal(manager, account, name,
+	                                                 NULL, NULL);
+}
+
+PurpleConversation *
+purple_conversation_manager_find_im(PurpleConversationManager *manager,
+                                    PurpleAccount *account, const gchar *name)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	return purple_conversation_manager_find_internal(manager, account, name,
+	                                                 purple_conversation_is_im,
+	                                                 NULL);
+}
+
+PurpleConversation *
+purple_conversation_manager_find_chat(PurpleConversationManager *manager,
+                                      PurpleAccount *account,
+                                      const gchar *name)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	return purple_conversation_manager_find_internal(manager, account, name,
+	                                                 purple_conversation_is_chat,
+	                                                 NULL);
+}
+
+PurpleConversation *
+purple_conversation_manager_find_chat_by_id(PurpleConversationManager *manager,
+                                            PurpleAccount *account, gint id)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+	return purple_conversation_manager_find_internal(manager, account, NULL,
+	                                                 purple_conversation_chat_has_id,
+	                                                 GINT_TO_POINTER(id));
+}
diff --git a/libpurple/purpleprivate.h b/libpurple/purpleprivate.h
--- a/libpurple/purpleprivate.h
+++ b/libpurple/purpleprivate.h
@@ -153,22 +153,6 @@
                                            PurpleChatConversation *chat);
 
 /**
- * _purple_conversations_update_cache:
- * @conv:    The conversation.
- * @name:    The new name. If no change, use %NULL.
- * @account: The new account. If no change, use %NULL.
- *
- * Updates the conversation cache to use a new conversation name and/or
- * account. This function only updates the conversation cache. It is the
- * caller's responsibility to actually update the conversation.
- *
- * Note: This function should only be called by purple_conversation_set_name()
- *       and purple_conversation_set_account() in conversation.c.
- */
-void _purple_conversations_update_cache(PurpleConversation *conv,
-		const char *name, PurpleAccount *account);
-
-/**
  * _purple_statuses_get_primitive_scores:
  *
  * Note: This function should only be called by
@@ -197,6 +181,24 @@
 _purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *msg);
 
 /**
+ * purple_conversation_manager_startup:
+ *
+ * Starts up the conversation manager by creating the default instance.
+ *
+ * Since: 3.0.0
+ */
+void purple_conversation_manager_startup(void);
+
+/**
+ * purple_conversation_manager_shutdown:
+ *
+ * Shuts down the conversation manager by destroying the default instance.
+ *
+ * Since: 3.0.0
+ */
+void purple_conversation_manager_shutdown(void);
+
+/**
  * purple_credential_manager_startup:
  *
  * Starts up the credential manager by creating the default instance.
diff --git a/pidgin/gtkblist.h b/pidgin/gtkblist.h
--- a/pidgin/gtkblist.h
+++ b/pidgin/gtkblist.h
@@ -55,10 +55,8 @@
  *                     into.  Your plugin might want to pack something in it
  *                     itself.  Go, plugins!
  * @treeview:          It's a treeview... d'uh.
- * @treemodel:         This is the treemodel. 
+ * @treemodel:         This is the treemodel.
  * @text_column:       Column
- * @menutray:          The menu tray widget.
- * @menutrayicon:      The menu tray icon.
  * @refresh_timer:     The timer for refreshing every 30 seconds
  * @drag_timeout:      The timeout for expanding contacts on drags
  * @drag_rect:         This is the bounding rectangle of the cell we're
@@ -97,8 +95,6 @@
 	GtkCellRenderer *text_rend;
 
 	GtkWidget *menu;
-	GtkWidget *menutray;
-	GtkWidget *menutrayicon;
 
 	guint refresh_timer;
 
diff --git a/pidgin/gtkblist.c b/pidgin/gtkblist.c
--- a/pidgin/gtkblist.c
+++ b/pidgin/gtkblist.c
@@ -54,7 +54,6 @@
 #include "pidgin/pidginstylecontext.h"
 #include "pidgin/pidgintooltip.h"
 #include "pidgin/pidginwindow.h"
-#include "pidginmenutray.h"
 #include "pidginstock.h"
 
 #include <gdk/gdkkeysyms.h>
@@ -4014,70 +4013,10 @@
 }
 
 static void
-unseen_conv_menu(GdkEvent *event)
-{
-	static GtkWidget *menu = NULL;
-	GList *convs = NULL;
-	GList *chats, *ims;
-
-	if (menu) {
-		gtk_widget_destroy(menu);
-		menu = NULL;
-	}
-
-	ims = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 0);
-
-	chats = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 0);
-
-	if(ims && chats)
-		convs = g_list_concat(ims, chats);
-	else if(ims && !chats)
-		convs = ims;
-	else if(!ims && chats)
-		convs = chats;
-
-	if (!convs)
-		/* no conversations added, don't show the menu */
-		return;
-
-	menu = gtk_menu_new();
-
-	pidgin_conversations_fill_menu(menu, convs);
-	g_list_free(convs);
-	gtk_widget_show_all(menu);
-	gtk_menu_popup_at_pointer(GTK_MENU(menu), event);
-}
-
-static gboolean
-menutray_press_cb(GtkWidget *widget, GdkEventButton *event)
-{
-	GList *convs;
-
-	if (event->button == GDK_BUTTON_PRIMARY) {
-		convs = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 1);
-		if(!convs)
-			convs = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 1);
-
-		if (convs) {
-			pidgin_conv_present_conversation((PurpleConversation*)convs->data);
-			g_list_free(convs);
-		}
-
-	} else if (gdk_event_triggers_context_menu((GdkEvent *)event)) {
-		unseen_conv_menu((GdkEvent *)event);
-	}
-
-	return TRUE;
-}
-
-static void
 conversation_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type,
                         PidginBuddyList *gtkblist)
 {
 	PurpleAccount *account = purple_conversation_get_account(conv);
-	GList *convs = NULL;
-	GList *ims, *chats;
-	GList *l = NULL;
 
 	if (type != PURPLE_CONVERSATION_UPDATE_UNSEEN)
 		return;
@@ -4087,60 +4026,6 @@
 		if(buddy != NULL)
 			pidgin_blist_update_buddy(NULL, PURPLE_BLIST_NODE(buddy), TRUE);
 	}
-
-	if (gtkblist->menutrayicon) {
-		gtk_widget_destroy(gtkblist->menutrayicon);
-		gtkblist->menutrayicon = NULL;
-	}
-
-	ims = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 0);
-
-	chats = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 0);
-
-	if(ims && chats)
-		convs = g_list_concat(ims, chats);
-	else if(ims && !chats)
-		convs = ims;
-	else if(!ims && chats)
-		convs = chats;
-
-	if (convs) {
-		GtkWidget *img = NULL;
-		GString *tooltip_text = NULL;
-
-		tooltip_text = g_string_new("");
-		l = convs;
-		while (l != NULL) {
-			int count = 0;
-			PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data);
-
-			if(gtkconv)
-				count = gtkconv->unseen_count;
-			else if(g_object_get_data(G_OBJECT(l->data), "unseen-count"))
-				count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(l->data), "unseen-count"));
-
-			g_string_append_printf(tooltip_text,
-					ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count),
-					count, purple_conversation_get_title(l->data));
-			l = l->next;
-		}
-		if(tooltip_text->len > 0) {
-			/* get rid of the last newline */
-			g_string_truncate(tooltip_text, tooltip_text->len -1);
-			img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_PENDING,
-							gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
-
-			gtkblist->menutrayicon = gtk_event_box_new();
-			gtk_container_add(GTK_CONTAINER(gtkblist->menutrayicon), img);
-			gtk_widget_show(img);
-			gtk_widget_show(gtkblist->menutrayicon);
-			g_signal_connect(G_OBJECT(gtkblist->menutrayicon), "button-press-event", G_CALLBACK(menutray_press_cb), NULL);
-
-			pidgin_menu_tray_append(PIDGIN_MENU_TRAY(gtkblist->menutray), gtkblist->menutrayicon, tooltip_text->str);
-		}
-		g_string_free(tooltip_text, TRUE);
-		g_list_free(convs);
-	}
 }
 
 static void
@@ -5160,9 +5045,6 @@
 	g_signal_connect(G_OBJECT(gtkblist->window), "key_press_event", G_CALLBACK(gtk_blist_window_key_press_cb), gtkblist);
 	gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK);
 
-	/******************************* Menu bar *************************************/
-	gtkblist->menutray = pidgin_contact_list_get_menu_tray(PIDGIN_CONTACT_LIST(gtkblist->window));
-
 	/****************************** Notebook *************************************/
 	gtkblist->notebook = gtk_notebook_new();
 	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gtkblist->notebook), FALSE);
diff --git a/pidgin/gtkconv.h b/pidgin/gtkconv.h
--- a/pidgin/gtkconv.h
+++ b/pidgin/gtkconv.h
@@ -188,48 +188,6 @@
 										guint max_count);
 
 /**
- * pidgin_conversations_get_unseen_ims:
- * @min_state:    The minimum unseen state.
- * @hidden_only:  If %TRUE, only consider hidden conversations.
- * @max_count:    Maximum number of conversations to return, or 0 for
- *                no maximum.
- *
- * Returns a list of IM conversations which have an unseen state greater
- * than or equal to the specified minimum state. Using the hidden_only
- * parameter, this search can be limited to hidden IM conversations. The
- * max_count parameter will limit the total number of IM converations
- * returned if greater than zero. The returned list should be freed by the
- * caller.
- *
- * Returns: (transfer container) (element-type PurpleConversation): List of PurpleIMConversation matching criteria, or %NULL.
- */
-GList *
-pidgin_conversations_get_unseen_ims(PidginUnseenState min_state,
-										gboolean hidden_only,
-										guint max_count);
-
-/**
- * pidgin_conversations_get_unseen_chats:
- * @min_state:    The minimum unseen state.
- * @hidden_only:  If %TRUE, only consider hidden conversations.
- * @max_count:    Maximum number of conversations to return, or 0 for
- *                no maximum.
- *
- * Returns a list of chat conversations which have an unseen state greater
- * than or equal to the specified minimum state. Using the hidden_only
- * parameter, this search can be limited to hidden chat conversations. The
- * max_count parameter will limit the total number of chat converations
- * returned if greater than zero. The returned list should be freed by the
- * caller.
- *
- * Returns: (transfer container) (element-type PurpleConversation): List of PurpleChatConversation matching criteria, or %NULL.
- */
-GList *
-pidgin_conversations_get_unseen_chats(PidginUnseenState min_state,
-										gboolean hidden_only,
-										guint max_count);
-
-/**
  * pidgin_conversations_fill_menu:
  * @menu: Menu widget to add items to.
  * @convs: (element-type PurpleConversation): List of PurpleConversation to add to menu.
diff --git a/pidgin/gtkconv.c b/pidgin/gtkconv.c
--- a/pidgin/gtkconv.c
+++ b/pidgin/gtkconv.c
@@ -1783,24 +1783,6 @@
 			min_state, hidden_only, max_count);
 }
 
-GList *
-pidgin_conversations_get_unseen_ims(PidginUnseenState min_state,
-										gboolean hidden_only,
-										guint max_count)
-{
-	return pidgin_conversations_get_unseen(purple_conversations_get_ims(),
-			min_state, hidden_only, max_count);
-}
-
-GList *
-pidgin_conversations_get_unseen_chats(PidginUnseenState min_state,
-										gboolean hidden_only,
-										guint max_count)
-{
-	return pidgin_conversations_get_unseen(purple_conversations_get_chats(),
-			min_state, hidden_only, max_count);
-}
-
 static void
 unseen_conv_menu_cb(GtkMenuItem *item, PurpleConversation *conv)
 {
@@ -4537,13 +4519,19 @@
 static void
 account_signing_off(PurpleConnection *gc)
 {
-	GList *list = purple_conversations_get_chats();
+	GList *list = purple_conversations_get_all();
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	/* We are about to sign off. See which chats we are currently in, and mark
 	 * them for rejoin on reconnect. */
-	while (list) {
-		PurpleConversation *conv = list->data;
+	while(list != NULL) {
+		PurpleConversation *conv = NULL;
+
+		if(!PURPLE_IS_CHAT_CONVERSATION(list->data)) {
+			continue;
+		}
+
+		conv = PURPLE_CONVERSATION(list->data);
 		if (!purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)) &&
 				purple_conversation_get_account(conv) == account) {
 			g_object_set_data(G_OBJECT(conv), "want-to-rejoin", GINT_TO_POINTER(TRUE));
@@ -4553,7 +4541,8 @@
 				"rejoin the chat when the account reconnects."),
 				PURPLE_MESSAGE_NO_LOG);
 		}
-		list = list->next;
+
+		list = g_list_delete_link(list, list);
 	}
 }
 
@@ -4784,12 +4773,16 @@
 		if (PURPLE_IS_IM_CONVERSATION(conv)) {
 			GList *convs;
 			list = g_list_copy(list);
-			for (convs = purple_conversations_get_ims(); convs; convs = convs->next)
+			for (convs = purple_conversations_get_all(); convs; convs = convs->next) {
+				if(!PURPLE_IS_IM_CONVERSATION(convs->data)) {
+					continue;
+				}
 				if (convs->data != conv &&
 						pidgin_conv_find_gtkconv(convs->data) == gtkconv) {
 					pidgin_conv_attach(convs->data);
 					list = g_list_concat(list, g_list_copy(purple_conversation_get_message_history(convs->data)));
 				}
+			}
 			list = g_list_sort(list, (GCompareFunc)message_compare);
 			gtkconv->attach_current = list;
 			list = g_list_last(list);
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -243,6 +243,7 @@
 libpurple/purplechatconversation.c
 libpurple/purplechatuser.c
 libpurple/purpleconversation.c
+libpurple/purpleconversationmanager.c
 libpurple/purplecredentialmanager.c
 libpurple/purplecredentialprovider.c
 libpurple/purple-gio.c
