telepathygabble/src/muc-factory.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * muc-factory.c - Source for GabbleMucFactory
       
     3  * Copyright (C) 2006 Collabora Ltd.
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Lesser General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2.1 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Lesser General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Lesser General Public
       
    16  * License along with this library; if not, write to the Free Software
       
    17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    18  */
       
    19 
       
    20 
       
    21 #include <stdlib.h>
       
    22 #include <string.h>
       
    23 #include <time.h>
       
    24 
       
    25 #include <glib.h>
       
    26 
       
    27 #include <dbus/dbus-glib.h>
       
    28 #include <dbus/dbus-glib-lowlevel.h>
       
    29 
       
    30 #include "loudmouth/loudmouth.h"
       
    31 
       
    32 #include "debug.h"
       
    33 #include "disco.h"
       
    34 #include "gabble-connection.h"
       
    35 #include "gabble-presence-cache.h"
       
    36 #include "gabble-muc-channel.h"
       
    37 #include "gabble-roomlist-channel.h"
       
    38 #include "handles.h"
       
    39 #include "muc-factory.h"
       
    40 #include "namespaces.h"
       
    41 #include "telepathy-interfaces.h"
       
    42 #include "text-mixin.h"
       
    43 #include "tp-channel-factory-iface.h"
       
    44 #include "util.h"
       
    45 
       
    46 static void gabble_muc_factory_iface_init (gpointer g_iface, gpointer iface_data);
       
    47 
       
    48 #ifndef EMULATOR
       
    49 G_DEFINE_TYPE_WITH_CODE (GabbleMucFactory, gabble_muc_factory, G_TYPE_OBJECT,
       
    50     G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, gabble_muc_factory_iface_init));
       
    51 #endif
       
    52     
       
    53 #ifdef EMULATOR
       
    54 #include "libgabble_wsd_solution.h"
       
    55 
       
    56     GET_STATIC_VAR_FROM_TLS(gabble_muc_factory_parent_class,muc_factory,gpointer)
       
    57 	#define gabble_muc_factory_parent_class (*GET_WSD_VAR_NAME(gabble_muc_factory_parent_class,muc_factory,s)())
       
    58 	
       
    59 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,muc_factory,GType)
       
    60 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,muc_factory,s)())
       
    61 
       
    62 static void gabble_muc_factory_init (GabbleMucFactory *self); 
       
    63 static void gabble_muc_factory_class_init (GabbleMucFactoryClass *klass); 
       
    64 static void gabble_muc_factory_class_intern_init (gpointer klass) 
       
    65 {
       
    66  gabble_muc_factory_parent_class = g_type_class_peek_parent (klass); 
       
    67  gabble_muc_factory_class_init ((GabbleMucFactoryClass*) klass); 
       
    68  } 
       
    69  EXPORT_C GType gabble_muc_factory_get_type (void) 
       
    70  {
       
    71    if ((g_define_type_id == 0)) { static const GTypeInfo g_define_type_info = { sizeof (GabbleMucFactoryClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_muc_factory_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleMucFactory), 0, (GInstanceInitFunc) gabble_muc_factory_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleMucFactory"), &g_define_type_info, (GTypeFlags) 0); { { static const GInterfaceInfo g_implement_interface_info = { (GInterfaceInitFunc) gabble_muc_factory_iface_init }; g_type_add_interface_static (g_define_type_id, tp_channel_factory_iface_get_type(), &g_implement_interface_info); } ; } } return g_define_type_id; }    ;
       
    72 
       
    73 #endif
       
    74 #define DBUS_API_SUBJECT_TO_CHANGE
       
    75 #define DEBUG_FLAG GABBLE_DEBUG_MUC
       
    76 
       
    77 #ifdef DEBUG_FLAG
       
    78 //#define DEBUG(format, ...)
       
    79 #define DEBUGGING 0
       
    80 #define NODE_DEBUG(n, s)
       
    81 #endif /* DEBUG_FLAG */
       
    82 
       
    83 /* properties */
       
    84 enum
       
    85 {
       
    86   PROP_CONNECTION = 1,
       
    87   LAST_PROPERTY
       
    88 };
       
    89 
       
    90 typedef struct _GabbleMucFactoryPrivate GabbleMucFactoryPrivate;
       
    91 struct _GabbleMucFactoryPrivate
       
    92 {
       
    93   GabbleConnection *conn;
       
    94 
       
    95   LmMessageHandler *message_cb;
       
    96   LmMessageHandler *presence_cb;
       
    97 
       
    98   GHashTable *channels;
       
    99   GabbleRoomlistChannel *roomlist_channel;
       
   100 
       
   101   GHashTable *disco_requests;
       
   102 
       
   103   gboolean dispose_has_run;
       
   104 };
       
   105 
       
   106 #define GABBLE_MUC_FACTORY_GET_PRIVATE(o)    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MUC_FACTORY, GabbleMucFactoryPrivate))
       
   107 
       
   108 static GObject *gabble_muc_factory_constructor (GType type, guint n_props, GObjectConstructParam *props);
       
   109 
       
   110 static void
       
   111 gabble_muc_factory_init (GabbleMucFactory *fac)
       
   112 {
       
   113   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   114 
       
   115   priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
       
   116                                           NULL, g_object_unref);
       
   117 
       
   118   priv->disco_requests = g_hash_table_new_full (g_direct_hash, g_direct_equal,
       
   119                                                 NULL, NULL);
       
   120 
       
   121   priv->message_cb = NULL;
       
   122   priv->presence_cb = NULL;
       
   123 
       
   124   priv->conn = NULL;
       
   125   priv->dispose_has_run = FALSE;
       
   126 }
       
   127 
       
   128 static GObject *
       
   129 gabble_muc_factory_constructor (GType type, guint n_props,
       
   130                                 GObjectConstructParam *props)
       
   131 {
       
   132   GObject *obj;
       
   133   /* GabbleMucFactoryPrivate *priv; */
       
   134 
       
   135   obj = G_OBJECT_CLASS (gabble_muc_factory_parent_class)->
       
   136            constructor (type, n_props, props);
       
   137   /* priv = GABBLE_MUC_FACTORY_GET_PRIVATE (obj); */
       
   138 
       
   139   return obj;
       
   140 }
       
   141 
       
   142 static void
       
   143 cancel_disco_request (gpointer key, gpointer value, gpointer user_data)
       
   144 {
       
   145   GabbleDisco *disco = GABBLE_DISCO (user_data);
       
   146   GabbleDiscoRequest *request = (GabbleDiscoRequest *) key;
       
   147 
       
   148   gabble_disco_cancel_request (disco, request);
       
   149 }
       
   150 
       
   151 static void
       
   152 gabble_muc_factory_dispose (GObject *object)
       
   153 {
       
   154   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object);
       
   155   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   156 
       
   157   if (priv->dispose_has_run)
       
   158     return;
       
   159 
       
   160   gabble_debug (DEBUG_FLAG, "dispose called");
       
   161   priv->dispose_has_run = TRUE;
       
   162 
       
   163   tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object));
       
   164 
       
   165   g_hash_table_foreach (priv->disco_requests, cancel_disco_request, priv->conn->disco);
       
   166   g_hash_table_destroy (priv->disco_requests);
       
   167 
       
   168   if (G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose)
       
   169     G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose (object);
       
   170 }
       
   171 
       
   172 static void
       
   173 gabble_muc_factory_get_property (GObject    *object,
       
   174                                  guint       property_id,
       
   175                                  GValue     *value,
       
   176                                  GParamSpec *pspec)
       
   177 {
       
   178   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object);
       
   179   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   180 
       
   181   switch (property_id) {
       
   182     case PROP_CONNECTION:
       
   183       g_value_set_object (value, priv->conn);
       
   184       break;
       
   185     default:
       
   186       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   187       break;
       
   188   }
       
   189 }
       
   190 
       
   191 static void
       
   192 gabble_muc_factory_set_property (GObject      *object,
       
   193                                  guint         property_id,
       
   194                                  const GValue *value,
       
   195                                  GParamSpec   *pspec)
       
   196 {
       
   197   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object);
       
   198   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   199 
       
   200   switch (property_id) {
       
   201     case PROP_CONNECTION:
       
   202       priv->conn = g_value_get_object (value);
       
   203       break;
       
   204     default:
       
   205       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   206       break;
       
   207   }
       
   208 }
       
   209 
       
   210 static void
       
   211 gabble_muc_factory_class_init (GabbleMucFactoryClass *gabble_muc_factory_class)
       
   212 {
       
   213   GObjectClass *object_class = G_OBJECT_CLASS (gabble_muc_factory_class);
       
   214   GParamSpec *param_spec;
       
   215 
       
   216   g_type_class_add_private (gabble_muc_factory_class, sizeof (GabbleMucFactoryPrivate));
       
   217 
       
   218   object_class->constructor = gabble_muc_factory_constructor;
       
   219   object_class->dispose = gabble_muc_factory_dispose;
       
   220 
       
   221   object_class->get_property = gabble_muc_factory_get_property;
       
   222   object_class->set_property = gabble_muc_factory_set_property;
       
   223 
       
   224   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   225                                     "Gabble connection object that owns this "
       
   226                                     "MUC factory object.",
       
   227                                     GABBLE_TYPE_CONNECTION,
       
   228                                     G_PARAM_CONSTRUCT_ONLY |
       
   229                                     G_PARAM_READWRITE |
       
   230                                     G_PARAM_STATIC_NICK |
       
   231                                     G_PARAM_STATIC_BLURB);
       
   232   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
       
   233 }
       
   234 
       
   235 
       
   236 static GabbleMucChannel *
       
   237 get_muc_from_jid (GabbleMucFactory *fac, const gchar *jid)
       
   238 {
       
   239   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   240   GabbleHandle handle;
       
   241   GabbleMucChannel *chan = NULL;
       
   242 
       
   243   if (gabble_handle_for_room_exists (priv->conn->handles, jid, TRUE))
       
   244     {
       
   245       handle = gabble_handle_for_room (priv->conn->handles, jid);
       
   246 
       
   247       chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (handle));
       
   248     }
       
   249 
       
   250   return chan;
       
   251 }
       
   252 
       
   253 
       
   254 /**
       
   255  * muc_channel_closed_cb:
       
   256  *
       
   257  * Signal callback for when a MUC channel is closed. Removes the references
       
   258  * that MucFactory holds to them.
       
   259  */
       
   260 static void
       
   261 muc_channel_closed_cb (GabbleMucChannel *chan, gpointer user_data)
       
   262 {
       
   263   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
       
   264   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   265   GabbleHandle room_handle;
       
   266 
       
   267   if (priv->channels != NULL)
       
   268     {
       
   269       g_object_get (chan, "handle", &room_handle, NULL);
       
   270 
       
   271       gabble_debug (DEBUG_FLAG, "removing MUC channel with handle %d", room_handle);
       
   272 
       
   273       g_hash_table_remove (priv->channels, GINT_TO_POINTER (room_handle));
       
   274     }
       
   275 }
       
   276 
       
   277 static void
       
   278 muc_ready_cb (GabbleMucChannel *chan,
       
   279               gpointer data)
       
   280 {
       
   281   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data);
       
   282 
       
   283   gabble_debug (DEBUG_FLAG, "chan=%p", chan);
       
   284 
       
   285   g_signal_emit_by_name (fac, "new-channel", chan);
       
   286 }
       
   287 
       
   288 static void
       
   289 muc_join_error_cb (GabbleMucChannel *chan,
       
   290                    GError *error,
       
   291                    gpointer data)
       
   292 {
       
   293   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data);
       
   294 
       
   295   gabble_debug (DEBUG_FLAG, "error->code=%u, error->message=\"%s\"", error->code, error->message);
       
   296 
       
   297   g_signal_emit_by_name (fac, "channel-error", chan, error);
       
   298 }
       
   299 
       
   300 /**
       
   301  * new_muc_channel
       
   302  */
       
   303 static GabbleMucChannel *
       
   304 new_muc_channel (GabbleMucFactory *fac, GabbleHandle handle, gboolean invite_self)
       
   305 {
       
   306   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   307   GabbleMucChannel *chan;
       
   308   char *object_path;
       
   309 
       
   310   g_assert (g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)) == NULL);
       
   311 
       
   312   object_path = g_strdup_printf ("%s/MucChannel%u", priv->conn->object_path, handle);
       
   313 
       
   314   gabble_debug (DEBUG_FLAG, "creating new chan, object path %s", object_path);
       
   315 
       
   316   chan = g_object_new (GABBLE_TYPE_MUC_CHANNEL,
       
   317                        "connection", priv->conn,
       
   318                        "object-path", object_path,
       
   319                        "handle", handle,
       
   320                        "invite-self", invite_self,
       
   321                        NULL);
       
   322 
       
   323   g_signal_connect (chan, "closed", (GCallback) muc_channel_closed_cb, fac);
       
   324 
       
   325   g_hash_table_insert (priv->channels, GINT_TO_POINTER (handle), chan);
       
   326 
       
   327   g_free (object_path);
       
   328 
       
   329   g_signal_connect (chan, "ready", G_CALLBACK (muc_ready_cb), fac);
       
   330   g_signal_connect (chan, "join-error", G_CALLBACK (muc_join_error_cb),
       
   331                     fac);
       
   332 
       
   333   return chan;
       
   334 }
       
   335 
       
   336 
       
   337 struct DiscoInviteData {
       
   338     GabbleMucFactory *factory;
       
   339     gchar *reason;
       
   340     GabbleHandle inviter;
       
   341 };
       
   342 
       
   343 /**
       
   344  * obsolete_invite_disco_cb:
       
   345  *
       
   346  * Callback for disco request we fired upon encountering obsolete disco.
       
   347  * If the object is in fact MUC room, create a channel for it.
       
   348  */
       
   349 static void
       
   350 obsolete_invite_disco_cb (GabbleDisco *self,
       
   351                           GabbleDiscoRequest *request,
       
   352                           const gchar *jid,
       
   353                           const gchar *node,
       
   354                           LmMessageNode *query_result,
       
   355                           GError* error,
       
   356                           gpointer user_data)
       
   357 {
       
   358   struct DiscoInviteData *data = (struct DiscoInviteData *) user_data;
       
   359 
       
   360   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data->factory);
       
   361   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   362   LmMessageNode *identity;
       
   363   const char *category, *type;
       
   364   GabbleHandle handle;
       
   365 
       
   366   g_hash_table_remove (priv->disco_requests, request);
       
   367 
       
   368   identity = lm_message_node_get_child (query_result, "identity");
       
   369   if (NULL == identity)
       
   370     return;
       
   371 
       
   372   category = lm_message_node_get_attribute (identity, "category");
       
   373   if (NULL == category)
       
   374     return;
       
   375 
       
   376   type = lm_message_node_get_attribute (identity, "type");
       
   377   if (NULL == type)
       
   378     return;
       
   379 
       
   380   if (0 != strcmp (category, "conference") ||
       
   381       0 != strcmp (type, "text"))
       
   382     {
       
   383       gabble_debug (DEBUG_FLAG, "obsolete invite request specified invalid jid '%s', ignoring", jid);
       
   384     }
       
   385 
       
   386   /* OK, it's MUC after all, create a new channel */
       
   387   handle = gabble_handle_for_room (priv->conn->handles, jid);
       
   388 
       
   389   if (g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)) == NULL)
       
   390     {
       
   391       GabbleMucChannel *chan;
       
   392       chan = new_muc_channel (fac, handle, FALSE);
       
   393       _gabble_muc_channel_handle_invited (chan, data->inviter, data->reason);
       
   394     }
       
   395   else
       
   396     {
       
   397       gabble_debug (DEBUG_FLAG, "ignoring invite to a room '%s' we're already in", jid);
       
   398     }
       
   399 
       
   400   g_free (data->reason);
       
   401   g_free (data);
       
   402 }
       
   403 
       
   404 
       
   405 /**
       
   406  * muc_factory_message_cb:
       
   407  *
       
   408  * Called by loudmouth when we get an incoming <message>.
       
   409  * We filter only groupchat and MUC messages, ignoring the rest.
       
   410  */
       
   411 static LmHandlerResult
       
   412 muc_factory_message_cb (LmMessageHandler *handler,
       
   413                         LmConnection *connection,
       
   414                         LmMessage *message,
       
   415                         gpointer user_data)
       
   416 {
       
   417   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
       
   418   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   419 
       
   420   const gchar *from, *body, *body_offset;
       
   421   time_t stamp;
       
   422   TpChannelTextMessageType msgtype;
       
   423   LmMessageNode *node;
       
   424   TpHandleType handle_type;
       
   425   GabbleHandle room_handle, handle;
       
   426   GabbleMucChannel *chan;
       
   427   GabbleTextMixinSendError send_error;
       
   428 
       
   429   if (!gabble_text_mixin_parse_incoming_message (message, &from, &stamp,
       
   430         &msgtype, &body, &body_offset, &send_error))
       
   431     return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   432 
       
   433   /* does it have a muc subnode? */
       
   434   node = lm_message_node_get_child_with_namespace (message->node, "x",
       
   435       NS_MUC_USER);
       
   436   if (node != NULL)
       
   437     {
       
   438       /* and an invitation? */
       
   439       node = lm_message_node_get_child (node, "invite");
       
   440       if (node != NULL)
       
   441         {
       
   442           LmMessageNode *reason_node;
       
   443           const gchar *invite_from, *reason;
       
   444           GabbleHandle inviter_handle;
       
   445 
       
   446           if (send_error != CHANNEL_TEXT_SEND_NO_ERROR)
       
   447             {
       
   448               NODE_DEBUG (message->node, "got a MUC invitation message "
       
   449                              "with a send error; ignoring");
       
   450 
       
   451               return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   452             }
       
   453 
       
   454           invite_from = lm_message_node_get_attribute (node, "from");
       
   455           if (invite_from == NULL)
       
   456             {
       
   457               NODE_DEBUG (message->node, "got a MUC invitation message "
       
   458                              "without a from field on the invite node, "
       
   459                              "ignoring");
       
   460 
       
   461               return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   462             }
       
   463 
       
   464           inviter_handle = gabble_handle_for_contact (priv->conn->handles,
       
   465                                                       invite_from, FALSE);
       
   466 
       
   467           reason_node = lm_message_node_get_child (node, "reason");
       
   468           if (reason_node != NULL)
       
   469             {
       
   470               reason = lm_message_node_get_value (reason_node);
       
   471             }
       
   472           else
       
   473             {
       
   474               reason = "";
       
   475               NODE_DEBUG (message->node, "no MUC invite reason specified");
       
   476             }
       
   477 
       
   478           /* create the channel */
       
   479           handle = gabble_handle_for_room (priv->conn->handles, from);
       
   480 
       
   481           if (g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)) == NULL)
       
   482             {
       
   483               chan = new_muc_channel (fac, handle, FALSE);
       
   484               _gabble_muc_channel_handle_invited (chan, inviter_handle, reason);
       
   485             }
       
   486           else
       
   487             {
       
   488               NODE_DEBUG (message->node, "ignoring invite to a room we're already in");
       
   489             }
       
   490 
       
   491           return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   492         }
       
   493     }
       
   494   else
       
   495     {
       
   496       GabbleHandle inviter_handle;
       
   497       GabbleDiscoRequest *request;
       
   498       const gchar *reason;
       
   499       struct DiscoInviteData *disco_udata;
       
   500 
       
   501       /* check for obsolete invite method */
       
   502       for (node = message->node->children; node != NULL; node = node->next)
       
   503         if (strcmp (node->name, "x") == 0)
       
   504           if (lm_message_node_has_namespace (node, NS_X_CONFERENCE, NULL))
       
   505             break;
       
   506 
       
   507       if (node == NULL)
       
   508         goto HANDLE_MESSAGE;
       
   509 
       
   510       /* the room JID is in x */
       
   511       from = lm_message_node_get_attribute (node, "jid");
       
   512       if (from == NULL)
       
   513         return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   514 
       
   515       /* the inviter JID is in "from" */
       
   516       inviter_handle = gabble_handle_for_contact (priv->conn->handles,
       
   517                                                   from, FALSE);
       
   518 
       
   519       /* reason is the body */
       
   520       reason = body;
       
   521 
       
   522       disco_udata = g_new0 (struct DiscoInviteData, 1);
       
   523       disco_udata->factory = fac;
       
   524       disco_udata->reason = g_strdup (reason);
       
   525       disco_udata->inviter = inviter_handle;
       
   526 
       
   527       NODE_DEBUG (message->node, "received obsolete invite method");
       
   528 
       
   529       request = gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO,
       
   530           from, NULL, obsolete_invite_disco_cb, disco_udata, G_OBJECT (fac), NULL);
       
   531 
       
   532       if (request != NULL)
       
   533         g_hash_table_insert (priv->disco_requests, request, NULL);
       
   534 
       
   535       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   536   }
       
   537 
       
   538 HANDLE_MESSAGE:
       
   539 
       
   540   /* check if a room with the jid exists */
       
   541   if (!gabble_handle_for_room_exists (priv->conn->handles, from, TRUE))
       
   542     {
       
   543       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   544     }
       
   545 
       
   546   room_handle = gabble_handle_for_room (priv->conn->handles, from);
       
   547 
       
   548   /* find the MUC channel */
       
   549   chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (room_handle));
       
   550 
       
   551   if (chan == NULL)
       
   552     {
       
   553       g_warning ("%s: ignoring groupchat message from known handle with "
       
   554                  "no MUC channel", G_STRFUNC);
       
   555 
       
   556       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   557     }
       
   558 
       
   559   /* get the handle of the sender, which is either the room
       
   560    * itself or one of its members */
       
   561   if (gabble_handle_for_room_exists (priv->conn->handles, from, FALSE))
       
   562     {
       
   563       handle_type = TP_HANDLE_TYPE_ROOM;
       
   564       handle = room_handle;
       
   565     }
       
   566   else
       
   567     {
       
   568       handle_type = TP_HANDLE_TYPE_CONTACT;
       
   569       handle = gabble_handle_for_contact (priv->conn->handles, from, TRUE);
       
   570     }
       
   571 
       
   572   if (send_error != CHANNEL_TEXT_SEND_NO_ERROR)
       
   573     {
       
   574       _gabble_text_mixin_send_error_signal (G_OBJECT (chan), send_error, stamp,
       
   575           msgtype, body_offset);
       
   576       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   577     }
       
   578 
       
   579   if (_gabble_muc_channel_receive (chan, msgtype, handle_type, handle, stamp,
       
   580                                    body_offset, message))
       
   581     return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   582 
       
   583   return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   584 }
       
   585 
       
   586 
       
   587 /**
       
   588  * connection_presence_muc_cb:
       
   589  * @handler: #LmMessageHandler for this message
       
   590  * @connection: #LmConnection that originated the message
       
   591  * @message: the presence message
       
   592  * @user_data: callback data
       
   593  *
       
   594  * Called by loudmouth when we get an incoming <presence>.
       
   595  */
       
   596 static LmHandlerResult
       
   597 muc_factory_presence_cb (LmMessageHandler *handler,
       
   598                             LmConnection *lmconn,
       
   599                             LmMessage *msg,
       
   600                             gpointer user_data)
       
   601 {
       
   602   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
       
   603   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   604   const char *from;
       
   605   LmMessageSubType sub_type;
       
   606   GabbleMucChannel *muc_chan;
       
   607   LmMessageNode *x_node;
       
   608 
       
   609   g_assert (lmconn == priv->conn->lmconn);
       
   610 
       
   611   from = lm_message_node_get_attribute (msg->node, "from");
       
   612 
       
   613   if (from == NULL)
       
   614     {
       
   615       NODE_DEBUG (msg->node, "presence stanza without from attribute, ignoring");
       
   616       return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   617     }
       
   618 
       
   619   sub_type = lm_message_get_sub_type (msg);
       
   620 
       
   621   muc_chan = get_muc_from_jid (fac, from);
       
   622 
       
   623   /* is it an error and for a MUC? */
       
   624   if (sub_type == LM_MESSAGE_SUB_TYPE_ERROR
       
   625       && muc_chan != NULL)
       
   626     {
       
   627       _gabble_muc_channel_presence_error (muc_chan, from, msg->node);
       
   628 
       
   629       return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   630     }
       
   631 
       
   632   x_node = lm_message_node_get_child_with_namespace (msg->node, "x", NS_MUC_USER);
       
   633 
       
   634   /* is it a MUC member presence? */
       
   635   if (x_node != NULL)
       
   636     {
       
   637       if (muc_chan != NULL)
       
   638         {
       
   639           GabbleHandle handle;
       
   640 
       
   641           handle = gabble_handle_for_contact (priv->conn->handles, from, TRUE);
       
   642           if (handle == 0)
       
   643             {
       
   644               NODE_DEBUG (msg->node, "discarding MUC presence from malformed jid");
       
   645               return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   646             }
       
   647 
       
   648           _gabble_muc_channel_member_presence_updated (muc_chan, handle,
       
   649                                                        msg, x_node);
       
   650         }
       
   651       else
       
   652         {
       
   653           NODE_DEBUG (msg->node, "discarding unexpected MUC member presence");
       
   654 
       
   655           return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   656         }
       
   657     }
       
   658 
       
   659   return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   660 
       
   661 }
       
   662 
       
   663 static void
       
   664 roomlist_channel_closed_cb (GabbleRoomlistChannel *chan, gpointer data)
       
   665 {
       
   666   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data);
       
   667   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   668 
       
   669   if (priv->roomlist_channel != NULL)
       
   670     {
       
   671       g_object_unref (priv->roomlist_channel);
       
   672       priv->roomlist_channel = NULL;
       
   673     }
       
   674 }
       
   675 
       
   676 static gboolean
       
   677 make_roomlist_channel (GabbleMucFactory *fac)
       
   678 {
       
   679   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   680 
       
   681   if (priv->roomlist_channel == NULL)
       
   682     {
       
   683       const gchar *server;
       
   684       gchar *object_path;
       
   685 
       
   686       server = _gabble_connection_find_conference_server (priv->conn);
       
   687 
       
   688       if (server == NULL)
       
   689         return FALSE;
       
   690 
       
   691       object_path = g_strdup_printf ("%s/RoomlistChannel", priv->conn->object_path);
       
   692 
       
   693       priv->roomlist_channel = _gabble_roomlist_channel_new (priv->conn,
       
   694           object_path, server);
       
   695 
       
   696       g_signal_connect (priv->roomlist_channel, "closed",
       
   697                         (GCallback) roomlist_channel_closed_cb, fac);
       
   698 
       
   699       g_signal_emit_by_name (fac, "new-channel", priv->roomlist_channel);
       
   700 
       
   701       g_free (object_path);
       
   702     }
       
   703 
       
   704   return TRUE;
       
   705 }
       
   706 
       
   707 
       
   708 
       
   709 static void
       
   710 gabble_muc_factory_iface_close_all (TpChannelFactoryIface *iface)
       
   711 {
       
   712   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface);
       
   713   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   714 
       
   715   gabble_debug (DEBUG_FLAG, "closing channels");
       
   716 
       
   717   if (priv->channels != NULL)
       
   718     {
       
   719       GHashTable *tmp = priv->channels;
       
   720       priv->channels = NULL;
       
   721       g_hash_table_destroy (tmp);
       
   722     }
       
   723 
       
   724   if (priv->roomlist_channel != NULL)
       
   725     {
       
   726       GObject *tmp = G_OBJECT (priv->roomlist_channel);
       
   727       priv->roomlist_channel = NULL;
       
   728       g_object_unref (tmp);
       
   729     }
       
   730 }
       
   731 
       
   732 static void
       
   733 gabble_muc_factory_iface_connecting (TpChannelFactoryIface *iface)
       
   734 {
       
   735   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface);
       
   736   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   737 
       
   738   gabble_debug (DEBUG_FLAG, "adding callbacks");
       
   739 
       
   740   g_assert (priv->message_cb == NULL);
       
   741   g_assert (priv->presence_cb == NULL);
       
   742 
       
   743   priv->message_cb = lm_message_handler_new (muc_factory_message_cb, fac, NULL);
       
   744   lm_connection_register_message_handler (priv->conn->lmconn, priv->message_cb,
       
   745                                           LM_MESSAGE_TYPE_MESSAGE,
       
   746                                           LM_HANDLER_PRIORITY_NORMAL);
       
   747 
       
   748   priv->presence_cb = lm_message_handler_new (muc_factory_presence_cb, fac, NULL);
       
   749   lm_connection_register_message_handler (priv->conn->lmconn, priv->presence_cb,
       
   750                                           LM_MESSAGE_TYPE_PRESENCE,
       
   751                                           LM_HANDLER_PRIORITY_NORMAL);
       
   752 }
       
   753 
       
   754 
       
   755 static void
       
   756 gabble_muc_factory_iface_connected (TpChannelFactoryIface *iface)
       
   757 {
       
   758   /* nothing to do */
       
   759 }
       
   760 
       
   761 static void
       
   762 gabble_muc_factory_iface_disconnected (TpChannelFactoryIface *iface)
       
   763 {
       
   764   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface);
       
   765   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   766 
       
   767   gabble_debug (DEBUG_FLAG, "removing callbacks");
       
   768 
       
   769   g_assert (priv->message_cb != NULL);
       
   770   g_assert (priv->presence_cb != NULL);
       
   771 
       
   772   lm_connection_unregister_message_handler (priv->conn->lmconn, priv->message_cb,
       
   773                                             LM_MESSAGE_TYPE_MESSAGE);
       
   774   lm_message_handler_unref (priv->message_cb);
       
   775   priv->message_cb = NULL;
       
   776 
       
   777   lm_connection_unregister_message_handler (priv->conn->lmconn, priv->presence_cb,
       
   778                                             LM_MESSAGE_TYPE_PRESENCE);
       
   779   lm_message_handler_unref (priv->presence_cb);
       
   780   priv->presence_cb = NULL;
       
   781 }
       
   782 
       
   783 struct _ForeachData
       
   784 {
       
   785   TpChannelFunc foreach;
       
   786   gpointer user_data;
       
   787 };
       
   788 
       
   789 static void
       
   790 _foreach_slave (gpointer key, gpointer value, gpointer user_data)
       
   791 {
       
   792   struct _ForeachData *data = (struct _ForeachData *) user_data;
       
   793   TpChannelIface *chan = TP_CHANNEL_IFACE (value);
       
   794 
       
   795   data->foreach (chan, data->user_data);
       
   796 }
       
   797 
       
   798 static void
       
   799 gabble_muc_factory_iface_foreach (TpChannelFactoryIface *iface, TpChannelFunc foreach, gpointer user_data)
       
   800 {
       
   801   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface);
       
   802   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   803   struct _ForeachData data;
       
   804 
       
   805   data.user_data = user_data;
       
   806   data.foreach = foreach;
       
   807 
       
   808   g_hash_table_foreach (priv->channels, _foreach_slave, &data);
       
   809 
       
   810   if (priv->roomlist_channel != NULL)
       
   811     foreach (TP_CHANNEL_IFACE (priv->roomlist_channel), user_data);
       
   812 }
       
   813 
       
   814 static TpChannelFactoryRequestStatus
       
   815 gabble_muc_factory_iface_request (TpChannelFactoryIface *iface,
       
   816                                   const gchar *chan_type,
       
   817                                   TpHandleType handle_type,
       
   818                                   guint handle,
       
   819                                   TpChannelIface **ret,
       
   820                                   GError **error)
       
   821 {
       
   822   GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface);
       
   823   GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
       
   824   GabbleMucChannel *chan;
       
   825 
       
   826   if (!g_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_ROOM_LIST))
       
   827     {
       
   828       /* FIXME - delay if services aren't discovered yet? */
       
   829       if (!make_roomlist_channel (fac))
       
   830         {
       
   831           gabble_debug (DEBUG_FLAG, "no conference server available for roomlist request");
       
   832           return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE;
       
   833         }
       
   834       *ret = TP_CHANNEL_IFACE (priv->roomlist_channel);
       
   835       return TP_CHANNEL_FACTORY_REQUEST_STATUS_DONE;
       
   836     }
       
   837 
       
   838   if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT))
       
   839     return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED;
       
   840 
       
   841   if (handle_type != TP_HANDLE_TYPE_ROOM)
       
   842     return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE;
       
   843 
       
   844   if (!gabble_handle_is_valid (priv->conn->handles, TP_HANDLE_TYPE_ROOM, handle, NULL))
       
   845     return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE;
       
   846 
       
   847   chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle));
       
   848   if (!chan)
       
   849     {
       
   850       chan = new_muc_channel (fac, handle, TRUE);
       
   851       return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED;
       
   852     }
       
   853 
       
   854   if (_gabble_muc_channel_is_ready (chan))
       
   855     {
       
   856       *ret = TP_CHANNEL_IFACE (chan);
       
   857       return TP_CHANNEL_FACTORY_REQUEST_STATUS_DONE;
       
   858     }
       
   859   else
       
   860     {
       
   861       return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED;
       
   862     }
       
   863 }
       
   864 
       
   865 static void
       
   866 gabble_muc_factory_iface_init (gpointer g_iface,
       
   867                               gpointer iface_data)
       
   868 {
       
   869   TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface;
       
   870 
       
   871   klass->close_all = gabble_muc_factory_iface_close_all;
       
   872   klass->connecting = gabble_muc_factory_iface_connecting;
       
   873   klass->connected = gabble_muc_factory_iface_connected;
       
   874   klass->disconnected = gabble_muc_factory_iface_disconnected;
       
   875   klass->foreach = gabble_muc_factory_iface_foreach;
       
   876   klass->request = gabble_muc_factory_iface_request;
       
   877 }
       
   878 
       
   879