[Telepathy-commits] [telepathy-salut/master] Split SalutMucManager to SalutMucManager and SalutRoomlistManager

Alban Crequy alban.crequy at collabora.co.uk
Thu Nov 27 10:07:57 PST 2008


---
 src/Makefile.am                    |    4 +
 src/salut-avahi-muc-manager.c      |  245 ---------------
 src/salut-avahi-roomlist-manager.c |  383 +++++++++++++++++++++++
 src/salut-avahi-roomlist-manager.h |   71 +++++
 src/salut-connection.c             |   25 ++-
 src/salut-muc-manager.c            |  592 +++++++++++++++++++----------------
 src/salut-muc-manager.h            |   19 --
 src/salut-roomlist-manager.c       |  440 ++++++++++++++++++++++++++
 src/salut-roomlist-manager.h       |   99 ++++++
 9 files changed, 1342 insertions(+), 536 deletions(-)
 create mode 100644 src/salut-avahi-roomlist-manager.c
 create mode 100644 src/salut-avahi-roomlist-manager.h
 create mode 100644 src/salut-roomlist-manager.c
 create mode 100644 src/salut-roomlist-manager.h

diff --git a/src/Makefile.am b/src/Makefile.am
index f094b18..34d88d5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,6 +25,8 @@ CORE_SOURCES =                                          \
     salut-im-channel.h                                  \
     salut-muc-manager.c                                 \
     salut-muc-manager.h                                 \
+    salut-roomlist-manager.c                            \
+    salut-roomlist-manager.h                            \
     salut-muc-channel.c                                 \
     salut-ft-manager.c                                  \
     salut-ft-manager.h                                  \
@@ -77,6 +79,8 @@ AVAHI_BACKEND_SOURCES = 				\
     salut-avahi-discovery-client.c                      \
     salut-avahi-muc-manager.h                           \
     salut-avahi-muc-manager.c                           \
+    salut-avahi-roomlist-manager.h                      \
+    salut-avahi-roomlist-manager.c                      \
     salut-avahi-muc-channel.h                           \
     salut-avahi-muc-channel.c                           \
     salut-avahi-contact-manager.h                       \
diff --git a/src/salut-avahi-muc-manager.c b/src/salut-avahi-muc-manager.c
index 02d06bf..8c64d14 100644
--- a/src/salut-avahi-muc-manager.c
+++ b/src/salut-avahi-muc-manager.c
@@ -60,10 +60,6 @@ typedef struct _SalutAvahiMucManagerPrivate SalutAvahiMucManagerPrivate;
 struct _SalutAvahiMucManagerPrivate
 {
   SalutAvahiDiscoveryClient *discovery_client;
-  GaServiceBrowser *browser;
-
-  /* room name => (GaServiceResolver *) */
-  GHashTable *room_resolvers;
 
   gboolean dispose_has_run;
 };
@@ -72,28 +68,12 @@ struct _SalutAvahiMucManagerPrivate
     ((SalutAvahiMucManagerPrivate *) ((SalutAvahiMucManager *)obj)->priv)
 
 static void
-room_resolver_removed (gpointer data)
-{
-  GArray *arr = (GArray *) data;
-  int i;
-  for (i = 0; i < arr->len; i++)
-    {
-      g_object_unref (g_array_index (arr, GObject *, i));
-    }
-  g_array_free (arr, TRUE);
-}
-
-static void
 salut_avahi_muc_manager_init (SalutAvahiMucManager *self)
 {
   SalutAvahiMucManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
       SALUT_TYPE_AVAHI_MUC_MANAGER, SalutAvahiMucManagerPrivate);
 
   self->priv = priv;
-  priv->browser = ga_service_browser_new (SALUT_DNSSD_CLIQUE);
-
-  priv->room_resolvers = g_hash_table_new_full (g_str_hash, g_str_equal,
-      g_free, room_resolver_removed);
 }
 
 static void
