# HG changeset patch
# Node ID 273c64ec406c
# Parent  f1bdc43bf086
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
@@ -124,6 +124,7 @@
     <xi:include href="xml/purpleprotocolmanager.xml" />
     <xi:include href="xml/purpleprotocolmedia.xml" />
     <xi:include href="xml/purpleprotocolprivacy.xml" />
+    <xi:include href="xml/purpleprotocolroomlist.xml" />
     <xi:include href="xml/purpleprotocolserver.xml" />
   </part>
 
diff --git a/finch/gntroomlist.c b/finch/gntroomlist.c
--- a/finch/gntroomlist.c
+++ b/finch/gntroomlist.c
@@ -95,7 +95,7 @@
 	protocol = purple_connection_get_protocol(gc);
 
 	if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, room_serialize)) {
-		name = purple_protocol_roomlist_iface_room_serialize(protocol, room);
+		name = purple_protocol_roomlist_room_serialize(PURPLE_PROTOCOL_ROOMLIST(protocol), room);
 	} else {
 		name = g_strdup(purple_roomlist_room_get_name(room));
 	}
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -69,6 +69,7 @@
 	'purpleprotocolmanager.c',
 	'purpleprotocolmedia.c',
 	'purpleprotocolprivacy.c',
+	'purpleprotocolroomlist.c',
 	'purpleprotocolserver.c',
 	'purpleuiinfo.c',
 	'purplewhiteboard.c',
@@ -164,6 +165,7 @@
 	'purpleprotocolmedia.h',
 	'purpleprotocolmanager.h',
 	'purpleprotocolprivacy.h',
+	'purpleprotocolroomlist.h',
 	'purpleprotocolserver.h',
 	'purpleuiinfo.h',
 	'purplewhiteboard.h',
diff --git a/libpurple/protocols/facebook/facebook.c b/libpurple/protocols/facebook/facebook.c
--- a/libpurple/protocols/facebook/facebook.c
+++ b/libpurple/protocols/facebook/facebook.c
@@ -1468,7 +1468,8 @@
 }
 
 static PurpleRoomlist *
-fb_roomlist_get_list(PurpleConnection *gc)
+fb_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
+                     PurpleConnection *gc)
 {
 	FbApi *api;
 	FbData *fata;
@@ -1503,7 +1504,8 @@
 }
 
 static void
-fb_roomlist_cancel(PurpleRoomlist *list)
+fb_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist,
+                   PurpleRoomlist *list)
 {
 	FbData *fata;
 	PurpleAccount *acct;
diff --git a/libpurple/protocols/gg/chat.h b/libpurple/protocols/gg/chat.h
--- a/libpurple/protocols/gg/chat.h
+++ b/libpurple/protocols/gg/chat.h
@@ -54,6 +54,6 @@
 void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id,
 	const char *message, time_t time, uin_t who);
 
-PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleConnection *gc);
+PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist, PurpleConnection *gc);
 
 #endif /* PURPLE_GG_CHAT_H */
diff --git a/libpurple/protocols/gg/chat.c b/libpurple/protocols/gg/chat.c
--- a/libpurple/protocols/gg/chat.c
+++ b/libpurple/protocols/gg/chat.c
@@ -570,7 +570,9 @@
 	return FALSE;
 }
 
-PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleConnection *gc)
+PurpleRoomlist *
+ggp_chat_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
+                           PurpleConnection *gc)
 {
 	ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc);
 	PurpleRoomlist *roomlist;
diff --git a/libpurple/protocols/irc/irc.c b/libpurple/protocols/irc/irc.c
--- a/libpurple/protocols/irc/irc.c
+++ b/libpurple/protocols/irc/irc.c
@@ -982,7 +982,9 @@
 	g_free(buf);
 }
 
-static PurpleRoomlist *irc_roomlist_get_list(PurpleConnection *gc)
+static PurpleRoomlist *
+irc_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
+                      PurpleConnection *gc)
 {
 	struct irc_conn *irc;
 	GList *fields = NULL;
@@ -1014,7 +1016,9 @@
 	return irc->roomlist;
 }
 
