[telepathy-gabble/master] First attempt at moving the muc backend over to wocky, and moving most of the tubes support into the muc-channel object. Builds, most tests fail as of this check in.

Vivek Dasmohapatra vivek at collabora.co.uk
Sun Dec 27 11:35:45 PST 2009


---
 src/muc-channel.c | 1396 ++++++++++++++++++++++++++++++++---------------------
 src/muc-channel.h |    9 +
 src/muc-factory.c |  456 +++---------------
 3 files changed, 918 insertions(+), 943 deletions(-)

diff --git a/src/muc-channel.c b/src/muc-channel.c
index 05396ea..f339e9a 100644
--- a/src/muc-channel.c
+++ b/src/muc-channel.c
@@ -25,6 +25,9 @@
 #include <stdio.h>
 #include <string.h>
 
+#include <wocky/wocky-muc.h>
+#include <wocky/wocky-xmpp-error.h>
+
 #include <dbus/dbus-glib.h>
 #include <telepathy-glib/dbus.h>
 #include <telepathy-glib/debug-ansi.h>
@@ -122,6 +125,8 @@ enum
   PROP_CHANNEL_DESTROYED,
   PROP_CHANNEL_PROPERTIES,
   PROP_SELF_JID,
+  PROP_WOCKY_MUC,
+  PROP_TUBE,
   LAST_PROPERTY
 };
 
@@ -159,22 +164,6 @@ typedef enum {
     INVALID_AFFILIATION,
 } GabbleMucAffiliation;
 
-static const gchar *muc_roles[NUM_ROLES] =
-{
-  "none",
-  "visitor",
-  "participant",
-  "moderator",
-};
-
-static const gchar *muc_affiliations[NUM_AFFILIATIONS] =
-{
-  "none",
-  "member",
-  "admin",
-  "owner",
-};
-
 /* room properties */
 enum
 {
@@ -229,13 +218,6 @@ const TpPropertySignature room_property_signatures[NUM_ROOM_PROPS] = {
 };
 
 /* private structures */
-
-typedef struct {
-    TpHandleSet *members;
-    TpHandleSet *owners;
-    GHashTable *owner_map;
-} InitialStateAggregator;
-
 struct _GabbleMucChannelPrivate
 {
   GabbleConnection *conn;
@@ -272,35 +254,17 @@ struct _GabbleMucChannelPrivate
 
   gchar *invitation_message;
 
-  /* Aggregate all presences when joining the chatroom */
-  InitialStateAggregator *initial_state_aggregator;
+  WockyMuc *wmuc;
+  GabbleTubesChannel *tube;
 };
 
 #define GABBLE_MUC_CHANNEL_GET_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MUC_CHANNEL, \
                                 GabbleMucChannelPrivate))
 
-
-static void
-initial_state_aggregator_free (InitialStateAggregator *isa)
-{
-  g_hash_table_destroy (isa->owner_map);
-  tp_handle_set_destroy (isa->members);
-  tp_handle_set_destroy (isa->owners);
-  g_slice_free (InitialStateAggregator, isa);
-}
-
-
 static void
 gabble_muc_channel_init (GabbleMucChannel *obj)
 {
-  GabbleMucChannelPrivate *priv;
-
-  priv = obj->priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (obj);
-
-  priv->initial_state_aggregator = g_slice_new0 (InitialStateAggregator);
-  priv->initial_state_aggregator->owner_map = g_hash_table_new (g_direct_hash,
-      g_direct_equal);
 }
 
 
@@ -309,6 +273,77 @@ static TpHandle create_room_identity (GabbleMucChannel *)
 
 #define NUM_SUPPORTED_MESSAGE_TYPES 3
 
+/*  signatures for presence handlers */
+
+static void handle_renamed (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    gpointer data);
+
+static void handle_error (GObject *source,
+    WockyXmppStanza *stanza,
+    WockyXmppError errnum,
+    const gchar *message,
+    gpointer data);
+
+static void handle_join (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    gpointer data);
+
+static void handle_parted (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    const gchar *actor_jid,
+    const gchar *why,
+    const gchar *msg,
+    gpointer data);
+
+static void handle_left (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    WockyMucMember *who,
+    const gchar *actor_jid,
+    const gchar *why,
+    const gchar *msg,
+    gpointer data);
+
+static void handle_presence (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    WockyMucMember *who,
+    gpointer data);
+
+static void handle_perms (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    const gchar *actor,
+    const gchar *why,
+    gpointer data);
+
+/* signatures for message handlers */
+static void handle_message (GObject *source,
+    WockyXmppStanza *stanza,
+    WockyMucMsgType type,
+    const gchar *xmpp_id,
+    time_t stamp,
+    WockyMucMember *who,
+    const gchar *text,
+    const gchar *subject,
+    WockyMucMsgState state,
+    gpointer data);
+
+static void handle_errmsg (GObject *source,
+    WockyXmppStanza *stanza,
+    WockyMucMsgType type,
+    const gchar *xmpp_id,
+    time_t stamp,
+    WockyMucMember *who,
+    const gchar *text,
+    WockyXmppError error,
+    const gchar *etype,
+    gpointer data);
+
 static GObject *
 gabble_muc_channel_constructor (GType type, guint n_props,
                                 GObjectConstructParam *props)
@@ -337,6 +372,8 @@ gabble_muc_channel_constructor (GType type, guint n_props,
   priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
   conn = (TpBaseConnection *) priv->conn;
 
+  priv->tube = NULL;
+
   room_handles = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_ROOM);
   contact_handles = tp_base_connection_get_handles (conn,
       TP_HANDLE_TYPE_CONTACT);
@@ -355,14 +392,40 @@ gabble_muc_channel_constructor (GType type, guint n_props,
   /* this causes us to have one ref to the self handle which is unreffed
    * at the end of this function */
 
-  priv->initial_state_aggregator->members = tp_handle_set_new (
-      contact_handles);
-  priv->initial_state_aggregator->owners = tp_handle_set_new (contact_handles);
-
   /* initialize our own role and affiliation */
   priv->self_role = ROLE_NONE;
   priv->self_affil = AFFILIATION_NONE;
 
+  /* initialise the wocky muc object */
+  {
+    WockyPorter *porter = gabble_connection_get_porter (priv->conn);
+    const gchar *room_jid = tp_handle_inspect (contact_handles, self_handle);
+    gchar *user_jid = gabble_connection_get_full_jid (priv->conn);
+    WockyMuc *wmuc = g_object_new (WOCKY_TYPE_MUC,
+        "porter", porter,
+        "jid", room_jid,  /* room at service.name/nick */
+        "user", user_jid, /* user at doma.in/resource  */
+        NULL);
+
+    /* various presence handlers */
+    g_signal_connect (wmuc, "nick-change", (GCallback) handle_renamed,  self);
+    g_signal_connect (wmuc, "presence",    (GCallback) handle_presence, self);
+    g_signal_connect (wmuc, "joined",      (GCallback) handle_join,     self);
+    g_signal_connect (wmuc, "permissions", (GCallback) handle_perms,    self);
+    g_signal_connect (wmuc, "parted",      (GCallback) handle_parted,   self);
+    g_signal_connect (wmuc, "left",        (GCallback) handle_left,     self);
+    g_signal_connect (wmuc, "error",       (GCallback) handle_error,    self);
+
+    /* message handler(s) (just one needed so far) */
+    g_signal_connect (wmuc, "message",      (GCallback) handle_message, self);
+    g_signal_connect (wmuc, "message-error",(GCallback) handle_errmsg,  self);
+
+    priv->wmuc = wmuc;
+
+    g_free (user_jid);
+    g_object_unref (porter);
+  }
+
   /* register object on the bus */
   bus = tp_get_bus ();
   dbus_g_connection_register_g_object (bus, priv->object_path, obj);
@@ -431,8 +494,7 @@ gabble_muc_channel_constructor (GType type, guint n_props,
       g_assert (priv->invitation_message == NULL);
 
       g_array_append_val (members, self_handle);
-      tp_group_mixin_add_members (obj, members,
-          "", &error);
+      tp_group_mixin_add_members (obj, members, "", &error);
       g_assert (error == NULL);
       g_array_free (members, TRUE);
     }
@@ -731,105 +793,39 @@ create_room_identity (GabbleMucChannel *chan)
       GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
 }
 
-static LmMessage *
-create_presence_message (GabbleMucChannel *self,
-                         LmMessageSubType sub_type,
-                         LmMessageNode **x_node)
-{
-  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self);
-  LmMessage *msg;
-  LmMessageNode *node;
-
-  /* only take the connection-wide presence if the MUC protocol doesn't rely on
-   * a particular subtype (eg, leaving the room requires type='unavailable') */
-  if (sub_type == LM_MESSAGE_SUB_TYPE_NOT_SET)
-    {
-      /* note that this message doesn't contain capabilities, but that's OK as
-       * most IQ-based protocols won't work in a MUC anyway */
-      msg = gabble_presence_as_message (priv->conn->self_presence,
-          priv->self_jid->str);
-    }
-  else
-    {
-      msg = lm_message_new_with_sub_type (priv->self_jid->str,
-          LM_MESSAGE_TYPE_PRESENCE, sub_type);
-    }
-
-  node = lm_message_node_add_child (msg->node, "x", NULL);
-  lm_message_node_set_attribute (node, "xmlns", NS_MUC);
-
-  if (x_node != NULL)
-    *x_node = node;
-
-  return msg;
-}
-
 static gboolean
