1 /* * Copyright (C) 2007-2009 Collabora Ltd.
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 * Authors: Xavier Claessens <xclaesse@gmail.com>
18 * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
19 * Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
26 #include <glib/gi18n-lib.h>
28 #include <telepathy-glib/enums.h>
29 #include <telepathy-glib/connection.h>
30 #include <telepathy-glib/util.h>
31 #include <telepathy-glib/dbus.h>
32 #include <telepathy-glib/proxy-subclass.h>
33 #include <telepathy-glib/gtypes.h>
35 #include <extensions/extensions.h>
37 #include "empathy-dispatcher.h"
38 #include "empathy-utils.h"
39 #include "empathy-tube-handler.h"
40 #include "empathy-account-manager.h"
41 #include "empathy-tp-contact-factory.h"
42 #include "empathy-chatroom-manager.h"
43 #include "empathy-utils.h"
45 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
46 #include <libempathy/empathy-debug.h>
48 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyDispatcher)
51 EmpathyAccountManager *account_manager;
53 /* connection to connection data mapping */
54 GHashTable *connections;
55 GHashTable *outstanding_classes_requests;
59 /* channels which the dispatcher is listening "invalidated" */
62 GHashTable *request_channel_class_async_ids;
63 } EmpathyDispatcherPriv;
65 G_DEFINE_TYPE (EmpathyDispatcher, empathy_dispatcher, G_TYPE_OBJECT);
75 static guint signals[LAST_SIGNAL];
76 static EmpathyDispatcher *dispatcher = NULL;
78 static GList * empathy_dispatcher_find_channel_classes
79 (EmpathyDispatcher *dispatcher, TpConnection *connection,
80 const gchar *channel_type, guint handle_type, GArray *fixed_properties);
85 EmpathyDispatcher *dispatcher;
86 EmpathyDispatchOperation *operation;
87 TpConnection *connection;
91 EmpathyContact *contact;
93 /* Properties to pass to the channel when requesting it */
95 EmpathyDispatcherRequestCb *cb;
97 gpointer *request_data;
98 } DispatcherRequestData;
103 /* Channel type specific wrapper object */
104 GObject *channel_wrapper;
109 /* ObjectPath => DispatchData.. */
110 GHashTable *dispatched_channels;
111 /* ObjectPath -> EmpathyDispatchOperations */
112 GHashTable *dispatching_channels;
114 /* ObjectPath -> EmpathyDispatchOperations
116 * This holds channels which were announced with NewChannel while we have an
117 * outstanding channel request for a channel of this type. On the Requests
118 * interface, CreateChannel and EnsureChannel are guaranteed by the spec to
119 * return before NewChannels is emitted, but there was no guarantee of the
120 * ordering of RequestChannel vs. NewChannel. So if necessary, channels are
121 * held in limbo here until we know whether they were requested.
123 GHashTable *outstanding_channels;
124 /* List of DispatcherRequestData */
125 GList *outstanding_requests;
126 /* List of requestable channel classes */
127 GPtrArray *requestable_channels;
132 EmpathyDispatcher *dispatcher;
133 TpConnection *connection;
137 EmpathyDispatcherFindChannelClassCb *callback;
139 } FindChannelRequest;
141 static DispatchData *
142 new_dispatch_data (TpChannel *channel,
143 GObject *channel_wrapper)
145 DispatchData *d = g_slice_new0 (DispatchData);
146 d->channel = g_object_ref (channel);
147 if (channel_wrapper != NULL)
148 d->channel_wrapper = g_object_ref (channel_wrapper);
154 free_dispatch_data (DispatchData *data)
156 g_object_unref (data->channel);
157 if (data->channel_wrapper != NULL)
158 g_object_unref (data->channel_wrapper);
160 g_slice_free (DispatchData, data);
163 static DispatcherRequestData *
164 new_dispatcher_request_data (EmpathyDispatcher *dispatcher,
165 TpConnection *connection,
166 const gchar *channel_type,
170 EmpathyContact *contact,
171 EmpathyDispatcherRequestCb *cb,
174 DispatcherRequestData *result = g_slice_new0 (DispatcherRequestData);
176 result->dispatcher = g_object_ref (dispatcher);
177 result->connection = connection;
179 result->channel_type = g_strdup (channel_type);
180 result->handle_type = handle_type;
181 result->handle = handle;
182 result->request = request;
185 result->contact = g_object_ref (contact);
188 result->user_data = user_data;
194 free_dispatcher_request_data (DispatcherRequestData *r)
196 g_free (r->channel_type);
198 if (r->dispatcher != NULL)
199 g_object_unref (r->dispatcher);
201 if (r->contact != NULL)
202 g_object_unref (r->contact);
204 if (r->request != NULL)
205 g_hash_table_unref (r->request);
207 g_slice_free (DispatcherRequestData, r);
210 static ConnectionData *
211 new_connection_data (void)
213 ConnectionData *cd = g_slice_new0 (ConnectionData);
215 cd->dispatched_channels = g_hash_table_new_full (g_str_hash, g_str_equal,
216 g_free, (GDestroyNotify) free_dispatch_data);
218 cd->dispatching_channels = g_hash_table_new_full (g_str_hash, g_str_equal,
219 g_free, g_object_unref);
221 cd->outstanding_channels = g_hash_table_new_full (g_str_hash, g_str_equal,
228 free_connection_data (ConnectionData *cd)
232 g_hash_table_destroy (cd->dispatched_channels);
233 g_hash_table_destroy (cd->dispatching_channels);
236 for (l = cd->outstanding_requests ; l != NULL; l = g_list_delete_link (l,l))
238 free_dispatcher_request_data (l->data);
241 if (cd->requestable_channels != NULL)
243 for (i = 0 ; i < cd->requestable_channels->len ; i++)
245 g_ptr_array_index (cd->requestable_channels, i));
246 g_ptr_array_free (cd->requestable_channels, TRUE);
251 free_find_channel_request (FindChannelRequest *r)
256 g_object_unref (r->dispatcher);
257 g_free (r->channel_type);
259 if (r->properties != NULL)
261 for (idx = 0; idx < r->properties->len ; idx++)
263 str = g_array_index (r->properties, char *, idx);
267 g_array_free (r->properties, TRUE);
270 g_slice_free (FindChannelRequest, r);
274 dispatcher_connection_invalidated_cb (TpConnection *connection,
278 EmpathyDispatcher *dispatcher)
280 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
282 DEBUG ("Error: %s", message);
283 g_hash_table_remove (priv->connections, connection);
287 dispatcher_operation_can_start (EmpathyDispatcher *self,
288 EmpathyDispatchOperation *operation,
292 const gchar *channel_type =
293 empathy_dispatch_operation_get_channel_type (operation);
295 for (l = cd->outstanding_requests; l != NULL; l = g_list_next (l))
297 DispatcherRequestData *d = (DispatcherRequestData *) l->data;
299 if (d->operation == NULL && !tp_strdiff (d->channel_type, channel_type))
309 dispatch_operation_flush_requests (EmpathyDispatcher *dispatcher,
310 EmpathyDispatchOperation *operation,
316 l = cd->outstanding_requests;
319 DispatcherRequestData *d = (DispatcherRequestData *) l->data;
324 if (d->operation == operation)
329 d->cb (NULL, error, d->user_data);
331 d->cb (operation, NULL, d->user_data);
334 cd->outstanding_requests = g_list_delete_link
335 (cd->outstanding_requests, lt);
337 free_dispatcher_request_data (d);
343 dispatcher_channel_invalidated_cb (TpProxy *proxy,
347 EmpathyDispatcher *dispatcher)
349 /* Channel went away... */
350 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
351 TpConnection *connection;
352 EmpathyDispatchOperation *operation;
354 const gchar *object_path;
356 connection = tp_channel_borrow_connection (TP_CHANNEL (proxy));
358 cd = g_hash_table_lookup (priv->connections, connection);
359 /* Connection itself invalidated? */
363 object_path = tp_proxy_get_object_path (proxy);
365 DEBUG ("Channel %s invalidated", object_path);
367 g_hash_table_remove (cd->dispatched_channels, object_path);
368 g_hash_table_remove (cd->dispatching_channels, object_path);
370 priv->channels = g_list_remove (priv->channels, proxy);
372 operation = g_hash_table_lookup (cd->outstanding_channels, object_path);
373 if (operation != NULL)
375 GError error = { domain, code, message };
376 dispatch_operation_flush_requests (dispatcher, operation, &error, cd);
377 g_hash_table_remove (cd->outstanding_channels, object_path);
378 g_object_unref (operation);
383 dispatch_operation_approved_cb (EmpathyDispatchOperation *operation,
384 EmpathyDispatcher *dispatcher)
386 g_assert (empathy_dispatch_operation_is_incoming (operation));
387 DEBUG ("Send of for dispatching: %s",
388 empathy_dispatch_operation_get_object_path (operation));
389 g_signal_emit (dispatcher, signals[DISPATCH], 0, operation);
393 dispatch_operation_claimed_cb (EmpathyDispatchOperation *operation,
394 EmpathyDispatcher *dispatcher)
396 /* Our job is done, remove the dispatch operation and mark the channel as
398 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
399 TpConnection *connection;
401 const gchar *object_path;
403 connection = empathy_dispatch_operation_get_tp_connection (operation);
404 cd = g_hash_table_lookup (priv->connections, connection);
405 g_assert (cd != NULL);
407 object_path = empathy_dispatch_operation_get_object_path (operation);
409 if (g_hash_table_lookup (cd->dispatched_channels, object_path) == NULL)
412 d = new_dispatch_data (
413 empathy_dispatch_operation_get_channel (operation),
414 empathy_dispatch_operation_get_channel_wrapper (operation));
415 g_hash_table_insert (cd->dispatched_channels,
416 g_strdup (object_path), d);
418 g_hash_table_remove (cd->dispatching_channels, object_path);
420 DEBUG ("Channel claimed: %s", object_path);
424 dispatch_operation_ready_cb (EmpathyDispatchOperation *operation,
425 EmpathyDispatcher *dispatcher)
427 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
428 TpConnection *connection;
430 EmpathyDispatchOperationState status;
432 g_signal_connect (operation, "approved",
433 G_CALLBACK (dispatch_operation_approved_cb), dispatcher);
435 g_signal_connect (operation, "claimed",
436 G_CALLBACK (dispatch_operation_claimed_cb), dispatcher);
438 /* Signal the observers */
439 DEBUG ("Send to observers: %s",
440 empathy_dispatch_operation_get_object_path (operation));
441 g_signal_emit (dispatcher, signals[OBSERVE], 0, operation);
443 empathy_dispatch_operation_start (operation);
445 /* Signal potential requestors */
446 connection = empathy_dispatch_operation_get_tp_connection (operation);
447 cd = g_hash_table_lookup (priv->connections, connection);
448 g_assert (cd != NULL);
450 g_object_ref (operation);
451 g_object_ref (dispatcher);
453 dispatch_operation_flush_requests (dispatcher, operation, NULL, cd);
454 status = empathy_dispatch_operation_get_status (operation);
455 g_object_unref (operation);
457 if (status == EMPATHY_DISPATCHER_OPERATION_STATE_CLAIMED)
460 if (status == EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
462 DEBUG ("Send to approvers: %s",
463 empathy_dispatch_operation_get_object_path (operation));
464 g_signal_emit (dispatcher, signals[APPROVE], 0, operation);
468 g_assert (status == EMPATHY_DISPATCHER_OPERATION_STATE_DISPATCHING);
469 DEBUG ("Send of for dispatching: %s",
470 empathy_dispatch_operation_get_object_path (operation));
471 g_signal_emit (dispatcher, signals[DISPATCH], 0, operation);
474 g_object_unref (dispatcher);
478 dispatcher_start_dispatching (EmpathyDispatcher *self,
479 EmpathyDispatchOperation *operation,
482 const gchar *object_path =
483 empathy_dispatch_operation_get_object_path (operation);
485 DEBUG ("Dispatching process started for %s", object_path);
487 if (g_hash_table_lookup (cd->dispatching_channels, object_path) == NULL)
489 g_assert (g_hash_table_lookup (cd->outstanding_channels,
490 object_path) == NULL);
492 g_hash_table_insert (cd->dispatching_channels,
493 g_strdup (object_path), operation);
495 switch (empathy_dispatch_operation_get_status (operation))
497 case EMPATHY_DISPATCHER_OPERATION_STATE_PREPARING:
498 g_signal_connect (operation, "ready",
499 G_CALLBACK (dispatch_operation_ready_cb), dispatcher);
501 case EMPATHY_DISPATCHER_OPERATION_STATE_PENDING:
502 dispatch_operation_ready_cb (operation, dispatcher);
505 g_assert_not_reached ();
509 else if (empathy_dispatch_operation_get_status (operation) >=
510 EMPATHY_DISPATCHER_OPERATION_STATE_PENDING)
512 /* Already dispatching and the operation is pending, thus the observers
513 * have seen it (if applicable), so we can flush the request right away.
515 dispatch_operation_flush_requests (self, operation, NULL, cd);
520 dispatcher_flush_outstanding_operations (EmpathyDispatcher *self,
526 g_hash_table_iter_init (&iter, cd->outstanding_channels);
527 while (g_hash_table_iter_next (&iter, NULL, &value))
529 EmpathyDispatchOperation *operation = EMPATHY_DISPATCH_OPERATION (value);
531 if (dispatcher_operation_can_start (self, operation, cd))
533 g_hash_table_iter_remove (&iter);
534 dispatcher_start_dispatching (dispatcher, operation, cd);
540 dispatcher_connection_new_channel (EmpathyDispatcher *dispatcher,
541 TpConnection *connection,
542 const gchar *object_path,
543 const gchar *channel_type,
546 GHashTable *properties,
549 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
552 EmpathyDispatchOperation *operation;
554 /* Channel types we never want to dispatch because they're either deprecated
555 * or can't sensibly be dispatch (e.g. channels that should always be
557 const char *blacklist[] = {
558 TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
559 TP_IFACE_CHANNEL_TYPE_TUBES,
560 TP_IFACE_CHANNEL_TYPE_ROOM_LIST,
564 cd = g_hash_table_lookup (priv->connections, connection);
566 /* Don't bother with channels we have already dispatched or are dispatching
567 * currently. This can happen when NewChannel(s) is fired after
568 * RequestChannel/CreateChannel/EnsureChannel */
569 if (g_hash_table_lookup (cd->dispatched_channels, object_path) != NULL)
572 if (g_hash_table_lookup (cd->dispatching_channels, object_path) != NULL)
575 /* Should never occur, but just in case a CM fires spurious NewChannel(s)
577 if (g_hash_table_lookup (cd->outstanding_channels, object_path) != NULL)
580 /* Only pick up non-requested text and file channels. For all other it
581 * doesn't make sense to handle it if we didn't request it. The same goes
582 * for channels we discovered by the Channels property or ListChannels */
583 if (!incoming && tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)
584 && tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
586 DEBUG ("Ignoring incoming channel of type %s on %s",
587 channel_type, object_path);
591 for (i = 0 ; blacklist[i] != NULL; i++)
593 if (!tp_strdiff (channel_type, blacklist[i]))
595 DEBUG ("Ignoring blacklisted channel type %s on %s",
596 channel_type, object_path);
601 DEBUG ("New channel of type %s on %s", channel_type, object_path);
603 if (properties == NULL)
604 channel = tp_channel_new (connection, object_path, channel_type,
605 handle_type, handle, NULL);
607 channel = tp_channel_new_from_properties (connection, object_path,
610 g_signal_connect (channel, "invalidated",
611 G_CALLBACK (dispatcher_channel_invalidated_cb),
614 priv->channels = g_list_prepend (priv->channels, channel);
616 operation = empathy_dispatch_operation_new (connection, channel, NULL,
619 g_object_unref (channel);
623 /* Request could either be by us or by a remote party. If there are no
624 * outstanding requests for this channel type we can assume it's remote.
625 * Otherwise we wait untill they are all satisfied */
626 if (dispatcher_operation_can_start (dispatcher, operation, cd))
627 dispatcher_start_dispatching (dispatcher, operation, cd);
629 g_hash_table_insert (cd->outstanding_channels,
630 g_strdup (object_path), operation);
634 dispatcher_start_dispatching (dispatcher, operation, cd);
639 dispatcher_connection_new_channel_cb (TpConnection *connection,
640 const gchar *object_path,
641 const gchar *channel_type,
644 gboolean suppress_handler,
648 EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (object);
650 /* Empathy heavily abuses surpress handler (don't try this at home), if
651 * surpress handler is true then it is an outgoing channel, which is
652 * requested either by us or some other party (like the megaphone applet).
653 * Otherwise it's an incoming channel */
654 dispatcher_connection_new_channel (dispatcher, connection,
655 object_path, channel_type, handle_type, handle, NULL, !suppress_handler);
659 dispatcher_connection_new_channel_with_properties (EmpathyDispatcher *dispatcher,
660 TpConnection *connection,
661 const gchar *object_path,
662 GHashTable *properties)
664 const gchar *channel_type;
671 channel_type = tp_asv_get_string (properties,
672 TP_IFACE_CHANNEL ".ChannelType");
673 if (channel_type == NULL)
675 g_message ("%s had an invalid ChannelType property", object_path);
679 handle_type = tp_asv_get_uint32 (properties,
680 TP_IFACE_CHANNEL ".TargetHandleType", &valid);
683 g_message ("%s had an invalid TargetHandleType property", object_path);
687 handle = tp_asv_get_uint32 (properties,
688 TP_IFACE_CHANNEL ".TargetHandle", &valid);
691 g_message ("%s had an invalid TargetHandle property", object_path);
695 /* We assume there is no channel dispather, so we're the only one dispatching
696 * it. Which means that a requested channel it is outgoing one */
697 requested = tp_asv_get_boolean (properties,
698 TP_IFACE_CHANNEL ".Requested", &valid);
701 g_message ("%s had an invalid Requested property", object_path);
705 dispatcher_connection_new_channel (dispatcher, connection,
706 object_path, channel_type, handle_type, handle, properties, !requested);
710 dispatcher_connection_new_channels_cb (TpConnection *connection,
711 const GPtrArray *channels,
715 EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (object);
718 for (i = 0; i < channels->len ; i++)
720 GValueArray *arr = g_ptr_array_index (channels, i);
721 const gchar *object_path;
722 GHashTable *properties;
724 object_path = g_value_get_boxed (g_value_array_get_nth (arr, 0));
725 properties = g_value_get_boxed (g_value_array_get_nth (arr, 1));
727 dispatcher_connection_new_channel_with_properties (dispatcher,
728 connection, object_path, properties);
733 dispatcher_connection_got_all (TpProxy *proxy,
734 GHashTable *properties,
739 EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (object);
740 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
742 GPtrArray *requestable_channels;
745 DEBUG ("Error: %s", error->message);
749 channels = tp_asv_get_boxed (properties, "Channels",
750 TP_ARRAY_TYPE_CHANNEL_DETAILS_LIST);
752 if (channels == NULL)
753 DEBUG ("No Channels property !?! on connection");
755 dispatcher_connection_new_channels_cb (TP_CONNECTION (proxy),
756 channels, NULL, object);
758 requestable_channels = tp_asv_get_boxed (properties,
759 "RequestableChannelClasses", TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST);
761 if (requestable_channels == NULL)
762 DEBUG ("No RequestableChannelClasses property !?! on connection");
767 FindChannelRequest *request;
770 cd = g_hash_table_lookup (priv->connections, proxy);
771 g_assert (cd != NULL);
773 cd->requestable_channels = g_boxed_copy (
774 TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, requestable_channels);
776 requests = g_hash_table_lookup (priv->outstanding_classes_requests,
779 for (l = requests; l != NULL; l = l->next)
783 retval = empathy_dispatcher_find_channel_classes (dispatcher,
784 TP_CONNECTION (proxy), request->channel_type,
785 request->handle_type, request->properties);
786 request->callback (retval, request->user_data);
788 free_find_channel_request (request);
789 g_list_free (retval);
792 g_list_free (requests);
794 g_hash_table_remove (priv->outstanding_classes_requests, proxy);
799 dispatcher_connection_list_channels_cb (TpConnection *connection,
800 const GPtrArray *channels,
809 DEBUG ("Error: %s", error->message);
813 for (i = 0; i < channels->len; i++)
817 values = g_ptr_array_index (channels, i);
818 /* We don't have any extra info, so assume already existing channels are
820 dispatcher_connection_new_channel (EMPATHY_DISPATCHER (dispatcher),
822 g_value_get_boxed (g_value_array_get_nth (values, 0)),
823 g_value_get_string (g_value_array_get_nth (values, 1)),
824 g_value_get_uint (g_value_array_get_nth (values, 2)),
825 g_value_get_uint (g_value_array_get_nth (values, 3)),
831 dispatcher_connection_advertise_capabilities_cb (TpConnection *connection,
832 const GPtrArray *capabilities,
838 DEBUG ("Error: %s", error->message);
842 dispatcher_new_connection_cb (EmpathyAccountManager *manager,
843 TpConnection *connection,
844 EmpathyDispatcher *dispatcher)
846 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
847 GPtrArray *capabilities;
850 const gchar *remove = NULL;
852 if (g_hash_table_lookup (priv->connections, connection) != NULL)
855 g_hash_table_insert (priv->connections, g_object_ref (connection),
856 new_connection_data ());
858 g_signal_connect (connection, "invalidated",
859 G_CALLBACK (dispatcher_connection_invalidated_cb), dispatcher);
861 if (tp_proxy_has_interface_by_id (TP_PROXY (connection),
862 TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS))
864 tp_cli_connection_interface_requests_connect_to_new_channels (connection,
865 dispatcher_connection_new_channels_cb,
866 NULL, NULL, G_OBJECT (dispatcher), NULL);
868 tp_cli_dbus_properties_call_get_all (connection, -1,
869 TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
870 dispatcher_connection_got_all,
871 NULL, NULL, G_OBJECT (dispatcher));
875 tp_cli_connection_connect_to_new_channel (connection,
876 dispatcher_connection_new_channel_cb,
877 NULL, NULL, G_OBJECT (dispatcher), NULL);
879 tp_cli_connection_call_list_channels (connection, -1,
880 dispatcher_connection_list_channels_cb, NULL, NULL,
881 G_OBJECT (dispatcher));
885 /* Advertise VoIP capabilities */
886 capabilities = g_ptr_array_sized_new (1);
887 cap_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING,
888 G_TYPE_UINT, G_TYPE_INVALID);
889 g_value_init (&cap, cap_type);
890 g_value_take_boxed (&cap, dbus_g_type_specialized_construct (cap_type));
891 dbus_g_type_struct_set (&cap,
892 0, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
893 1, TP_CHANNEL_MEDIA_CAPABILITY_AUDIO |
894 TP_CHANNEL_MEDIA_CAPABILITY_VIDEO |
895 TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_STUN |
896 TP_CHANNEL_MEDIA_CAPABILITY_NAT_TRAVERSAL_GTALK_P2P, G_MAXUINT);
897 g_ptr_array_add (capabilities, g_value_get_boxed (&cap));
899 tp_cli_connection_interface_capabilities_call_advertise_capabilities (
900 connection, -1, capabilities, &remove,
901 dispatcher_connection_advertise_capabilities_cb,
902 NULL, NULL, G_OBJECT (dispatcher));
904 g_value_unset (&cap);
905 g_ptr_array_free (capabilities, TRUE);
909 remove_idle_handlers (gpointer key,
915 source_id = GPOINTER_TO_UINT (value);
916 g_source_remove (source_id);
920 dispatcher_constructor (GType type,
921 guint n_construct_params,
922 GObjectConstructParam *construct_params)
926 if (dispatcher == NULL)
928 retval = G_OBJECT_CLASS (empathy_dispatcher_parent_class)->constructor
929 (type, n_construct_params, construct_params);
931 dispatcher = EMPATHY_DISPATCHER (retval);
932 g_object_add_weak_pointer (retval, (gpointer) &dispatcher);
936 retval = g_object_ref (dispatcher);
943 dispatcher_finalize (GObject *object)
945 EmpathyDispatcherPriv *priv = GET_PRIV (object);
951 if (priv->request_channel_class_async_ids != NULL)
953 g_hash_table_foreach (priv->request_channel_class_async_ids,
954 remove_idle_handlers, NULL);
955 g_hash_table_destroy (priv->request_channel_class_async_ids);
958 g_signal_handlers_disconnect_by_func (priv->account_manager,
959 dispatcher_new_connection_cb, object);
961 for (l = priv->channels; l; l = l->next)
963 g_signal_handlers_disconnect_by_func (l->data,
964 dispatcher_channel_invalidated_cb, object);
967 g_list_free (priv->channels);
969 g_hash_table_iter_init (&iter, priv->connections);
970 while (g_hash_table_iter_next (&iter, &connection, NULL))
972 g_signal_handlers_disconnect_by_func (connection,
973 dispatcher_connection_invalidated_cb, object);
976 g_hash_table_iter_init (&iter, priv->outstanding_classes_requests);
977 while (g_hash_table_iter_next (&iter, &connection, (gpointer *) &list))
979 g_list_foreach (list, (GFunc) free_find_channel_request, NULL);
983 g_object_unref (priv->account_manager);
984 g_object_unref (priv->mc);
986 g_hash_table_destroy (priv->connections);
987 g_hash_table_destroy (priv->outstanding_classes_requests);
991 empathy_dispatcher_class_init (EmpathyDispatcherClass *klass)
993 GObjectClass *object_class = G_OBJECT_CLASS (klass);
995 object_class->finalize = dispatcher_finalize;
996 object_class->constructor = dispatcher_constructor;
999 g_signal_new ("observe",
1000 G_TYPE_FROM_CLASS (klass),
1004 g_cclosure_marshal_VOID__OBJECT,
1006 1, EMPATHY_TYPE_DISPATCH_OPERATION);
1009 g_signal_new ("approve",
1010 G_TYPE_FROM_CLASS (klass),
1014 g_cclosure_marshal_VOID__OBJECT,
1016 1, EMPATHY_TYPE_DISPATCH_OPERATION);
1019 g_signal_new ("dispatch",
1020 G_TYPE_FROM_CLASS (klass),
1024 g_cclosure_marshal_VOID__OBJECT,
1026 1, EMPATHY_TYPE_DISPATCH_OPERATION);
1028 g_type_class_add_private (object_class, sizeof (EmpathyDispatcherPriv));
1033 empathy_dispatcher_init (EmpathyDispatcher *dispatcher)
1035 GList *connections, *l;
1036 EmpathyDispatcherPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (dispatcher,
1037 EMPATHY_TYPE_DISPATCHER, EmpathyDispatcherPriv);
1039 dispatcher->priv = priv;
1040 priv->mc = empathy_mission_control_dup_singleton ();
1041 priv->account_manager = empathy_account_manager_dup_singleton ();
1043 g_signal_connect (priv->account_manager, "new-connection",
1044 G_CALLBACK (dispatcher_new_connection_cb),
1047 priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1048 g_object_unref, (GDestroyNotify) free_connection_data);
1050 priv->outstanding_classes_requests = g_hash_table_new_full (g_direct_hash,
1051 g_direct_equal, g_object_unref, NULL);
1053 priv->channels = NULL;
1055 connections = empathy_account_manager_dup_connections (priv->account_manager);
1056 for (l = connections; l; l = l->next)
1058 dispatcher_new_connection_cb (priv->account_manager, l->data, dispatcher);
1059 g_object_unref (l->data);
1061 g_list_free (connections);
1063 priv->request_channel_class_async_ids = g_hash_table_new (g_direct_hash,
1068 empathy_dispatcher_dup_singleton (void)
1070 return EMPATHY_DISPATCHER (g_object_new (EMPATHY_TYPE_DISPATCHER, NULL));
1074 dispatcher_request_failed (EmpathyDispatcher *dispatcher,
1075 DispatcherRequestData *request_data,
1076 const GError *error)
1078 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
1079 ConnectionData *conn_data;
1081 conn_data = g_hash_table_lookup (priv->connections, request_data->connection);
1082 if (request_data->cb != NULL)
1083 request_data->cb (NULL, error, request_data->user_data);
1085 conn_data->outstanding_requests =
1086 g_list_remove (conn_data->outstanding_requests, request_data);
1087 free_dispatcher_request_data (request_data);
1091 dispatcher_connection_new_requested_channel (EmpathyDispatcher *dispatcher,
1092 DispatcherRequestData *request_data,
1093 const gchar *object_path,
1094 GHashTable *properties,
1095 const GError *error)
1097 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
1098 EmpathyDispatchOperation *operation = NULL;
1099 ConnectionData *conn_data;
1101 conn_data = g_hash_table_lookup (priv->connections,
1102 request_data->connection);
1106 DEBUG ("Channel request failed: %s", error->message);
1108 dispatcher_request_failed (dispatcher, request_data, error);
1113 operation = g_hash_table_lookup (conn_data->outstanding_channels,
1116 if (operation != NULL)
1117 g_hash_table_remove (conn_data->outstanding_channels, object_path);
1119 operation = g_hash_table_lookup (conn_data->dispatching_channels,
1122 if (operation == NULL)
1124 DispatchData *data = g_hash_table_lookup (conn_data->dispatched_channels,
1129 operation = empathy_dispatch_operation_new_with_wrapper (
1130 request_data->connection,
1131 data->channel, request_data->contact, FALSE,
1132 data->channel_wrapper);
1138 if (properties != NULL)
1139 channel = tp_channel_new_from_properties (request_data->connection,
1140 object_path, properties, NULL);
1142 channel = tp_channel_new (request_data->connection, object_path,
1143 request_data->channel_type, request_data->handle_type,
1144 request_data->handle, NULL);
1146 g_signal_connect (channel, "invalidated",
1147 G_CALLBACK (dispatcher_channel_invalidated_cb),
1148 request_data->dispatcher);
1150 priv->channels = g_list_prepend (priv->channels, channel);
1152 operation = empathy_dispatch_operation_new (request_data->connection,
1153 channel, request_data->contact, FALSE);
1154 g_object_unref (channel);
1159 /* Already existed set potential extra information */
1160 g_object_set (G_OBJECT (operation),
1161 "contact", request_data->contact,
1165 request_data->operation = operation;
1167 /* (pre)-approve this right away as we requested it
1168 * This might cause the channel to be claimed, in which case the operation
1169 * will disappear. So ref it, and check the status before starting the
1172 g_object_ref (operation);
1173 empathy_dispatch_operation_approve (operation);
1175 if (empathy_dispatch_operation_get_status (operation) <
1176 EMPATHY_DISPATCHER_OPERATION_STATE_APPROVING)
1177 dispatcher_start_dispatching (request_data->dispatcher, operation,
1180 g_object_unref (operation);
1183 dispatcher_flush_outstanding_operations (request_data->dispatcher,
1188 dispatcher_request_channel_cb (TpConnection *connection,
1189 const gchar *object_path,
1190 const GError *error,
1192 GObject *weak_object)
1194 EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (weak_object);
1195 DispatcherRequestData *request_data = (DispatcherRequestData *) user_data;
1197 dispatcher_connection_new_requested_channel (dispatcher,
1198 request_data, object_path, NULL, error);
1202 dispatcher_request_channel (DispatcherRequestData *request_data)
1204 tp_cli_connection_call_request_channel (request_data->connection, -1,
1205 request_data->channel_type,
1206 request_data->handle_type,
1207 request_data->handle,
1208 TRUE, dispatcher_request_channel_cb,
1209 request_data, NULL, G_OBJECT (request_data->dispatcher));
1213 empathy_dispatcher_chat_with_contact (EmpathyContact *contact,
1214 EmpathyDispatcherRequestCb *callback,
1217 EmpathyDispatcher *dispatcher;
1218 EmpathyDispatcherPriv *priv;
1219 TpConnection *connection;
1220 ConnectionData *connection_data;
1221 DispatcherRequestData *request_data;
1223 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
1225 dispatcher = empathy_dispatcher_dup_singleton ();
1226 priv = GET_PRIV (dispatcher);
1228 connection = empathy_contact_get_connection (contact);
1229 connection_data = g_hash_table_lookup (priv->connections, connection);
1231 /* The contact handle might not be known yet */
1232 request_data = new_dispatcher_request_data (dispatcher, connection,
1233 TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_CONTACT,
1234 empathy_contact_get_handle (contact), NULL, contact, callback, user_data);
1236 connection_data->outstanding_requests = g_list_prepend
1237 (connection_data->outstanding_requests, request_data);
1239 dispatcher_request_channel (request_data);
1241 g_object_unref (dispatcher);
1246 EmpathyDispatcher *dispatcher;
1247 EmpathyDispatcherRequestCb *callback;
1249 } ChatWithContactIdData;
1252 dispatcher_chat_with_contact_id_cb (EmpathyTpContactFactory *factory,
1253 EmpathyContact *contact,
1254 const GError *error,
1256 GObject *weak_object)
1258 ChatWithContactIdData *data = user_data;
1262 /* FIXME: Should call data->callback with the error */
1263 DEBUG ("Error: %s", error->message);
1267 empathy_dispatcher_chat_with_contact (contact, data->callback, data->user_data);
1270 g_object_unref (data->dispatcher);
1271 g_slice_free (ChatWithContactIdData, data);
1275 empathy_dispatcher_chat_with_contact_id (TpConnection *connection,
1276 const gchar *contact_id,
1277 EmpathyDispatcherRequestCb *callback,
1280 EmpathyDispatcher *dispatcher;
1281 EmpathyTpContactFactory *factory;
1282 ChatWithContactIdData *data;
1284 g_return_if_fail (TP_IS_CONNECTION (connection));
1285 g_return_if_fail (!EMP_STR_EMPTY (contact_id));
1287 dispatcher = empathy_dispatcher_dup_singleton ();
1288 factory = empathy_tp_contact_factory_dup_singleton (connection);
1289 data = g_slice_new0 (ChatWithContactIdData);
1290 data->dispatcher = dispatcher;
1291 data->callback = callback;
1292 data->user_data = user_data;
1293 empathy_tp_contact_factory_get_from_id (factory, contact_id,
1294 dispatcher_chat_with_contact_id_cb, data, NULL, NULL);
1296 g_object_unref (factory);
1300 dispatcher_request_handles_cb (TpConnection *connection,
1301 const GArray *handles,
1302 const GError *error,
1306 DispatcherRequestData *request_data = (DispatcherRequestData *) user_data;
1310 EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (object);
1311 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
1314 cd = g_hash_table_lookup (priv->connections, request_data->connection);
1316 if (request_data->cb)
1317 request_data->cb (NULL, error, request_data->user_data);
1319 cd->outstanding_requests = g_list_remove (cd->outstanding_requests,
1322 free_dispatcher_request_data (request_data);
1324 dispatcher_flush_outstanding_operations (dispatcher, cd);
1328 request_data->handle = g_array_index (handles, guint, 0);
1329 dispatcher_request_channel (request_data);
1333 empathy_dispatcher_join_muc (TpConnection *connection,
1334 const gchar *roomname,
1335 EmpathyDispatcherRequestCb *callback,
1338 EmpathyDispatcher *dispatcher;
1339 EmpathyDispatcherPriv *priv;
1340 DispatcherRequestData *request_data;
1341 ConnectionData *connection_data;
1342 const gchar *names[] = { roomname, NULL };
1344 g_return_if_fail (TP_IS_CONNECTION (connection));
1345 g_return_if_fail (!EMP_STR_EMPTY (roomname));
1347 dispatcher = empathy_dispatcher_dup_singleton ();
1348 priv = GET_PRIV (dispatcher);
1350 connection_data = g_hash_table_lookup (priv->connections, connection);
1352 /* Don't know the room handle yet */
1353 request_data = new_dispatcher_request_data (dispatcher, connection,
1354 TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_ROOM, 0, NULL,
1355 NULL, callback, user_data);
1357 connection_data->outstanding_requests = g_list_prepend
1358 (connection_data->outstanding_requests, request_data);
1360 tp_cli_connection_call_request_handles (connection, -1,
1361 TP_HANDLE_TYPE_ROOM, names,
1362 dispatcher_request_handles_cb, request_data, NULL,
1363 G_OBJECT (dispatcher));
1365 g_object_unref (dispatcher);
1369 dispatcher_create_channel_cb (TpConnection *connect,
1370 const gchar *object_path,
1371 GHashTable *properties,
1372 const GError *error,
1374 GObject *weak_object)
1376 EmpathyDispatcher *dispatcher = EMPATHY_DISPATCHER (weak_object);
1377 DispatcherRequestData *request_data = (DispatcherRequestData *) user_data;
1379 dispatcher_connection_new_requested_channel (dispatcher,
1380 request_data, object_path, properties, error);
1384 empathy_dispatcher_create_channel (EmpathyDispatcher *dispatcher,
1385 TpConnection *connection,
1386 GHashTable *request,
1387 EmpathyDispatcherRequestCb *callback,
1390 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
1391 ConnectionData *connection_data;
1392 DispatcherRequestData *request_data;
1393 const gchar *channel_type;
1398 g_return_if_fail (EMPATHY_IS_DISPATCHER (dispatcher));
1399 g_return_if_fail (TP_IS_CONNECTION (connection));
1400 g_return_if_fail (request != NULL);
1402 connection_data = g_hash_table_lookup (priv->connections, connection);
1403 g_assert (connection_data != NULL);
1405 channel_type = tp_asv_get_string (request, TP_IFACE_CHANNEL ".ChannelType");
1407 handle_type = tp_asv_get_uint32 (request,
1408 TP_IFACE_CHANNEL ".TargetHandleType", &valid);
1410 handle_type = TP_UNKNOWN_HANDLE_TYPE;
1412 handle = tp_asv_get_uint32 (request, TP_IFACE_CHANNEL ".TargetHandle", NULL);
1414 request_data = new_dispatcher_request_data (dispatcher, connection,
1415 channel_type, handle_type, handle, request,
1416 NULL, callback, user_data);
1418 connection_data->outstanding_requests = g_list_prepend
1419 (connection_data->outstanding_requests, request_data);
1421 tp_cli_connection_interface_requests_call_create_channel (
1422 request_data->connection, -1,
1423 request_data->request, dispatcher_create_channel_cb, request_data, NULL,
1424 G_OBJECT (request_data->dispatcher));
1428 channel_class_matches (GValueArray *class,
1429 const char *channel_type,
1431 GArray *fixed_properties)
1439 v = g_value_array_get_nth (class, 0);
1441 /* if the class doesn't match channel type discard it. */
1442 fprops = g_value_get_boxed (v);
1443 c_type = tp_asv_get_string (fprops, TP_IFACE_CHANNEL ".ChannelType");
1445 if (tp_strdiff (channel_type, c_type))
1448 /* we have the right channel type, see if the handle type matches */
1449 h_type = tp_asv_get_uint32 (fprops,
1450 TP_IFACE_CHANNEL ".TargetHandleType", &valid);
1452 if (!valid || handle_type != h_type)
1455 if (fixed_properties != NULL)
1457 gpointer h_key, h_val;
1459 GHashTableIter iter;
1462 g_hash_table_iter_init (&iter, fprops);
1464 while (g_hash_table_iter_next (&iter, &h_key, &h_val))
1466 /* discard ChannelType and TargetHandleType, as we already
1469 if (!tp_strdiff ((char *) h_key, TP_IFACE_CHANNEL ".ChannelType") ||
1471 ((char *) h_key, TP_IFACE_CHANNEL ".TargetHandleType"))
1476 for (idx = 0; idx < fixed_properties->len; idx++)
1478 /* if |key| doesn't exist in |fixed_properties|, discard
1483 g_array_index (fixed_properties, char *, idx)))
1486 /* exit the for() loop */
1497 /* if no fixed_properties are specified, discard the classes
1498 * with some fixed properties other than the two we already
1501 if (g_hash_table_size (fprops) > 2)
1509 empathy_dispatcher_find_channel_classes (EmpathyDispatcher *dispatcher,
1510 TpConnection *connection,
1511 const gchar *channel_type,
1513 GArray *fixed_properties)
1515 EmpathyDispatcherPriv *priv = GET_PRIV (dispatcher);
1518 GList *matching_classes;
1522 g_return_val_if_fail (channel_type != NULL, NULL);
1523 g_return_val_if_fail (handle_type != 0, NULL);
1525 cd = g_hash_table_lookup (priv->connections, connection);
1530 classes = cd->requestable_channels;
1531 if (classes == NULL)
1534 matching_classes = NULL;
1536 for (i = 0; i < classes->len; i++)
1538 class = g_ptr_array_index (classes, i);
1540 if (!channel_class_matches
1541 (class, channel_type, handle_type, fixed_properties))
1544 matching_classes = g_list_prepend (matching_classes, class);
1547 return matching_classes;
1551 find_channel_class_idle_cb (gpointer user_data)
1555 FindChannelRequest *request = user_data;
1557 gboolean is_ready = TRUE;
1558 EmpathyDispatcherPriv *priv = GET_PRIV (request->dispatcher);
1560 g_hash_table_remove (priv->request_channel_class_async_ids, request);
1562 cd = g_hash_table_lookup (priv->connections, request->connection);
1566 else if (cd->requestable_channels == NULL)
1571 retval = empathy_dispatcher_find_channel_classes (request->dispatcher,
1572 request->connection, request->channel_type, request->handle_type,
1573 request->properties);
1575 request->callback (retval, request->user_data);
1576 free_find_channel_request (request);
1577 g_list_free (retval);
1582 requests = g_hash_table_lookup (priv->outstanding_classes_requests,
1583 request->connection);
1584 requests = g_list_prepend (requests, request);
1586 g_hash_table_insert (priv->outstanding_classes_requests,
1587 request->connection, requests);
1593 setup_varargs (va_list var_args,
1594 const char *channel_namespace,
1595 const char *first_property_name)
1601 if (first_property_name == NULL)
1604 name = first_property_name;
1605 properties = g_array_new (TRUE, TRUE, sizeof (char *));
1607 while (name != NULL)
1609 name_full = g_strdup (name);
1610 properties = g_array_append_val (properties, name_full);
1611 name = va_arg (var_args, char *);
1618 * empathy_dispatcher_find_requestable_channel_classes:
1619 * @dispatcher: an #EmpathyDispatcher
1620 * @connection: a #TpConnection
1621 * @channel_type: a string identifying the type of the channel to lookup
1622 * @handle_type: the handle type for the channel
1623 * @first_property_name: %NULL, or the name of the first fixed property,
1624 * followed optionally by more names, followed by %NULL.
1626 * Returns all the channel classes that a client can request for the connection
1627 * @connection, of the type identified by @channel_type, @handle_type and the
1628 * fixed properties list.
1629 * The classes which are compatible with a fixed properties list (i.e. those
1630 * that will be returned by this function) are intended as those that do not
1631 * contain any fixed property other than those in the list; note that this
1632 * doesn't guarantee that all the classes compatible with the list will contain
1633 * all the requested fixed properties, so the clients will have to filter
1634 * the returned list themselves.
1635 * If @first_property_name is %NULL, only the classes with no other fixed
1636 * properties than ChannelType and TargetHandleType will be returned.
1637 * Note that this function may return %NULL without performing any lookup if
1638 * @connection is not ready. To ensure that @connection is always ready,
1639 * use the empathy_dispatcher_find_requestable_channel_classes_async() variant.
1641 * Return value: a #GList of #GValueArray objects, where the first element in
1642 * the array is a #GHashTable of the fixed properties, and the second is
1643 * a #GStrv of the allowed properties for the class. The list should be free'd
1644 * with g_list_free() when done, but the objects inside the list are owned
1645 * by the #EmpathyDispatcher and must not be modified.
1648 empathy_dispatcher_find_requestable_channel_classes
1649 (EmpathyDispatcher *dispatcher,
1650 TpConnection *connection,
1651 const gchar *channel_type,
1653 const char *first_property_name,
1658 EmpathyDispatcherPriv *priv;
1663 g_return_val_if_fail (EMPATHY_IS_DISPATCHER (dispatcher), NULL);
1664 g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
1665 g_return_val_if_fail (channel_type != NULL, NULL);
1666 g_return_val_if_fail (handle_type != 0, NULL);
1668 priv = GET_PRIV (dispatcher);
1670 va_start (var_args, first_property_name);
1672 properties = setup_varargs (var_args, channel_type, first_property_name);
1676 retval = empathy_dispatcher_find_channel_classes (dispatcher, connection,
1677 channel_type, handle_type, properties);
1679 if (properties != NULL)
1681 /* free the properties array */
1682 for (idx = 0; idx < properties->len ; idx++)
1684 str = g_array_index (properties, char *, idx);
1688 g_array_free (properties, TRUE);
1695 * empathy_dispatcher_find_requestable_channel_classes_async:
1696 * @dispatcher: an #EmpathyDispatcher
1697 * @connection: a #TpConnection
1698 * @channel_type: a string identifying the type of the channel to lookup
1699 * @handle_type: the handle type for the channel
1700 * @callback: the callback to call when @connection is ready
1701 * @user_data: the user data to pass to @callback
1702 * @first_property_name: %NULL, or the name of the first fixed property,
1703 * followed optionally by more names, followed by %NULL.
1705 * Please see the documentation of
1706 * empathy_dispatcher_find_requestable_channel_classes() for a detailed
1707 * description of this function.
1710 empathy_dispatcher_find_requestable_channel_classes_async
1711 (EmpathyDispatcher *dispatcher,
1712 TpConnection *connection,
1713 const gchar *channel_type,
1715 EmpathyDispatcherFindChannelClassCb callback,
1717 const char *first_property_name,
1722 FindChannelRequest *request;
1723 EmpathyDispatcherPriv *priv;
1726 g_return_if_fail (EMPATHY_IS_DISPATCHER (dispatcher));
1727 g_return_if_fail (TP_IS_CONNECTION (connection));
1728 g_return_if_fail (channel_type != NULL);
1729 g_return_if_fail (handle_type != 0);
1731 priv = GET_PRIV (dispatcher);
1733 va_start (var_args, first_property_name);
1735 properties = setup_varargs (var_args, channel_type, first_property_name);
1739 /* append another request for this connection */
1740 request = g_slice_new0 (FindChannelRequest);
1741 request->dispatcher = dispatcher;
1742 request->channel_type = g_strdup (channel_type);
1743 request->handle_type = handle_type;
1744 request->connection = connection;
1745 request->callback = callback;
1746 request->user_data = user_data;
1747 request->properties = properties;
1749 source_id = g_idle_add (find_channel_class_idle_cb, request);
1751 g_hash_table_insert (priv->request_channel_class_async_ids,
1752 request, GUINT_TO_POINTER (source_id));