-static void irc_roomlist_cancel(PurpleRoomlist *list)
+static void
+irc_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist,
+                    PurpleRoomlist *list)
 {
 	PurpleAccount *account = purple_roomlist_get_account(list);
 	PurpleConnection *gc = purple_account_get_connection(account);
diff --git a/libpurple/protocols/jabber/chat.h b/libpurple/protocols/jabber/chat.h
--- a/libpurple/protocols/jabber/chat.h
+++ b/libpurple/protocols/jabber/chat.h
@@ -101,13 +101,12 @@
 		const char *role, const char *why);
 gboolean jabber_chat_role_list(JabberChat *chat, const char *role);
 
-PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc);
-void jabber_roomlist_cancel(PurpleRoomlist *list);
+PurpleRoomlist *jabber_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist, PurpleConnection *gc);
+void jabber_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlist *list);
+char *jabber_roomlist_room_serialize(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlistRoom *room);
 
 void jabber_chat_disco_traffic(JabberChat *chat);
 
-char *jabber_roomlist_room_serialize(PurpleRoomlistRoom *room);
-
 gboolean jabber_chat_all_participants_have_capability(const JabberChat *chat,
 	const gchar *cap);
 guint jabber_chat_get_num_participants(const JabberChat *chat);
diff --git a/libpurple/protocols/jabber/chat.c b/libpurple/protocols/jabber/chat.c
--- a/libpurple/protocols/jabber/chat.c
+++ b/libpurple/protocols/jabber/chat.c
@@ -891,13 +891,9 @@
 	jabber_iq_send(iq);
 }
 
-char *jabber_roomlist_room_serialize(PurpleRoomlistRoom *room)
-{
-	GList *fields = purple_roomlist_room_get_fields(room);
-	return g_strdup_printf("%s@%s", (char*)fields->data, (char*)fields->next->data);
-}
-
-PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc)
+PurpleRoomlist *
+jabber_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
+                         PurpleConnection *gc)
 {
 	JabberStream *js = purple_connection_get_protocol_data(gc);
 	GList *fields = NULL;
@@ -932,7 +928,9 @@
 	return js->roomlist;
 }
 
-void jabber_roomlist_cancel(PurpleRoomlist *list)
+void
+jabber_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist,
+                       PurpleRoomlist *list)
 {
 	PurpleAccount *account;
 	PurpleConnection *gc;
@@ -950,6 +948,14 @@
 	}
 }
 
+char *
+jabber_roomlist_room_serialize(PurpleProtocolRoomlist *protocol_roomlist,
+                               PurpleRoomlistRoom *room)
+{
+	GList *fields = purple_roomlist_room_get_fields(room);
+	return g_strdup_printf("%s@%s", (char*)fields->data, (char*)fields->next->data);
+}
+
 void jabber_chat_member_free(JabberChatMember *jcm)
 {
 	g_free(jcm->handle);
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
@@ -1057,7 +1057,10 @@
   return FALSE;
 }
 
-static PurpleRoomlist *null_roomlist_get_list(PurpleConnection *gc) {
+static PurpleRoomlist *
+null_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
+                       PurpleConnection *gc)
+{
   const char *username = purple_account_get_username(purple_connection_get_account(gc));
   PurpleConversationManager *manager;
   PurpleRoomlist *roomlist = purple_roomlist_new(purple_connection_get_account(gc));
@@ -1117,14 +1120,20 @@
   return roomlist;
 }
 
-static void null_roomlist_cancel(PurpleRoomlist *list) {
+static void
+null_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist,
+                     PurpleRoomlist *list)
+{
  PurpleAccount *account = purple_roomlist_get_account(list);
  purple_debug_info("nullprpl", "%s asked to cancel room list request\n",
                    purple_account_get_username(account));
 }
 
-static void null_roomlist_expand_category(PurpleRoomlist *list,
-                                              PurpleRoomlistRoom *category) {
+static void
+null_roomlist_expand_category(PurpleProtocolRoomlist *protocol_roomlist,
+                              PurpleRoomlist *list,
+                              PurpleRoomlistRoom *category)
+{
  PurpleAccount *account = purple_roomlist_get_account(list);
  purple_debug_info("nullprpl", "%s asked to expand room list category %s\n",
                    purple_account_get_username(account),
diff --git a/libpurple/protocols/silc/chat.c b/libpurple/protocols/silc/chat.c
--- a/libpurple/protocols/silc/chat.c
+++ b/libpurple/protocols/silc/chat.c
@@ -1329,7 +1329,9 @@
 				 chu->channel->channel_name, topic, NULL);
 }
 
-PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
+PurpleRoomlist *
+silcpurple_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
+                             PurpleConnection *gc)
 {
 	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
@@ -1364,7 +1366,9 @@
 	return sg->roomlist;
 }
 