-send_join_request (GabbleMucChannel *channel,
+send_join_request (GabbleMucChannel *gmuc,
                    const gchar *password,
                    GError **error)
 {
-  GabbleMucChannelPrivate *priv;
-  LmMessage *msg;
-  LmMessageNode *x_node;
-  gboolean ret;
-
-  priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (channel);
-
-  /* build the message */
-  msg = create_presence_message (channel, LM_MESSAGE_SUB_TYPE_NOT_SET,
-      &x_node);
-
-  g_free (priv->password);
-
-  if (password != NULL)
-    {
-      priv->password = g_strdup (password);
-      lm_message_node_add_child (x_node, "password", password);
-    }
-
-  g_signal_emit (channel, signals[PRE_PRESENCE], 0, msg);
-
-  /* send it */
-  ret = _gabble_connection_send (priv->conn, msg, error);
-  if (!ret)
-    {
-      DEBUG ("_gabble_connection_send failed");
-    }
-  else
-    {
-      DEBUG ("join request sent");
-    }
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
 
-  lm_message_unref (msg);
+  g_object_set (priv->wmuc, "password", password, NULL);
+  wocky_muc_join (priv->wmuc, NULL);
 
-  return ret;
+  /* this used to be a meaningful success/failure return, but the two-stage  *
+   * async op involved means we don't have any way of detecting failure here */
+  return TRUE;
 }
 
 static gboolean
-send_leave_message (GabbleMucChannel *channel,
+send_leave_message (GabbleMucChannel *gmuc,
                     const gchar *reason)
 {
-  GabbleMucChannelPrivate *priv;
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
   LmMessage *msg;
   GError *error = NULL;
   gboolean ret;
 
-  priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (channel);
-
   /* build the message */
-  msg = create_presence_message (channel, LM_MESSAGE_SUB_TYPE_UNAVAILABLE,
-      NULL);
+  msg = (LmMessage *) wocky_muc_create_presence (priv->wmuc,
+      WOCKY_STANZA_SUB_TYPE_UNAVAILABLE, reason, NULL);
 
-  if (reason != NULL)
-    {
-      lm_message_node_add_child (msg->node, "status", reason);
-    }
-
-  g_signal_emit (channel, signals[PRE_PRESENCE], 0, msg);
+  g_signal_emit (gmuc, signals[PRE_PRESENCE], 0, msg);
 
   /* send it */
   ret = _gabble_connection_send (priv->conn, msg, &error);
+
   if (!ret)
     {
       DEBUG ("_gabble_connection_send failed");
@@ -917,6 +913,12 @@ gabble_muc_channel_get_property (GObject    *object,
     case PROP_SELF_JID:
       g_value_set_string (value, priv->self_jid->str);
       break;
+    case PROP_TUBE:
+      if (priv->tube)
+        g_value_set_object (value, priv->tube);
+      else
+        g_value_set_pointer (value, NULL);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -1103,6 +1105,16 @@ gabble_muc_channel_class_init (GabbleMucChannelClass *gabble_muc_channel_class)
   g_object_class_install_property (object_class, PROP_SELF_JID,
       param_spec);
 
+  param_spec = g_param_spec_object ("tube", "Tube Channel",
+      "The GabbleTubesChannel associated with this MUC (if any)",
+      GABBLE_TYPE_TUBES_CHANNEL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_TUBE, param_spec);
+
+  param_spec = g_param_spec_object ("wocky-muc", "Wocky MUC Object",
+      "The backend (Wocky) MUC instance",
+      WOCKY_TYPE_MUC, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_property (object_class, PROP_WOCKY_MUC, param_spec);
+
   signals[READY] =
     g_signal_new ("ready",
                   G_OBJECT_CLASS_TYPE (gabble_muc_channel_class),
@@ -1209,12 +1221,6 @@ gabble_muc_channel_finalize (GObject *object)
 
   DEBUG ("called");
 
-  if (priv->initial_state_aggregator != NULL)
-    {
-      initial_state_aggregator_free (priv->initial_state_aggregator);
-      priv->initial_state_aggregator = NULL;
-    }
-
   /* free any data held directly by the object here */
   tp_handle_unref (room_handles, priv->handle);
 
@@ -1490,151 +1496,6 @@ handle_nick_conflict (GabbleMucChannel *chan,
   return send_join_request (chan, priv->password, tp_error);
 }
 
-/**
- * _gabble_muc_channel_presence_error
- */
-void
-_gabble_muc_channel_presence_error (GabbleMucChannel *chan,
-                                    const gchar *jid,
-                                    LmMessageNode *pres_node)
-{
-  GabbleMucChannelPrivate *priv;
-  LmMessageNode *error_node;
-  GabbleXmppError error;
-  TpHandle actor = 0;
-  guint reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
-
-  g_assert (GABBLE_IS_MUC_CHANNEL (chan));
-
-  priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
-
-  if (strcmp (jid, priv->self_jid->str) != 0)
-    {
-      DEBUG ("presence error from other jids than self not handled");
-      return;
-    }
-
-  error_node = lm_message_node_get_child (pres_node, "error");
-  if (error_node == NULL)
-    {
-      DEBUG ("missing required node 'error'");
-      return;
-    }
-
-  error = gabble_xmpp_error_from_node (error_node, NULL);
-
-  if (priv->state >= MUC_STATE_JOINED)
-    {
-      DEBUG ("presence error while already member of the channel "
-          "-- NYI");
-      return;
-    }
-
-  /* We're not a member, find out why the join request failed
-   * and act accordingly. */
-  if (error == XMPP_ERROR_NOT_AUTHORIZED)
-    {
-      /* channel can sit requiring a password indefinitely */
-      clear_join_timer (chan);
-
-      /* Password already provided and incorrect? */
-      if (priv->state == MUC_STATE_AUTH)
-        {
-          provide_password_return_if_pending (chan, FALSE);
-
-          return;
-        }
-
-      DEBUG ("password required to join, changing password flags");
-
-      change_password_flags (chan, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, 0);
-
-      g_object_set (chan, "state", MUC_STATE_AUTH, NULL);
-    }
-  else
-    {
-      GError *tp_error /* doesn't need initializing */;
-
-      switch (error) {
-        case XMPP_ERROR_FORBIDDEN:
-          tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_BANNED,
-                                  "banned from room");
-          reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
-          break;
-        case XMPP_ERROR_SERVICE_UNAVAILABLE:
-          tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_FULL,
-                                  "room is full");
-          break;
-        case XMPP_ERROR_REGISTRATION_REQUIRED:
-          tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_INVITE_ONLY,
-                                  "room is invite only");
-          break;
-        case XMPP_ERROR_CONFLICT:
-          if (handle_nick_conflict (chan, &tp_error))
-            return;
-
-          break;
-        default:
-          tp_error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
-              "%s", gabble_xmpp_error_description (error));
-          break;
-      }
-
-      g_signal_emit (chan, signals[JOIN_ERROR], 0, tp_error);
-
-      close_channel (chan, tp_error->message, FALSE, actor, reason_code);
-
-      g_error_free (tp_error);
-    }
-}
-
-static GabbleMucRole
-get_role_from_string (const gchar *role)
-{
-  guint i;
-
-  if (role == NULL)
-    {
-      return ROLE_VISITOR;
-    }
-
-  for (i = 0; i < NUM_ROLES; i++)
-    {
-      if (strcmp (role, muc_roles[i]) == 0)
-        {
-          return i;
-        }
-    }
-
-  DEBUG ("unknown role '%s' -- defaulting to ROLE_VISITOR", role);
-
-  return ROLE_VISITOR;
-}
-
-static GabbleMucAffiliation
-get_affiliation_from_string (const gchar *affil)
-{
-  guint i;
-
-  if (affil == NULL)
-    {
-      return AFFILIATION_NONE;
-    }
-
-  for (i = 0; i < NUM_AFFILIATIONS; i++)
-    {
-      if (strcmp (affil, muc_affiliations[i]) == 0)
-        {
-          return i;
-        }
-    }
-
-  DEBUG ("unknown affiliation '%s' -- defaulting to "
-             "AFFILIATION_NONE", affil);
-
-  return AFFILIATION_NONE;
-}
-
 static LmHandlerResult
 room_created_submit_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
                               LmMessage *reply_msg, GObject *object,
@@ -1896,358 +1757,766 @@ update_permissions (GabbleMucChannel *chan)
   tp_intset_destroy (changed_props_flags);
 }
 
+
+
+/* ************************************************************************* */
+/* wocky MUC implementation */
+static GabbleMucRole
+get_role_from_backend (WockyMucRole role)
+{
+  switch (role)
+    {
+      case WOCKY_MUC_ROLE_NONE:
+        return ROLE_NONE;
+      case WOCKY_MUC_ROLE_VISITOR:
+        return ROLE_VISITOR;
+      case WOCKY_MUC_ROLE_PARTICIPANT:
+        return ROLE_PARTICIPANT;
+      case WOCKY_MUC_ROLE_MODERATOR:
+        return ROLE_MODERATOR;
+      default:
+        DEBUG ("unknown role '%d' -- defaulting to ROLE_VISITOR", role);
+        return ROLE_VISITOR;
+    }
+}
+
+static GabbleMucAffiliation
+get_aff_from_backend (WockyMucAffiliation aff)
+{
+  switch (aff)
+    {
+      case WOCKY_MUC_AFFILIATION_OUTCAST:
+      case WOCKY_MUC_AFFILIATION_NONE:
+        return AFFILIATION_NONE;
+      case WOCKY_MUC_AFFILIATION_MEMBER:
+        return AFFILIATION_MEMBER;
+      case WOCKY_MUC_AFFILIATION_ADMIN:
+        return AFFILIATION_ADMIN;
+      case WOCKY_MUC_AFFILIATION_OWNER:
+        return AFFILIATION_OWNER;
+      default:
+        DEBUG ("unknown affiliation %d -- defaulting to AFFILIATION_NONE", aff);
+        return AFFILIATION_NONE;
+    }
+}
+
+/* connect to wocky-muc:SIG_PRESENCE_ERROR */
 static void
-handle_unavailable_presence_update (GabbleMucChannel *chan,
-                                    TpHandleRepoIface *contact_handles,
-                                    TpHandle handle,
-                                    TpIntSet *handle_singleton,
-                                    LmMessageNode *item_node,
-                                    const gchar *status_code)
+handle_error (GObject *source,
+    WockyXmppStanza *stanza,
+    WockyXmppError errnum,
+    const gchar *message,
+    gpointer data)
 {
-  TpGroupMixin *mixin = TP_GROUP_MIXIN (chan);
-  LmMessageNode *reason_node, *actor_node;
-  const gchar *reason = "", *actor_jid = "";
-  TpHandle actor = 0;
-  TpChannelGroupChangeReason reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpChannelGroupChangeReason reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
+
+  g_assert (GABBLE_IS_MUC_CHANNEL (gmuc));
 
-  actor_node = lm_message_node_get_child (item_node, "actor");
-  if (actor_node != NULL)
+  if (priv->state >= MUC_STATE_JOINED)
     {
-      actor_jid = lm_message_node_get_attribute (actor_node, "jid");
-      if (actor_jid != NULL)
-        {
-          actor = tp_handle_ensure (contact_handles, actor_jid, NULL,
-              NULL);
-          if (actor == 0)
-            {
-              DEBUG ("ignoring invalid actor JID %s", actor_jid);
-            }
-        }
+      DEBUG ("presence error while already member of the channel -- NYI");
+      return;
     }
 
-  /* Possible reasons we could have been removed from the room:
-   * 301 banned
-   * 307 kicked
-   * 321 "because of an affiliation change" - no reason_code
-   * 322 room has become members-only and we're not a member - no
-   *    reason_code
-   * 332 system (server) is being shut down - no reason code
-   */
-  if (status_code)
+  if (errnum == WOCKY_XMPP_ERROR_NOT_AUTHORIZED)
     {
-      if (strcmp (status_code, "301") == 0)
+      /* channel can sit requiring a password indefinitely */
+      clear_join_timer (gmuc);
+
+      /* Password already provided and incorrect? */
+      if (priv->state == MUC_STATE_AUTH)
         {
-          reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
+          provide_password_return_if_pending (gmuc, FALSE);
+          return;
         }
-      else if (strcmp (status_code, "307") == 0)
+
+      DEBUG ("password required to join, changing password flags");
+      change_password_flags (gmuc, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, 0);
+      g_object_set (gmuc, "state", MUC_STATE_AUTH, NULL);
+    }
+  else
+    {
+      GError *tp_error /* doesn't need initializing */;
+
+      // FIXME, the wocky enum is identical to the gabble enum, but
+      // don't rely on this:
+      switch ((GabbleXmppError) errnum)
         {
-          reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_KICKED;
+          case XMPP_ERROR_FORBIDDEN:
+            tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_BANNED,
+                "banned from room");
+            reason = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
+            break;
+          case XMPP_ERROR_SERVICE_UNAVAILABLE:
+            tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_FULL,
+                "room is full");
+            break;
+
+          case XMPP_ERROR_REGISTRATION_REQUIRED:
+            tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_INVITE_ONLY,
+                "room is invite only");
+            break;
+
+          case XMPP_ERROR_CONFLICT:
+            if (handle_nick_conflict (gmuc, &tp_error))
+              return;
+            break;
+
+          default:
+            tp_error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+                "%s", gabble_xmpp_error_description (errnum));
+            break;
         }
