]> git.0d.be Git - empathy.git/blobdiff - libempathy/empathy-dispatcher.c
Remove useless mission-control includes
[empathy.git] / libempathy / empathy-dispatcher.c
index 04e248d2d97e128feef7ec70b327563b2dbce1c8..8618be6e0fc1ccc5064d3f64306b61bd90ebc3d2 100644 (file)
@@ -1,4 +1,4 @@
-/* * Copyright (C) 2007-2008 Collabora Ltd.
+/* * Copyright (C) 2007-2009 Collabora Ltd.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,6 +15,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  * Authors: Xavier Claessens <xclaesse@gmail.com>
+ *          Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *          Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
  */
 
 #include <config.h>
@@ -30,8 +32,6 @@
 #include <telepathy-glib/proxy-subclass.h>
 #include <telepathy-glib/gtypes.h>
 
-#include <libmissioncontrol/mission-control.h>
-
 #include <extensions/extensions.h>
 
 #include "empathy-dispatcher.h"
@@ -58,6 +58,8 @@ typedef struct
 
   /* channels which the dispatcher is listening "invalidated" */
   GList *channels;
+
+  GHashTable *request_channel_class_async_ids;
 } EmpathyDispatcherPriv;
 
 G_DEFINE_TYPE (EmpathyDispatcher, empathy_dispatcher, G_TYPE_OBJECT);
@@ -73,6 +75,11 @@ enum
 static guint signals[LAST_SIGNAL];
 static EmpathyDispatcher *dispatcher = NULL;
 
+static GList * empathy_dispatcher_find_channel_classes
+  (EmpathyDispatcher *dispatcher, TpConnection *connection,
+   const gchar *channel_type, guint handle_type, GArray *fixed_properties);
+
+
 typedef struct
 {
   EmpathyDispatcher *dispatcher;
@@ -103,7 +110,16 @@ typedef struct
   GHashTable *dispatched_channels;
   /* ObjectPath -> EmpathyDispatchOperations */
   GHashTable *dispatching_channels;
-  /* ObjectPath -> EmpathyDispatchOperations */
+
+  /* ObjectPath -> EmpathyDispatchOperations
+   *
+   * This holds channels which were announced with NewChannel while we have an
+   * outstanding channel request for a channel of this type. On the Requests
+   * interface, CreateChannel and EnsureChannel are guaranteed by the spec to
+   * return before NewChannels is emitted, but there was no guarantee of the
+   * ordering of RequestChannel vs. NewChannel. So if necessary, channels are
+   * held in limbo here until we know whether they were requested.
+   */
   GHashTable *outstanding_channels;
   /* List of DispatcherRequestData */
   GList *outstanding_requests;
@@ -117,6 +133,7 @@ typedef struct
   TpConnection *connection;
   char *channel_type;
   guint handle_type;
+  GArray *properties;
   EmpathyDispatcherFindChannelClassCb *callback;
   gpointer user_data;
 } FindChannelRequest;
@@ -233,8 +250,23 @@ free_connection_data (ConnectionData *cd)
 static void
 free_find_channel_request (FindChannelRequest *r)
 {
+  int idx;
+  char *str;
+
   g_object_unref (r->dispatcher);
   g_free (r->channel_type);
+
+  if (r->properties != NULL)
+    {
+      for (idx = 0; idx < r->properties->len ; idx++)
+        {
+          str = g_array_index (r->properties, char *, idx);
+          g_free (str);
+        }
+
+      g_array_free (r->properties, TRUE);
+    }
+
   g_slice_free (FindChannelRequest, r);
 }
 
@@ -498,8 +530,8 @@ dispatcher_flush_outstanding_operations (EmpathyDispatcher *self,
 
       if (dispatcher_operation_can_start (self, operation, cd))
         {
-          dispatcher_start_dispatching (dispatcher, operation, cd);
           g_hash_table_iter_remove (&iter);
+          dispatcher_start_dispatching (dispatcher, operation, cd);
         }
     }
 }