-void silcpurple_roomlist_cancel(PurpleRoomlist *list)
+void
+silcpurple_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist,
+                           PurpleRoomlist *list)
 {
 	PurpleAccount *account = purple_roomlist_get_account(list);
 	PurpleConnection *gc = purple_account_get_connection(account);
diff --git a/libpurple/protocols/silc/silcpurple.h b/libpurple/protocols/silc/silcpurple.h
--- a/libpurple/protocols/silc/silcpurple.h
+++ b/libpurple/protocols/silc/silcpurple.h
@@ -138,8 +138,8 @@
 void silcpurple_chat_leave(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id);
 int silcpurple_chat_send(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, PurpleMessage *msg);
 void silcpurple_chat_set_topic(PurpleProtocolChat *protocol_chat, PurpleConnection *gc, int id, const char *topic);
-PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc);
-void silcpurple_roomlist_cancel(PurpleRoomlist *list);
+PurpleRoomlist *silcpurple_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist, PurpleConnection *gc);
+void silcpurple_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlist *list);
 void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
 				 SilcDList channel_pubkeys);
 void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
diff --git a/libpurple/purpleprotocolroomlist.h b/libpurple/purpleprotocolroomlist.h
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleprotocolroomlist.h
@@ -0,0 +1,130 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, 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_PROTOCOL_ROOMLIST_H
+#define PURPLE_PROTOCOL_ROOMLIST_H
+
+#include <glib.h>
+
+#include "connection.h"
+#include "roomlist.h"
+#include "protocol.h"
+
+/**
+ * SECTION:protocolroomlist
+ * @section_id: libpurple-protocolroomlist
+ * @title: Room List API
+ * @short_description: Protocol Roomlist Interface
+ *
+ * #PurpleProtocolRoomlist is an interface to abstract how to handle rooms
+ * lists at the protocol level.
+ */
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_PROTOCOL_ROOMLIST (purple_protocol_roomlist_get_type())
+G_DECLARE_INTERFACE(PurpleProtocolRoomlist, purple_protocol_roomlist, PURPLE,
+                    PROTOCOL_ROOMLIST, PurpleProtocol)
+
+/**
+ * PurpleProtocolRoomlistInterface:
+ *
+ * The protocol roomlist interface.
+ *
+ * This interface provides callbacks for room listing.
+ *
+ * Since: 3.0.0
+ */
+struct _PurpleProtocolRoomlistInterface {
+	/*< private >*/
+	GTypeInterface parent_iface;
+
+	/*< public >*/
+	PurpleRoomlist *(*get_list)(PurpleProtocolRoomlist *protocol_roomlist, PurpleConnection *gc);
+
+	void (*cancel)(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlist *list);
+
+	void (*expand_category)(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlist *list, PurpleRoomlistRoom *category);
+
+	gchar *(*room_serialize)(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlistRoom *room);
+
+    /*< private >*/
+    gpointer reserved[4];
+};
+
+/**
+ * purple_protocol_roomlist_get_list:
+ * @protocol_roomlist: The #PurpleProtocolRoomlist instance.
+ * @gc: The #PurpleAccount to get the roomlist for.
+ *
+ * Gets the list of rooms for @gc.
+ *
+ * Returns: (transfer full): The roomlist for @gc.
+ *
+ * Since: 3.0.0
+ */
+PurpleRoomlist *purple_protocol_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist, PurpleConnection *gc);
+
+/**
+ * purple_protocol_roomlist_cancel:
+ * @protocol_roomlist: The #PurpleProtocolRoomlist instance.
+ * @list: The #PurpleRoomlist instance.
+ *
+ * Requesting a roomlist can take a long time. This function cancels a request
+ * that's already in progress.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlist *list);
+
+/**
+ * purple_protocol_roomlist_expand_category:
+ * @protocol_roomlist: The #PurpleProtocolRoomlist instance.
+ * @list: The #PurpleRoomlist instance.
+ * @category: The category to expand.
+ *
+ * Expands the given @category for @list.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_roomlist_expand_category(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlist *list, PurpleRoomlistRoom *category);
+
+/**
+ * purple_protocol_roomlist_room_serialize:
+ * @protocol_roomlist: The #PurpleProtocolRoomlist instance.
+ * @room: The #PurpleRoomlistRoom instance.
+ *
+ * Serializes @room into a string that will be displayed in a user interface.
+ *
+ * Returns: (transfer full): The serialized form of @room.
+ *
+ * Since: 3.0.0
+ */
+char *purple_protocol_roomlist_room_serialize(PurpleProtocolRoomlist *protocol_roomlist, PurpleRoomlistRoom *room);
+
+G_END_DECLS
+
+#endif /* PURPLE_PROTOCOL_ROOMLIST_H */
diff --git a/libpurple/purpleprotocolroomlist.c b/libpurple/purpleprotocolroomlist.c
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleprotocolroomlist.c
@@ -0,0 +1,101 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more dteails.
+ *
+ * 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 "purpleprotocolroomlist.h"
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_INTERFACE(PurpleProtocolRoomlist, purple_protocol_roomlist,
+                   PURPLE_TYPE_PROTOCOL)
+
+static void
+purple_protocol_roomlist_default_init(PurpleProtocolRoomlistInterface *iface) {
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleRoomlist *
+purple_protocol_roomlist_get_list(PurpleProtocolRoomlist *protocol_roomlist,
+                                  PurpleConnection *gc)
+{
+    PurpleProtocolRoomlistInterface *iface = NULL;
+
+    g_return_val_if_fail(PURPLE_IS_PROTOCOL_ROOMLIST(protocol_roomlist), NULL);
+    g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
+
+    iface = PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol_roomlist);
+    if(iface != NULL && iface->get_list != NULL) {
+        return iface->get_list(protocol_roomlist, gc);
+    }
+
+    return NULL;
+}
+
+void
+purple_protocol_roomlist_cancel(PurpleProtocolRoomlist *protocol_roomlist,
+                                PurpleRoomlist *list)
+{
+    PurpleProtocolRoomlistInterface *iface = NULL;
+
+    g_return_if_fail(PURPLE_IS_PROTOCOL_ROOMLIST(protocol_roomlist));
+    g_return_if_fail(PURPLE_IS_ROOMLIST(list));
+
+    iface = PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol_roomlist);
+    if(iface != NULL && iface->cancel != NULL) {
+        iface->cancel(protocol_roomlist, list);
+    }
+}
+
+void
+purple_protocol_roomlist_expand_category(PurpleProtocolRoomlist *protocol_roomlist,
+                                         PurpleRoomlist *list,
+                                         PurpleRoomlistRoom *category)
+{
+    PurpleProtocolRoomlistInterface *iface = NULL;
+
+    g_return_if_fail(PURPLE_IS_PROTOCOL_ROOMLIST(protocol_roomlist));
+    g_return_if_fail(PURPLE_IS_ROOMLIST(list));
+
+    iface = PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol_roomlist);
+    if(iface != NULL && iface->expand_category != NULL) {
+        iface->expand_category(protocol_roomlist, list, category);
+    }
+}
+
+gchar *
+purple_protocol_roomlist_room_serialize(PurpleProtocolRoomlist *protocol_roomlist,
+                                        PurpleRoomlistRoom *room)
+{
+    PurpleProtocolRoomlistInterface *iface = NULL;
+
+    g_return_val_if_fail(PURPLE_IS_PROTOCOL_ROOMLIST(protocol_roomlist), NULL);
+    g_return_val_if_fail(room != NULL, NULL);
+
+    iface = PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol_roomlist);
+    if(iface != NULL && iface->room_serialize != NULL) {
+        return iface->room_serialize(protocol_roomlist, room);
+    }
+
+    return NULL;
+}
diff --git a/libpurple/roomlist.h b/libpurple/roomlist.h
--- a/libpurple/roomlist.h
+++ b/libpurple/roomlist.h
@@ -284,108 +284,6 @@
 GList *purple_roomlist_get_fields(PurpleRoomlist *roomlist);
 
 /**************************************************************************/