-    }
 
-  reason_node = lm_message_node_get_child (item_node, "reason");
-  if (reason_node != NULL)
-    {
-      reason = lm_message_node_get_value (reason_node);
-    }
+      g_signal_emit (gmuc, signals[JOIN_ERROR], 0, tp_error);
 
-  if (handle != mixin->self_handle)
-    {
-      tp_group_mixin_change_members ((GObject *) chan, reason,
-                                         NULL, handle_singleton, NULL, NULL,
-                                         actor, reason_code);
+      close_channel (gmuc, tp_error->message, FALSE, 0, reason);
+      g_error_free (tp_error);
     }
-  else
+}
+
+static void
+tube_closed_cb (GabbleTubesChannel *chan, gpointer user_data)
+{
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (user_data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpHandle room;
+
+  tp_channel_manager_emit_channel_closed_for_object (gmuc,
+      TP_EXPORTABLE_CHANNEL (chan));
+
+  if (priv->tube != NULL)
     {
-      close_channel (chan, reason, FALSE, actor, reason_code);
+      priv->tube = NULL;
+      g_object_get (chan, "handle", &room, NULL);
+      DEBUG ("removing MUC tubes channel with handle %d", room);
+      g_object_unref (chan);
     }
-
-  if (actor)
-    tp_handle_unref (contact_handles, actor);
 }
 
-static gboolean
-renamed_by_server (LmMessageNode *x_node)
+static GabbleTubesChannel *
+new_tube (GabbleMucChannel *gmuc,
+    TpHandle initiator,
+    gboolean requested)
 {
-  gboolean is_self = FALSE;
-  gboolean renamed = FALSE;
-  NodeIter i;
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
+  char *object_path;
 
-  for (i = node_iter (x_node); i; i = node_iter_next (i))
-    {
-      LmMessageNode *child = node_iter_data (i);
-      const gchar *code;
+  g_assert (priv->tube == NULL);
 
-      if (strcmp (child->name, "status") != 0)
-        continue;
+  object_path = g_strdup_printf ("%s/MucTubesChannel%u",
+      conn->object_path, priv->handle);
 
-      code = lm_message_node_get_attribute (child, "code");
+  DEBUG ("creating new tubes chan, object path %s", object_path);
 
-      if (!tp_strdiff (code, "110"))
-        is_self = TRUE;
-      else if (!tp_strdiff (code, "210"))
-        renamed = TRUE;
-    }
+  priv->tube = g_object_new (GABBLE_TYPE_TUBES_CHANNEL,
+      "connection", priv->conn,
+      "object-path", object_path,
+      "handle", priv->handle,
+      "handle-type", TP_HANDLE_TYPE_ROOM,
+      "muc", gmuc,
+      "initiator-handle", initiator,
+      "requested", requested,
+      NULL);
 
-  return (is_self && renamed);
+  g_signal_connect (priv->tube, "closed", (GCallback) tube_closed_cb, gmuc);
+
+  tp_channel_manager_emit_new_channel (gmuc,
+      TP_EXPORTABLE_CHANNEL (priv->tube), NULL);
+
+  g_free (object_path);
+
+  return priv->tube;
 }
 
+/* ************************************************************************* */
+/* presence related signal handlers                                          */
+
+/* not actually a signal handler, but used by them:                        *
+ * creates a tube if none exists, and then prods the presence handler      *
+ * in the gabble tubes implementation to do whatever else needs to be done */
 static void
-handle_member_added (GabbleMucChannel *chan,
-                     GabbleMucChannelPrivate *priv,
-                     TpGroupMixin *mixin,
-                     TpHandleRepoIface *contact_handles,
-                     TpHandle handle,
-                     TpIntSet *handle_singleton,
-                     LmMessageNode *x_node,
-                     LmMessageNode *item_node)
+handle_tube_presence (GabbleMucChannel *gmuc,
+    TpHandle from,
+    WockyXmppStanza *stanza)
 {
-  TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
-  const gchar *owner_jid = lm_message_node_get_attribute (item_node, "jid");
-  TpHandle owner_handle = 0;
-  TpIntSet *old_self_handle_singleton = NULL;
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  WockyXmppNode *node = stanza->node;
+
+  if (from == 0)
+    return;
 
-  if (owner_jid != NULL)
+  if (priv->tube == NULL)
     {
-      owner_handle = tp_handle_ensure (contact_handles, owner_jid,
-          GUINT_TO_POINTER (GABBLE_JID_GLOBAL), NULL);
+      WockyXmppNode *tubes;
+      tubes = wocky_xmpp_node_get_child_ns (node, "tubes", NS_TUBES);
+
+      /* presence doesn't contain tubes information, no need
+       * to create a tubes channel */
+      if (tubes == NULL)
+        return;
 
-      if (owner_handle == 0)
-        DEBUG ("Invalid owner handle '%s', treating as no owner", owner_jid);
+      /* MUC Tubes channels (as opposed to the individual tubes) don't
+       * have a well-defined initiator (they're a consensus) so use 0 */
+      priv->tube = new_tube (gmuc, 0, FALSE);
     }
 
-  if (renamed_by_server (x_node))
-    {
-      old_self_handle_singleton = tp_intset_new ();
-      tp_intset_add (old_self_handle_singleton, mixin->self_handle);
+  gabble_tubes_channel_presence_updated (priv->tube, from, node);
+}
 
-      tp_group_mixin_change_self_handle ((GObject *) chan, handle);
-    }
 
-  if (handle == mixin->self_handle &&
-      owner_handle != conn->self_handle)
-    {
-      /* In XEP-0045-compliant MUCs, if we get presence for the jid we asked
-       * for (or for another jid, with status 110 and 210) then we know it's
-       * us. There can't be another user in the MUC with the nick we asked for:
-       * the service MUST reject us with code 409/"conflict" in this case. So,
-       * if someone in the room has the nick we want, it's us.
-       *
-       * If the MUC service fails to comply with this requirement, we get
-       * hopelessly confused. Given that the service isn't required to label
-       * our own presence as 110 ("this is you") and there's no way to label a
-       * presence for the jid we asked for as "not you" it's not possible for a
-       * client not to get hopelessly confused if the service is broken. So
-       * this is the best we can do.
-       */
-      DEBUG ("Overriding ownership of channel-specific handle %u "
-          "from %u to %u because I know it's mine",
-          mixin->self_handle, owner_handle, conn->self_handle);
+/* connect to wocky-muc:SIG_PARTED, which we will receive when the MUC tells *
+ * us that we have left the channel                                          */
+static void
+handle_parted (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    const gchar *actor_jid,
+    const gchar *why,
+    const gchar *msg,
+    gpointer data)
+{
+  WockyMuc *wmuc = WOCKY_MUC (source);
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpChannelGroupChangeReason reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
+  TpHandleRepoIface *contact_repo =
+    tp_base_connection_get_handles ((TpBaseConnection *) priv->conn,
+        TP_HANDLE_TYPE_CONTACT);
+  TpIntSet *handles = NULL;
+  TpHandle member = 0;
+  TpHandle actor = 0;
+  int x = 0;
+  static const gpointer banned = GUINT_TO_POINTER (WOCKY_MUC_CODE_BANNED);
+  static const gpointer const kicked[] =
+    { GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED),
+      GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_AFFILIATION),
+      GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED),
+      GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_SHUTDOWN),
+      NULL };
+  const char *jid = wocky_muc_jid (wmuc);
 
-      if (owner_handle != 0)
-        tp_handle_unref (contact_handles, owner_handle);
+  member = tp_handle_ensure (contact_repo, jid, NULL, NULL);
 
-      tp_handle_ref (contact_handles, conn->self_handle);
-      owner_handle = conn->self_handle;
+  if (member == 0)
+    {
+      DEBUG ("bizarre: ignoring our own malformed MUC JID '%s'", jid);
+      return;
     }
 
-  if (priv->initial_state_aggregator == NULL)
+  handles = tp_intset_new ();
+  tp_intset_add (handles, member);
+
+  if (actor_jid != NULL)
     {
-      /* we've already had the initial batch of presence stanzas */
-      tp_group_mixin_add_handle_owner ((GObject *) chan, handle,
-          owner_handle);
-      tp_group_mixin_change_members ((GObject *) chan, "",
-          handle_singleton, NULL, NULL, NULL, 0, 0);
+      actor = tp_handle_ensure (contact_repo, actor_jid, NULL, NULL);
+      if (actor == 0)
+        DEBUG ("ignoring invalid actor JID %s", actor_jid);
     }