@@ -139,215 +119,6 @@ salut_avahi_muc_manager_set_property (GObject *object,
 
 static void salut_avahi_muc_manager_dispose (GObject *object);
 
-static void
-browser_found (GaServiceBrowser *browser,
-               AvahiIfIndex interface,
-               AvahiProtocol protocol,
-               const char *name,
-               const char *type,
-               const char *domain,
-               GaLookupResultFlags flags,
-               SalutAvahiMucManager *self)
-{
-  SalutAvahiMucManagerPrivate *priv = SALUT_AVAHI_MUC_MANAGER_GET_PRIVATE (self);
-  GArray *arr;
-  GaServiceResolver *resolver;
-  GError *error = NULL;
-
-  DEBUG ("found room: %s.%s.%s", name, type, domain);
-  resolver = ga_service_resolver_new (interface, protocol,
-      name, type, domain, protocol, 0);
-
-  if (!ga_service_resolver_attach (resolver,
-        priv->discovery_client->avahi_client, &error))
-    {
-      DEBUG ("resolver attach failed: %s", error->message);
-      g_object_unref (resolver);
-      g_error_free (error);
-      return;
-    }
-
-  arr = g_hash_table_lookup (priv->room_resolvers, name);
-  if (arr == NULL)
-    {
-      arr = g_array_new (FALSE, FALSE, sizeof (GObject *));
-      g_hash_table_insert (priv->room_resolvers, g_strdup (name), arr);
-      salut_muc_manager_room_discovered (SALUT_MUC_MANAGER (self),
-          name);
-    }
-  g_array_append_val (arr, resolver);
-}
-
-static void
-browser_removed (GaServiceBrowser *browser,
-                 AvahiIfIndex interface,
-                 AvahiProtocol protocol,
-                 const char *name,
-                 const char *type,
-                 const char *domain,
-                 GaLookupResultFlags flags,
-                 SalutAvahiMucManager *self)
-{
-  SalutAvahiMucManagerPrivate *priv = SALUT_AVAHI_MUC_MANAGER_GET_PRIVATE (self);
-  GArray *arr;
-  int i;
-
-  arr = g_hash_table_lookup (priv->room_resolvers, name);
-
-  if (arr == NULL) {
-    DEBUG ("Browser removed for %s, but didn't have any resolvers", name);
-    return;
-  }
-
-  for (i = 0; i < arr->len; i++)
-    {
-      GaServiceResolver *resolver;
-      AvahiIfIndex r_interface;
-      AvahiProtocol r_protocol;
-      gchar *r_name;
-      gchar *r_type;
-      gchar *r_domain;
-
-      resolver = g_array_index (arr, GaServiceResolver *, i);
-      g_object_get ((gpointer) resolver,
-          "interface", &r_interface,
-          "protocol", &r_protocol,
-          "name", &r_name,
-          "type", &r_type,
-          "domain", &r_domain,
-          NULL);
-      if (interface == r_interface
-          && protocol == r_protocol
-          && !tp_strdiff (name, r_name)
-          && !tp_strdiff (type, r_type)
-          && !tp_strdiff (domain, r_domain))
-        {
-          g_free (r_name);
-          g_free (r_type);
-          g_free (r_domain);
-          g_object_unref (resolver);
-          g_array_remove_index_fast (arr, i);
-          break;
-        }
-
-      g_free (r_name);
-      g_free (r_type);
-      g_free (r_domain);
-    }
-
-  if (arr->len > 0)
-    return;
-
-  DEBUG ("remove room: %s.%s.%s", name, type, domain);
-
-  g_hash_table_remove (priv->room_resolvers, name);
-
-  salut_muc_manager_room_removed (SALUT_MUC_MANAGER (self), name);
-}
-
-static void
-browser_failed (GaServiceBrowser *browser,
-                GError *error,
-                SalutAvahiMucManager *self)
-{
-  /* FIXME proper error handling */
-  DEBUG ("browser failed -> %s", error->message);
-}
-
-static gboolean
-salut_avahi_muc_manager_start (SalutMucManager *mgr,
-                               GError **error)
-{
-  SalutAvahiMucManager *self = SALUT_AVAHI_MUC_MANAGER (mgr);
-  SalutAvahiMucManagerPrivate *priv = SALUT_AVAHI_MUC_MANAGER_GET_PRIVATE (self);
-
-  g_signal_connect (priv->browser, "new-service",
-      G_CALLBACK (browser_found), self);
-  g_signal_connect (priv->browser, "removed-service",
-      G_CALLBACK (browser_removed), self);
-  g_signal_connect (priv->browser, "failure",
-      G_CALLBACK (browser_failed), self);
-
-  if (!ga_service_browser_attach (priv->browser,
-        priv->discovery_client->avahi_client, error))
-    {
-      DEBUG ("browser attach failed");
-      return FALSE;
-   }
-
-  return TRUE;
-}
-
-static gchar *
-_avahi_address_to_string_address (const AvahiAddress *address)
-{
-  gchar str[AVAHI_ADDRESS_STR_MAX];
-
-  if (avahi_address_snprint (str, sizeof (str), address) == NULL)
-    {
-      DEBUG ("Failed to convert AvahiAddress to string");
-      return NULL;
-    }
-  return g_strdup (str);
-}
-
-static gboolean
-salut_avahi_muc_manager_find_muc_address (SalutMucManager *mgr,
-                                          const gchar *name,
-                                          gchar **address,
-                                          guint16 *port)
-{
-  SalutAvahiMucManager *self = SALUT_AVAHI_MUC_MANAGER (mgr);
-  SalutAvahiMucManagerPrivate *priv = SALUT_AVAHI_MUC_MANAGER_GET_PRIVATE (self);
-  GArray *arr;
-  AvahiAddress avahi_address;
-  guint i;
-
-  arr = g_hash_table_lookup (priv->room_resolvers, name);
-  if (arr == NULL || arr->len == 0)
-    return FALSE;
-
-  for (i = 0; i < arr->len; i++)
-    {
-      GaServiceResolver *resolver;
-      resolver = g_array_index (arr, GaServiceResolver *, i);
-
-       if (!ga_service_resolver_get_address (resolver, &avahi_address, port))
-         {
-           DEBUG ("..._get_address failed: creating a new MUC room instead");
-           return FALSE;
-         }
-       else
-         {
-           *address = _avahi_address_to_string_address (&avahi_address);
-           return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-static void
-add_room_to_list (const gchar *room,
-                  GaServiceResolver *resolver,
-                  GSList **list)
-{
-  *list = g_slist_prepend (*list, (gchar *) room);
-}
-
-static GSList *
-salut_avahi_muc_manager_get_rooms (SalutMucManager *mgr)
-{
-  SalutAvahiMucManager *self = SALUT_AVAHI_MUC_MANAGER (mgr);
-  SalutAvahiMucManagerPrivate *priv = SALUT_AVAHI_MUC_MANAGER_GET_PRIVATE (self);
-  GSList *rooms = NULL;
-
-  g_hash_table_foreach (priv->room_resolvers, (GHFunc) add_room_to_list,
-      &rooms);
-
-  return rooms;
-}
-
 static SalutMucChannel *
 salut_avahi_muc_manager_create_muc_channel (
     SalutMucManager *mgr,
@@ -385,10 +156,6 @@ salut_avahi_muc_manager_class_init (
 
   object_class->dispose = salut_avahi_muc_manager_dispose;
 
-  muc_manager_class->start = salut_avahi_muc_manager_start;
-  muc_manager_class->find_muc_address =
-    salut_avahi_muc_manager_find_muc_address;
-  muc_manager_class->get_rooms = salut_avahi_muc_manager_get_rooms;
   muc_manager_class->create_muc_channel =
     salut_avahi_muc_manager_create_muc_channel;
 
@@ -414,18 +181,6 @@ salut_avahi_muc_manager_dispose (GObject *object)
 
   priv->dispose_has_run = TRUE;
 
-  if (priv->room_resolvers != NULL)
-    {
-      g_hash_table_destroy (priv->room_resolvers);
-      priv->room_resolvers = NULL;
-    }
-
-  if (priv->browser != NULL)
-    {
-      g_object_unref (priv->browser);
-      priv->browser = NULL;
-    }
-
   if (priv->discovery_client != NULL)
     {
       g_object_unref (priv->discovery_client);
diff --git a/src/salut-avahi-roomlist-manager.c b/src/salut-avahi-roomlist-manager.c
new file mode 100644
index 0000000..b21aeda
--- /dev/null
+++ b/src/salut-avahi-roomlist-manager.c
@@ -0,0 +1,383 @@
+/*
+ * salut-avahi-roomlist-manager.c - Source for SalutAvahiRoomlistManager
+ * Copyright (C) 2006 Collabora Ltd.
+ *
+ * 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.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "salut-avahi-roomlist-manager.h"
+
+#include <avahi-gobject/ga-service-browser.h>
+#include <avahi-gobject/ga-service-resolver.h>
+
+#include <gibber/gibber-muc-connection.h>
+#include <gibber/gibber-namespaces.h>
+#include <gibber/gibber-xmpp-error.h>
+
+#include "salut-contact-manager.h"
+#include "salut-tubes-channel.h"
+#include "salut-roomlist-channel.h"
+#include "salut-xmpp-connection-manager.h"
+
+#include <telepathy-glib/channel-factory-iface.h>
+#include <telepathy-glib/interfaces.h>
+
+#define DEBUG_FLAG DEBUG_MUC
+#include "debug.h"
+
+G_DEFINE_TYPE (SalutAvahiRoomlistManager, salut_avahi_roomlist_manager,
+    SALUT_TYPE_ROOMLIST_MANAGER);
+
+/* properties */
+enum {
+  PROP_CLIENT = 1,
+  LAST_PROP
+};
+
+/* private structure */
+typedef struct _SalutAvahiRoomlistManagerPrivate
+    SalutAvahiRoomlistManagerPrivate;
+
+struct _SalutAvahiRoomlistManagerPrivate
+{
+  SalutAvahiDiscoveryClient *discovery_client;
+  GaServiceBrowser *browser;
+
+  /* room name => (GaServiceResolver *) */
+  GHashTable *room_resolvers;
+
+  gboolean dispose_has_run;
+};
+
+#define SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE(obj) \
+    ((SalutAvahiRoomlistManagerPrivate *) \
+    ((SalutAvahiRoomlistManager *)obj)->priv)
+
+static void
+room_resolver_removed (gpointer data)
+{
+  GArray *arr = (GArray *) data;
+  int i;
+  for (i = 0; i < arr->len; i++)
+    {
+      g_object_unref (g_array_index (arr, GObject *, i));
+    }
+  g_array_free (arr, TRUE);
+}
+
+static void
+salut_avahi_roomlist_manager_init (SalutAvahiRoomlistManager *self)
+{
+  SalutAvahiRoomlistManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+      SALUT_TYPE_AVAHI_ROOMLIST_MANAGER, SalutAvahiRoomlistManagerPrivate);
+
+  self->priv = priv;
+  priv->browser = ga_service_browser_new (SALUT_DNSSD_CLIQUE);
+
+  priv->room_resolvers = g_hash_table_new_full (g_str_hash, g_str_equal,
+      g_free, room_resolver_removed);
+}
+
+static void
+salut_avahi_roomlist_manager_get_property (GObject *object,
+                                           guint property_id,
+                                           GValue *value,
+                                           GParamSpec *pspec)
+{
+  SalutAvahiRoomlistManager *self = SALUT_AVAHI_ROOMLIST_MANAGER (object);
+  SalutAvahiRoomlistManagerPrivate *priv = SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  switch (property_id)
+    {
+      case PROP_CLIENT:
+        g_value_set_object (value, priv->discovery_client);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+salut_avahi_roomlist_manager_set_property (GObject *object,
+                                           guint property_id,
+                                           const GValue *value,
+                                           GParamSpec *pspec)
+{
+  SalutAvahiRoomlistManager *self = SALUT_AVAHI_ROOMLIST_MANAGER (object);
+  SalutAvahiRoomlistManagerPrivate *priv = SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  switch (property_id)
+    {
+      case PROP_CLIENT:
+        priv->discovery_client = g_value_get_object (value);
+        g_object_ref (priv->discovery_client);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void salut_avahi_roomlist_manager_dispose (GObject *object);
+
+static void
+browser_found (GaServiceBrowser *browser,
+               AvahiIfIndex interface,
+               AvahiProtocol protocol,
+               const char *name,
+               const char *type,
+               const char *domain,
+               GaLookupResultFlags flags,
+               SalutAvahiRoomlistManager *self)
+{
+  SalutAvahiRoomlistManagerPrivate *priv = SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE (self);
+  GArray *arr;
+  GaServiceResolver *resolver;
+  GError *error = NULL;
+
+  DEBUG ("found room: %s.%s.%s", name, type, domain);
+  resolver = ga_service_resolver_new (interface, protocol,
+      name, type, domain, protocol, 0);
+
+  if (!ga_service_resolver_attach (resolver,
+        priv->discovery_client->avahi_client, &error))
+    {
+      DEBUG ("resolver attach failed: %s", error->message);
+      g_object_unref (resolver);
+      g_error_free (error);
+      return;
+    }
+
+  arr = g_hash_table_lookup (priv->room_resolvers, name);
+  if (arr == NULL)
+    {
+      arr = g_array_new (FALSE, FALSE, sizeof (GObject *));
+      g_hash_table_insert (priv->room_resolvers, g_strdup (name), arr);
+      salut_roomlist_manager_room_discovered (SALUT_ROOMLIST_MANAGER (self),
+          name);
+    }
+  g_array_append_val (arr, resolver);
+}
+
+static void
+browser_removed (GaServiceBrowser *browser,
+                 AvahiIfIndex interface,
+                 AvahiProtocol protocol,
+                 const char *name,
+                 const char *type,
+                 const char *domain,
+                 GaLookupResultFlags flags,
+                 SalutAvahiRoomlistManager *self)
+{
+  SalutAvahiRoomlistManagerPrivate *priv = SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE (self);
+  GArray *arr;
+  int i;
+
+  arr = g_hash_table_lookup (priv->room_resolvers, name);
+
+  if (arr == NULL) {
+    DEBUG ("Browser removed for %s, but didn't have any resolvers", name);
+    return;
+  }
+
+  for (i = 0; i < arr->len; i++)
+    {
+      GaServiceResolver *resolver;
+      AvahiIfIndex r_interface;
+      AvahiProtocol r_protocol;
+      gchar *r_name;
+      gchar *r_type;
+      gchar *r_domain;
+
+      resolver = g_array_index (arr, GaServiceResolver *, i);
+      g_object_get ((gpointer) resolver,
+          "interface", &r_interface,
+          "protocol", &r_protocol,
+          "name", &r_name,
+          "type", &r_type,
+          "domain", &r_domain,
+          NULL);
+      if (interface == r_interface
+          && protocol == r_protocol
+          && !tp_strdiff (name, r_name)
+          && !tp_strdiff (type, r_type)
+          && !tp_strdiff (domain, r_domain))
+        {
+          g_free (r_name);
+          g_free (r_type);
+          g_free (r_domain);
+          g_object_unref (resolver);
+          g_array_remove_index_fast (arr, i);
+          break;
+        }
+
+      g_free (r_name);
+      g_free (r_type);
+      g_free (r_domain);
+    }
+
+  if (arr->len > 0)
+    return;
+
+  DEBUG ("remove room: %s.%s.%s", name, type, domain);
+
+  g_hash_table_remove (priv->room_resolvers, name);
+
+  salut_roomlist_manager_room_removed (SALUT_ROOMLIST_MANAGER (self), name);
+}
+
+static void
+browser_failed (GaServiceBrowser *browser,
+                GError *error,
+                SalutAvahiRoomlistManager *self)
+{
+  /* FIXME proper error handling */
+  DEBUG ("browser failed -> %s", error->message);
+}
+
+static gboolean
+salut_avahi_roomlist_manager_start (SalutRoomlistManager *mgr,
+                               GError **error)
+{
+  SalutAvahiRoomlistManager *self = SALUT_AVAHI_ROOMLIST_MANAGER (mgr);
+  SalutAvahiRoomlistManagerPrivate *priv = SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  g_signal_connect (priv->browser, "new-service",
+      G_CALLBACK (browser_found), self);
+  g_signal_connect (priv->browser, "removed-service",
+      G_CALLBACK (browser_removed), self);
+  g_signal_connect (priv->browser, "failure",
+      G_CALLBACK (browser_failed), self);
+
+  if (!ga_service_browser_attach (priv->browser,
+        priv->discovery_client->avahi_client, error))
+    {
+      DEBUG ("browser attach failed");
+      return FALSE;
+   }
+
+  return TRUE;
+}
+
+static void
+add_room_to_list (const gchar *room,
+                  GaServiceResolver *resolver,
+                  GSList **list)
+{
+  *list = g_slist_prepend (*list, (gchar *) room);
+}
+
+static GSList *
+salut_avahi_roomlist_manager_get_rooms (SalutRoomlistManager *mgr)
+{
+  SalutAvahiRoomlistManager *self = SALUT_AVAHI_ROOMLIST_MANAGER (mgr);
+  SalutAvahiRoomlistManagerPrivate *priv = SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE (self);
+  GSList *rooms = NULL;
+
+  g_hash_table_foreach (priv->room_resolvers, (GHFunc) add_room_to_list,
+      &rooms);
+
+  return rooms;
+}
+
+static void
+salut_avahi_roomlist_manager_class_init (
+    SalutAvahiRoomlistManagerClass *salut_avahi_roomlist_manager_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (salut_avahi_roomlist_manager_class);
+  SalutRoomlistManagerClass *roomlist_manager_class = SALUT_ROOMLIST_MANAGER_CLASS (
+      salut_avahi_roomlist_manager_class);
+  GParamSpec *param_spec;
+
+  g_type_class_add_private (salut_avahi_roomlist_manager_class,
+                              sizeof (SalutAvahiRoomlistManagerPrivate));
+
+  object_class->get_property = salut_avahi_roomlist_manager_get_property;
+  object_class->set_property = salut_avahi_roomlist_manager_set_property;
+
+  object_class->dispose = salut_avahi_roomlist_manager_dispose;
+
+  roomlist_manager_class->start = salut_avahi_roomlist_manager_start;
+  roomlist_manager_class->get_rooms = salut_avahi_roomlist_manager_get_rooms;
+
+  param_spec = g_param_spec_object (
+      "discovery-client",
+      "SalutAvahiDiscoveryClient object",
+      "The Salut Avahi Discovery client associated with this roomlist manager",
+      SALUT_TYPE_AVAHI_DISCOVERY_CLIENT,
+      G_PARAM_CONSTRUCT_ONLY |
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CLIENT,
+      param_spec);
+}
+
+static void
+salut_avahi_roomlist_manager_dispose (GObject *object)
+{
+  SalutAvahiRoomlistManager *self = SALUT_AVAHI_ROOMLIST_MANAGER (object);
+  SalutAvahiRoomlistManagerPrivate *priv = SALUT_AVAHI_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  if (priv->dispose_has_run)
+    return;
+
+  priv->dispose_has_run = TRUE;
+
+  if (priv->room_resolvers != NULL)
+    {
+      g_hash_table_destroy (priv->room_resolvers);
+      priv->room_resolvers = NULL;
+    }
+
+  if (priv->browser != NULL)
+    {
+      g_object_unref (priv->browser);
+      priv->browser = NULL;
+    }
+
+  if (priv->discovery_client != NULL)
+    {
+      g_object_unref (priv->discovery_client);
+      priv->discovery_client = NULL;
+    }
+
+  /* release any references held by the object here */
+
+  if (G_OBJECT_CLASS (salut_avahi_roomlist_manager_parent_class)->dispose)
+    G_OBJECT_CLASS (salut_avahi_roomlist_manager_parent_class)->dispose (object);
+}
+
+/* public functions */
+SalutAvahiRoomlistManager *
+salut_avahi_roomlist_manager_new (SalutConnection *connection,
+                             SalutXmppConnectionManager *xmpp_connection_manager,
+                             SalutAvahiDiscoveryClient *discovery_client)
+{
+  g_assert (connection != NULL);
+  g_assert (xmpp_connection_manager != NULL);
+  g_assert (discovery_client != NULL);
+
+  return g_object_new (SALUT_TYPE_AVAHI_ROOMLIST_MANAGER,
+      "connection", connection,
+      "xmpp-connection-manager", xmpp_connection_manager,
+      "discovery-client", discovery_client,
+      NULL);
+}
diff --git a/src/salut-avahi-roomlist-manager.h b/src/salut-avahi-roomlist-manager.h
new file mode 100644
index 0000000..542f695
--- /dev/null
+++ b/src/salut-avahi-roomlist-manager.h
@@ -0,0 +1,71 @@
+/*
+ * salut-avahi-roomlist-manager.h - Header for SalutAvahiRoomlistManager
+ * Copyright (C) 2006 Collabora Ltd.
+ *
+ * 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.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SALUT_AVAHI_ROOMLIST_MANAGER_H__
+#define __SALUT_AVAHI_ROOMLIST_MANAGER_H__
+
+#include <glib-object.h>
+
+#include "salut-roomlist-manager.h"
+#include "salut-connection.h"
+#include "salut-xmpp-connection-manager.h"
+#include "salut-avahi-discovery-client.h"
+
+G_BEGIN_DECLS
+
+typedef struct _SalutAvahiRoomlistManager SalutAvahiRoomlistManager;
+typedef struct _SalutAvahiRoomlistManagerClass SalutAvahiRoomlistManagerClass;
+
+struct _SalutAvahiRoomlistManagerClass {
+    SalutRoomlistManagerClass parent_class;
+};
+
+struct _SalutAvahiRoomlistManager {
+    SalutRoomlistManager parent;
+
+    gpointer priv;
+};
+
+GType salut_avahi_roomlist_manager_get_type (void);
+
+/* TYPE MACROS */
+#define SALUT_TYPE_AVAHI_ROOMLIST_MANAGER \
+  (salut_avahi_roomlist_manager_get_type ())
+#define SALUT_AVAHI_ROOMLIST_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), SALUT_TYPE_AVAHI_ROOMLIST_MANAGER, \
+   SalutAvahiRoomlistManager))
+#define SALUT_AVAHI_ROOMLIST_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), SALUT_TYPE_AVAHI_ROOMLIST_MANAGER,  \
+   SalutAvahiRoomlistManagerClass))
+#define SALUT_IS_AVAHI_ROOMLIST_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), SALUT_TYPE_AVAHI_ROOMLIST_MANAGER))
+#define SALUT_IS_AVAHI_ROOMLIST_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), SALUT_TYPE_AVAHI_ROOMLIST_MANAGER))
+#define SALUT_AVAHI_ROOMLIST_MANAGER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_AVAHI_ROOMLIST_MANAGER, \
+   SalutAvahiRoomlistManagerClass))
+
+SalutAvahiRoomlistManager *
+salut_avahi_roomlist_manager_new (SalutConnection *connection,
+    SalutXmppConnectionManager *xmpp_connection_manager,
+    SalutAvahiDiscoveryClient *discovery_client);
+
+G_END_DECLS
+
+#endif /* #ifndef __SALUT_AVAHI_ROOMLIST_MANAGER_H__*/
diff --git a/src/salut-connection.c b/src/salut-connection.c
index 74434cd..8457fe6 100644
--- a/src/salut-connection.c
+++ b/src/salut-connection.c
@@ -39,6 +39,7 @@
 #include "salut-muc-manager.h"
 #include "salut-ft-manager.h"
 #include "salut-contact.h"
+#include "salut-roomlist-manager.h"
 #include "salut-self.h"
 #include "salut-xmpp-connection-manager.h"
 #include "salut-si-bytestream-manager.h"
@@ -140,6 +141,7 @@ enum {
   PROP_IM_MANAGER,
   PROP_MUC_MANAGER,
   PROP_TUBES_MANAGER,
+  PROP_ROOMLIST_MANAGER,
   PROP_CONTACT_MANAGER,
   PROP_SELF,
   PROP_XCM,
@@ -190,6 +192,9 @@ struct _SalutConnectionPrivate
   /* FT channel manager */
   SalutFtManager *ft_manager;
 
+  /* MUC channel manager */
+  SalutRoomlistManager *roomlist_manager;
+
   /* Tubes channel manager */
   SalutTubesManager *tubes_manager;
 
@@ -342,6 +347,9 @@ salut_connection_get_property (GObject *object,
     case PROP_TUBES_MANAGER:
       g_value_set_object (value, priv->tubes_manager);
       break;
+    case PROP_ROOMLIST_MANAGER:
+      g_value_set_object (value, priv->roomlist_manager);
+      break;
     case PROP_CONTACT_MANAGER:
       g_value_set_object (value, priv->contact_manager);
       break;
@@ -710,6 +718,17 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class)
       param_spec);
 
   param_spec = g_param_spec_object (
+      "roomlist-manager",
+      "SalutRoomlistManager object",
+      "The Salut Roomlist Manager associated with this Salut Connection",
+      SALUT_TYPE_ROOMLIST_MANAGER,
+      G_PARAM_READABLE |
+      G_PARAM_STATIC_NICK |
+      G_PARAM_STATIC_BLURB);
+  g_object_class_install_property (object_class, PROP_ROOMLIST_MANAGER,
+      param_spec);
+
+  param_spec = g_param_spec_object (
       "contact-manager",
       "SalutContactManager object",
       "The Salut Contact Manager associated with this Salut Connection",
@@ -887,7 +906,7 @@ _self_established_cb (SalutSelf *s, gpointer data)
       return;
   }
 
-  if (!salut_muc_manager_start (priv->muc_manager, NULL))
+  if (!salut_roomlist_manager_start (priv->roomlist_manager, NULL))
     {
       /* XXX handle error */
       return;
@@ -2845,9 +2864,13 @@ salut_connection_create_channel_managers (TpBaseConnection *base)
   priv->ft_manager = salut_ft_manager_new (self, priv->contact_manager,
       priv->xmpp_connection_manager);
 
+  priv->muc_manager = salut_discovery_client_create_muc_manager (
+      priv->discovery_client, self, priv->xmpp_connection_manager);
+
   g_ptr_array_add (managers, priv->im_manager);
   g_ptr_array_add (managers, priv->contact_manager);
   g_ptr_array_add (managers, priv->ft_manager);
+  g_ptr_array_add (managers, priv->muc_manager);
 
   return managers;
 }
diff --git a/src/salut-muc-manager.c b/src/salut-muc-manager.c
index 6359066..d105e6b 100644
--- a/src/salut-muc-manager.c
+++ b/src/salut-muc-manager.c
@@ -33,11 +33,14 @@
 #include "salut-contact-manager.h"
 #include "salut-tubes-channel.h"
 #include "salut-roomlist-channel.h"
+#include "salut-roomlist-manager.h"
 #include "salut-xmpp-connection-manager.h"
 #include "salut-discovery-client.h"
 
-#include <telepathy-glib/channel-factory-iface.h>
+#include <telepathy-glib/channel-manager.h>
+#include <telepathy-glib/dbus.h>
 #include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/util.h>
 
 #define DEBUG_FLAG DEBUG_MUC
 #include "debug.h"
@@ -53,13 +56,13 @@ invite_stanza_callback (SalutXmppConnectionManager *mgr,
     SalutContact *contact, gpointer user_data);
 
 
-static void salut_muc_manager_factory_iface_init (gpointer g_iface,
+static void salut_muc_manager_iface_init (gpointer g_iface,
     gpointer iface_data);
 
 G_DEFINE_TYPE_WITH_CODE(SalutMucManager, salut_muc_manager,
                         G_TYPE_OBJECT,
-                        G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE,
-                                        salut_muc_manager_factory_iface_init));
+                        G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
+                                        salut_muc_manager_iface_init));
 
 /* properties */
 enum {
@@ -74,6 +77,7 @@ typedef struct _SalutMucManagerPrivate SalutMucManagerPrivate;
 struct _SalutMucManagerPrivate
 {
   SalutConnection *connection;
+  gulong status_changed_id;
   SalutXmppConnectionManager *xmpp_connection_manager;
 
   /* GUINT_TO_POINTER (room_handle) => (SalutMucChannel *) */
@@ -82,7 +86,11 @@ struct _SalutMucManagerPrivate
    /* GUINT_TO_POINTER(room_handle) => (SalutTubesChannel *) */
   GHashTable *tubes_channels;
 #endif
-  GSList *roomlist_channels;
+
+  /* Map from channels to the request-tokens of requests that they will
+   * satisfy when they're ready.
+   * Borrowed TpExportableChannel => GSList of gpointer */
+  GHashTable *queued_requests;
 
   gboolean dispose_has_run;
 };
@@ -103,8 +111,6 @@ salut_muc_manager_init (SalutMucManager *obj)
   priv->tubes_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
       NULL, g_object_unref);
 #endif
-
-  priv->roomlist_channels = NULL;
 }
 
 static void
@@ -152,6 +158,59 @@ salut_muc_manager_set_property (GObject *object,
     }
 }
 