-/* Protocol Roomlist Interface API                                        */
-/**************************************************************************/
-
-#define PURPLE_TYPE_PROTOCOL_ROOMLIST \
-	(purple_protocol_roomlist_iface_get_type())
-
-typedef struct _PurpleProtocolRoomlistInterface PurpleProtocolRoomlistInterface;
-
-/**
- * PurpleProtocolRoomlistInterface:
- *
- * The protocol roomlist interface.
- *
- * This interface provides callbacks for room listing.
- */
-struct _PurpleProtocolRoomlistInterface
-{
-	/*< private >*/
-	GTypeInterface parent_iface;
-
-	/*< public >*/
-	PurpleRoomlist *(*get_list)(PurpleConnection *gc);
-
-	void (*cancel)(PurpleRoomlist *list);
-
-	void (*expand_category)(PurpleRoomlist *list,
-						 PurpleRoomlistRoom *category);
-
-	/* room list serialize */
-	char *(*room_serialize)(PurpleRoomlistRoom *room);
-};
-
-#define PURPLE_IS_PROTOCOL_ROOMLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST))
-#define PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST, \
-                                                 PurpleProtocolRoomlistInterface))
-
-/**
- * purple_protocol_roomlist_iface_get_type:
- *
- * The standard _get_type function for #PurpleProtocolRoomlistInterface.
- *
- * Returns: The #GType for the protocol roomlist interface.
- *
- * Since: 3.0.0
- */
-GType purple_protocol_roomlist_iface_get_type(void);
-
-/**
- * purple_protocol_roomlist_iface_get_list:
- * @protocol: The #PurpleProtocol instance.
- * @gc: The #PurpleAccount to get the roomlist for.
- *
- * Gets the list of rooms for @gc.
- *
- * Returns: (transfer full): The roomlist for @gc.
- *
- * Since: 3.0.0
- */
-PurpleRoomlist *purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol,
-		PurpleConnection *gc);
-
-/**
- * purple_protocol_roomlist_iface_cancel:
- * @protocol: The #PurpleProtocol instance.
- * @list: The #PurpleRoomlist instance.
- *
- * Requesting a roomlist can take a long time.  This function cancels a request
- * that's already in progress.
- *
- * Since: 3.0.0
- */
-void purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol,
-		PurpleRoomlist *list);
-
-/**
- * purple_protocol_roomlist_iface_expand_category:
- * @protocol: The #PurpleProtocol instance.
- * @list: The #PurpleRoomlist instance.
- * @category: The category to expand.
- *
- * Expands the given @category for @list.
- *
- * Since: 3.0.0
- */
-void purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol,
-		PurpleRoomlist *list, PurpleRoomlistRoom *category);
-
-/**
- * purple_protocol_roomlist_iface_room_serialize:
- * @protocol: The #PurpleProtocol instance.
- * @room: The #PurpleRoomlistRoom instance.
- *
- * Serializes @room into a string that will be displayed in a user interface.
- *
- * Returns: (transfer full): The serialized form of @room.
- *
- * Since: 3.0.0
- */
-char *purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol,
-		PurpleRoomlistRoom *room);
-
-/**************************************************************************/
 /* Room API                                                               */
 /**************************************************************************/
 