@@ -733,7 +765,7 @@ dispatcher_connection_got_all (TpProxy *proxy,
       ConnectionData *cd;
       GList *requests, *l;
       FindChannelRequest *request;
-      GStrv retval;
+      GList *retval;
 
       cd = g_hash_table_lookup (priv->connections, proxy);
       g_assert (cd != NULL);
@@ -748,12 +780,13 @@ dispatcher_connection_got_all (TpProxy *proxy,
         {
           request = l->data;
 
-          retval = empathy_dispatcher_find_channel_class (dispatcher,
+          retval = empathy_dispatcher_find_channel_classes (dispatcher,
               TP_CONNECTION (proxy), request->channel_type,
-              request->handle_type);
+              request->handle_type, request->properties);
           request->callback (retval, request->user_data);
 
           free_find_channel_request (request);
+          g_list_free (retval);
         }
 
       g_list_free (requests);
@@ -872,6 +905,17 @@ dispatcher_new_connection_cb (EmpathyAccountManager *manager,
   g_ptr_array_free (capabilities, TRUE);
 }
 
+static void
+remove_idle_handlers (gpointer key,
+                      gpointer value,
+                      gpointer user_data)
+{
+  guint source_id;
+
+  source_id = GPOINTER_TO_UINT (value);
+  g_source_remove (source_id);
+}
+
 static GObject *
 dispatcher_constructor (GType type,
                         guint n_construct_params,
@@ -904,6 +948,13 @@ dispatcher_finalize (GObject *object)
   gpointer connection;
   GList *list;
 
+  if (priv->request_channel_class_async_ids != NULL)
+    {
+      g_hash_table_foreach (priv->request_channel_class_async_ids,
+        remove_idle_handlers, NULL);
+      g_hash_table_destroy (priv->request_channel_class_async_ids);
+    }
+
   g_signal_handlers_disconnect_by_func (priv->account_manager,
       dispatcher_new_connection_cb, object);
 
@@ -1008,6 +1059,9 @@ empathy_dispatcher_init (EmpathyDispatcher *dispatcher)
       g_object_unref (l->data);
     }
   g_list_free (connections);
+
+  priv->request_channel_class_async_ids = g_hash_table_new (g_direct_hash,
+    g_direct_equal);
 }
 
 EmpathyDispatcher *
@@ -1370,16 +1424,100 @@ empathy_dispatcher_create_channel (EmpathyDispatcher *dispatcher,
     G_OBJECT (request_data->dispatcher));
 }
 