+static void
+closed_channel_foreach (TpHandle handle,
+                        SalutMucChannel *channel,
+                        SalutMucManager *self)
+{
+  salut_muc_channel_emit_closed (channel);
+}
+
+static void
+salut_muc_manager_close_all (SalutMucManager *self)
+{
+  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
+
+  DEBUG ("closing channels");
+
+  if (priv->status_changed_id != 0)
+    {
+      g_signal_handler_disconnect (priv->connection, priv->status_changed_id);
+      priv->status_changed_id = 0;
+    }
+
+  if (priv->text_channels)
+    {
+      GHashTable *tmp = priv->text_channels;
+      priv->text_channels = NULL;
+      g_hash_table_foreach (tmp, (GHFunc) closed_channel_foreach, self);
+      g_hash_table_destroy (tmp);
+    }
+
+#ifdef ENABLE_DBUS_TUBES
+  if (priv->tubes_channels != NULL)
+    {
+      GHashTable *tmp = priv->tubes_channels;
+      priv->tubes_channels = NULL;
+      g_hash_table_destroy (tmp);
+    }
+#endif
+}
+
+static void
+connection_status_changed_cb (SalutConnection *conn,
+                              guint status,
+                              guint reason,
+                              SalutMucManager *self)
+{
+  switch (status)
+    {
+    case TP_CONNECTION_STATUS_DISCONNECTED:
+      salut_muc_manager_close_all (self);
+      break;
+    }
+}
+
 static GObject *
 salut_muc_manager_constructor (GType type,
                                guint n_props,
@@ -169,6 +228,9 @@ salut_muc_manager_constructor (GType type,
       priv->xmpp_connection_manager, NULL,
       invite_stanza_filter, invite_stanza_callback, obj);
 
+  priv->status_changed_id = g_signal_connect (priv->connection,
+      "status-changed", (GCallback) connection_status_changed_cb, obj);
+
   return obj;
 }
 