diff --git a/libpurple/roomlist.c b/libpurple/roomlist.c
--- a/libpurple/roomlist.c
+++ b/libpurple/roomlist.c
@@ -28,6 +28,9 @@
 #include "roomlist.h"
 #include "server.h"
 
+/* This must be after roomlist.h otherwise you'll get an include cycle. */
+#include "purpleprotocolroomlist.h"
+
 /*
  * Private data for a room list.
  */
@@ -158,8 +161,10 @@
 
 	protocol = purple_connection_get_protocol(gc);
 
-	if(protocol)
-		return purple_protocol_roomlist_iface_get_list(protocol, gc);
+	if(PURPLE_IS_PROTOCOL_ROOMLIST(protocol)) {
+		return purple_protocol_roomlist_get_list(PURPLE_PROTOCOL_ROOMLIST(protocol),
+		                                         gc);
+	}
 
 	return NULL;
 }
@@ -180,8 +185,9 @@
 	if(gc)
 		protocol = purple_connection_get_protocol(gc);
 
-	if(protocol)
-		purple_protocol_roomlist_iface_cancel(protocol, list);
+	if(PURPLE_IS_PROTOCOL_ROOMLIST(protocol)) {
+		purple_protocol_roomlist_cancel(PURPLE_PROTOCOL_ROOMLIST(protocol), list);
+	}
 }
 
 void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category)
