diff options
Diffstat (limited to 'libempathy/empathy-dispatcher.c')
-rw-r--r-- | libempathy/empathy-dispatcher.c | 493 |
1 files changed, 393 insertions, 100 deletions
diff --git a/libempathy/empathy-dispatcher.c b/libempathy/empathy-dispatcher.c index 88ee5bb00..6550c1f25 100644 --- a/libempathy/empathy-dispatcher.c +++ b/libempathy/empathy-dispatcher.c @@ -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> @@ -39,7 +41,6 @@ #include "empathy-tube-handler.h" #include "empathy-account-manager.h" #include "empathy-tp-contact-factory.h" -#include "empathy-tp-file.h" #include "empathy-chatroom-manager.h" #include "empathy-utils.h" @@ -53,11 +54,14 @@ typedef struct MissionControl *mc; /* connection to connection data mapping */ GHashTable *connections; + GHashTable *outstanding_classes_requests; gpointer token; GSList *tubes; /* 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 +77,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; @@ -111,6 +120,17 @@ typedef struct GPtrArray *requestable_channels; } ConnectionData; +typedef struct +{ + EmpathyDispatcher *dispatcher; + TpConnection *connection; + char *channel_type; + guint handle_type; + GArray *properties; + EmpathyDispatcherFindChannelClassCb *callback; + gpointer user_data; +} FindChannelRequest; + static DispatchData * new_dispatch_data (TpChannel *channel, GObject *channel_wrapper) @@ -221,6 +241,29 @@ 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); +} + +static void dispatcher_connection_invalidated_cb (TpConnection *connection, guint domain, gint code, @@ -713,12 +756,35 @@ dispatcher_connection_got_all (TpProxy *proxy, else { ConnectionData *cd; + GList *requests, *l; + FindChannelRequest *request; + GList *retval; cd = g_hash_table_lookup (priv->connections, proxy); g_assert (cd != NULL); cd->requestable_channels = g_boxed_copy ( TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, requestable_channels); + + requests = g_hash_table_lookup (priv->outstanding_classes_requests, + proxy); + + for (l = requests; l != NULL; l = l->next) + { + request = l->data; + + retval = empathy_dispatcher_find_channel_classes (dispatcher, + TP_CONNECTION (proxy), request->channel_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); + + g_hash_table_remove (priv->outstanding_classes_requests, proxy); } } @@ -832,6 +898,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, @@ -862,6 +939,14 @@ dispatcher_finalize (GObject *object) GList *l; GHashTableIter iter; 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); @@ -881,10 +966,18 @@ dispatcher_finalize (GObject *object) dispatcher_connection_invalidated_cb, object); } + g_hash_table_iter_init (&iter, priv->outstanding_classes_requests); + while (g_hash_table_iter_next (&iter, &connection, (gpointer *) &list)) + { + g_list_foreach (list, (GFunc) free_find_channel_request, NULL); + g_list_free (list); + } + g_object_unref (priv->account_manager); g_object_unref (priv->mc); g_hash_table_destroy (priv->connections); + g_hash_table_destroy (priv->outstanding_classes_requests); } static void @@ -947,6 +1040,9 @@ empathy_dispatcher_init (EmpathyDispatcher *dispatcher) priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, (GDestroyNotify) free_connection_data); + priv->outstanding_classes_requests = g_hash_table_new_full (g_direct_hash, + g_direct_equal, g_object_unref, NULL); + priv->channels = NULL; connections = empathy_account_manager_dup_connections (priv->account_manager); @@ -956,6 +1052,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 * @@ -1318,93 +1417,100 @@ empathy_dispatcher_create_channel (EmpathyDispatcher *dispatcher, G_OBJECT (request_data->dispatcher)); } -void -empathy_dispatcher_send_file_to_contact (EmpathyContact *contact, - const gchar *filename, - guint64 size, - guint64 date, - const gchar *content_type, - EmpathyDispatcherRequestCb *callback, - gpointer user_data) +static gboolean +channel_class_matches (GValueArray *class, + const char *channel_type, + guint handle_type, + GArray *fixed_properties) { - EmpathyDispatcher *dispatcher = empathy_dispatcher_dup_singleton (); - EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher); - TpConnection *connection = empathy_contact_get_connection (contact); - ConnectionData *connection_data = - g_hash_table_lookup (priv->connections, connection); - DispatcherRequestData *request_data; - GValue *value; - GHashTable *request = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - (GDestroyNotify) tp_g_value_slice_free); + GHashTable *fprops; + GValue *v; + const char *c_type; + guint h_type; + gboolean valid; - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - g_return_if_fail (!EMP_STR_EMPTY (filename)); - g_return_if_fail (!EMP_STR_EMPTY (content_type)); - - /* org.freedesktop.Telepathy.Channel.ChannelType */ - value = tp_g_value_slice_new (G_TYPE_STRING); - g_value_set_string (value, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER); - g_hash_table_insert (request, TP_IFACE_CHANNEL ".ChannelType", value); - - /* org.freedesktop.Telepathy.Channel.TargetHandleType */ - value = tp_g_value_slice_new (G_TYPE_UINT); - g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); - g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandleType", value); - - /* org.freedesktop.Telepathy.Channel.TargetHandle */ - value = tp_g_value_slice_new (G_TYPE_UINT); - g_value_set_uint (value, empathy_contact_get_handle (contact)); - g_hash_table_insert (request, TP_IFACE_CHANNEL ".TargetHandle", value); - - /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.ContentType */ - value = tp_g_value_slice_new (G_TYPE_STRING); - g_value_set_string (value, content_type); - g_hash_table_insert (request, - TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".ContentType", value); - - /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.Filename */ - value = tp_g_value_slice_new (G_TYPE_STRING); - g_value_set_string (value, filename); - g_hash_table_insert (request, - TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".Filename", value); - - /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.Size */ - value = tp_g_value_slice_new (G_TYPE_UINT64); - g_value_set_uint64 (value, size); - g_hash_table_insert (request, - TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".Size", value); - - /* org.freedesktop.Telepathy.Channel.Type.FileTransfer.Date */ - value = tp_g_value_slice_new (G_TYPE_UINT64); - g_value_set_uint64 (value, date); - g_hash_table_insert (request, - TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER ".Date", value); - - request_data = new_dispatcher_request_data (dispatcher, connection, - TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, TP_HANDLE_TYPE_CONTACT, - empathy_contact_get_handle (contact), request, contact, callback, - user_data); - connection_data->outstanding_requests = g_list_prepend - (connection_data->outstanding_requests, request_data); + v = g_value_array_get_nth (class, 0); - tp_cli_connection_interface_requests_call_create_channel ( - request_data->connection, -1, - request_data->request, dispatcher_create_channel_cb, request_data, NULL, - G_OBJECT (request_data->dispatcher)); + /* 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"); - g_object_unref (dispatcher); + 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; } -GStrv -empathy_dispatcher_find_channel_class (EmpathyDispatcher *dispatcher, - TpConnection *connection, - const gchar *channel_type, - guint handle_type) +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); @@ -1418,36 +1524,223 @@ 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; + + matching_classes = g_list_prepend (matching_classes, class); + } - h_type = tp_asv_get_uint32 (fprops, - TP_IFACE_CHANNEL ".TargetHandleType", &valid); + return matching_classes; +} - if (!valid || handle_type != h_type) - continue; +static gboolean +find_channel_class_idle_cb (gpointer user_data) +{ + GList *retval; + GList *requests; + FindChannelRequest *request = user_data; + ConnectionData *cd; + gboolean is_ready = TRUE; + EmpathyDispatcherPriv *priv = GET_PRIV (request->dispatcher); + + 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 (is_ready) + { + retval = empathy_dispatcher_find_channel_classes (request->dispatcher, + request->connection, request->channel_type, request->handle_type, + request->properties); - allowed = g_value_array_get_nth (class, 1); + request->callback (retval, request->user_data); + free_find_channel_request (request); + g_list_free (retval); - return g_value_get_boxed (allowed); + return FALSE; } - return NULL; + requests = g_hash_table_lookup (priv->outstanding_classes_requests, + request->connection); + requests = g_list_prepend (requests, request); + + g_hash_table_insert (priv->outstanding_classes_requests, + request->connection, requests); + + 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_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; + request->channel_type = g_strdup (channel_type); + request->handle_type = handle_type; + 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_hash_table_insert (priv->request_channel_class_async_ids, + request, GUINT_TO_POINTER (source_id)); +} |