@@ -228,7 +290,7 @@ salut_muc_manager_dispose (GObject *object)
       priv->xmpp_connection_manager, NULL,
       invite_stanza_filter, invite_stanza_callback, self);
 
-  tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object));
+  salut_muc_manager_close_all (self);
   g_assert (priv->text_channels == NULL);
 #ifdef ENABLE_DBUS_TUBES
   g_assert (priv->tubes_channels == NULL);
@@ -257,108 +319,95 @@ salut_muc_manager_finalize (GObject *object)
   G_OBJECT_CLASS (salut_muc_manager_parent_class)->finalize (object);
 }
 
-static void
-closed_channel_foreach (TpHandle handle,
-                        SalutMucChannel *channel,
-                        SalutMucManager *self)
-{
-  salut_muc_channel_emit_closed (channel);
-}
-
 /* Channel Factory interface */
 
-static void
-salut_muc_manager_factory_iface_close_all (TpChannelFactoryIface *iface) {
-  SalutMucManager *mgr = SALUT_MUC_MANAGER (iface);
-  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (mgr);
-
-  if (priv->text_channels)
-    {
-      GHashTable *tmp = priv->text_channels;
-      priv->text_channels = NULL;
-      g_hash_table_foreach (tmp, (GHFunc) closed_channel_foreach, mgr);
-      g_hash_table_destroy (tmp);
-  }
-
-#ifdef ENABLE_DBUS_TUBES
-  if (priv->tubes_channels != NULL)
-    {
-      GHashTable *tmp = priv->tubes_channels;
-      priv->tubes_channels = NULL;
-      g_hash_table_destroy (tmp);
-    }
-#endif
+struct _ForeachData
+{
+  TpExportableChannelFunc foreach;
+  gpointer user_data;
+};
 
-  if (priv->roomlist_channels != NULL)
-    {
-      GSList *l = priv->roomlist_channels;
-      priv->roomlist_channels = NULL;
-      g_slist_foreach (l, (GFunc) g_object_unref, NULL);
-      g_slist_free (l);
-    }
-}
 
 static void
-salut_muc_manager_factory_iface_connecting (TpChannelFactoryIface *iface)
+_foreach_slave (gpointer key, gpointer value, gpointer user_data)
 {
-}
+  struct _ForeachData *data = (struct _ForeachData *) user_data;
+  TpExportableChannel *channel = TP_EXPORTABLE_CHANNEL (value);
 
-static void
-salut_muc_manager_factory_iface_connected (TpChannelFactoryIface *iface)
-{
+  data->foreach (channel, data->user_data);
 }
 
 static void
-salut_muc_manager_factory_iface_disconnected (TpChannelFactoryIface *iface)
+salut_muc_manager_foreach_channel (TpChannelManager *iface,
+                                   TpExportableChannelFunc foreach,
+                                   gpointer user_data)
 {
-  /* FIMXE close all channels ? */
+  SalutMucManager *fac = SALUT_MUC_MANAGER (iface);
+  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (fac);
+  struct _ForeachData data;
+
+  data.user_data = user_data;
+  data.foreach = foreach;
+
+
+  g_hash_table_foreach (priv->text_channels, _foreach_slave, &data);
+#ifdef ENABLE_DBUS_TUBES
+  g_hash_table_foreach (priv->tubes_channels, _foreach_slave, &data);
+#endif
 }
 