+
+  if (g_hash_table_lookup (code, banned) != NULL)
+    reason = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
   else
-    {
-      /* aggregate this presence */
-      tp_handle_set_add (priv->initial_state_aggregator->members,
-          handle);
+    for (x = 0; kicked[x] != NULL; x++)
+      {
+        if (g_hash_table_lookup (code, kicked[x]) != NULL)
+          reason = TP_CHANNEL_GROUP_CHANGE_REASON_KICKED;
+      }
 
-      g_hash_table_insert (priv->initial_state_aggregator->owner_map,
-          GUINT_TO_POINTER (handle), GUINT_TO_POINTER (owner_handle));
+  /* handle_tube_presence creates tubes if need be, so bypass it here: */
+  if (priv->tube != NULL)
+    gabble_tubes_channel_presence_updated (priv->tube, member, stanza->node);
 
-      if (owner_handle != 0)
-        tp_handle_set_add (priv->initial_state_aggregator->owners,
-            owner_handle);
+  close_channel (gmuc, why, FALSE, actor, reason);
 
-      /* Do not emit one signal per presence. Instead, get all
-       * presences, and add them in priv->initial_state_aggregator.
-       * When we get the last presence, emit the signal. The last
-       * presence is ourselves. */
-      if (handle == mixin->self_handle)
-        {
-          /* Add all handle owners in a single operation */
-          tp_group_mixin_add_handle_owners ((GObject *) chan,
-              priv->initial_state_aggregator->owner_map);
-
-          /* Change all presences in a single operation */
-          tp_group_mixin_change_members ((GObject *) chan, "",
-              tp_handle_set_peek (
-                  priv->initial_state_aggregator->members),
-              old_self_handle_singleton, NULL, NULL, 0, 0);
-
-          initial_state_aggregator_free (
-              priv->initial_state_aggregator);
-          priv->initial_state_aggregator = NULL;
-          g_object_set (chan, "state", MUC_STATE_JOINED, NULL);
-        }
-    }
+  if (actor != 0)
+    tp_handle_unref (contact_repo, actor);
+  tp_intset_destroy (handles);
+  tp_handle_unref (contact_repo, member);
+}
+
+
+/* connect to wocky-muc:SIG_LEFT, which we will receive when the MUC informs *
+ * us someone [else] has left the channel                                    */
+static void
+handle_left (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    WockyMucMember *who,
+    const gchar *actor_jid,
+    const gchar *why,
+    const gchar *msg,
+    gpointer data)
+{
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpChannelGroupChangeReason reason = TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
+  TpHandleRepoIface *contact_repo =
+    tp_base_connection_get_handles ((TpBaseConnection *) priv->conn,
+        TP_HANDLE_TYPE_CONTACT);
+  TpIntSet *handles = NULL;
+  TpHandle member = 0;
+  TpHandle actor = 0;
+  int x = 0;
+  static const gpointer banned = GUINT_TO_POINTER (WOCKY_MUC_CODE_BANNED);
+  static const gpointer const kicked[] =
+    { GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED),
+      GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_AFFILIATION),
+      GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED),
+      GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_SHUTDOWN),
+      NULL };
+
+  member = tp_handle_ensure (contact_repo, who->from, NULL, NULL);
 
-  if (owner_handle != 0)
+  if (member == 0)
     {
-      if (handle != mixin->self_handle)
-        {
-          /* If at least one other handle in the channel has an owner,
-           * the HANDLE_OWNERS_NOT_AVAILABLE flag should be removed.
-           */
-          tp_group_mixin_change_flags ((GObject *) chan, 0,
-              TP_CHANNEL_GROUP_FLAG_HANDLE_OWNERS_NOT_AVAILABLE);
-        }
+      DEBUG ("ignoring malformed MUC JID '%s'", who->from);
+      return;
+    }
 
-      g_signal_emit (chan, signals[CONTACT_JOIN], 0, owner_handle);
+  handles = tp_intset_new ();
+  tp_intset_add (handles, member);
 
-      tp_handle_unref (contact_handles, owner_handle);
+  if (actor_jid != NULL)
+    {
+      actor = tp_handle_ensure (contact_repo, actor_jid, NULL, NULL);
+      if (actor == 0)
+        DEBUG ("ignoring invalid actor JID %s", actor_jid);
     }
 
-  if (old_self_handle_singleton != NULL)
-    tp_intset_destroy (old_self_handle_singleton);
+  if (g_hash_table_lookup (code, banned) != NULL)
+    reason = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
+  else
+    for (x = 0; kicked[x] != NULL; x++)
+      {
+        if (g_hash_table_lookup (code, kicked[x]) != NULL)
+          reason = TP_CHANNEL_GROUP_CHANGE_REASON_KICKED;
+      }
+
+  /* handle_tube_presence creates tubes if need be, so bypass it here: */
+  if (priv->tube != NULL)
+    gabble_tubes_channel_presence_updated (priv->tube, member, stanza->node);
+
+  tp_group_mixin_change_members (data, why, NULL, handles, NULL, NULL,
+      actor, reason);
+
+  if (actor != 0)
+    tp_handle_unref (contact_repo, actor);
+  tp_intset_destroy (handles);
+  tp_handle_unref (contact_repo, member);
 }
 
+/* connect to wocky-muc:SIG_PERM_CHANGE, which we will receive when the *
+ * MUC informs us our role/affiliation has been altered                 */
 static void