-GStrv
-empathy_dispatcher_find_channel_class (EmpathyDispatcher *dispatcher,
-                                       TpConnection *connection,
-                                       const gchar *channel_type,
-                                       guint handle_type)
+static gboolean
+channel_class_matches (GValueArray *class,
+                       const char *channel_type,
+                       guint handle_type,
+                       GArray *fixed_properties)
+{
+  GHashTable *fprops;
+  GValue *v;
+  const char *c_type;
+  guint h_type;
+  gboolean valid;
+
+  v = g_value_array_get_nth (class, 0);
+
+  /* if the class doesn't match channel type discard it. */
+  fprops = g_value_get_boxed (v);
+  c_type = tp_asv_get_string (fprops, TP_IFACE_CHANNEL ".ChannelType");
+
+  if (tp_strdiff (channel_type, c_type))
+    return FALSE;
+
+  /* we have the right channel type, see if the handle type matches */
+  h_type = tp_asv_get_uint32 (fprops,
+                              TP_IFACE_CHANNEL ".TargetHandleType", &valid);
+
+  if (!valid || handle_type != h_type)
+    return FALSE;
+
+  if (fixed_properties != NULL)
+    {
+      gpointer h_key, h_val;
+      int idx;
+      GHashTableIter iter;
+      gboolean found;
+
+      g_hash_table_iter_init (&iter, fprops);
+
+      while (g_hash_table_iter_next (&iter, &h_key, &h_val))
+        {
+          /* discard ChannelType and TargetHandleType, as we already
+           * checked them.
+           */
+          if (!tp_strdiff ((char *) h_key, TP_IFACE_CHANNEL ".ChannelType") ||
+              !tp_strdiff
+                ((char *) h_key, TP_IFACE_CHANNEL ".TargetHandleType"))
+            continue;
+
+          found = FALSE;
+
+          for (idx = 0; idx < fixed_properties->len; idx++)
+            {
+              /* if |key| doesn't exist in |fixed_properties|, discard
+               * the class.
+               */
+              if (!tp_strdiff
+                    ((char *) h_key,
+                     g_array_index (fixed_properties, char *, idx)))
+                {
+                  found = TRUE;
+                  /* exit the for() loop */
+                  break;
+                }
+            }
+
+          if (!found)
+            return FALSE;
+        }
+    }
+  else
+    {
+      /* if no fixed_properties are specified, discard the classes
+       * with some fixed properties other than the two we already
+       * checked.
+       */
+      if (g_hash_table_size (fprops) > 2)
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static GList *
+empathy_dispatcher_find_channel_classes (EmpathyDispatcher *dispatcher,
+                                         TpConnection *connection,
+                                         const gchar *channel_type,
+                                         guint handle_type,
+                                         GArray *fixed_properties)
 {
   EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
-  ConnectionData *cd;
-  int i;
+  GValueArray *class;
   GPtrArray *classes;
+  GList *matching_classes;
+  int i;
+  ConnectionData *cd;
 
   g_return_val_if_fail (channel_type != NULL, NULL);
   g_return_val_if_fail (handle_type != 0, NULL);
@@ -1393,54 +1531,51 @@ empathy_dispatcher_find_channel_class (EmpathyDispatcher *dispatcher,
   if (classes == NULL)
     return NULL;
 
+  matching_classes = NULL;
+
   for (i = 0; i < classes->len; i++)
     {
-      GValueArray *class;
-      GValue *fixed;
-      GValue *allowed;
-      GHashTable *fprops;
-      const gchar *c_type;
-      guint32 h_type;
-      gboolean valid;
-
       class = g_ptr_array_index (classes, i);
-      fixed = g_value_array_get_nth (class, 0);
 
-      fprops = g_value_get_boxed (fixed);
-      c_type = tp_asv_get_string (fprops, TP_IFACE_CHANNEL ".ChannelType");
-
-      if (tp_strdiff (channel_type, c_type))
+      if (!channel_class_matches
+          (class, channel_type, handle_type, fixed_properties))
         continue;
 
-      h_type = tp_asv_get_uint32 (fprops,
-        TP_IFACE_CHANNEL ".TargetHandleType", &valid);
-
-      if (!valid || handle_type != h_type)
-        continue;
-
-      allowed = g_value_array_get_nth (class, 1);
-
-      return g_value_get_boxed (allowed);
+      matching_classes = g_list_prepend (matching_classes, class);
     }
 
-  return NULL;
+  return matching_classes;
 }
 
 static gboolean
 find_channel_class_idle_cb (gpointer user_data)
 {
-  GStrv retval;
+  GList *retval;
   GList *requests;
   FindChannelRequest *request = user_data;
+  ConnectionData *cd;
+  gboolean is_ready = TRUE;
   EmpathyDispatcherPriv *priv = GET_PRIV (request->dispatcher);
 
-  retval = empathy_dispatcher_find_channel_class (request->dispatcher,
-      request->connection, request->channel_type, request->handle_type);
+  g_hash_table_remove (priv->request_channel_class_async_ids, request);
+
+  cd = g_hash_table_lookup (priv->connections, request->connection);
+
+  if (cd == NULL)
+    is_ready = FALSE;
+  else if (cd->requestable_channels == NULL)
+    is_ready = FALSE;
 
-  if (retval)
+  if (is_ready)
     {
+      retval = empathy_dispatcher_find_channel_classes (request->dispatcher,
+          request->connection, request->channel_type, request->handle_type,
+          request->properties);
+
       request->callback (retval, request->user_data);
       free_find_channel_request (request);
+      g_list_free (retval);
+
       return FALSE;
     }
 
@@ -1454,21 +1589,153 @@ find_channel_class_idle_cb (gpointer user_data)
   return FALSE;
 }
 
+static GArray *
+setup_varargs (va_list var_args,
+               const char *channel_namespace,
+               const char *first_property_name)
+{
+  const char *name;
+  char *name_full;
+  GArray *properties;
+
+  if (first_property_name == NULL)
+    return NULL;
+
+  name = first_property_name;
+  properties = g_array_new (TRUE, TRUE, sizeof (char *));
+
+  while (name != NULL)
+    {
+      name_full = g_strdup (name);
+      properties = g_array_append_val (properties, name_full);
+      name = va_arg (var_args, char *);
+    }
+
+  return properties;
+}
+
+/**
+ * empathy_dispatcher_find_requestable_channel_classes:
+ * @dispatcher: an #EmpathyDispatcher
+ * @connection: a #TpConnection
+ * @channel_type: a string identifying the type of the channel to lookup
+ * @handle_type: the handle type for the channel
+ * @first_property_name: %NULL, or the name of the first fixed property,
+ * followed optionally by more names, followed by %NULL.
+ *
+ * Returns all the channel classes that a client can request for the connection
+ * @connection, of the type identified by @channel_type, @handle_type and the
+ * fixed properties list.
+ * The classes which are compatible with a fixed properties list (i.e. those
+ * that will be returned by this function) are intended as those that do not
+ * contain any fixed property other than those in the list; note that this
+ * doesn't guarantee that all the classes compatible with the list will contain
+ * all the requested fixed properties, so the clients will have to filter
+ * the returned list themselves.
+ * If @first_property_name is %NULL, only the classes with no other fixed
+ * properties than ChannelType and TargetHandleType will be returned.
+ * Note that this function may return %NULL without performing any lookup if
+ * @connection is not ready. To ensure that @connection is always ready,
+ * use the empathy_dispatcher_find_requestable_channel_classes_async() variant.
+ *
+ * Return value: a #GList of #GValueArray objects, where the first element in
+ * the array is a #GHashTable of the fixed properties, and the second is
+ * a #GStrv of the allowed properties for the class. The list should be free'd
+ * with g_list_free() when done, but the objects inside the list are owned
+ * by the #EmpathyDispatcher and must not be modified.
+ */
+GList *
+empathy_dispatcher_find_requestable_channel_classes
+                                 (EmpathyDispatcher *dispatcher,
+                                  TpConnection *connection,
+                                  const gchar *channel_type,
+                                  guint handle_type,
+                                  const char *first_property_name,
+                                  ...)
+{
+  va_list var_args;
+  GArray *properties;
+  EmpathyDispatcherPriv *priv;
+  GList *retval;
+  int idx;
+  char *str;
+
+  g_return_val_if_fail (EMPATHY_IS_DISPATCHER (dispatcher), NULL);
+  g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
+  g_return_val_if_fail (channel_type != NULL, NULL);
+  g_return_val_if_fail (handle_type != 0, NULL);
+
+  priv = GET_PRIV (dispatcher);
+
+  va_start (var_args, first_property_name);
+
+  properties = setup_varargs (var_args, channel_type, first_property_name);
+
+  va_end (var_args);
+
+  retval = empathy_dispatcher_find_channel_classes (dispatcher, connection,
+    channel_type, handle_type, properties);
+
+  if (properties != NULL)
+    {
+      /* free the properties array */
+      for (idx = 0; idx < properties->len ; idx++)
+        {
+          str = g_array_index (properties, char *, idx);
+          g_free (str);
+        }
+
+      g_array_free (properties, TRUE);
+    }
+
+  return retval;
+}
+
+/**
+ * empathy_dispatcher_find_requestable_channel_classes_async:
+ * @dispatcher: an #EmpathyDispatcher
+ * @connection: a #TpConnection
+ * @channel_type: a string identifying the type of the channel to lookup
+ * @handle_type: the handle type for the channel
+ * @callback: the callback to call when @connection is ready
+ * @user_data: the user data to pass to @callback
+ * @first_property_name: %NULL, or the name of the first fixed property,
+ * followed optionally by more names, followed by %NULL.
+ *
+ * Please see the documentation of
+ * empathy_dispatcher_find_requestable_channel_classes() for a detailed
+ * description of this function.
+ */
 void
-empathy_dispatcher_find_channel_class_async (EmpathyDispatcher *dispatcher,
-                                             TpConnection *connection,
-                                             const gchar *channel_type,
-                                             guint handle_type,
-                                             EmpathyDispatcherFindChannelClassCb callback,
-                                             gpointer user_data)
+empathy_dispatcher_find_requestable_channel_classes_async
+                                 (EmpathyDispatcher *dispatcher,
+                                  TpConnection *connection,
+                                  const gchar *channel_type,
+                                  guint handle_type,
+                                  EmpathyDispatcherFindChannelClassCb callback,
+                                  gpointer user_data,
+                                  const char *first_property_name,
+                                  ...)
 {
+  va_list var_args;
+  GArray *properties;
   FindChannelRequest *request;
+  EmpathyDispatcherPriv *priv;
+  guint source_id;
 
   g_return_if_fail (EMPATHY_IS_DISPATCHER (dispatcher));
   g_return_if_fail (TP_IS_CONNECTION (connection));
   g_return_if_fail (channel_type != NULL);
   g_return_if_fail (handle_type != 0);
 
+  priv = GET_PRIV (dispatcher);
+
+  va_start (var_args, first_property_name);
+
+  properties = setup_varargs (var_args, channel_type, first_property_name);
+
+  va_end (var_args);
+
   /* append another request for this connection */
   request = g_slice_new0 (FindChannelRequest);
   request->dispatcher = dispatcher;
@@ -1477,6 +1744,10 @@ empathy_dispatcher_find_channel_class_async (EmpathyDispatcher *dispatcher,
   request->connection = connection;
   request->callback = callback;
   request->user_data = user_data;
+  request->properties = properties;
+
+  source_id = g_idle_add (find_channel_class_idle_cb, request);
 
-  g_idle_add (find_channel_class_idle_cb, request);
+  g_hash_table_insert (priv->request_channel_class_async_ids,
+    request, GUINT_TO_POINTER (source_id));
 }