-struct foreach_data {
-  TpChannelFunc func;
-  gpointer data;
+static const gchar * const muc_channel_fixed_properties[] = {
+    TP_IFACE_CHANNEL ".ChannelType",
+    TP_IFACE_CHANNEL ".TargetHandleType",
+    NULL
 };
 
-static void
-salut_muc_manager_iface_foreach_one (gpointer key,
-                                     gpointer value,
-                                     gpointer data)
-{
-  TpChannelIface *chan = TP_CHANNEL_IFACE (value);
-  struct foreach_data *f = (struct foreach_data *) data;
+static const gchar * const * muc_tubes_channel_fixed_properties =
+    muc_channel_fixed_properties;
+
+static const gchar * const muc_channel_allowed_properties[] = {
+    TP_IFACE_CHANNEL ".TargetHandle",
+    TP_IFACE_CHANNEL ".TargetID",
+    NULL
+};
+
+static const gchar * const * muc_tubes_channel_allowed_properties =
+    muc_channel_allowed_properties;
 
-  f->func (chan, f->data);
-}
 
 static void
-salut_muc_manager_iface_foreach_one_list (TpChannelIface *chan,
-                                          gpointer data)
+salut_muc_manager_foreach_channel_class (TpChannelManager *manager,
+                                         TpChannelManagerChannelClassFunc func,
+                                         gpointer user_data)
 {
-  struct foreach_data *f = (struct foreach_data *) data;
+  GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal,
+      NULL, (GDestroyNotify) tp_g_value_slice_free);
+  GValue *channel_type_value, *handle_type_value;
 
-  f->func (chan, f->data);
-}
+  channel_type_value = tp_g_value_slice_new (G_TYPE_STRING);
+  /* no string value yet - we'll change it for each channel class */
+  g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+      channel_type_value);
 
-static void
-salut_muc_manager_factory_iface_foreach (TpChannelFactoryIface *iface,
-                                         TpChannelFunc func, gpointer data) {
-  SalutMucManager *mgr = SALUT_MUC_MANAGER(iface);
-  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE(mgr);
-  struct foreach_data f;
-  f.func = func;
-  f.data = data;
+  handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
+  g_value_set_uint (handle_type_value, TP_HANDLE_TYPE_ROOM);
+  g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+      handle_type_value);
 
-  g_hash_table_foreach (priv->text_channels,
-      salut_muc_manager_iface_foreach_one, &f);
-#ifdef ENABLE_DBUS_TUBES
-  g_hash_table_foreach (priv->tubes_channels,
-      salut_muc_manager_iface_foreach_one, &f);
-#endif
+  g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TEXT);
+  func (manager, table, muc_channel_allowed_properties,
+      user_data);
 
-  g_slist_foreach (priv->roomlist_channels,
-      (GFunc) salut_muc_manager_iface_foreach_one_list, &f);
+  g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TUBES);
+  func (manager, table, muc_tubes_channel_allowed_properties,
+      user_data);
+
+  /* FIXME: add roomlist channels */
+
+  g_hash_table_destroy (table);
 }
 
+
 static void
 muc_channel_closed_cb (SalutMucChannel *chan,
                        gpointer user_data)
@@ -367,6 +416,9 @@ muc_channel_closed_cb (SalutMucChannel *chan,
   SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
   TpHandle handle;
 
+  tp_channel_manager_emit_channel_closed_for_object (self,
+      TP_EXPORTABLE_CHANNEL (chan));
+
   if (priv->text_channels)
     {
       g_object_get (chan, "handle", &handle, NULL);
@@ -402,6 +454,9 @@ tubes_channel_closed_cb (SalutTubesChannel *chan, gpointer user_data)
   SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (fac);
   TpHandle room_handle;
 
+  tp_channel_manager_emit_channel_closed_for_object (fac,
+      TP_EXPORTABLE_CHANNEL (chan));
+
   if (priv->tubes_channels != NULL)
     {
       g_object_get (chan, "handle", &room_handle, NULL);
@@ -430,6 +485,20 @@ _get_connection (SalutMucManager *mgr,
       protocol, parameters, error);
 }
 
+static void
+salut_muc_manager_emit_new_channel (SalutMucManager *self,
+                                    TpExportableChannel *channel)
+{
+  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
+  GSList *requests_satisfied;
+
+  requests_satisfied = g_hash_table_lookup (priv->queued_requests, channel);
+  g_hash_table_steal (priv->queued_requests, channel);
+  requests_satisfied = g_slist_reverse (requests_satisfied);
+  tp_channel_manager_emit_new_channel (self, channel, requests_satisfied);
+  g_slist_free (requests_satisfied);
+}
+
 
 static SalutMucChannel *
 salut_muc_manager_new_muc_channel (SalutMucManager *mgr,
@@ -462,8 +531,7 @@ salut_muc_manager_new_muc_channel (SalutMucManager *mgr,
   g_free (path);
 
   g_signal_connect (chan, "closed", G_CALLBACK (muc_channel_closed_cb), mgr);
-  tp_channel_factory_iface_emit_new_channel (mgr, TP_CHANNEL_IFACE (chan),
-      NULL);
+  salut_muc_manager_emit_new_channel (mgr, TP_EXPORTABLE_CHANNEL (chan));
 
   g_hash_table_insert (priv->text_channels, GUINT_TO_POINTER (handle), chan);
 
@@ -505,8 +573,7 @@ new_tubes_channel (SalutMucManager *self,
       NULL);
 
   g_signal_connect (chan, "closed", (GCallback) tubes_channel_closed_cb, self);
-  tp_channel_factory_iface_emit_new_channel (self,
-      TP_CHANNEL_IFACE (chan), NULL);
+  salut_muc_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan));
 
   g_hash_table_insert (priv->tubes_channels, GUINT_TO_POINTER (room), chan);
 
@@ -533,11 +600,14 @@ salut_muc_manager_request_new_muc_channel (SalutMucManager *mgr,
   gchar *address;
   guint16 p;
   gboolean r;
+  SalutRoomlistManager *roomlist_manager;
+
+  g_object_get (priv->connection, "roomlist-manager", &roomlist_manager, NULL);
 
   room_name = tp_handle_inspect (room_repo, handle);
 
-  if (SALUT_MUC_MANAGER_GET_CLASS (mgr)->find_muc_address (mgr, room_name,
-        &address, &p))
+  if (SALUT_ROOMLIST_MANAGER_GET_CLASS (roomlist_manager)->find_muc_address
+      (roomlist_manager, room_name, &address, &p))
     {
       /* This MUC already exists on the network, so we reuse its
        * address */
@@ -594,58 +664,6 @@ salut_muc_manager_request_new_muc_channel (SalutMucManager *mgr,
   return text_chan;
 }
 
-static void
-roomlist_channel_closed_cb (SalutRoomlistChannel *chan,
-                            gpointer data)
-{
-  SalutMucManager *self = SALUT_MUC_MANAGER (data);
-  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
-
-  if (priv->roomlist_channels != NULL)
-    {
-      g_object_unref (chan);
-      priv->roomlist_channels = g_slist_remove (priv->roomlist_channels, chan);
-    }
-}
-
-static SalutRoomlistChannel *
-make_roomlist_channel (SalutMucManager *self,
-                       gpointer request)
-{
-  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
-  TpBaseConnection *conn = (TpBaseConnection *) priv->connection;
-  SalutRoomlistChannel *roomlist_channel;
-  gchar *object_path;
-  static guint cpt = 0;
-  GSList *rooms, *l;
-
-  object_path = g_strdup_printf ("%s/RoomlistChannel%u",
-      conn->object_path, cpt++);
-
-  roomlist_channel = salut_roomlist_channel_new (priv->connection,
-      object_path);
-
-  rooms = SALUT_MUC_MANAGER_GET_CLASS (self)->get_rooms (self);
-  for (l = rooms; l != NULL; l = g_slist_next (l))
-    {
-      const gchar *room_name = l->data;
-
-      salut_roomlist_channel_add_room (roomlist_channel, room_name);
-    }
-
-  priv->roomlist_channels = g_slist_prepend (priv->roomlist_channels,
-      roomlist_channel);
-
-  g_signal_connect (roomlist_channel, "closed",
-      (GCallback) roomlist_channel_closed_cb, self);
-
-  tp_channel_factory_iface_emit_new_channel (self,
-      (TpChannelIface *) roomlist_channel, request);
-
-  g_free (object_path);
-  return roomlist_channel;
-}
-
 static SalutTubesChannel *
 create_tubes_channel (SalutMucManager *self,
                       TpHandle handle,
@@ -675,112 +693,201 @@ create_tubes_channel (SalutMucManager *self,
   return tubes_chan;
 }
 
-static TpChannelFactoryRequestStatus
-salut_muc_manager_factory_iface_request (TpChannelFactoryIface *iface,
-                                         const gchar *chan_type,
-                                         TpHandleType handle_type,
-                                         guint handle,
-                                         gpointer request,
-                                         TpChannelIface **ret,
-                                         GError **error)
+/**
+ * ensure_muc_channel:
+ *
+ * Create a MUC channel in response to RequestChannel.
+ *
+ * Return TRUE if it already existed, or return FALSE
+ * if it needed to be created (so isn't ready yet).
+ */
+static gboolean
+ensure_muc_channel (SalutMucManager *fac,
+                    SalutMucManagerPrivate *priv,
+                    TpHandle handle,
+                    SalutMucChannel **ret)
 {
-  SalutMucManager *mgr = SALUT_MUC_MANAGER (iface);
-  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (mgr);
-  TpBaseConnection *base_connection = (TpBaseConnection *) (priv->connection);
-  TpHandleRepoIface *room_repo =
-      tp_base_connection_get_handles (base_connection, TP_HANDLE_TYPE_ROOM);
-  SalutMucChannel *text_chan;
-  TpChannelFactoryRequestStatus status;
+  *ret = g_hash_table_lookup (priv->text_channels, GINT_TO_POINTER (handle));
 
-  DEBUG ("MUC request: ctype=%s htype=%u handle=%u", chan_type, handle_type,
-      handle);
-
-  if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_ROOM_LIST))
+  if (*ret == NULL)
     {
-      SalutRoomlistChannel *roomlist_channel;
-
-      roomlist_channel = make_roomlist_channel (mgr, request);
-      *ret = TP_CHANNEL_IFACE (roomlist_channel);
-      return TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED;
+      *ret = salut_muc_manager_request_new_muc_channel (fac, handle, NULL);
+      return FALSE;
     }
 
-  if (handle_type != TP_HANDLE_TYPE_ROOM)
-    {
-      return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE;
-    }
+  return TRUE;
+}
 
-  /* Most be a valid room handle */
-  if (!tp_handle_is_valid (room_repo, handle, NULL))
-    {
-      return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE;
-    }
+static void
+salut_muc_manager_associate_request (SalutMucManager *self,
+                                     gpointer channel,
+                                     gpointer request)
+{
+  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
+  GSList *list = g_hash_table_lookup (priv->queued_requests, channel);
+
+  g_assert (TP_IS_EXPORTABLE_CHANNEL (channel));
+
+  g_hash_table_steal (priv->queued_requests, channel);
+  list = g_slist_prepend (list, request);
+  g_hash_table_insert (priv->queued_requests, channel, list);
+}
+
+
+static gboolean
+salut_muc_manager_request (SalutMucManager *self,
+                           gpointer request_token,
+                           GHashTable *request_properties,
+                           gboolean require_new)
+{
+  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
+  TpBaseConnection *base_conn = (TpBaseConnection *) priv->connection;
+  TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base_conn,
+      TP_HANDLE_TYPE_ROOM);
+  GError *error = NULL;
+  TpHandle handle;
+  const gchar *channel_type;
+  SalutMucChannel *text_chan;
+  SalutTubesChannel *tubes_chan;
+
+  if (tp_asv_get_uint32 (request_properties,
+      TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_ROOM)
+    return FALSE;
 
-  if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT))
+  handle = tp_asv_get_uint32 (request_properties,
+      TP_IFACE_CHANNEL ".TargetHandle", NULL);
+
+  if (!tp_handle_is_valid (room_repo, handle, &error))
+    goto error;
+
+  channel_type = tp_asv_get_string (request_properties,
+      TP_IFACE_CHANNEL ".ChannelType");
+
+  if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
     {
-      text_chan = g_hash_table_lookup (priv->text_channels,
-          GUINT_TO_POINTER (handle));
+      if (tp_channel_manager_asv_has_unknown_properties (request_properties,
+              muc_channel_fixed_properties, muc_channel_allowed_properties,
+              &error))
+        goto error;
 
-      if (text_chan != NULL)
+      if (ensure_muc_channel (self, priv, handle, &text_chan))
         {
-          status = TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING;
+          if (require_new)
+            {
+              g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+                  "That channel has already been created (or requested)");
+              goto error;
+            }
+          else
+            {
+              tp_channel_manager_emit_request_already_satisfied (self,
+                  request_token, TP_EXPORTABLE_CHANNEL (text_chan));
+            }
         }
       else
         {
-          text_chan = salut_muc_manager_request_new_muc_channel (mgr,
-              handle, error);
-          if (text_chan == NULL)
-            return TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR;
-
-          status = TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED;
+          salut_muc_manager_associate_request (self, text_chan,
+              request_token);
         }
 
-      g_assert (text_chan != NULL);
-      *ret = TP_CHANNEL_IFACE (text_chan);
+      return TRUE;
     }
-#ifdef ENABLE_DBUS_TUBES
-  else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TUBES))
+  else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES))
     {
-      SalutTubesChannel *tubes_chan;
+      if (tp_channel_manager_asv_has_unknown_properties (request_properties,
+              muc_tubes_channel_fixed_properties,
+              muc_tubes_channel_allowed_properties,
+              &error))
+        goto error;
 
       tubes_chan = g_hash_table_lookup (priv->tubes_channels,
           GUINT_TO_POINTER (handle));
 
       if (tubes_chan != NULL)
         {
-          status = TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING;
-          *ret = TP_CHANNEL_IFACE (tubes_chan);
+          if (require_new)
+            {
+              g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+                  "That channel has already been created (or requested)");
+              goto error;
+            }
+          else
+            {
+              tp_channel_manager_emit_request_already_satisfied (self,
+                  request_token, TP_EXPORTABLE_CHANNEL (tubes_chan));
+            }
         }
       else
         {
-          tubes_chan = create_tubes_channel (mgr, handle,
-              base_connection->self_handle, error);
-          if (tubes_chan == NULL)
-            return TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR;
-
-          *ret = TP_CHANNEL_IFACE (tubes_chan);
-
-          status = TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED;
+          ensure_muc_channel (self, priv, handle, &text_chan);
+          tubes_chan = new_tubes_channel (self, handle, text_chan,
+              base_conn->self_handle);
+          salut_muc_manager_associate_request (self, tubes_chan,
+              request_token);
+          salut_muc_manager_emit_new_channel (self,
+              TP_EXPORTABLE_CHANNEL (tubes_chan));
         }
