diff --git a/ChangeLog.API b/ChangeLog.API
--- a/ChangeLog.API
+++ b/ChangeLog.API
@@ -501,6 +501,8 @@ version 3.0.0 (??/??/????):
 		  purple_request_field_choice_add_full instead
 		* purple_request_field_list_add
 		* purple_request_field_list_get_icons
+		* purple_roomlist_get_protocol_data and
+		  purple_roomlist_set_protocol_data
 		* PurpleSetPublicAliasFailureCallback
 		* PurpleSetPublicAliasSuccessCallback
 		* purple_socket_get_family
diff --git a/finch/gntroomlist.c b/finch/gntroomlist.c
--- a/finch/gntroomlist.c
+++ b/finch/gntroomlist.c
@@ -57,27 +57,11 @@ static void
 unset_roomlist(gpointer null)
 {
 	froomlist.window = NULL;
-	if (froomlist.roomlist) {
-		g_object_unref(froomlist.roomlist);
-		froomlist.roomlist = NULL;
-	}
+	g_clear_object(&froomlist.roomlist);
 	froomlist.account = NULL;
 	froomlist.tree = NULL;
 }
 
-static void
-update_roomlist(PurpleRoomlist *list)
-{
-	if (froomlist.roomlist == list)
-		return;
-
-	if (froomlist.roomlist)
-		g_object_unref(froomlist.roomlist);
-
-	if ((froomlist.roomlist = list) != NULL)
-		g_object_ref(list);
-}
-
 static void fl_stop(GntWidget *button, gpointer null)
 {
 	if (froomlist.roomlist &&
@@ -93,7 +77,7 @@ static void fl_get_list(GntWidget *butto
 	if (!gc)
 		return;
 
-	update_roomlist(NULL);
+	g_clear_object(&froomlist.roomlist);
 	froomlist.roomlist = purple_roomlist_get_list(gc);
 	gnt_box_give_focus_to_child(GNT_BOX(froomlist.window), froomlist.tree);
 }
@@ -214,7 +198,7 @@ roomlist_account_changed(GntWidget *widg
 	if (froomlist.roomlist) {
 		if (purple_roomlist_get_in_progress(froomlist.roomlist))
 			purple_roomlist_cancel_get_list(froomlist.roomlist);
-		update_roomlist(NULL);
+		g_clear_object(&froomlist.roomlist);
 	}
 
 	gnt_tree_remove_all(GNT_TREE(froomlist.tree));
@@ -333,11 +317,26 @@ fl_show_with_account(PurpleAccount *acco
 }
 
 static void
+fl_destroy(G_GNUC_UNUSED gpointer data, GObject *list)
+{
+	if (!froomlist.window) {
+		return;
+	}
+
+	if (G_OBJECT(froomlist.roomlist) == list) {
+		froomlist.roomlist = NULL;
+		gnt_tree_remove_all(GNT_TREE(froomlist.tree));
+		gnt_widget_draw(froomlist.tree);
+	}
+}
+
+static void
 fl_create(PurpleRoomlist *list)
 {
 	g_object_set_data(G_OBJECT(list), "finch-ui", &froomlist);
+	g_object_weak_ref(G_OBJECT(list), (GWeakNotify)fl_destroy, NULL);
 	setup_roomlist(NULL);
-	update_roomlist(list);
+	g_set_object(&froomlist.roomlist, list);
 }
 
 static void
@@ -362,27 +361,12 @@ fl_add_room(PurpleRoomlist *roomlist, Pu
 	gnt_tree_set_expanded(GNT_TREE(froomlist.tree), room, !category);
 }
 
-static void
-fl_destroy(PurpleRoomlist *list)
-{
-	if (!froomlist.window)
-		return;
-
-	if (froomlist.roomlist == list) {
-		froomlist.roomlist = NULL;
-		gnt_tree_remove_all(GNT_TREE(froomlist.tree));
-		gnt_widget_draw(froomlist.tree);
-	}
-}
-
 static PurpleRoomlistUiOps ui_ops =
 {
 	fl_show_with_account, /* void (*show_with_account)(PurpleAccount *account); **< Force the ui to pop up a dialog and get the list */
 	fl_create, /* void (*create)(PurpleRoomlist *list); **< A new list was created. */
 	fl_set_fields, /* void (*set_fields)(PurpleRoomlist *list, GList *fields); **< Sets the columns. */
 	fl_add_room, /* void (*add_room)(PurpleRoomlist *list, PurpleRoomlistRoom *room); **< Add a room to the list. */
-	NULL, /* void (*in_progress)(PurpleRoomlist *list, gboolean flag); **< Are we fetching stuff still? */
-	fl_destroy, /* void (*destroy)(PurpleRoomlist *list); **< We're destroying list. */
 
 	NULL, /* void (*_purple_reserved1)(void); */
 	NULL, /* void (*_purple_reserved2)(void); */
diff --git a/libpurple/roomlist.h b/libpurple/roomlist.h
--- a/libpurple/roomlist.h
+++ b/libpurple/roomlist.h
@@ -116,8 +116,6 @@ typedef enum
  * @create:            A new list was created.
  * @set_fields:        Sets the columns.
  * @add_room:          Add a room to the list.
- * @in_progress:       Are we fetching stuff still?
- * @destroy:           We're destroying list.
  *
  * The room list ops to be filled out by the UI.
  */
@@ -126,8 +124,6 @@ struct _PurpleRoomlistUiOps {
 	void (*create)(PurpleRoomlist *list);
 	void (*set_fields)(PurpleRoomlist *list, GList *fields);
 	void (*add_room)(PurpleRoomlist *list, PurpleRoomlistRoom *room);
-	void (*in_progress)(PurpleRoomlist *list, gboolean flag);
-	void (*destroy)(PurpleRoomlist *list);
 
 	/*< private >*/
 	void (*_purple_reserved1)(void);
@@ -287,27 +283,6 @@ void purple_roomlist_expand_category(Pur
  */
 GList *purple_roomlist_get_fields(PurpleRoomlist *roomlist);
 
-/**
- * purple_roomlist_get_protocol_data:
- * @list: The roomlist, which must not be %NULL.
- *
- * Get the protocol data associated with this room list.
- *
- * Returns: The protocol data associated with this room list.  This is a
- *         convenience field provided to the protocol -- it is not
- *         used the libpurple core.
- */
-gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list);
-
-/**
- * purple_roomlist_set_protocol_data:
- * @list: The roomlist, which must not be %NULL.
- * @proto_data: A pointer to associate with this room list.
- *
- * Set the protocol data associated with this room list.
- */
-void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data);
-
 /**************************************************************************/
 /* Protocol Roomlist Interface API                                        */
 /**************************************************************************/
diff --git a/libpurple/roomlist.c b/libpurple/roomlist.c
--- a/libpurple/roomlist.c
+++ b/libpurple/roomlist.c
@@ -28,20 +28,15 @@
 #include "roomlist.h"
 #include "server.h"
 
-typedef struct _PurpleRoomlistPrivate  PurpleRoomlistPrivate;
-
 /*
  * Private data for a room list.
  */
-struct _PurpleRoomlistPrivate {
+typedef struct {
 	PurpleAccount *account;  /* The account this list belongs to. */
 	GList *fields;           /* The fields.                       */
 	GList *rooms;            /* The list of rooms.                */
 	gboolean in_progress;    /* The listing is in progress.       */
-
-	/* TODO Remove this and use protocol-specific subclasses. */
-	gpointer proto_data;     /* Protocol private data.             */
-};
+} PurpleRoomlistPrivate;
 
 /*
  * Represents a room.
@@ -127,9 +122,6 @@ void purple_roomlist_set_in_progress(Pur
 	priv = purple_roomlist_get_instance_private(list);
 	priv->in_progress = in_progress;
 
-	if (ops && ops->in_progress)
-		ops->in_progress(list, in_progress);
-
 	g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_IN_PROGRESS]);
 }
 
@@ -224,26 +216,6 @@ GList * purple_roomlist_get_fields(Purpl
 	return priv->fields;
 }
 
-gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list)
-{
-	PurpleRoomlistPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
-
-	priv = purple_roomlist_get_instance_private(list);
-	return priv->proto_data;
-}
-
-void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data)
-{
-	PurpleRoomlistPrivate *priv = NULL;
-
-	g_return_if_fail(PURPLE_IS_ROOMLIST(list));
-
-	priv = purple_roomlist_get_instance_private(list);
-	priv->proto_data = proto_data;
-}
-
 /**************************************************************************/
 /* Room List GObject code                                                 */
 /**************************************************************************/
@@ -324,9 +296,6 @@ purple_roomlist_finalize(GObject *object
 
 	purple_debug_misc("roomlist", "destroying list %p\n", list);
 
-	if (ops && ops->destroy)
-		ops->destroy(list);
-
 	for (l = priv->rooms; l; l = l->next) {
 		PurpleRoomlistRoom *r = l->data;
 		purple_roomlist_room_destroy(list, r);
diff --git a/pidgin/gtkroomlist.c b/pidgin/gtkroomlist.c
--- a/pidgin/gtkroomlist.c
+++ b/pidgin/gtkroomlist.c
@@ -73,8 +73,6 @@ enum {
 	NUM_OF_COLUMNS,
 };
 
-static GList *roomlists = NULL;
-
 static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
 {
 	PidginRoomlistDialog *dialog = PIDGIN_ROOMLIST_DIALOG(w);
@@ -220,7 +218,8 @@ selection_changed_cb(GtkTreeSelection *s
 	}
 }
 
-static void do_add_room_cb(GtkWidget *w, struct _menu_cb_info *info)
+static void
+do_add_room_cb(G_GNUC_UNUSED GtkWidget *w, struct _menu_cb_info *info)
 {
 	char *name;
 	PurpleAccount *account = purple_roomlist_get_account(info->list);
@@ -240,33 +239,30 @@ static void do_add_room_cb(GtkWidget *w,
 	g_free(name);
 }
 
-static void add_room_to_blist_cb(GtkButton *button, PidginRoomlistDialog *dialog)
+static void
+add_room_to_blist_cb(GtkButton *button, G_GNUC_UNUSED gpointer data)
 {
-	PurpleRoomlist *rl = dialog->roomlist;
-	PidginRoomlist *grl = NULL;
 	struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info");
 
-	grl = g_object_get_data(G_OBJECT(rl), PIDGIN_ROOMLIST_UI_DATA);
-
-	if(info != NULL)
-		do_add_room_cb(grl->tree, info);
+	if(info != NULL) {
+		do_add_room_cb(NULL, info);
+	}
 }
 
-static void do_join_cb(GtkWidget *w, struct _menu_cb_info *info)
+static void
+do_join_cb(G_GNUC_UNUSED GtkWidget *w, struct _menu_cb_info *info)
 {
 	purple_roomlist_room_join(info->list, info->room);
 }
 
-static void join_button_cb(GtkButton *button, PidginRoomlistDialog *dialog)
+static void
+join_button_cb(GtkButton *button, G_GNUC_UNUSED gpointer data)
 {
-	PurpleRoomlist *rl = dialog->roomlist;
-	PidginRoomlist *grl = NULL;
 	struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info");
 
-	grl = g_object_get_data(G_OBJECT(rl), PIDGIN_ROOMLIST_UI_DATA);
-
-	if(info != NULL)
-		do_join_cb(grl->tree, info);
+	if(info != NULL) {
+		do_join_cb(NULL, info);
+	}
 }
 
 static void row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *arg2,
@@ -290,7 +286,7 @@ static void row_activated_cb(GtkTreeView
 	info.list = list;
 	info.room = room;
 
-	do_join_cb(GTK_WIDGET(tv), &info);
+	do_join_cb(NULL, &info);
 }
 
 static gboolean room_click_cb(GtkWidget *tv, GdkEventButton *event, PurpleRoomlist *list)
@@ -578,17 +574,6 @@ void pidgin_roomlist_dialog_show(void)
 	pidgin_roomlist_dialog_new_with_account(NULL);
 }
 
-static void pidgin_roomlist_new(PurpleRoomlist *list)
-{
-	PidginRoomlist *rl = g_new0(PidginRoomlist, 1);
-
-	g_object_set_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA, rl);
-
-	rl->cats = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free);
-
-	roomlists = g_list_append(roomlists, list);
-}
-
 static void int_cell_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer,
                                    GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
 {
@@ -836,42 +821,50 @@ static void pidgin_roomlist_add_room(Pur
 	}
 }
 
-static void pidgin_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progress)
+static void
+pidgin_roomlist_in_progress(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec,
+                            gpointer data)
 {
-	PidginRoomlist *rl = NULL;
+	PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
+	PidginRoomlist *rl = data;
 
-	rl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA);
-	if (!rl || !rl->dialog)
-		return;
-
-	if (in_progress) {
-		if (rl->dialog->account_widget)
+	if (purple_roomlist_get_in_progress(list)) {
+		if (rl->dialog->account_widget) {
 			gtk_widget_set_sensitive(rl->dialog->account_widget, FALSE);
+		}
 		gtk_widget_set_sensitive(rl->dialog->stop_button, TRUE);
 		gtk_widget_set_sensitive(rl->dialog->list_button, FALSE);
 	} else {
 		rl->dialog->pg_needs_pulse = FALSE;
 		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(rl->dialog->progress), 0.0);
-		if (rl->dialog->account_widget)
+		if (rl->dialog->account_widget) {
 			gtk_widget_set_sensitive(rl->dialog->account_widget, TRUE);
+		}
 		gtk_widget_set_sensitive(rl->dialog->stop_button, FALSE);
 		gtk_widget_set_sensitive(rl->dialog->list_button, TRUE);
 	}
 }
 
-static void pidgin_roomlist_destroy(PurpleRoomlist *list)
+static void
+pidgin_roomlist_destroy(PidginRoomlist *rl)
 {
-	PidginRoomlist *rl = NULL;
-
-	rl = g_object_get_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA);
-
-	roomlists = g_list_remove(roomlists, list);
-
-	g_return_if_fail(rl != NULL);
-
 	g_hash_table_destroy(rl->cats);
 	g_free(rl);
-	g_object_set_data(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA, NULL);
+}
+
+static void
+pidgin_roomlist_new(PurpleRoomlist *list)
+{
+	PidginRoomlist *rl = g_new0(PidginRoomlist, 1);
+
+	g_object_set_data_full(G_OBJECT(list), PIDGIN_ROOMLIST_UI_DATA, rl,
+	                       (GDestroyNotify)pidgin_roomlist_destroy);
+
+	rl->cats = g_hash_table_new_full(
+	        NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free);
+
+	g_signal_connect(list, "notify::in-progress",
+	                 G_CALLBACK(pidgin_roomlist_in_progress), rl);
 }
 
 static PurpleRoomlistUiOps ops = {
@@ -879,8 +872,6 @@ static PurpleRoomlistUiOps ops = {
 	pidgin_roomlist_new,
 	pidgin_roomlist_set_fields,
 	pidgin_roomlist_add_room,
-	pidgin_roomlist_in_progress,
-	pidgin_roomlist_destroy,
 	NULL,
 	NULL,
 	NULL,