@@ -199,11 +205,14 @@
 	gc = purple_account_get_connection(priv->account);
 	g_return_if_fail(PURPLE_IS_CONNECTION(gc));
 
-	if(gc)
+	if(gc) {
 		protocol = purple_connection_get_protocol(gc);
+	}
 
-	if(protocol)
-		purple_protocol_roomlist_iface_expand_category(protocol, list, category);
+	if(PURPLE_IS_PROTOCOL_ROOMLIST(protocol)) {
+		purple_protocol_roomlist_expand_category(PURPLE_PROTOCOL_ROOMLIST(protocol),
+		                                         list, category);
+	}
 }
 
 GList * purple_roomlist_get_fields(PurpleRoomlist *list)
@@ -365,70 +374,6 @@
 	return list;
 }
 
-/**************************************************************************
- * Protocol Roomlist Interface API
- **************************************************************************/
-#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
-	PurpleProtocolRoomlistInterface *roomlist_iface = \
-		PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
-	if (roomlist_iface && roomlist_iface->funcname) \
-		roomlist_iface->funcname(__VA_ARGS__);
-
-#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
-	PurpleProtocolRoomlistInterface *roomlist_iface = \
-		PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
-	if (roomlist_iface && roomlist_iface->funcname) \
-		return roomlist_iface->funcname(__VA_ARGS__); \
-	else \
-		return defaultreturn;
-
-GType
-purple_protocol_roomlist_iface_get_type(void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY(type == 0)) {
-		static const GTypeInfo info = {
-			.class_size = sizeof(PurpleProtocolRoomlistInterface),
-		};
-
-		type = g_type_register_static(G_TYPE_INTERFACE,
-				"PurpleProtocolRoomlistInterface", &info, 0);
-	}
-	return type;
-}
-
-PurpleRoomlist *
-purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol,
-		PurpleConnection *gc)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_list, gc);
-}
-
-void
-purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol,
-		PurpleRoomlist *list)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, cancel, list);
-}
-
-void
-purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol,
-		PurpleRoomlist *list, PurpleRoomlistRoom *category)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, expand_category, list, category);
-}
-
-char *
-purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol,
-		PurpleRoomlistRoom *room)
-{
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, room_serialize, room);
-}
-
-#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
-#undef DEFINE_PROTOCOL_FUNC
-
 /**************************************************************************/
 /* Room API                                                               */
 /**************************************************************************/
diff --git a/pidgin/gtkroomlist.c b/pidgin/gtkroomlist.c
--- a/pidgin/gtkroomlist.c
+++ b/pidgin/gtkroomlist.c
@@ -225,13 +225,16 @@
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurpleProtocol *protocol = NULL;
 
-	if(gc != NULL)
+	if(gc != NULL) {
 		protocol = purple_connection_get_protocol(gc);
+	}
 
-	if(protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, room_serialize))
-		name = purple_protocol_roomlist_iface_room_serialize(protocol, info->room);
-	else
+	if(protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, room_serialize)) {
+		name = purple_protocol_roomlist_room_serialize(PURPLE_PROTOCOL_ROOMLIST(protocol),
+		                                               info->room);
+	} else {
 		name = g_strdup(purple_roomlist_room_get_name(info->room));
+	}
 
 	purple_blist_request_add_chat(account, NULL, NULL, name);
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -264,6 +264,7 @@
 libpurple/purpleprotocolmedia.c
 libpurple/purpleprotocolmanager.c
 libpurple/purpleprotocolprivacy.c
+libpurple/purpleprotocolroomlist.c
 libpurple/purpleprotocolserver.c
 libpurple/purpleuiinfo.c
 libpurple/purplewhiteboard.c