+
+      return TRUE;
     }
-#endif
   else
     {
-      return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED;
+      return FALSE;
     }
 
-  return status;
+error:
+  tp_channel_manager_emit_request_failed (self, request_token,
+      error->domain, error->code, error->message);
+  g_error_free (error);
+  return TRUE;
+}
+
+static gboolean
+salut_muc_manager_create_channel (TpChannelManager *manager,
+                                   gpointer request_token,
+                                   GHashTable *request_properties)
+{
+  SalutMucManager *self = SALUT_MUC_MANAGER (manager);
+
+  return salut_muc_manager_request (self, request_token, request_properties,
+      TRUE);
+}
+
+
+static gboolean
+salut_muc_manager_request_channel (TpChannelManager *manager,
+                                    gpointer request_token,
+                                    GHashTable *request_properties)
+{
+  SalutMucManager *self = SALUT_MUC_MANAGER (manager);
+
+  return salut_muc_manager_request (self, request_token, request_properties,
+      FALSE);
 }
 
-static void salut_muc_manager_factory_iface_init (gpointer g_iface,
-                                                  gpointer iface_data) {
-   TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *)g_iface;
 
-   klass->close_all = salut_muc_manager_factory_iface_close_all;
-   klass->connecting = salut_muc_manager_factory_iface_connecting;
-   klass->connected = salut_muc_manager_factory_iface_connected;
-   klass->disconnected = salut_muc_manager_factory_iface_disconnected;
-   klass->foreach = salut_muc_manager_factory_iface_foreach;
-   klass->request = salut_muc_manager_factory_iface_request;
+static gboolean
+salut_muc_manager_ensure_channel (TpChannelManager *manager,
+                                    gpointer request_token,
+                                    GHashTable *request_properties)
+{
+  SalutMucManager *self = SALUT_MUC_MANAGER (manager);
+
+  return salut_muc_manager_request (self, request_token, request_properties,
+      FALSE);
+}
+
+
+static void salut_muc_manager_iface_init (gpointer g_iface,
+                                          gpointer iface_data)
+{
+  TpChannelManagerIface *iface = g_iface;
+
+  iface->foreach_channel = salut_muc_manager_foreach_channel;
+  iface->foreach_channel_class = salut_muc_manager_foreach_channel_class;
+  iface->request_channel = salut_muc_manager_request_channel;
+  iface->create_channel = salut_muc_manager_create_channel;
+  iface->ensure_channel = salut_muc_manager_ensure_channel;
 }
 
 static gboolean
@@ -921,13 +1028,6 @@ discard:
 
 /* public functions */
 
-gboolean
-salut_muc_manager_start (SalutMucManager *self,
-                         GError **error)
-{
-  return SALUT_MUC_MANAGER_GET_CLASS (self)->start (self, error);
-}
-
 SalutMucChannel *
 salut_muc_manager_get_text_channel (SalutMucManager *self,
                                     TpHandle handle)
@@ -997,53 +1097,3 @@ salut_muc_manager_ensure_tubes_channel (SalutMucManager *self,
   return tubes_chan;
 }
 