-handle_presence_update (GabbleMucChannel *chan,
-                        TpHandleRepoIface *contact_handles,
-                        TpHandle handle,
-                        TpIntSet *handle_singleton,
-                        LmMessageNode *x_node,
-                        LmMessageNode *item_node,
-                        const gchar *status_code)
+handle_perms (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    const gchar *actor,
+    const gchar *why,
+    gpointer data)
 {
-  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
-  TpGroupMixin *mixin = TP_GROUP_MIXIN (chan);
+  WockyMuc *wmuc = WOCKY_MUC (source);
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpHandle myself = TP_GROUP_MIXIN (gmuc)->self_handle;
 
-  if (!tp_handle_set_is_member (mixin->members, handle))
-    handle_member_added (chan, priv, mixin, contact_handles, handle,
-        handle_singleton, x_node, item_node);
+  priv->self_role = get_role_from_backend (wocky_muc_role (wmuc));
+  priv->self_affil = get_aff_from_backend (wocky_muc_affiliation (wmuc));
 
-  if (handle == mixin->self_handle)
+  room_properties_update (gmuc);
+  update_permissions (gmuc);
+
+  handle_tube_presence (gmuc, myself, stanza);
+}
+
+/* connect to wocky-muc:SIG_NICK_CHANGE, which we will receive when the *
+ * MUC informs us our nick has been changed for some reason             */
+static void
+handle_renamed (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    gpointer data)
+{
+  WockyMuc *wmuc = WOCKY_MUC (source);
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpHandleRepoIface *contact_repo =
+    tp_base_connection_get_handles ((TpBaseConnection *) priv->conn,
+        TP_HANDLE_TYPE_CONTACT);
+  TpIntSet *old_self = tp_intset_new ();
+  const gchar *me = wocky_muc_jid (wmuc);
+  const gchar *me2 = wocky_muc_user (wmuc);
+  TpHandle myself = tp_handle_ensure (contact_repo, me,
+      GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
+  TpHandle userid = tp_handle_ensure (contact_repo, me2,
+      GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
+
+  tp_intset_add (old_self, TP_GROUP_MIXIN (gmuc)->self_handle);
+  tp_group_mixin_change_self_handle (data, myself);
+  tp_group_mixin_add_handle_owner (data, myself, userid);
+  tp_group_mixin_change_members (data, "", NULL, old_self, NULL, NULL, 0, 0);
+
+  handle_tube_presence (gmuc, myself, stanza);
+
+  tp_intset_destroy (old_self);
+  tp_handle_unref (contact_repo, userid);
+  tp_handle_unref (contact_repo, myself);
+}
+
+struct _roster_foreach
+{
+  GabbleMucChannel *gmuc;
+  TpHandleRepoIface *contact_repo;
+  TpHandleSet *members;
+  TpHandleSet *owners;
+  GHashTable  *omap;
+};
+
+static void
+roster_presence (gpointer key, gpointer val, gpointer data)
+{
+  const WockyMucMember *member = val;
+  struct _roster_foreach *blob = data;
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (blob->gmuc);
+  TpHandleSet *members = blob->members;
+  TpHandleSet *owners = blob->owners;
+  GHashTable *omap = blob->omap;
+  TpHandleRepoIface *contact_repo = blob->contact_repo;
+  TpHandle owner = 0;
+  TpHandle handle = tp_handle_ensure (contact_repo, member->from,
+      GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
+
+  if (member->jid != NULL)
     {
-      const gchar *role, *affil;
-      GabbleMucRole new_role;
-      GabbleMucAffiliation new_affil;
+      owner = tp_handle_ensure (contact_repo, member->jid,
+          GUINT_TO_POINTER (GABBLE_JID_GLOBAL), NULL);
+      if (owner == 0)
+        DEBUG ("Invalid owner handle '%s', treating as no owner", member->jid);
+      else
+        tp_handle_set_add (owners, owner);
+    }
 
-      /* accept newly-created room settings before we send anything
-       * below which queryies them. */
-      if (status_code && strcmp (status_code, "201") == 0)
-        {
-          LmMessage *msg;
-          LmMessageNode *node;
-          GError *error = NULL;
+  tp_handle_set_add (members, handle);
+  g_hash_table_insert (omap,
+      GUINT_TO_POINTER (handle),
+      GUINT_TO_POINTER (owner));
 
-          msg = lm_message_new_with_sub_type (priv->jid,
-              LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_SET);
+  tp_handle_unref (contact_repo, handle);
+  /* make a note of the fact that owner JIDs are visible to us    */
+  /* notify whomever that an identifiable contact joined the MUC  */
+  if (owner != 0)
+    {
+      tp_group_mixin_change_flags (G_OBJECT (data), 0,
+          TP_CHANNEL_GROUP_FLAG_HANDLE_OWNERS_NOT_AVAILABLE);
+      g_signal_emit (gmuc, signals[CONTACT_JOIN], 0, owner);
+      tp_handle_unref (contact_repo, owner);
+    }
+}
+
+/* connect to wocky_muc SIG_JOINED which we should receive when we receive   *
+ * the final (ie our own) presence in the roster: (note that if our nick was *
+ * changed by the MUC we will already have received a SIG_NICK_CHANGE:       */
+static void
+handle_join (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    gpointer data)
+{
+  WockyMuc *wmuc = WOCKY_MUC (source);
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpHandleRepoIface *contact_repo =
+    tp_base_connection_get_handles ((TpBaseConnection *) priv->conn,
+        TP_HANDLE_TYPE_CONTACT);
+  TpHandleSet *members = tp_handle_set_new (contact_repo);
+  TpHandleSet *owners = tp_handle_set_new (contact_repo);
+  GHashTable *omap = g_hash_table_new (g_direct_hash, g_direct_equal);
+  GHashTable *member_jids = wocky_muc_members (wmuc);
+  const gchar *me = wocky_muc_jid (wmuc);
+  const gchar *me2 = wocky_muc_user (wmuc);
+  TpHandle myself = tp_handle_ensure (contact_repo, me,
+      GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
+  TpHandle userid = tp_handle_ensure (contact_repo, me2,
+      GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
 
-          node = lm_message_node_add_child (msg->node, "query", NULL);
-          lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER);
+  struct _roster_foreach blob = { gmuc, contact_repo, members, owners, omap };
 
-          node = lm_message_node_add_child (node, "x", NULL);
-          lm_message_node_set_attributes (node,
-                                          "xmlns", NS_X_DATA,
-                                          "type", "submit",
-                                          NULL);
+  g_hash_table_foreach (member_jids, roster_presence, &blob);
 
-          if (!_gabble_connection_send_with_reply (priv->conn, msg,
-                room_created_submit_reply_cb, G_OBJECT (chan), NULL,
-                &error))
-            {
-              DEBUG ("failed to send submit message: %s",
-                  error->message);
-              g_error_free (error);
+  g_hash_table_insert (omap,
+      GUINT_TO_POINTER (userid),
+      GUINT_TO_POINTER (myself));
 
-              lm_message_unref (msg);
-              close_channel (chan, NULL, TRUE, 0,
-                  TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
+  tp_handle_set_add (members, myself);
+  tp_group_mixin_add_handle_owners (source, omap);
+  tp_group_mixin_change_members (source, "",
+      tp_handle_set_peek (members), NULL, NULL, NULL, 0, 0);
 
-              return;
-            }
+  /* accept the config of the room if it was created for us: */
+  if (g_hash_table_lookup (code, (gpointer) WOCKY_MUC_CODE_NEW_ROOM))
+    {
+      GError *error = NULL;
+      gboolean sent = FALSE;
+      WockyXmppStanza *accept = wocky_xmpp_stanza_build (
+          WOCKY_STANZA_TYPE_IQ,
+          WOCKY_STANZA_SUB_TYPE_SET,
+            WOCKY_NODE, "query", WOCKY_NODE_XMLNS, WOCKY_NS_MUC_OWN,
+              WOCKY_NODE, "x", WOCKY_NODE_XMLNS, WOCKY_XMPP_NS_DATA,
+                WOCKY_NODE_ATTRIBUTE, "type", "submit",
+              WOCKY_NODE_END,
+            WOCKY_NODE_END,
+          WOCKY_STANZA_END);
+
+      sent = _gabble_connection_send_with_reply (priv->conn, accept,
+          room_created_submit_reply_cb, data, NULL, &error);
+
+      g_object_unref (accept);
+
+      if (!sent)
+        {
+          DEBUG ("failed to send submit message: %s", error->message);
+          g_error_free (error);
+
+          g_object_unref (accept);
+          close_channel (gmuc, NULL, TRUE, 0,
+              TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
 
-          lm_message_unref (msg);
+          goto out;
         }
+    }
 
-      /* Update room properties */
-      room_properties_update (chan);
+  g_object_set (gmuc, "state", MUC_STATE_JOINED, NULL);
+
+ out:
+  tp_handle_unref (contact_repo, myself);
+  tp_handle_unref (contact_repo, userid);
+  tp_handle_set_destroy (members);
+  tp_handle_set_destroy (owners);
+  g_hash_table_unref (omap);
+  g_hash_table_unref (member_jids);
+}
 
-      /* update permissions after requesting new properties so that if we
-       * become an owner, we get our configuration form reply after the
-       * discovery reply, so we know whether there is a description
-       * property before we try and decide whether we can write to it. */
-      role = lm_message_node_get_attribute (item_node, "role");
-      affil = lm_message_node_get_attribute (item_node, "affiliation");
-      new_role = get_role_from_string (role);
-      new_affil = get_affiliation_from_string (affil);
+/* connect to wocky-muc:SIG_PRESENCE, which is fired for presences that are *
+ * NOT our own after the initial roster has been received:                  */
+static void
+handle_presence (GObject *source,
+    WockyXmppStanza *stanza,
+    GHashTable *code,
+    WockyMucMember *who,
+    gpointer data)
+{
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpHandleRepoIface *contact_repo =
+    tp_base_connection_get_handles ((TpBaseConnection *) priv->conn,
+        TP_HANDLE_TYPE_CONTACT);
+  TpHandle owner = 0;
+  TpHandle handle = tp_handle_ensure (contact_repo, who->from,
+      GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
+  TpHandleSet *handles = tp_handle_set_new (contact_repo);
 
-      if (new_role != priv->self_role || new_affil != priv->self_affil)
-        {
-          priv->self_role = new_role;
-          priv->self_affil = new_affil;
+  /* is the 'real' jid field of the presence set? If so, use it: */
+  if (who->jid != NULL)
+    {
+      /* ******************************************************* */
+      /* OLPC Hack:                                              *
+       * We drop OLPC Gadget's inspector presence as activities  *
+       * doesn't have to see it as a member of the room and the  *
+       * presence cache should ignore it as well.                */
+      if (!tp_strdiff (who->jid, priv->conn->olpc_gadget_activity))
+        goto out;
+      /* ******************************************************* */
 
-          update_permissions (chan);
+      owner = tp_handle_ensure (contact_repo, who->jid,
+          GUINT_TO_POINTER (GABBLE_JID_GLOBAL), NULL);
+      if (owner == 0)
+        {
+          DEBUG ("Invalid owner handle '%s' ignored", who->jid);
+        }
+      else /* note that JIDs are known to us in this MUC */
+        {
+          tp_group_mixin_change_flags (G_OBJECT (data), 0,
+              TP_CHANNEL_GROUP_FLAG_HANDLE_OWNERS_NOT_AVAILABLE);
         }
     }
+
+  /* add the member in quesion */
+  tp_handle_set_add (handles, handle);
+  tp_group_mixin_change_members (data, "", tp_handle_set_peek (handles),
+      NULL, NULL, NULL, 0, 0);
+
+  /* record the owner (0 for no owner) */
+  tp_group_mixin_add_handle_owner (data, handle, owner);
+
+  handle_tube_presence (gmuc, handle, stanza);
+
+  /* zap the handle refs we created */
+ out:
+  tp_handle_unref (contact_repo, handle);
+  if (owner != 0)
+    tp_handle_unref (contact_repo, owner);
+  tp_handle_set_destroy (handles);
 }
 
-/**
- * _gabble_muc_channel_member_presence_updated:
- *
- * Handles <presence> stanzas with type='unavailable' or other non-'error'
- * types, updating channel members and/or closing the channel as appropriate.
- * (<presence type='error'> is handled by _gabble_muc_channel_presence_error.)
- */
-void
-_gabble_muc_channel_member_presence_updated (GabbleMucChannel *chan,
-                                             TpHandle handle,
-                                             LmMessage *message,
-                                             LmMessageNode *x_node,
-                                             LmMessageNode *item_node)
-{
-  GabbleMucChannelPrivate *priv;
-  TpBaseConnection *conn;
-  TpIntSet *handle_singleton;
-  LmMessageNode *status_node;
-  const gchar *status_code = NULL;
-  TpHandleRepoIface *contact_handles;
+/* ************************************************************************ */
+/* message signal handlers */
 
-  DEBUG ("called");
+static void
+handle_message (GObject *source,
+    WockyXmppStanza *stanza,
+    WockyMucMsgType type,
+    const gchar *xmpp_id,
+    time_t stamp,
+    WockyMucMember *who,
+    const gchar *text,
+    const gchar *subject,
+    WockyMucMsgState state,
+    gpointer data)
+{
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
+  gboolean from_member = (who != NULL);
 
-  g_assert (GABBLE_IS_MUC_CHANNEL (chan));
-  g_assert (handle != 0);
+  TpChannelTextMessageType msg_type;
+  TpHandleRepoIface *repo;
+  TpHandleType handle_type;
+  TpHandle from;
 
-  priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan);
-  conn = (TpBaseConnection *) priv->conn;
-  contact_handles = tp_base_connection_get_handles (conn,
-      TP_HANDLE_TYPE_CONTACT);
+  if (from_member)
+    {
+      handle_type = TP_HANDLE_TYPE_CONTACT;
+      repo = tp_base_connection_get_handles (conn, handle_type);
+      from = tp_handle_ensure (repo, who->from,
+          GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
 
-  status_node = lm_message_node_get_child (x_node, "status");
+      if (from == 0)
+        {
+          DEBUG ("Message from MUC member with no handle, discarding.");
+          return;
+        }
+    }
+  else /* directly from MUC itself */
+    {
+      handle_type = TP_HANDLE_TYPE_ROOM;
+      repo = tp_base_connection_get_handles (conn, handle_type);
+      from = priv->handle;
+      tp_handle_ref (repo, from);
+    }
 
-  if (status_node != NULL)
-    status_code = lm_message_node_get_attribute (status_node, "code");
+  switch (type)
+    {
+      case WOCKY_MUC_MSG_NORMAL:
+        msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
+        break;
+      case WOCKY_MUC_MSG_ACTION:
+        msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
+        break;
+      default:
+        msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
+    }
 
-  /* update channel members according to presence */
-  handle_singleton = tp_intset_new ();
-  tp_intset_add (handle_singleton, handle);
+  if (text != NULL)
+    _gabble_muc_channel_receive (gmuc,
+        msg_type, handle_type, from, stamp, xmpp_id, text, stanza,
+        GABBLE_TEXT_CHANNEL_SEND_NO_ERROR, TP_DELIVERY_STATUS_DELIVERED);
 
-  if (lm_message_get_sub_type (message) == LM_MESSAGE_SUB_TYPE_UNAVAILABLE)
-    handle_unavailable_presence_update (chan, contact_handles, handle,
-        handle_singleton, item_node, status_code);
-  else
-    handle_presence_update (chan, contact_handles, handle, handle_singleton,
-        x_node, item_node, status_code);
+  if (from_member && state != WOCKY_MUC_MSG_STATE_NONE)
+    {
+      gint tp_msg_state;
+      switch (state)
+        {
+          case WOCKY_MUC_MSG_STATE_ACTIVE:
+            tp_msg_state = TP_CHANNEL_CHAT_STATE_ACTIVE;
+            break;
+          case WOCKY_MUC_MSG_STATE_TYPING:
+            tp_msg_state = TP_CHANNEL_CHAT_STATE_COMPOSING;
+            break;
+          case WOCKY_MUC_MSG_STATE_INACTIVE:
+            tp_msg_state = TP_CHANNEL_CHAT_STATE_INACTIVE;
+            break;
+          case WOCKY_MUC_MSG_STATE_PAUSED:
+            tp_msg_state = TP_CHANNEL_CHAT_STATE_PAUSED;
+            break;
+          case WOCKY_MUC_MSG_STATE_GONE:
+            tp_msg_state = TP_CHANNEL_CHAT_STATE_GONE;
+          default:
+            tp_msg_state = TP_CHANNEL_CHAT_STATE_ACTIVE;
+        }
+      _gabble_muc_channel_state_receive (gmuc, tp_msg_state, from);
+    }
+
+  if (subject != NULL)
+    _gabble_muc_channel_handle_subject (gmuc, msg_type, handle_type, from,
+        stamp, subject, stanza);
 
-  tp_intset_destroy (handle_singleton);
+  tp_handle_unref (repo, from);
 }
 
+static void
+handle_errmsg (GObject *source,
+    WockyXmppStanza *stanza,
+    WockyMucMsgType type,
+    const gchar *xmpp_id,
+    time_t stamp,
+    WockyMucMember *who,
+    const gchar *text,
+    WockyXmppError error,
+    const gchar *etype,
+    gpointer data)
+{
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+  TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
+  gboolean from_member = (who != NULL);
+  GabbleXmppErrorType status = XMPP_ERROR_TYPE_UNDEFINED;
+  TpChannelTextSendError tp_err = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN;
+  TpDeliveryStatus ds = TP_DELIVERY_STATUS_DELIVERED;
+  TpHandleRepoIface *repo = NULL;
+  TpHandleType handle_type;
+  TpHandle from = 0;
+
+  if (from_member)
+    {
+      handle_type = TP_HANDLE_TYPE_CONTACT;
+      repo = tp_base_connection_get_handles (conn, handle_type);
+      from = tp_handle_ensure (repo, who->from,
+          GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
+
+      if (from == 0)
+        {
+          DEBUG ("Message from MUC member with no handle, discarding.");
+          return;
+        }
+    }
+  else /* directly from MUC itself */
+    {
+      handle_type = TP_HANDLE_TYPE_ROOM;
+      repo = tp_base_connection_get_handles (conn, handle_type);
+      from = priv->handle;
+      tp_handle_ref (repo, from);
+    }
+
+  tp_err = gabble_tp_send_error_from_wocky_xmpp_error (error);
+  status = gabble_xmpp_error_type_to_enum (etype);
+
+  if (status == XMPP_ERROR_TYPE_WAIT)
+    ds = TP_DELIVERY_STATUS_TEMPORARILY_FAILED;
+  else
+    ds = TP_DELIVERY_STATUS_PERMANENTLY_FAILED;
+
+  if (text != NULL)
+    _gabble_muc_channel_receive (gmuc, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE,
+        handle_type, from, stamp, xmpp_id, text, stanza, tp_err, ds);
+
+  tp_handle_unref (repo, from);
+}
 
+/* ************************************************************************* */
 /**
  * _gabble_muc_channel_handle_subject: handle room subject updates
  */
@@ -3354,7 +3623,9 @@ gabble_muc_channel_send_presence (GabbleMucChannel *self,
   if (priv->state < MUC_STATE_INITIATED)
     return TRUE;
 
-  msg = create_presence_message (self, LM_MESSAGE_SUB_TYPE_NOT_SET, NULL);
+  msg = (LmMessage *) wocky_muc_create_presence (priv->wmuc,
+      WOCKY_STANZA_SUB_TYPE_NONE, NULL, NULL);
+
   g_signal_emit (self, signals[PRE_PRESENCE], 0, msg);
   result = _gabble_connection_send (priv->conn, msg, error);
 
@@ -3362,6 +3633,41 @@ gabble_muc_channel_send_presence (GabbleMucChannel *self,
   return result;
 }
 
+GabbleTubesChannel *
+gabble_muc_channel_open_tube (GabbleMucChannel *gmuc,
+    TpHandle initiator,
+    gboolean requested)
+{
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+
+  if (priv->tube == NULL)
+    priv->tube = new_tube (gmuc, initiator, requested);
+
+  if (priv->tube != NULL)
+    return g_object_ref (priv->tube);
+
+  return NULL;
+}
+
+
+void
+gabble_muc_channel_close_tube (GabbleMucChannel *gmuc)
+{
+  GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (gmuc);
+
+  if (priv->tube != NULL)
+    {
+      TpHandle room;
+      GabbleTubesChannel *tube = priv->tube;
+
+      priv->tube = NULL;
+      g_object_get (tube, "handle", &room, NULL);
+      DEBUG ("removing MUC tubes channel with handle %d", room);
+      gabble_tubes_channel_close (tube);
+      g_object_unref (tube);
+    }
+}
+
 static void
 channel_iface_init (gpointer g_iface, gpointer iface_data)
 {
diff --git a/src/muc-channel.h b/src/muc-channel.h
index a8481ec..a7e5ce3 100644
--- a/src/muc-channel.h
+++ b/src/muc-channel.h
@@ -31,6 +31,7 @@
 #include <telepathy-glib/properties-mixin.h>
 
 #include "types.h"
+#include "tubes-channel.h"
 
 G_BEGIN_DECLS
 
@@ -106,6 +107,14 @@ gboolean gabble_muc_channel_send_presence (GabbleMucChannel *chan,
 gboolean gabble_muc_channel_send_invite (GabbleMucChannel *self,
     const gchar *jid, const gchar *message, GError **error);
 
+GabbleTubesChannel *
+gabble_muc_channel_open_tube (GabbleMucChannel *gmuc,
+    TpHandle initiator,
+    gboolean requested);
+
+void gabble_muc_channel_close_tube (GabbleMucChannel *gmuc);
+
+
 G_END_DECLS
 
 #endif /* #ifndef __GABBLE_MUC_CHANNEL_H__*/
diff --git a/src/muc-factory.c b/src/muc-factory.c
index fd3b10d..eda7719 100644
--- a/src/muc-factory.c
+++ b/src/muc-factory.c
@@ -68,11 +68,8 @@ struct _GabbleMucFactoryPrivate
   gulong status_changed_id;
 
   LmMessageHandler *message_cb;
-  LmMessageHandler *presence_cb;
   /* GUINT_TO_POINTER(room_handle) => (GabbleMucChannel *) */
   GHashTable *text_channels;
-  /* GUINT_TO_POINTER(room_handle) => (GabbleTubesChannel *) */
-  GHashTable *tubes_channels;
   /* Tubes channels which will be considered ready when the corresponding
    * text channel is created.
    * Borrowed GabbleMucChannel => borrowed GabbleTubesChannel */
@@ -108,8 +105,6 @@ gabble_muc_factory_init (GabbleMucFactory *fac)
 
   priv->text_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
       NULL, g_object_unref);
-  priv->tubes_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
-      NULL, g_object_unref);
   priv->text_needed_for_tubes = g_hash_table_new_full (g_direct_hash,
       g_direct_equal, NULL, NULL);
   priv->tubes_needed_for_tube = g_hash_table_new_full (g_direct_hash,
@@ -122,7 +117,6 @@ gabble_muc_factory_init (GabbleMucFactory *fac)
       g_direct_equal, NULL, NULL);
 
   priv->message_cb = NULL;
-  priv->presence_cb = NULL;
 
   priv->conn = NULL;
   priv->dispose_has_run = FALSE;
@@ -156,7 +150,6 @@ gabble_muc_factory_dispose (GObject *object)
 
   gabble_muc_factory_close_all (fac);
   g_assert (priv->text_channels == NULL);
-  g_assert (priv->tubes_channels == NULL);
   g_assert (priv->text_needed_for_tubes == NULL);
   g_assert (priv->tubes_needed_for_tube == NULL);
   g_assert (priv->queued_requests == NULL);
@@ -251,49 +244,12 @@ muc_channel_closed_cb (GabbleMucChannel *chan, gpointer user_data)
 
       DEBUG ("removing MUC channel with handle %d", room_handle);
 
-      if (priv->tubes_channels != NULL)
-        {
-          GabbleTubesChannel *tubes;
-
-          tubes = g_hash_table_lookup (priv->tubes_channels,
-              GUINT_TO_POINTER (room_handle));
-          if (tubes != NULL)
-            gabble_tubes_channel_close (tubes);
-        }
-
-      g_hash_table_remove (priv->text_channels,
-          GUINT_TO_POINTER (room_handle));
-    }
-}
-
-/**
- * tubes_channel_closed_cb:
- *
- * Signal callback for when a tubes channel is closed. Removes the references
- * that MucFactory holds to them.
- */
-static void
-tubes_channel_closed_cb (GabbleTubesChannel *chan, gpointer user_data)
-{
-  GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
-  GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_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);
-
-      DEBUG ("removing MUC tubes channel with handle %d", room_handle);
+      gabble_muc_channel_close_tube (chan);
 
-      g_hash_table_remove (priv->tubes_channels,
-          GUINT_TO_POINTER (room_handle));
+      g_hash_table_remove (priv->text_channels, GUINT_TO_POINTER (room_handle));
     }
 }
 
-
 static void
 muc_ready_cb (GabbleMucChannel *text_chan,
               gpointer data)
@@ -478,49 +434,7 @@ new_muc_channel (GabbleMucFactory *fac,
   return chan;
 }
 
-/**
- * new_tubes_channel:
- *
- * Creates the GabbleTubesChannel object with the given parameters.
- */
-static GabbleTubesChannel *
-new_tubes_channel (GabbleMucFactory *fac,
-                   TpHandle room,
-                   GabbleMucChannel *muc,
-                   TpHandle initiator,
-                   gboolean requested)
-{
-  GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
-  TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
-  GabbleTubesChannel *chan;
-  char *object_path;
-
-  g_assert (g_hash_table_lookup (priv->tubes_channels,
-        GUINT_TO_POINTER (room)) == NULL);
-
-  object_path = g_strdup_printf ("%s/MucTubesChannel%u",
-      conn->object_path, room);
-
-  DEBUG ("creating new tubes chan, object path %s", object_path);
-
-  chan = g_object_new (GABBLE_TYPE_TUBES_CHANNEL,
-      "connection", priv->conn,
-      "object-path", object_path,
-      "handle", room,
-      "handle-type", TP_HANDLE_TYPE_ROOM,
-      "muc", muc,
-      "initiator-handle", initiator,
-      "requested", requested,
-      NULL);
-
-  g_signal_connect (chan, "closed", (GCallback) tubes_channel_closed_cb, fac);
-
-  g_hash_table_insert (priv->tubes_channels, GUINT_TO_POINTER (room), chan);
-
-  g_free (object_path);
-
-  return chan;
-}
+// tubes_channel_closed_cb
 
 static void
 do_invite (GabbleMucFactory *fac,
@@ -785,24 +699,13 @@ muc_factory_message_cb (LmMessageHandler *handler,
 {
   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
-  TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
-  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
-      TP_HANDLE_TYPE_CONTACT);
-  TpHandleRepoIface *room_repo = tp_base_connection_get_handles (conn,
-      TP_HANDLE_TYPE_ROOM);
 
-  const gchar *from, *body, *subject, *id;
+  const gchar *from, *body, *id;
   time_t stamp;
   TpChannelTextMessageType msgtype;
-  TpHandleRepoIface *handle_source;
-  TpHandleType handle_type;
-  TpHandle room_handle, handle;
-  GabbleMucChannel *chan;
   gint state;
   TpChannelTextSendError send_error;
   TpDeliveryStatus delivery_status;
-  gchar *room;
-  LmMessageNode *subj_node;
 
   if (!gabble_message_util_parse_incoming_message (message, &from, &stamp,
         &msgtype, &id, &body, &state, &send_error, &delivery_status))
@@ -822,236 +725,12 @@ muc_factory_message_cb (LmMessageHandler *handler,
   if (process_obsolete_invite (fac, message, from, body, send_error))
     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
 
-  /* check if a room with the jid exists */
-  room = gabble_remove_resource (from);
-  room_handle = tp_handle_lookup (room_repo, room, NULL, NULL);
-  g_free (room);
-
-  /* the message is nothing to do with MUC, do nothing */
-  if (room_handle == 0)
-    {
-      return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-    }
-
-  /* find the MUC channel */
-  chan = g_hash_table_lookup (priv->text_channels,
-      GUINT_TO_POINTER (room_handle));
-
-  if (chan == NULL)
-    {
-      NODE_DEBUG (message->node, "ignoring MUC message from known "
-          "handle with no corresponding channel");
-
-      return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-    }
-
-  /* get the handle of the sender, which is either the room
-   * itself or one of its members */
-  if (strchr (from, '/') == NULL)
-    {
-      handle_source = room_repo;
-      handle_type = TP_HANDLE_TYPE_ROOM;
-      handle = room_handle;
-      tp_handle_ref (room_repo, handle);
-    }
-  else
-    {
-      handle_source = contact_repo;
-      handle_type = TP_HANDLE_TYPE_CONTACT;
-      handle = tp_handle_ensure (contact_repo, from,
-          GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
-
-      if (handle == 0)
-        {
-          NODE_DEBUG (message->node, "MUC message from invalid JID; ignoring");
-          return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-        }
-
-      /* anything other than a type="groupchat" is from the person directly and
-       * simply relayed by the MUC, so should be left to the normal handlers */
-      if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_GROUPCHAT)
-        {
-          tp_handle_unref (contact_repo, handle);
-
-          return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-        }
-    }
-
-  if (body != NULL)
-    _gabble_muc_channel_receive (chan, msgtype, handle_type, handle, stamp,
-        id, body, message, send_error, delivery_status);
-
-  if (send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
-    {
-      if (state != -1 && handle_type == TP_HANDLE_TYPE_CONTACT)
-        _gabble_muc_channel_state_receive (chan, state, handle);
-
-      subj_node = lm_message_node_get_child (message->node, "subject");
-      if (subj_node != NULL)
-        {
-          subject = lm_message_node_get_value (subj_node);
-          _gabble_muc_channel_handle_subject (chan, msgtype, handle_type, handle,
-              stamp, subject, message);
-        }
-    }
-
-  tp_handle_unref (handle_source, handle);
-
-  return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-
-/**
- * connection_presence_muc_cb:
- * @handler: #LmMessageHandler for this message
- * @connection: #LmConnection that originated the message
- * @message: the presence message
- * @user_data: callback data
- *
- * Called by loudmouth when we get an incoming <presence>.
- */
-static LmHandlerResult
-muc_factory_presence_cb (LmMessageHandler *handler,
-                            LmConnection *lmconn,
-                            LmMessage *msg,
-                            gpointer user_data)
-{
-  GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
-  GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
-  TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
-      (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
-  TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
-      (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM);
-  const char *from;
-  LmMessageSubType sub_type;
-  GabbleMucChannel *muc_chan = NULL;
-  LmMessageNode *x_node;
-  TpHandle room_handle;
-
-  g_assert (lmconn == priv->conn->lmconn);
-
-  from = lm_message_node_get_attribute (msg->node, "from");
-
-  if (from == NULL)
-    {
-      NODE_DEBUG (msg->node,
-          "presence stanza without from attribute, ignoring");
-      return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-    }
-
-  sub_type = lm_message_get_sub_type (msg);
-
-  room_handle = gabble_get_room_handle_from_jid (room_repo, from);
-  if (room_handle != 0)
-    muc_chan = g_hash_table_lookup (priv->text_channels,
-        GUINT_TO_POINTER (room_handle));
-
-  /* is it an error and for a MUC? */
-  if (sub_type == LM_MESSAGE_SUB_TYPE_ERROR
-      && muc_chan != NULL)
-    {
-      _gabble_muc_channel_presence_error (muc_chan, from, msg->node);
-
-      return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-    }
-
-  x_node = lm_message_node_get_child_with_namespace (msg->node, "x",
-      NS_MUC_USER);
-
-  /* is it a MUC member presence? */
-  if (x_node != NULL)
-    {
-      if (muc_chan != NULL)
-        {
-          TpHandle handle;
-          LmMessageNode *item_node;
-          const gchar *owner_jid;
-
-          handle = tp_handle_ensure (contact_repo, from,
-              GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
-          if (handle == 0)
-            {
-              NODE_DEBUG (msg->node,
-                  "discarding MUC presence from malformed jid");
-              return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-            }
-
-          item_node = lm_message_node_get_child (x_node, "item");
-          if (item_node == NULL)
-            {
-              DEBUG ("node missing 'item' child, ignoring");
-              return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-            }
-
-          owner_jid = lm_message_node_get_attribute (item_node, "jid");
-          /* We drop OLPC Gadget's inspector presence as activities
-           * doesn't have to see it as a member of the room and the
-           * presence cache should ignore it as well. */
-          if (owner_jid != NULL &&
-              !tp_strdiff (owner_jid, priv->conn->olpc_gadget_activity))
-            {
-              return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-            }
-
-          _gabble_muc_channel_member_presence_updated (muc_chan, handle,
-              msg, x_node, item_node);
-          tp_handle_unref (contact_repo, handle);
-        }
-      else
-        {
-          NODE_DEBUG (msg->node, "discarding unexpected MUC member presence");
-
-          return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-        }
-    }
-
-  /* is it presence from an in-MUC JID containing tubes info? */
-
-  if (muc_chan != NULL)
-    {
-      TpHandle handle;
-      GabbleTubesChannel *tubes_chan;
-
-      tubes_chan = g_hash_table_lookup (priv->tubes_channels,
-          GUINT_TO_POINTER (room_handle));
-      if (tubes_chan == NULL)
-        {
-          LmMessageNode *tubes_node;
-
-          tubes_node = lm_message_node_get_child_with_namespace (
-              msg->node, "tubes", NS_TUBES);
-
-          if (tubes_node == NULL)
-            /* presence doesn't contain tubes information, no need
-             * to create a tubes channel */
-            return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
-          /* MUC Tubes channels (as opposed to the individual tubes) don't
-           * have a well-defined initiator (they're a consensus) so use 0 */
-          tubes_chan = new_tubes_channel (fac, room_handle, muc_chan,
-              0, FALSE);
-          tp_channel_manager_emit_new_channel (fac,
-              TP_EXPORTABLE_CHANNEL (tubes_chan), NULL);
-        }
-
-      handle = tp_handle_ensure (contact_repo, from,
-          GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL);
-      if (handle == 0)
-        {
-          NODE_DEBUG (msg->node,
-              "discarding Tubes presence from malformed jid");
-          return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-        }
-
-      gabble_tubes_channel_presence_updated (tubes_chan, handle,
-          msg);
-      tp_handle_unref (contact_repo, handle);
-    }
-
+  /* we used to check if a room with the jid exists, instead at this  *
+   * point we stop caring: actual MUC messages are handled internally *
+   * by the wocky muc implementation                                  */
   return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
 }
 
-
 void
 gabble_muc_factory_broadcast_presence (GabbleMucFactory *self)
 {
@@ -1068,7 +747,6 @@ gabble_muc_factory_broadcast_presence (GabbleMucFactory *self)
     }
 }
 
-
 static void
 gabble_muc_factory_associate_request (GabbleMucFactory *self,
                                       gpointer channel,
@@ -1153,27 +831,14 @@ gabble_muc_factory_close_all (GabbleMucFactory *self)
       g_hash_table_destroy (tmp);
     }
 
-  if (priv->tubes_channels != NULL)
-    {
-      GHashTable *tmp = priv->tubes_channels;
-      priv->tubes_channels = NULL;
-      g_hash_table_destroy (tmp);
-    }
-
   if (priv->message_cb != NULL)
     {
       DEBUG ("removing callbacks");
-      g_assert (priv->presence_cb != NULL);
 
       lm_connection_unregister_message_handler (priv->conn->lmconn,
           priv->message_cb, LM_MESSAGE_TYPE_MESSAGE);
       lm_message_handler_unref (priv->message_cb);
       priv->message_cb = NULL;
-
-      lm_connection_unregister_message_handler (priv->conn->lmconn,
-          priv->presence_cb, LM_MESSAGE_TYPE_PRESENCE);
-      lm_message_handler_unref (priv->presence_cb);
-      priv->presence_cb = NULL;
     }
 }
 
@@ -1191,20 +856,12 @@ connection_status_changed_cb (GabbleConnection *conn,
     case TP_CONNECTION_STATUS_CONNECTING:
       DEBUG ("adding callbacks");
       g_assert (priv->message_cb == NULL);
-      g_assert (priv->presence_cb == NULL);
 
       priv->message_cb = lm_message_handler_new (muc_factory_message_cb,
           self, NULL);
       lm_connection_register_message_handler (priv->conn->lmconn,
           priv->message_cb, LM_MESSAGE_TYPE_MESSAGE,
           LM_HANDLER_PRIORITY_NORMAL);
-
-      priv->presence_cb = lm_message_handler_new (
-          muc_factory_presence_cb, self, NULL);
-      lm_connection_register_message_handler (priv->conn->lmconn,
-          priv->presence_cb, LM_MESSAGE_TYPE_PRESENCE,
-          LM_HANDLER_PRIORITY_NORMAL);
-
       break;
 
     case TP_CONNECTION_STATUS_DISCONNECTED:
@@ -1241,8 +898,20 @@ _foreach_slave (gpointer key, gpointer value, gpointer user_data)
 {
   struct _ForeachData *data = (struct _ForeachData *) user_data;
   TpExportableChannel *channel = TP_EXPORTABLE_CHANNEL (value);
+  GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (value);
+  GabbleTubesChannel *tube = NULL;
 
   data->foreach (channel, data->user_data);
+
+  g_object_get (gmuc, "tube", &tube, NULL);
+
+  if (tube != NULL)
+    {
+      channel = TP_EXPORTABLE_CHANNEL (tube);
+      data->foreach (channel, data->user_data);
+      gabble_tubes_channel_foreach (tube, data->foreach, data->user_data);
+      g_object_unref (tube);
+    }
 }
 
 static void
@@ -1253,27 +922,11 @@ gabble_muc_factory_foreach_channel (TpChannelManager *manager,
   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (manager);
   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
   struct _ForeachData data;
-  GHashTableIter iter;
-  gpointer value;
 
   data.user_data = user_data;
   data.foreach = foreach;
 
   g_hash_table_foreach (priv->text_channels, _foreach_slave, &data);
-
-  g_hash_table_iter_init (&iter, priv->tubes_channels);
-  while (g_hash_table_iter_next (&iter, NULL, &value))
-  {
-    TpExportableChannel *chan = TP_EXPORTABLE_CHANNEL (value);
-
-    /* Add channels of type Channel.Type.Tubes */
-    foreach (chan, user_data);
-
-    /* Add channels of type Channel.Type.{Stream|DBus}Tube which live in the
-     * GabbleTubesChannel object */
-    gabble_tubes_channel_foreach (GABBLE_TUBES_CHANNEL (chan), foreach,
-        user_data);
-  }
 }
 
 
@@ -1320,13 +973,16 @@ gabble_muc_factory_handle_si_stream_request (GabbleMucFactory *self,
   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
   TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
      (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM);
-  GabbleTubesChannel *chan;
+  GabbleMucChannel *gmuc = NULL;
+  GabbleTubesChannel *tube = NULL;
 
   g_return_if_fail (tp_handle_is_valid (room_repo, room_handle, NULL));
 
-  chan = g_hash_table_lookup (priv->tubes_channels,
+  gmuc = g_hash_table_lookup (priv->text_channels,
       GUINT_TO_POINTER (room_handle));
-  if (chan == NULL)
+  g_object_get (gmuc, "tube", &tube, NULL);
+
+  if (tube == NULL)
     {
       GError e = { GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
           "No tubes channel available for this MUC" };
@@ -1336,7 +992,8 @@ gabble_muc_factory_handle_si_stream_request (GabbleMucFactory *self,
       return;
     }
 
-  gabble_tubes_channel_bytestream_offered (chan, bytestream, msg);
+  gabble_tubes_channel_bytestream_offered (tube, bytestream, msg);
+  g_object_unref (tube);
 }
 
 GabbleMucChannel *
@@ -1421,17 +1078,18 @@ ensure_tubes_channel (GabbleMucFactory *self,
   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
   TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
   GabbleMucChannel *text_chan;
+  TpHandle initiator = base_conn->self_handle;
   gboolean result;
 
   result = ensure_muc_channel (self, priv, handle, &text_chan, FALSE);
 
-  *tubes_chan = new_tubes_channel (self, handle, text_chan,
-      base_conn->self_handle, requested);
+  /* this refs the tube channel object */
+  *tubes_chan = gabble_muc_channel_open_tube (text_chan, initiator, requested);
 
   if (!result)
-    {
-      g_hash_table_insert (priv->text_needed_for_tubes, text_chan, *tubes_chan);
-    }
+    g_hash_table_insert (priv->text_needed_for_tubes, text_chan, *tubes_chan);
+
+  g_object_unref (tubes_chan);
 
   return result;
 }
@@ -1485,7 +1143,8 @@ handle_tubes_channel_request (GabbleMucFactory *self,
                               GError **error)
 {
   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
-  GabbleTubesChannel *tubes_chan;
+  GabbleTubesChannel *tube = NULL;
+  GabbleMucChannel *gmuc = NULL;
 
   if (tp_channel_manager_asv_has_unknown_properties (request_properties,
           muc_tubes_channel_fixed_properties,
@@ -1493,10 +1152,12 @@ handle_tubes_channel_request (GabbleMucFactory *self,
           error))
     return FALSE;
 
-  tubes_chan = g_hash_table_lookup (priv->tubes_channels,
-      GUINT_TO_POINTER (handle));
+  gmuc = g_hash_table_lookup (priv->text_channels, GUINT_TO_POINTER (handle));
 
-  if (tubes_chan != NULL)
+  if (gmuc != NULL)
+    g_object_get (gmuc, "tube", &tube, NULL);
+
+  if (tube != NULL)
     {
       if (require_new)
         {
@@ -1507,22 +1168,21 @@ handle_tubes_channel_request (GabbleMucFactory *self,
       else
         {
           tp_channel_manager_emit_request_already_satisfied (self,
-              request_token, TP_EXPORTABLE_CHANNEL (tubes_chan));
+              request_token, TP_EXPORTABLE_CHANNEL (tube));
         }
     }
-  else if (ensure_tubes_channel (self, handle, &tubes_chan, TRUE))
+  else if (ensure_tubes_channel (self, handle, &tube, TRUE))
     {
       GSList *list = NULL;
 
       list = g_slist_prepend (list, request_token);
       tp_channel_manager_emit_new_channel (self,
-          TP_EXPORTABLE_CHANNEL (tubes_chan), list);
+          TP_EXPORTABLE_CHANNEL (tube), list);
       g_slist_free (list);
     }
   else
     {
-      gabble_muc_factory_associate_request (self, tubes_chan,
-          request_token);
+      gabble_muc_factory_associate_request (self, tube, request_token);
     }
 
   return TRUE;
@@ -1540,29 +1200,29 @@ handle_tube_channel_request (GabbleMucFactory *self,
   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
   gboolean can_announce_now = TRUE;
   gboolean tubes_channel_created = FALSE;
-  GabbleTubesChannel *tubes_chan;
+  GabbleTubesChannel *tube;
+  GabbleMucChannel * gmuc;
   GabbleTubeIface *new_channel;
 
-  tubes_chan = g_hash_table_lookup (priv->tubes_channels,
-      GUINT_TO_POINTER (handle));
-  if (tubes_chan == NULL)
+  gmuc = g_hash_table_lookup (priv->text_channels, GUINT_TO_POINTER (handle));
+  g_object_get (gmuc, "tube", &tube, NULL);
+
+  if (tube == NULL)
     {
       /* Need to create a tubes channel */
-      if (!ensure_tubes_channel (self, handle, &tubes_chan, FALSE))
+      if (!ensure_tubes_channel (self, handle, &tube, FALSE))
       {
         /* We have to wait the tubes channel before announcing */
         can_announce_now = FALSE;
-
-        gabble_muc_factory_associate_request (self, tubes_chan,
-            request_token);
+        gabble_muc_factory_associate_request (self, tube, request_token);
       }
 
       tubes_channel_created = TRUE;
     }
 
-  g_assert (tubes_chan != NULL);
+  g_assert (tube != NULL);
 
-  new_channel = gabble_tubes_channel_tube_request (tubes_chan,
+  new_channel = gabble_tubes_channel_tube_request (tube,
       request_token, request_properties, TRUE);
   g_assert (new_channel != NULL);
 
@@ -1575,7 +1235,7 @@ handle_tube_channel_request (GabbleMucFactory *self,
         NULL, NULL);
 
       if (tubes_channel_created)
-        g_hash_table_insert (channels, tubes_chan, NULL);
+        g_hash_table_insert (channels, tube, NULL);
 
       request_tokens = g_slist_prepend (NULL, request_token);
 
@@ -1589,11 +1249,11 @@ handle_tube_channel_request (GabbleMucFactory *self,
     {
       GSList *l;
 
-      l = g_hash_table_lookup (priv->tubes_needed_for_tube, tubes_chan);
-      g_hash_table_steal (priv->tubes_needed_for_tube, tubes_chan);
+      l = g_hash_table_lookup (priv->tubes_needed_for_tube, tube);
+      g_hash_table_steal (priv->tubes_needed_for_tube, tube);
 
       l = g_slist_prepend (l, new_channel);
-      g_hash_table_insert (priv->tubes_needed_for_tube, tubes_chan, l);
+      g_hash_table_insert (priv->tubes_needed_for_tube, tube, l);
     }
 
   return TRUE;
-- 
1.5.6.5




More information about the telepathy-commits mailing list