-static void
-add_room_foreach (SalutRoomlistChannel *roomlist_channel,
-                  const gchar *room)
-{
-  salut_roomlist_channel_add_room (roomlist_channel, room);
-}
-
-void
-salut_muc_manager_room_discovered (SalutMucManager *self,
-                                  const gchar *room)
-{
-  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
-
-  g_slist_foreach (priv->roomlist_channels, (GFunc) add_room_foreach,
-      (gchar *) room);
-}
-
-static void
-remove_room_foreach (SalutRoomlistChannel *roomlist_channel,
-                     const gchar *room)
-{
-  salut_roomlist_channel_remove_room (roomlist_channel, room);
-}
-
-void
-salut_muc_manager_room_removed (SalutMucManager *self,
-                                const gchar *room)
-{
-  SalutMucManagerPrivate *priv = SALUT_MUC_MANAGER_GET_PRIVATE (self);
-  TpBaseConnection *base_connection = TP_BASE_CONNECTION (priv->connection);
-  TpHandleRepoIface *room_repo =
-      tp_base_connection_get_handles (base_connection, TP_HANDLE_TYPE_ROOM);
-  TpHandle handle;
-  SalutMucChannel *muc;
-
-  g_slist_foreach (priv->roomlist_channels, (GFunc) remove_room_foreach,
-      (gchar *) room);
-
-    /* Do we have to re-announce this room ? */
-  handle = tp_handle_lookup (room_repo, room, NULL, NULL);
-  if (handle == 0)
-    return;
-
-  muc = g_hash_table_lookup (priv->text_channels, GUINT_TO_POINTER (handle));
-  if (muc == NULL)
-    return;
-
-  DEBUG ("We know this room %s. Try to re-announce it", room);
-  salut_muc_channel_publish_service (muc);
-}
diff --git a/src/salut-muc-manager.h b/src/salut-muc-manager.h
index 8bffb24..3b3e07a 100644
--- a/src/salut-muc-manager.h
+++ b/src/salut-muc-manager.h
@@ -38,15 +38,6 @@ typedef struct _SalutMucManagerClass SalutMucManagerClass;
 struct _SalutMucManagerClass {
     GObjectClass parent_class;
 
-    /* public abstract methods */
-    gboolean (*start) (SalutMucManager *self, GError **error);
-
-    /* private abstract methods */
-    gboolean (*find_muc_address) (SalutMucManager *self, const gchar *name,
-        gchar **address, guint16 *port);
-
-    GSList * (*get_rooms) (SalutMucManager *self);
-
     SalutMucChannel * (*create_muc_channel) (SalutMucManager *self,
         SalutConnection *connection, const gchar *path,
         GibberMucConnection *muc_connection, TpHandle handle,
@@ -74,9 +65,6 @@ GType salut_muc_manager_get_type (void);
 #define SALUT_MUC_MANAGER_GET_CLASS(obj) \
   (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_MUC_MANAGER, SalutMucManagerClass))
 
-gboolean
-salut_muc_manager_start (SalutMucManager *muc_manager, GError **error);
-
 SalutMucChannel *
 salut_muc_manager_get_text_channel (SalutMucManager *muc_manager,
     TpHandle handle);
@@ -89,13 +77,6 @@ SalutTubesChannel * salut_muc_manager_ensure_tubes_channel (
     SalutMucManager *muc_manager, TpHandle handle, TpHandle actor);
 
 
-/* "protected" methods */
-void salut_muc_manager_room_discovered (SalutMucManager *muc_manager,
-    const gchar *room);
-
-void salut_muc_manager_room_removed (SalutMucManager *muc_manager,
-    const gchar *room);
-
 G_END_DECLS
 
 #endif /* #ifndef __SALUT_MUC_MANAGER_H__*/
diff --git a/src/salut-roomlist-manager.c b/src/salut-roomlist-manager.c
new file mode 100644
index 0000000..64d2c34
--- /dev/null
+++ b/src/salut-roomlist-manager.c
@@ -0,0 +1,440 @@
+/*
+ * salut-roomlist-manager.c - Source for SalutRoomlistManager
+ * Copyright (C) 2006 Collabora Ltd.
+ *
+ * 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.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "salut-roomlist-manager.h"
+
+#include <gibber/gibber-muc-connection.h>
+#include <gibber/gibber-namespaces.h>
+#include <gibber/gibber-xmpp-error.h>
+
+#include "salut-roomlist-channel.h"
+#include "salut-contact-manager.h"
+#include "salut-muc-manager.h"
+#include "salut-tubes-channel.h"
+#include "salut-roomlist-channel.h"
+#include "salut-xmpp-connection-manager.h"
+#include "salut-discovery-client.h"
+
+#include <telepathy-glib/channel-manager.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/util.h>
+
+#define DEBUG_FLAG DEBUG_MUC
+#include "debug.h"
+
+static void salut_roomlist_manager_iface_init (gpointer g_iface,
+    gpointer iface_data);
+
+G_DEFINE_TYPE_WITH_CODE(SalutRoomlistManager, salut_roomlist_manager,
+                        G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
+                                        salut_roomlist_manager_iface_init));
+
+/* properties */
+enum {
+  PROP_CONNECTION = 1,
+  LAST_PROP
+};
+
+/* private structure */
+typedef struct _SalutRoomlistManagerPrivate SalutRoomlistManagerPrivate;
+
+struct _SalutRoomlistManagerPrivate
+{
+  SalutConnection *connection;
+  gulong status_changed_id;
+
+  GSList *roomlist_channels;
+
+  /* Map from channels to the request-tokens of requests that they will
+   * satisfy when they're ready.
+   * Borrowed TpExportableChannel => GSList of gpointer */
+  GHashTable *queued_requests;
+
+  gboolean dispose_has_run;
+};
+
+#define SALUT_ROOMLIST_MANAGER_GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), SALUT_TYPE_ROOMLIST_MANAGER, \
+                                SalutRoomlistManagerPrivate))
+
+static void
+salut_roomlist_manager_init (SalutRoomlistManager *obj)
+{
+  SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (obj);
+  priv->connection = NULL;
+}
+
+static void
+salut_roomlist_manager_get_property (GObject *object,
+    guint property_id, GValue *value, GParamSpec *pspec)
+{
+  SalutRoomlistManager *self = SALUT_ROOMLIST_MANAGER (object);
+  SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  switch (property_id)
+    {
+      case PROP_CONNECTION:
+        g_value_set_object (value, priv->connection);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+salut_roomlist_manager_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+  SalutRoomlistManager *self = SALUT_ROOMLIST_MANAGER (object);
+  SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  switch (property_id)
+    {
+      case PROP_CONNECTION:
+        priv->connection = g_value_get_object (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+salut_roomlist_manager_close_all (SalutRoomlistManager *self)
+{
+  SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  DEBUG ("closing channels");
+
+  if (priv->status_changed_id != 0)
+    {
+      g_signal_handler_disconnect (priv->connection, priv->status_changed_id);
+      priv->status_changed_id = 0;
+    }
+
+  /* FIXME: close roomlist channels */
+}
+
+static void
+connection_status_changed_cb (SalutConnection *conn,
+                              guint status,
+                              guint reason,
+                              SalutRoomlistManager *self)
+{
+  switch (status)
+    {
+    case TP_CONNECTION_STATUS_DISCONNECTED:
+      salut_roomlist_manager_close_all (self);
+      break;
+    }
+}
+
+static GObject *
+salut_roomlist_manager_constructor (GType type,
+                               guint n_props,
+                               GObjectConstructParam *props)
+{
+  GObject *obj;
+  SalutRoomlistManagerPrivate *priv;
+
+  obj = G_OBJECT_CLASS (salut_roomlist_manager_parent_class)->
+    constructor (type, n_props, props);
+
+  priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (obj);
+
+  priv->status_changed_id = g_signal_connect (priv->connection,
+      "status-changed", (GCallback) connection_status_changed_cb, obj);
+
+  return obj;
+}
+
+static void salut_roomlist_manager_dispose (GObject *object);
+static void salut_roomlist_manager_finalize (GObject *object);
+
+static void
+salut_roomlist_manager_class_init (
+    SalutRoomlistManagerClass *salut_roomlist_manager_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (salut_roomlist_manager_class);
+  GParamSpec *param_spec;
+
+  g_type_class_add_private (salut_roomlist_manager_class,
+                              sizeof (SalutRoomlistManagerPrivate));
+
+  object_class->get_property = salut_roomlist_manager_get_property;
+  object_class->set_property = salut_roomlist_manager_set_property;
+
+  object_class->constructor = salut_roomlist_manager_constructor;
+  object_class->dispose = salut_roomlist_manager_dispose;
+  object_class->finalize = salut_roomlist_manager_finalize;
+
+  param_spec = g_param_spec_object (
+      "connection",
+      "SalutConnection object",
+      "The Salut Connection associated with this roomlist manager",
+      SALUT_TYPE_CONNECTION,
+      G_PARAM_CONSTRUCT_ONLY |
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_CONNECTION,
+      param_spec);
+}
+
+void
+salut_roomlist_manager_dispose (GObject *object)
+{
+  SalutRoomlistManager *self = SALUT_ROOMLIST_MANAGER (object);
+  SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  if (priv->dispose_has_run)
+    return;
+
+  priv->dispose_has_run = TRUE;
+
+  salut_roomlist_manager_close_all (self);
+
+  /* release any references held by the object here */
+
+  if (G_OBJECT_CLASS (salut_roomlist_manager_parent_class)->dispose)
+    G_OBJECT_CLASS (salut_roomlist_manager_parent_class)->dispose (object);
+}
+
+void
+salut_roomlist_manager_finalize (GObject *object)
+{
+  /*SalutRoomlistManager *self = SALUT_ROOMLIST_MANAGER (object);*/
+  /*SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (self);*/
+
+  /* free any data held directly by the object here */
+
+  G_OBJECT_CLASS (salut_roomlist_manager_parent_class)->finalize (object);
+}
+
+/* Channel Factory interface */
+
+struct _ForeachData
+{
+  TpExportableChannelFunc foreach;
+  gpointer user_data;
+};
+
+
+static void
+salut_roomlist_manager_foreach_one_list (TpChannelManager *chan,
+                                         gpointer user_data)
+{
+  struct _ForeachData *data = (struct _ForeachData *) user_data;
+  TpExportableChannel *channel = TP_EXPORTABLE_CHANNEL (chan);
+
+  data->foreach (channel, data->user_data);
+}
+
+
+static void
+salut_roomlist_manager_foreach_channel (TpChannelManager *iface,
+                                   TpExportableChannelFunc foreach,
+                                   gpointer user_data)
+{
+  SalutRoomlistManager *fac = SALUT_ROOMLIST_MANAGER (iface);
+  SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (fac);
+  struct _ForeachData data;
+
+  data.user_data = user_data;
+  data.foreach = foreach;
+
+  g_slist_foreach (priv->roomlist_channels,
+      (GFunc) salut_roomlist_manager_foreach_one_list, &data);
+}
+
+static const gchar * const roomlist_channel_fixed_properties[] = {
+    TP_IFACE_CHANNEL ".ChannelType",
+    TP_IFACE_CHANNEL ".TargetHandleType",
+    NULL
+};
+
+static const gchar * const roomlist_channel_allowed_properties[] = {
+    TP_IFACE_CHANNEL ".TargetHandle",
+    TP_IFACE_CHANNEL ".TargetID",
+    NULL
+};
+
+
+static void
+salut_roomlist_manager_foreach_channel_class (
+    TpChannelManager *manager,
+    TpChannelManagerChannelClassFunc func,
+    gpointer user_data)
+{
+  GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal,
+      NULL, (GDestroyNotify) tp_g_value_slice_free);
+  GValue *channel_type_value, *handle_type_value;
+
+  channel_type_value = tp_g_value_slice_new (G_TYPE_STRING);
+  /* no string value yet - we'll change it for each channel class */
+  g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+      channel_type_value);
+
+  handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
+  g_value_set_uint (handle_type_value, TP_HANDLE_TYPE_ROOM);
+  g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+      handle_type_value);
+
+  g_value_set_static_string (channel_type_value,
+      TP_IFACE_CHANNEL_TYPE_ROOM_LIST);
+  func (manager, table, roomlist_channel_allowed_properties,
+      user_data);
+
+
+  g_hash_table_destroy (table);
+}
+
+
+static gboolean
+salut_roomlist_manager_request (SalutRoomlistManager *self,
+                                gpointer request_token,
+                                GHashTable *request_properties,
+                                gboolean require_new)
+{
+  return FALSE;
+}
+
+
+static gboolean
+salut_roomlist_manager_create_channel (TpChannelManager *manager,
+                                   gpointer request_token,
+                                   GHashTable *request_properties)
+{
+  SalutRoomlistManager *self = SALUT_ROOMLIST_MANAGER (manager);
+
+  return salut_roomlist_manager_request (self, request_token, request_properties,
+      TRUE);
+}
+
+
+static gboolean
+salut_roomlist_manager_request_channel (TpChannelManager *manager,
+                                    gpointer request_token,
+                                    GHashTable *request_properties)
+{
+  SalutRoomlistManager *self = SALUT_ROOMLIST_MANAGER (manager);
+
+  return salut_roomlist_manager_request (self, request_token, request_properties,
+      FALSE);
+}
+
+
+static gboolean
+salut_roomlist_manager_ensure_channel (TpChannelManager *manager,
+                                    gpointer request_token,
+                                    GHashTable *request_properties)
+{
+  SalutRoomlistManager *self = SALUT_ROOMLIST_MANAGER (manager);
+
+  return salut_roomlist_manager_request (self, request_token, request_properties,
+      FALSE);
+}
+
+
+static void salut_roomlist_manager_iface_init (gpointer g_iface,
+                                          gpointer iface_data)
+{
+  TpChannelManagerIface *iface = g_iface;
+
+  iface->foreach_channel = salut_roomlist_manager_foreach_channel;
+  iface->foreach_channel_class = salut_roomlist_manager_foreach_channel_class;
+  iface->request_channel = salut_roomlist_manager_request_channel;
+  iface->create_channel = salut_roomlist_manager_create_channel;
+  iface->ensure_channel = salut_roomlist_manager_ensure_channel;
+}
+
+/* public functions */
+
+gboolean
+salut_roomlist_manager_start (SalutRoomlistManager *self,
+                              GError **error)
+{
+  return SALUT_ROOMLIST_MANAGER_GET_CLASS (self)->start (self, error);
+}
+
+static void
+add_room_foreach (SalutRoomlistChannel *roomlist_channel,
+                  const gchar *room)
+{
+  salut_roomlist_channel_add_room (roomlist_channel, room);
+}
+
+void
+salut_roomlist_manager_room_discovered (SalutRoomlistManager *self,
+                                  const gchar *room)
+{
+  SalutRoomlistManagerPrivate *priv =
+    SALUT_ROOMLIST_MANAGER_GET_PRIVATE (self);
+
+  g_slist_foreach (priv->roomlist_channels, (GFunc) add_room_foreach,
+      (gchar *) room);
+}
+
+static void
+remove_room_foreach (SalutRoomlistChannel *roomlist_channel,
+                     const gchar *room)
+{
+  salut_roomlist_channel_remove_room (roomlist_channel, room);
+}
+
+void
+salut_roomlist_manager_room_removed (SalutRoomlistManager *self,
+                                     const gchar *room)
+{
+  SalutRoomlistManagerPrivate *priv = SALUT_ROOMLIST_MANAGER_GET_PRIVATE (self);
+  TpBaseConnection *base_connection = TP_BASE_CONNECTION (priv->connection);
+  TpHandleRepoIface *room_repo =
+      tp_base_connection_get_handles (base_connection, TP_HANDLE_TYPE_ROOM);
+  TpHandle handle;
+  SalutMucChannel *muc;
+  SalutMucManager *muc_manager;
+
+  g_slist_foreach (priv->roomlist_channels, (GFunc) remove_room_foreach,
+      (gchar *) room);
+
+  /* Do we have to re-announce this room ? */
+  handle = tp_handle_lookup (room_repo, room, NULL, NULL);
+  if (handle == 0)
+    return;
+
+  g_object_get (priv->connection, "muc-manager", &muc_manager, NULL);
+  g_assert (muc_manager != NULL);
+
+  muc = salut_muc_manager_get_text_channel (muc_manager, handle);
+  if (muc == NULL)
+    return;
+
+  DEBUG ("We know this room %s. Try to re-announce it", room);
+  salut_muc_channel_publish_service (muc);
+  g_object_unref (muc);
+}
diff --git a/src/salut-roomlist-manager.h b/src/salut-roomlist-manager.h
new file mode 100644
index 0000000..dab7b11
--- /dev/null
+++ b/src/salut-roomlist-manager.h
@@ -0,0 +1,99 @@
+/*
+ * salut-roomlist-manager.h - Header for SalutRoomlistManager
+ * Copyright (C) 2006 Collabora Ltd.
+ *
+ * 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.1 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SALUT_ROOMLIST_MANAGER_H__
+#define __SALUT_ROOMLIST_MANAGER_H__
+
+#include <glib-object.h>
+
+#include <gibber/gibber-bytestream-iface.h>
+
+#include <salut-connection.h>
+#include "salut-xmpp-connection-manager.h"
+#include "salut-tubes-channel.h"
+#include "salut-roomlist-channel.h"
+
+G_BEGIN_DECLS
+
+typedef struct _SalutRoomlistManager SalutRoomlistManager;
+typedef struct _SalutRoomlistManagerClass SalutRoomlistManagerClass;
+
+struct _SalutRoomlistManagerClass {
+    GObjectClass parent_class;
+
+    /* public abstract methods */
+    gboolean (*start) (SalutRoomlistManager *self, GError **error);
+
+    /* private abstract methods */
+    gboolean (*find_muc_address) (SalutRoomlistManager *self, const gchar *name,
+        gchar **address, guint16 *port);
+
+    GSList * (*get_rooms) (SalutRoomlistManager *self);
+};
+
+struct _SalutRoomlistManager {
+    GObject parent;
+};
+
+GType salut_roomlist_manager_get_type (void);
+
+/* TYPE MACROS */
+#define SALUT_TYPE_ROOMLIST_MANAGER \
+  (salut_roomlist_manager_get_type ())
+#define SALUT_ROOMLIST_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), SALUT_TYPE_ROOMLIST_MANAGER, \
+  SalutRoomlistManager))
+#define SALUT_ROOMLIST_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), SALUT_TYPE_ROOMLIST_MANAGER, \
+  SalutRoomlistManagerClass))
+#define SALUT_IS_ROOMLIST_MANAGER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), SALUT_TYPE_ROOMLIST_MANAGER))
+#define SALUT_IS_ROOMLIST_MANAGER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), SALUT_TYPE_ROOMLIST_MANAGER))
+#define SALUT_ROOMLIST_MANAGER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_ROOMLIST_MANAGER, \
+  SalutRoomlistManagerClass))
+
+gboolean
+salut_roomlist_manager_start (SalutRoomlistManager *roomlist_manager,
+    GError **error);
+
+SalutRoomlistChannel *
+salut_roomlist_manager_get_text_channel (SalutRoomlistManager *roomlist_manager,
+    TpHandle handle);
+
+void salut_roomlist_manager_handle_si_stream_request (
+    SalutRoomlistManager *roomlist_manager,
+    GibberBytestreamIface *bytestream, TpHandle room_handle,
+    const gchar *stream_id, GibberXmppStanza *msg);
+
+SalutTubesChannel * salut_roomlist_manager_ensure_tubes_channel (
+    SalutRoomlistManager *roomlist_manager, TpHandle handle, TpHandle actor);
+
+/* "protected" methods */
+void salut_roomlist_manager_room_discovered (
+    SalutRoomlistManager *roomlist_manager, const gchar *room);
+
+void salut_roomlist_manager_room_removed (
+    SalutRoomlistManager *roomlist_manager, const gchar *room);
+
+
+G_END_DECLS
+
+#endif /* #ifndef __SALUT_ROOMLIST_MANAGER_H__*/
-- 
1.5.6.5




More information about the Telepathy-commits mailing list