1 /*
2 * base-connection.c - Source for TpBaseConnection
3 *
4 * Copyright © 2005-2010 Collabora Ltd.
5 * Copyright © 2005-2009 Nokia Corporation
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * SECTION:base-connection
24 * @title: TpBaseConnection
25 * @short_description: base class for #TpSvcConnection implementations
26 * @see_also: #TpBaseConnectionManager, #TpSvcConnection
27 *
28 * This base class makes it easier to write #TpSvcConnection implementations
29 * by managing connection status, channel factories and handle tracking.
30 * A subclass should often not need to implement any of the Connection
31 * methods itself.
32 *
33 * However, methods may be reimplemented if needed: for instance, Gabble
34 * overrides RequestHandles so it can validate MUC rooms, which must be done
35 * asynchronously.
36 */
37
38 /**
39 * TpBaseConnectionProc:
40 * @self: The connection object
41 *
42 * Signature of a virtual method on #TpBaseConnection that takes no
43 * additional parameters and returns nothing.
44 */
45
46 /**
47 * TpBaseConnectionStartConnectingImpl:
48 * @self: The connection object
49 * @error: Set to the error if %FALSE is returned
50 *
51 * Signature of an implementation of the start_connecting method
52 * of #TpBaseConnection.
53 *
54 * On entry, the implementation may assume that it is in state NEW.
55 *
56 * If %TRUE is returned, the Connect D-Bus method succeeds; the
57 * implementation must either have already set the status to CONNECTED by
58 * calling tp_base_connection_change_status(), or have arranged for a
59 * status change to either state DISCONNECTED or CONNECTED to be signalled by
60 * calling tp_base_connection_change_status() at some later time.
61 * If the status is still NEW after returning %TRUE, #TpBaseConnection will
62 * automatically change it to CONNECTING for reason REQUESTED.
63 *
64 * If %FALSE is returned, the error will be raised from Connect as an
65 * exception. If the status is not DISCONNECTED after %FALSE is returned,
66 * #TpBaseConnection will automatically change it to DISCONNECTED
67 * with a reason appropriate to the error; NetworkError results in
68 * NETWORK_ERROR, PermissionDenied results in AUTHENTICATION_FAILED, and all
69 * other errors currently result in NONE_SPECIFIED.
70 *
71 * All except the simplest connection managers are expected to implement this
72 * asynchronously, returning %TRUE in most cases and changing the status
73 * to CONNECTED or DISCONNECTED later.
74 *
75 * Returns: %FALSE if failure has already occurred, else %TRUE.
76 */
77
78 /**
79 * TpBaseConnectionCreateHandleReposImpl: (skip)
80 * @self: The connection object
81 * @repos: An array of pointers to be filled in; the implementation
82 * may assume all are initially NULL.
83 *
84 * Signature of an implementation of the create_handle_repos method
85 * of #TpBaseConnection.
86 */
87
88 /**
89 * TpBaseConnectionCreateChannelFactoriesImpl:
90 * @self: The implementation, a subclass of TpBaseConnection
91 *
92 * Signature of an implementation of the create_channel_factories method
93 * of #TpBaseConnection.
94 *
95 * Returns: (transfer full): a GPtrArray of objects implementing
96 * #TpChannelFactoryIface which, between them, implement all channel types
97 * this Connection supports.
98 */
99
100 /**
101 * TpBaseConnectionCreateChannelManagersImpl:
102 * @self: The implementation, a subclass of TpBaseConnection
103 *
104 * Signature of an implementation of the create_channel_managers method
105 * of #TpBaseConnection.
106 *
107 * Returns: (transfer full): a GPtrArray of objects implementing
108 * #TpChannelManager which, between them, implement all channel types this
109 * Connection supports.
110 */
111
112 /**
113 * TpBaseConnectionGetUniqueConnectionNameImpl:
114 * @self: The implementation, a subclass of TpBaseConnection
115 *
116 * Signature of the @get_unique_connection_name virtual method
117 * on #TpBaseConnection.
118 *
119 * Returns: (transfer full): a name for this connection which will be unique
120 * within this connection manager process, as a string which the caller must
121 * free with #g_free.
122 */
123
124 /**
125 * TpBaseConnectionGetInterfacesImpl:
126 * @self: a #TpBaseConnection
127 *
128 * Signature of an implementation of
129 * #TpBaseConnectionClass.get_interfaces_always_present virtual
130 * function.
131 *
132 * Implementation must first chainup on parent class implementation and then
133 * add extra interfaces into the #GPtrArray.
134 *
135 * |[
136 * static GPtrArray *
137 * my_connection_get_interfaces_always_present (TpBaseConnection *self)
138 * {
139 * GPtrArray *interfaces;
140 *
141 * interfaces = TP_BASE_CONNECTION_CLASS (
142 * my_connection_parent_class)->get_interfaces_always_present (self);
143 *
144 * g_ptr_array_add (interfaces, TP_IFACE_BADGERS);
145 *
146 * return interfaces;
147 * }
148 * ]|
149 *
150 * Returns: (transfer container): a #GPtrArray of static strings for D-Bus
151 * interfaces implemented by this client.
152 *
153 * Since: 0.19.4
154 */
155
156 /**
157 * TpBaseConnectionClass:
158 * @parent_class: The superclass' structure
159 * @create_handle_repos: Fill in suitable handle repositories in the
160 * given array for all those handle types this Connection supports.
161 * Must be set by subclasses to a non-%NULL value; the function must create
162 * at least a CONTACT handle repository (failing to do so will cause a crash).
163 * @create_channel_factories: Create an array of channel factories for this
164 * Connection. At least one of this or @create_channel_managers must be set by
165 * subclasses to a non-%NULL value; in new code, setting this to %NULL and
166 * using channel managers exclusively is recommended.
167 * @get_unique_connection_name: Construct a unique name for this connection
168 * (for example using the protocol's format for usernames). If %NULL (the
169 * default), a unique name will be generated. Subclasses should usually
170 * override this to get more obvious names, to aid debugging and prevent
171 * multiple connections to the same account.
172 * @connecting: If set by subclasses, will be called just after the state
173 * changes to CONNECTING. May be %NULL if nothing special needs to happen.
174 * @connected: If set by subclasses, will be called just after the state
175 * changes to CONNECTED. May be %NULL if nothing special needs to happen.
176 * @disconnected: If set by subclasses, will be called just after the state
177 * changes to DISCONNECTED. May be %NULL if nothing special needs to happen.
178 * @shut_down: Called after disconnected() is called, to clean up the
179 * connection. Must start the shutdown process for the underlying
180 * network connection, and arrange for tp_base_connection_finish_shutdown()
181 * to be called after the underlying connection has been closed. May not
182 * be left as %NULL.
183 * @start_connecting: Asynchronously start connecting - called to implement
184 * the Connect D-Bus method. See #TpBaseConnectionStartConnectingImpl for
185 * details. May not be left as %NULL.
186 * @get_interfaces_always_present: Returns a #GPtrArray of extra D-Bus
187 * interfaces which are always implemented by instances of this class,
188 * which may be filled in by subclasses. The default is to list no
189 * additional interfaces. Individual instances may detect which
190 * additional interfaces they support and signal them before going
191 * to state CONNECTED by calling tp_base_connection_add_interfaces().
192 * @create_channel_managers: Create an array of channel managers for this
193 * Connection. At least one of this or @create_channel_factories must be set
194 * by subclasses to a non-%NULL value. Since: 0.7.15
195 *
196 * The class of a #TpBaseConnection. Many members are virtual methods etc.
197 * to be filled in in the subclass' class_init function.
198 */
199
200 /**
201 * TP_INTERNAL_CONNECTION_STATUS_NEW: (skip)
202 *
203 * A special value for #TpConnectionStatus, used within GLib connection
204 * managers to indicate that the connection is disconnected because
205 * connection has never been attempted (as distinct from disconnected
206 * after connection has started, either by user request or an error).
207 *
208 * Must never be visible on the D-Bus - %TP_CONNECTION_STATUS_DISCONNECTED
209 * is sent instead.
210 */
211
212 /**
213 * TpBaseConnection:
214 *
215 * Data structure representing a generic #TpSvcConnection implementation.
216 *
217 * Since 0.19.1, accessing the fields of this structure is deprecated.
218 * Use tp_base_connection_get_bus_name(), tp_base_connection_get_object_path(),
219 * tp_base_connection_get_status(), tp_base_connection_get_self_handle()
220 * instead.
221 */
222
223 /**
224 * TpChannelManagerIter: (skip)
225 *
226 * An iterator over the #TpChannelManager objects known to a #TpBaseConnection.
227 * It has no public fields.
228 *
229 * Use tp_base_connection_channel_manager_iter_init() to start iteration and
230 * tp_base_connection_channel_manager_iter_next() to continue.
231 *
232 * Since: 0.7.15
233 */
234
235 /**
236 * TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED: (skip)
237 * @conn: A TpBaseConnection
238 * @context: A DBusGMethodInvocation
239 *
240 * If @conn is not in state #TP_CONNECTION_STATUS_CONNECTED, complete the
241 * D-Bus method invocation @context by raising the Telepathy error
242 * #TP_ERROR_DISCONNECTED, and return from the current function (which
243 * must be void). For use in D-Bus method implementations.
244 */
245
246 #include "config.h"
247
248 #include <telepathy-glib/base-connection.h>
249 #include <telepathy-glib/base-connection-internal.h>
250
251 #include <string.h>
252
253 #include <dbus/dbus-glib-lowlevel.h>
254
255 #include <telepathy-glib/channel-factory-iface.h>
256 #include <telepathy-glib/channel-manager.h>
257 #include <telepathy-glib/connection-manager.h>
258 #include <telepathy-glib/contacts-mixin.h>
259 #include <telepathy-glib/dbus-properties-mixin.h>
260 #include <telepathy-glib/dbus.h>
261 #include <telepathy-glib/dbus-internal.h>
262 #include <telepathy-glib/exportable-channel.h>
263 #include <telepathy-glib/gtypes.h>
264 #include <telepathy-glib/interfaces.h>
265 #include <telepathy-glib/svc-generic.h>
266 #include <telepathy-glib/util.h>
267
268 #define DEBUG_FLAG TP_DEBUG_CONNECTION
269 #include "telepathy-glib/debug-internal.h"
270 #include "telepathy-glib/variant-util-internal.h"
271
272 static void conn_iface_init (gpointer, gpointer);
273 static void requests_iface_init (gpointer, gpointer);
274
275 G_DEFINE_ABSTRACT_TYPE_WITH_CODE(TpBaseConnection,
276 tp_base_connection,
277 G_TYPE_OBJECT,
278 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION,
279 conn_iface_init);
280 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
281 tp_dbus_properties_mixin_iface_init);
282 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_REQUESTS,
283 requests_iface_init))
284
285 enum
286 {
287 PROP_PROTOCOL = 1,
288 PROP_SELF_HANDLE,
289 PROP_SELF_ID,
290 PROP_INTERFACES,
291 PROP_DBUS_STATUS,
292 PROP_DBUS_DAEMON,
293 PROP_HAS_IMMORTAL_HANDLES,
294 PROP_ACCOUNT_PATH_SUFFIX,
295 N_PROPS
296 };
297
298 /* signal enum */
299 enum
300 {
301 INVALID_SIGNAL,
302 SHUTDOWN_FINISHED,
303 CLIENTS_INTERESTED,
304 CLIENTS_UNINTERESTED,
305 N_SIGNALS
306 };
307
308 static guint signals[N_SIGNALS] = {0};
309
310 typedef struct _ChannelRequest ChannelRequest;
311
312 typedef enum {
313 METHOD_REQUEST_CHANNEL,
314 METHOD_CREATE_CHANNEL,
315 METHOD_ENSURE_CHANNEL,
316 NUM_METHODS
317 } ChannelRequestMethod;
318
319 struct _ChannelRequest
320 {
321 DBusGMethodInvocation *context;
322 ChannelRequestMethod method;
323
324 gchar *channel_type;
325 guint handle_type;
326 guint handle;
327 /* always TRUE for CREATE; always FALSE for ENSURE */
328 unsigned suppress_handler : 1;
329
330 /* only meaningful for METHOD_ENSURE_CHANNEL; only true if this is the first
331 * request to be satisfied with a particular channel, and no other request
332 * satisfied by that channel has a different method.
333 */
334 unsigned yours : 1;
335 };
336
337 static ChannelRequest *
channel_request_new(DBusGMethodInvocation * context,ChannelRequestMethod method,const char * channel_type,guint handle_type,guint handle,gboolean suppress_handler)338 channel_request_new (DBusGMethodInvocation *context,
339 ChannelRequestMethod method,
340 const char *channel_type,
341 guint handle_type,
342 guint handle,
343 gboolean suppress_handler)
344 {
345 ChannelRequest *ret;
346
347 g_assert (NULL != context);
348 g_assert (NULL != channel_type);
349 g_assert (method < NUM_METHODS);
350
351 ret = g_slice_new0 (ChannelRequest);
352 ret->context = context;
353 ret->method = method;
354 ret->channel_type = g_strdup (channel_type);
355 ret->handle_type = handle_type;
356 ret->handle = handle;
357 ret->suppress_handler = suppress_handler;
358 ret->yours = FALSE;
359
360 DEBUG("New channel request at %p: ctype=%s htype=%d handle=%d suppress=%d",
361 ret, channel_type, handle_type, handle, suppress_handler);
362
363 return ret;
364 }
365
366 static void
channel_request_free(ChannelRequest * request)367 channel_request_free (ChannelRequest *request)
368 {
369 g_assert (NULL == request->context);
370 DEBUG("Freeing channel request at %p: ctype=%s htype=%d handle=%d "
371 "suppress=%d", request, request->channel_type, request->handle_type,
372 request->handle, request->suppress_handler);
373 g_free (request->channel_type);
374 g_slice_free (ChannelRequest, request);
375 }
376
377 static void
channel_request_cancel(gpointer data,gpointer user_data)378 channel_request_cancel (gpointer data, gpointer user_data)
379 {
380 ChannelRequest *request = (ChannelRequest *) data;
381 GError error = { TP_ERROR, TP_ERROR_DISCONNECTED,
382 "unable to service this channel request, we're disconnecting!" };
383
384 DEBUG ("cancelling request at %p for %s/%u/%u", request,
385 request->channel_type, request->handle_type, request->handle);
386
387 dbus_g_method_return_error (request->context, &error);
388 request->context = NULL;
389
390 channel_request_free (request);
391 }
392
393 struct _TpBaseConnectionPrivate
394 {
395 const gchar *self_id;
396
397 /* Telepathy properties */
398 gchar *protocol;
399
400 /* if TRUE, the object has gone away */
401 gboolean dispose_has_run;
402 /* array of (TpChannelFactoryIface *) */
403 GPtrArray *channel_factories;
404 /* array of (TpChannelManager *) */
405 GPtrArray *channel_managers;
406 /* array of (ChannelRequest *) */
407 GPtrArray *channel_requests;
408
409 TpHandleRepoIface *handles[TP_NUM_HANDLE_TYPES];
410
411 /* Created in constructed, this is an array of static strings which
412 * represent the interfaces on this connection.
413 *
414 * Note that this is a GArray of gchar*, not a GPtrArray,
415 * so that we can use GArray's convenient auto-null-termination. */
416 GArray *interfaces;
417
418 /* Array of DBusGMethodInvocation * representing Disconnect calls.
419 * If NULL and we are in a state != DISCONNECTED, then we have not started
420 * shutting down yet.
421 * If NULL and we are in state DISCONNECTED, then we have finished shutting
422 * down.
423 * If not NULL, we are trying to shut down (and must be in state
424 * DISCONNECTED). */
425 GPtrArray *disconnect_requests;
426
427 TpDBusDaemon *bus_proxy;
428 /* TRUE after constructor() returns */
429 gboolean been_constructed;
430 /* TRUE if on D-Bus */
431 gboolean been_registered;
432
433 /* g_strdup (unique name) => gsize total count
434 *
435 * This is derivable from @client_interests: e.g. if
436 * client_interests = { LOCATION => { ":1.23" => 5, ":1.42" => 2 },
437 * MAIL_NOTIFICATION => { ":1.23" => 1 } }
438 * then it implies
439 * interested_clients = { ":1.23" => 6, ":1.42" => 2 }
440 */
441 GHashTable *interested_clients;
442 /* GQuark iface => GHashTable {
443 * unique name borrowed from interested_clients => gsize count } */
444 GHashTable *client_interests;
445
446 gchar *account_path_suffix;
447 };
448
449 static const gchar * const *tp_base_connection_get_interfaces (
450 TpBaseConnection *self);
451
452 static gboolean
tp_base_connection_ensure_dbus(TpBaseConnection * self,GError ** error)453 tp_base_connection_ensure_dbus (TpBaseConnection *self,
454 GError **error)
455 {
456 if (self->priv->bus_proxy == NULL)
457 {
458 self->priv->bus_proxy = tp_dbus_daemon_dup (error);
459
460 if (self->priv->bus_proxy == NULL)
461 return FALSE;
462 }
463
464 return TRUE;
465 }
466
467 static void
tp_base_connection_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)468 tp_base_connection_get_property (GObject *object,
469 guint property_id,
470 GValue *value,
471 GParamSpec *pspec)
472 {
473 TpBaseConnection *self = (TpBaseConnection *) object;
474 TpBaseConnectionPrivate *priv = self->priv;
475
476 switch (property_id)
477 {
478 case PROP_PROTOCOL:
479 g_value_set_string (value, priv->protocol);
480 break;
481
482 case PROP_SELF_HANDLE:
483 g_value_set_uint (value, self->self_handle);
484 break;
485
486 case PROP_SELF_ID:
487 g_value_set_string (value, self->priv->self_id);
488 break;
489
490 case PROP_INTERFACES:
491 g_value_set_boxed (value, tp_base_connection_get_interfaces (self));
492 break;
493
494 case PROP_DBUS_STATUS:
495 g_value_set_uint (value, tp_base_connection_get_status (self));
496 break;
497
498 case PROP_DBUS_DAEMON:
499 g_value_set_object (value, self->priv->bus_proxy);
500 break;
501
502 case PROP_HAS_IMMORTAL_HANDLES:
503 g_value_set_boolean (value, TRUE);
504 break;
505
506 case PROP_ACCOUNT_PATH_SUFFIX:
507 g_value_set_string (value, self->priv->account_path_suffix);
508 break;
509
510 default:
511 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
512 break;
513 }
514 }
515
516 static void
tp_base_connection_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)517 tp_base_connection_set_property (GObject *object,
518 guint property_id,
519 const GValue *value,
520 GParamSpec *pspec)
521 {
522 TpBaseConnection *self = (TpBaseConnection *) object;
523 TpBaseConnectionPrivate *priv = self->priv;
524
525 switch (property_id) {
526 case PROP_PROTOCOL:
527 g_free (priv->protocol);
528 priv->protocol = g_value_dup_string (value);
529 g_assert (priv->protocol != NULL);
530 break;
531
532 case PROP_SELF_HANDLE:
533 tp_base_connection_set_self_handle (self, g_value_get_uint (value));
534 break;
535
536 case PROP_DBUS_DAEMON:
537 {
538 TpDBusDaemon *dbus_daemon = g_value_get_object (value);
539
540 g_assert (self->priv->bus_proxy == NULL); /* construct-only */
541
542 if (dbus_daemon != NULL)
543 self->priv->bus_proxy = g_object_ref (dbus_daemon);
544 }
545 break;
546
547 case PROP_ACCOUNT_PATH_SUFFIX:
548 g_assert (self->priv->account_path_suffix == NULL); /* construct-only */
549 self->priv->account_path_suffix = g_value_dup_string (value);
550 break;
551
552 default:
553 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
554 break;
555 }
556 }
557
558 static void tp_base_connection_interested_name_owner_changed_cb (
559 TpDBusDaemon *it,
560 const gchar *unique_name,
561 const gchar *new_owner,
562 gpointer user_data);
563
564 static void
tp_base_connection_unregister(TpBaseConnection * self)565 tp_base_connection_unregister (TpBaseConnection *self)
566 {
567 TpBaseConnectionPrivate *priv = self->priv;
568
569 if (priv->bus_proxy != NULL)
570 {
571 GHashTableIter iter;
572 gpointer k;
573
574 if (priv->been_registered)
575 {
576 tp_dbus_daemon_unregister_object (priv->bus_proxy, self);
577
578 if (self->bus_name != NULL)
579 tp_dbus_daemon_release_name (priv->bus_proxy, self->bus_name, NULL);
580
581 priv->been_registered = FALSE;
582 }
583
584 g_hash_table_iter_init (&iter, self->priv->interested_clients);
585
586 while (g_hash_table_iter_next (&iter, &k, NULL))
587 {
588 tp_dbus_daemon_cancel_name_owner_watch (priv->bus_proxy, k,
589 tp_base_connection_interested_name_owner_changed_cb, self);
590 g_hash_table_iter_remove (&iter);
591 }
592 }
593 }
594
595 static void
tp_base_connection_dispose(GObject * object)596 tp_base_connection_dispose (GObject *object)
597 {
598 TpBaseConnection *self = TP_BASE_CONNECTION (object);
599 TpBaseConnectionPrivate *priv = self->priv;
600 guint i;
601
602 if (priv->dispose_has_run)
603 return;
604
605 priv->dispose_has_run = TRUE;
606
607 g_assert ((self->status == TP_CONNECTION_STATUS_DISCONNECTED) ||
608 (self->status == TP_INTERNAL_CONNECTION_STATUS_NEW));
609
610 tp_base_connection_unregister (self);
611
612 tp_clear_object (&priv->bus_proxy);
613
614 g_ptr_array_foreach (priv->channel_factories, (GFunc) g_object_unref, NULL);
615 g_ptr_array_unref (priv->channel_factories);
616 priv->channel_factories = NULL;
617
618 g_ptr_array_foreach (priv->channel_managers, (GFunc) g_object_unref, NULL);
619 g_ptr_array_unref (priv->channel_managers);
620 priv->channel_managers = NULL;
621
622 if (priv->channel_requests)
623 {
624 g_assert (priv->channel_requests->len == 0);
625 g_ptr_array_unref (priv->channel_requests);
626 priv->channel_requests = NULL;
627 }
628
629 for (i = 0; i < TP_NUM_HANDLE_TYPES; i++)
630 tp_clear_object (priv->handles + i);
631
632 if (priv->interfaces)
633 {
634 g_array_unref (priv->interfaces);
635 }
636
637 if (G_OBJECT_CLASS (tp_base_connection_parent_class)->dispose)
638 G_OBJECT_CLASS (tp_base_connection_parent_class)->dispose (object);
639 }
640
641 static void
tp_base_connection_finalize(GObject * object)642 tp_base_connection_finalize (GObject *object)
643 {
644 TpBaseConnection *self = TP_BASE_CONNECTION (object);
645 TpBaseConnectionPrivate *priv = self->priv;
646
647 g_free (priv->protocol);
648 g_free (self->bus_name);
649 g_free (self->object_path);
650 g_hash_table_unref (priv->client_interests);
651 g_hash_table_unref (priv->interested_clients);
652 g_free (priv->account_path_suffix);
653
654 G_OBJECT_CLASS (tp_base_connection_parent_class)->finalize (object);
655 }
656
657
658 /**
659 * exportable_channel_get_old_info:
660 * @channel: a channel
661 * @object_path_out: address at which to store the channel's object path,
662 * which the caller should g_free()
663 * @channel_type_out: address at which to store the channel's type, which the
664 * caller should g_free()
665 * @handle_type_out: address at which to store the channel's associated handle
666 * type
667 * @handle_out: address at which to store the channel's associated
668 * handle, if any. This is a borrowed reference; the caller
669 * does not need to tp_handle_unref() it.
670 *
671 * Given a new-style exportable channel, as used by the Requests interface's
672 * API, fetches the information needed for the old-style ListChannels method
673 * on Connections.
674 */
675 static void
exportable_channel_get_old_info(TpExportableChannel * channel,gchar ** object_path_out,gchar ** channel_type_out,guint * handle_type_out,guint * handle_out)676 exportable_channel_get_old_info (TpExportableChannel *channel,
677 gchar **object_path_out,
678 gchar **channel_type_out,
679 guint *handle_type_out,
680 guint *handle_out)
681 {
682 gchar *object_path;
683 GHashTable *channel_properties;
684 gboolean valid;
685
686 g_object_get (channel,
687 "object-path", &object_path,
688 "channel-properties", &channel_properties,
689 NULL);
690
691 g_assert (object_path != NULL);
692 g_assert (tp_dbus_check_valid_object_path (object_path, NULL));
693
694 if (object_path_out != NULL)
695 *object_path_out = object_path;
696 else
697 g_free (object_path);
698
699 if (channel_type_out != NULL)
700 {
701 *channel_type_out = g_strdup (tp_asv_get_string (channel_properties,
702 TP_PROP_CHANNEL_CHANNEL_TYPE));
703 g_assert (*channel_type_out != NULL);
704 g_assert (tp_dbus_check_valid_interface_name (*channel_type_out, NULL));
705 }
706
707 if (handle_type_out != NULL)
708 {
709 *handle_type_out = tp_asv_get_uint32 (channel_properties,
710 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid);
711 g_assert (valid);
712 }
713
714 if (handle_out != NULL)
715 {
716 *handle_out = tp_asv_get_uint32 (channel_properties,
717 TP_PROP_CHANNEL_TARGET_HANDLE, &valid);
718 g_assert (valid);
719
720 if (handle_type_out != NULL)
721 {
722 if (*handle_type_out == TP_HANDLE_TYPE_NONE)
723 g_assert (*handle_out == 0);
724 else
725 g_assert (*handle_out != 0);
726 }
727 }
728
729 g_hash_table_unref (channel_properties);
730 }
731
732
733 /*
734 * get_channel_details:
735 * @obj: a channel, which must implement one of #TpExportableChannel and
736 * #TpChannelIface
737 *
738 * Returns: (oa{sv}: o.fd.T.Conn.Iface.Requests.Channel_Details), suitable for
739 * inclusion in the NewChannels signal.
740 */
741 static GValueArray *
get_channel_details(GObject * obj)742 get_channel_details (GObject *obj)
743 {
744 GValueArray *structure;
745 GHashTable *table;
746 GValue *value;
747 gchar *object_path;
748
749 g_object_get (obj,
750 "object-path", &object_path,
751 NULL);
752
753 g_assert (TP_IS_EXPORTABLE_CHANNEL (obj) || TP_IS_CHANNEL_IFACE (obj));
754
755 if (TP_IS_EXPORTABLE_CHANNEL (obj))
756 {
757 g_object_get (obj,
758 "channel-properties", &table,
759 NULL);
760 }
761 else
762 {
763 table = g_hash_table_new_full (g_str_hash, g_str_equal,
764 NULL, (GDestroyNotify) tp_g_value_slice_free);
765
766 value = tp_g_value_slice_new (G_TYPE_UINT);
767 g_object_get_property (obj, "handle", value);
768 g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE, value);
769
770 value = tp_g_value_slice_new (G_TYPE_UINT);
771 g_object_get_property (obj, "handle-type", value);
772 g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, value);
773
774 value = tp_g_value_slice_new (G_TYPE_STRING);
775 g_object_get_property (obj, "channel-type", value);
776 g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE, value);
777 }
778
779 structure = tp_value_array_build (2,
780 DBUS_TYPE_G_OBJECT_PATH, object_path,
781 TP_HASH_TYPE_QUALIFIED_PROPERTY_VALUE_MAP, table,
782 G_TYPE_INVALID);
783
784 g_free (object_path);
785 g_hash_table_unref (table);
786
787 return structure;
788 }
789
790
791 static GPtrArray *
find_matching_channel_requests(TpBaseConnection * conn,const gchar * channel_type,guint handle_type,guint handle,ChannelRequest * channel_request,gboolean * suppress_handler)792 find_matching_channel_requests (TpBaseConnection *conn,
793 const gchar *channel_type,
794 guint handle_type,
795 guint handle,
796 ChannelRequest *channel_request,
797 gboolean *suppress_handler)
798 {
799 TpBaseConnectionPrivate *priv = conn->priv;
800 GPtrArray *requests;
801 guint i;
802
803 requests = g_ptr_array_sized_new (1);
804
805 if (handle_type == 0)
806 {
807 /* It's an anonymous channel, which can only satisfy the request for
808 * which it was created (or if it's returned as EXISTING, it can only
809 * satisfy the request for which it was returned as EXISTING).
810 */
811 g_assert (handle == 0);
812 g_assert (channel_request == NULL ||
813 tp_g_ptr_array_contains (priv->channel_requests, channel_request));
814
815 if (channel_request)
816 {
817 g_ptr_array_add (requests, channel_request);
818
819 if (suppress_handler && channel_request->suppress_handler)
820 *suppress_handler = TRUE;
821 }
822
823 /* whether we've put any matches in requests or not */
824 return requests;
825 }
826
827 /* for identifiable channels (those which are to a particular handle),
828 * satisfy any queued requests.
829 */
830 for (i = 0; i < priv->channel_requests->len; i++)
831 {
832 ChannelRequest *request = g_ptr_array_index (priv->channel_requests, i);
833
834 if (tp_strdiff (request->channel_type, channel_type))
835 continue;
836
837 if (handle_type != request->handle_type)
838 continue;
839
840 if (handle != request->handle)
841 continue;
842
843 if (request->suppress_handler && suppress_handler)
844 *suppress_handler = TRUE;
845
846 g_ptr_array_add (requests, request);
847 }
848
849 /* if this channel was created or returned as a result of a particular
850 * request, that request had better be among the matching ones in the queue
851 */
852 g_assert (channel_request == NULL ||
853 tp_g_ptr_array_contains (requests, channel_request));
854
855 return requests;
856 }
857
858
859 static void
satisfy_request(TpBaseConnection * conn,ChannelRequest * request,GObject * channel,const gchar * object_path)860 satisfy_request (TpBaseConnection *conn,
861 ChannelRequest *request,
862 GObject *channel,
863 const gchar *object_path)
864 {
865 TpBaseConnectionPrivate *priv = conn->priv;
866
867 DEBUG ("completing queued request %p with success, "
868 "channel_type=%s, handle_type=%u, "
869 "handle=%u, suppress_handler=%u", request, request->channel_type,
870 request->handle_type, request->handle, request->suppress_handler);
871
872 switch (request->method)
873 {
874 case METHOD_REQUEST_CHANNEL:
875 tp_svc_connection_return_from_request_channel (request->context,
876 object_path);
877 break;
878
879 case METHOD_CREATE_CHANNEL:
880 {
881 GHashTable *properties;
882
883 g_assert (TP_IS_EXPORTABLE_CHANNEL (channel));
884 g_object_get (channel,
885 "channel-properties", &properties,
886 NULL);
887 tp_svc_connection_interface_requests_return_from_create_channel (
888 request->context, object_path, properties);
889 g_hash_table_unref (properties);
890 }
891 break;
892
893 case METHOD_ENSURE_CHANNEL:
894 {
895 GHashTable *properties;
896
897 g_assert (TP_IS_EXPORTABLE_CHANNEL (channel));
898 g_object_get (channel,
899 "channel-properties", &properties,
900 NULL);
901 tp_svc_connection_interface_requests_return_from_ensure_channel (
902 request->context, request->yours, object_path, properties);
903 g_hash_table_unref (properties);
904 }
905 break;
906
907 default:
908 g_assert_not_reached ();
909 }
910 request->context = NULL;
911
912 g_ptr_array_remove (priv->channel_requests, request);
913
914 channel_request_free (request);
915 }
916
917
918 static void
factory_satisfy_requests(TpBaseConnection * conn,TpChannelFactoryIface * factory,TpChannelIface * chan,ChannelRequest * channel_request,gboolean is_new)919 factory_satisfy_requests (TpBaseConnection *conn,
920 TpChannelFactoryIface *factory,
921 TpChannelIface *chan,
922 ChannelRequest *channel_request,
923 gboolean is_new)
924 {
925 gchar *object_path = NULL, *channel_type = NULL;
926 guint handle_type = 0, handle = 0;
927 gboolean suppress_handler = FALSE;
928 GPtrArray *tmp;
929 guint i;
930
931 g_object_get (chan,
932 "object-path", &object_path,
933 "channel-type", &channel_type,
934 "handle-type", &handle_type,
935 "handle", &handle,
936 NULL);
937
938 DEBUG ("called for %s", object_path);
939
940 tmp = find_matching_channel_requests (conn, channel_type, handle_type,
941 handle, channel_request,
942 &suppress_handler);
943
944 for (i = 0; i < tmp->len; i++)
945 satisfy_request (conn, g_ptr_array_index (tmp, i), G_OBJECT (chan),
946 object_path);
947
948 if (is_new)
949 {
950 GPtrArray *array = g_ptr_array_sized_new (1);
951
952 g_ptr_array_add (array, get_channel_details (G_OBJECT (chan)));
953 tp_svc_connection_interface_requests_emit_new_channels (conn, array);
954 tp_value_array_free (g_ptr_array_index (array, 0));
955 g_ptr_array_unref (array);
956
957 tp_svc_connection_emit_new_channel (conn, object_path, channel_type,
958 handle_type, handle, suppress_handler);
959 }
960
961 g_ptr_array_unref (tmp);
962
963 g_free (object_path);
964 g_free (channel_type);
965 }
966
967
968 static void
fail_channel_request(TpBaseConnection * conn,ChannelRequest * request,GError * error)969 fail_channel_request (TpBaseConnection *conn,
970 ChannelRequest *request,
971 GError *error)
972 {
973 TpBaseConnectionPrivate *priv = conn->priv;
974
975 DEBUG ("completing queued request %p with error, channel_type=%s, "
976 "handle_type=%u, handle=%u, suppress_handler=%u",
977 request, request->channel_type,
978 request->handle_type, request->handle, request->suppress_handler);
979
980 dbus_g_method_return_error (request->context, error);
981 request->context = NULL;
982
983 g_ptr_array_remove (priv->channel_requests, request);
984
985 channel_request_free (request);
986 }
987
988
989 /* Channel factory signal handlers */
990
991 static void
factory_channel_closed_cb(GObject * channel,TpBaseConnection * conn)992 factory_channel_closed_cb (GObject *channel,
993 TpBaseConnection *conn)
994 {
995 gchar *object_path;
996
997 g_object_get (channel,
998 "object-path", &object_path,
999 NULL);
1000
1001 tp_svc_connection_interface_requests_emit_channel_closed (conn,
1002 object_path);
1003
1004 g_free (object_path);
1005 }
1006
1007 static void
factory_new_channel_cb(TpChannelFactoryIface * factory,GObject * chan,ChannelRequest * channel_request,gpointer data)1008 factory_new_channel_cb (TpChannelFactoryIface *factory,
1009 GObject *chan,
1010 ChannelRequest *channel_request,
1011 gpointer data)
1012 {
1013 factory_satisfy_requests (TP_BASE_CONNECTION (data), factory,
1014 TP_CHANNEL_IFACE (chan), channel_request, TRUE);
1015
1016 g_signal_connect (chan, "closed", (GCallback) factory_channel_closed_cb,
1017 data);
1018 }
1019
1020
1021 static void
factory_channel_error_cb(TpChannelFactoryIface * factory,GObject * chan,GError * error,ChannelRequest * channel_request,gpointer data)1022 factory_channel_error_cb (TpChannelFactoryIface *factory,
1023 GObject *chan,
1024 GError *error,
1025 ChannelRequest *channel_request,
1026 gpointer data)
1027 {
1028 TpBaseConnection *conn = TP_BASE_CONNECTION (data);
1029 gchar *channel_type = NULL;
1030 guint handle_type = 0, handle = 0;
1031 GPtrArray *tmp;
1032 guint i;
1033
1034 DEBUG ("channel_type=%s, handle_type=%u, handle=%u, error_code=%u, "
1035 "error_message=\"%s\"", channel_type, handle_type, handle,
1036 error->code, error->message);
1037
1038 g_object_get (chan,
1039 "channel-type", &channel_type,
1040 "handle-type", &handle_type,
1041 "handle", &handle,
1042 NULL);
1043
1044 tmp = find_matching_channel_requests (conn, channel_type, handle_type,
1045 handle, channel_request, NULL);
1046
1047 for (i = 0; i < tmp->len; i++)
1048 fail_channel_request (conn, g_ptr_array_index (tmp, i), error);
1049
1050 g_ptr_array_unref (tmp);
1051 g_free (channel_type);
1052 }
1053
1054
1055 /* Channel manager signal handlers */
1056
1057 typedef struct {
1058 TpBaseConnection *self;
1059 /* borrowed TpExportableChannel => itself if suppress_handler,
1060 * omitted otherwise */
1061 GHashTable *suppress_handler;
1062 } ManagerNewChannelContext;
1063
1064 static void
manager_new_channel(gpointer key,gpointer value,gpointer data)1065 manager_new_channel (gpointer key,
1066 gpointer value,
1067 gpointer data)
1068 {
1069 TpExportableChannel *channel = TP_EXPORTABLE_CHANNEL (key);
1070 GSList *request_tokens = value;
1071 ManagerNewChannelContext *context = data;
1072 TpBaseConnection *self = TP_BASE_CONNECTION (context->self);
1073 gchar *object_path;
1074 GSList *iter;
1075 gboolean suppress_handler = FALSE;
1076 gboolean satisfies_create_channel = FALSE;
1077 gboolean satisfies_request_channel = FALSE;
1078 ChannelRequest *first_ensure = NULL;
1079
1080 g_object_get (channel,
1081 "object-path", &object_path,
1082 NULL);
1083
1084 /* suppress_handler on Connection.NewChannel should be TRUE if:
1085 * - any satisfied requests were calls to CreateChannel; or
1086 * - at least one satisfied RequestChannel call had suppress_handler=TRUE;
1087 * or
1088 * - any EnsureChannel call will receive Yours=TRUE (that is, if the
1089 * channel satisfies no CreateChannel or RequestChannel calls).
1090 *
1091 * So, it should be FALSE if:
1092 * - all the requests were RequestChannel(..., suppress_handler=FALSE) or
1093 * EnsureChannel and there was at least one RequestChannel; or
1094 * - no requests were satisfied by the channel.
1095 */
1096 for (iter = request_tokens; iter != NULL; iter = iter->next)
1097 {
1098 ChannelRequest *request = iter->data;
1099
1100 switch (request->method)
1101 {
1102 case METHOD_REQUEST_CHANNEL:
1103 satisfies_request_channel = TRUE;
1104 if (request->suppress_handler)
1105 {
1106 suppress_handler = TRUE;
1107 goto break_loop_early;
1108 }
1109 break;
1110
1111 case METHOD_CREATE_CHANNEL:
1112 satisfies_create_channel = TRUE;
1113 goto break_loop_early;
1114 break;
1115
1116 case METHOD_ENSURE_CHANNEL:
1117 if (first_ensure == NULL)
1118 first_ensure = request;
1119 break;
1120
1121 case NUM_METHODS:
1122 g_assert_not_reached ();
1123 }
1124
1125 }
1126 break_loop_early:
1127
1128 /* put the channel in the suppress_handler hash table if it needs
1129 * suppress_handler set when signalling NewChannel */
1130 if (request_tokens != NULL &&
1131 (satisfies_create_channel || !satisfies_request_channel))
1132 suppress_handler = TRUE;
1133
1134 if (suppress_handler)
1135 g_hash_table_insert (context->suppress_handler, channel, channel);
1136
1137 /* If the only type of request satisfied by this new channel is
1138 * EnsureChannel, give exactly one request Yours=True.
1139 * If other kinds of requests are involved, don't give anyone Yours=True.
1140 */
1141 if (!satisfies_request_channel
1142 && !satisfies_create_channel
1143 && first_ensure != NULL)
1144 {
1145 first_ensure->yours = TRUE;
1146 }
1147
1148
1149 for (iter = request_tokens; iter != NULL; iter = iter->next)
1150 {
1151 satisfy_request (self, iter->data, G_OBJECT (channel),
1152 object_path);
1153 }
1154
1155 g_free (object_path);
1156 }
1157
1158
1159 static void
manager_new_channels_cb(TpChannelManager * manager,GHashTable * channels,TpBaseConnection * self)1160 manager_new_channels_cb (TpChannelManager *manager,
1161 GHashTable *channels,
1162 TpBaseConnection *self)
1163 {
1164 GPtrArray *array;
1165 ManagerNewChannelContext context = { self, g_hash_table_new (NULL, NULL) };
1166 GHashTableIter iter;
1167 gpointer key, value;
1168
1169 g_assert (TP_IS_CHANNEL_MANAGER (manager));
1170 g_assert (TP_IS_BASE_CONNECTION (self));
1171
1172 /* satisfy the RequestChannel/CreateChannel/EnsureChannel calls; as
1173 * a side-effect, fill in context.suppress_handler with those channels
1174 * that will have to be signalled with suppress_handler = TRUE */
1175 g_hash_table_foreach (channels, manager_new_channel, &context);
1176
1177 /* Emit NewChannels */
1178 array = g_ptr_array_sized_new (g_hash_table_size (channels));
1179 g_hash_table_iter_init (&iter, channels);
1180
1181 while (g_hash_table_iter_next (&iter, &key, &value))
1182 {
1183 g_ptr_array_add (array, get_channel_details (G_OBJECT (key)));
1184 }
1185
1186 tp_svc_connection_interface_requests_emit_new_channels (self,
1187 array);
1188
1189 g_ptr_array_foreach (array, (GFunc) tp_value_array_free, NULL);
1190 g_ptr_array_unref (array);
1191
1192 /* Emit NewChannel */
1193 g_hash_table_iter_init (&iter, channels);
1194
1195 while (g_hash_table_iter_next (&iter, &key, &value))
1196 {
1197 gboolean suppress_handler = (
1198 g_hash_table_lookup (context.suppress_handler, key) != NULL);
1199 gchar *object_path, *channel_type;
1200 guint handle_type, handle;
1201
1202 exportable_channel_get_old_info (TP_EXPORTABLE_CHANNEL (key),
1203 &object_path, &channel_type, &handle_type, &handle);
1204
1205 tp_svc_connection_emit_new_channel (self, object_path, channel_type,
1206 handle_type, handle, suppress_handler);
1207
1208 g_free (object_path);
1209 g_free (channel_type);
1210 }
1211
1212 g_hash_table_unref (context.suppress_handler);
1213 }
1214
1215
1216 static void
manager_request_already_satisfied_cb(TpChannelManager * manager,gpointer request_token,TpExportableChannel * channel,TpBaseConnection * self)1217 manager_request_already_satisfied_cb (TpChannelManager *manager,
1218 gpointer request_token,
1219 TpExportableChannel *channel,
1220 TpBaseConnection *self)
1221 {
1222 gchar *object_path;
1223
1224 g_assert (TP_IS_CHANNEL_MANAGER (manager));
1225 g_assert (TP_IS_EXPORTABLE_CHANNEL (channel));
1226 g_assert (TP_IS_BASE_CONNECTION (self));
1227
1228 g_object_get (channel,
1229 "object-path", &object_path,
1230 NULL);
1231
1232 satisfy_request (self, request_token, G_OBJECT (channel), object_path);
1233 g_free (object_path);
1234 }
1235
1236
1237 static void
manager_request_failed_cb(TpChannelManager * manager,gpointer request_token,guint domain,gint code,gchar * message,TpBaseConnection * self)1238 manager_request_failed_cb (TpChannelManager *manager,
1239 gpointer request_token,
1240 guint domain,
1241 gint code,
1242 gchar *message,
1243 TpBaseConnection *self)
1244 {
1245 GError error = { domain, code, message };
1246
1247 g_assert (TP_IS_CHANNEL_MANAGER (manager));
1248 g_assert (domain > 0);
1249 g_assert (message != NULL);
1250 g_assert (TP_IS_BASE_CONNECTION (self));
1251
1252 fail_channel_request (self, request_token, &error);
1253 }
1254
1255
1256 static void
manager_channel_closed_cb(TpChannelManager * manager,const gchar * path,TpBaseConnection * self)1257 manager_channel_closed_cb (TpChannelManager *manager,
1258 const gchar *path,
1259 TpBaseConnection *self)
1260 {
1261 g_assert (TP_IS_CHANNEL_MANAGER (manager));
1262 g_assert (path != NULL);
1263 g_assert (TP_IS_BASE_CONNECTION (self));
1264
1265 tp_svc_connection_interface_requests_emit_channel_closed (self, path);
1266 }
1267
1268 /*
1269 * Set the @handle_type'th handle repository, which must be %NULL, to
1270 * @handle_repo. This method can only be called from code run during the
1271 * constructor(), after handle repository instantiation (in practice, this
1272 * means it can only be called from the @create_channel_managers callback).
1273 */
1274 void
_tp_base_connection_set_handle_repo(TpBaseConnection * self,TpHandleType handle_type,TpHandleRepoIface * handle_repo)1275 _tp_base_connection_set_handle_repo (TpBaseConnection *self,
1276 TpHandleType handle_type,
1277 TpHandleRepoIface *handle_repo)
1278 {
1279 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
1280 g_return_if_fail (!self->priv->been_constructed);
1281 g_return_if_fail (tp_handle_type_is_valid (handle_type, NULL));
1282 g_return_if_fail (self->priv->handles[TP_HANDLE_TYPE_CONTACT] != NULL);
1283 g_return_if_fail (self->priv->handles[handle_type] == NULL);
1284 g_return_if_fail (TP_IS_HANDLE_REPO_IFACE (handle_repo));
1285
1286 self->priv->handles[handle_type] = g_object_ref (handle_repo);
1287 }
1288
1289 static void
tp_base_connection_create_interfaces_array(TpBaseConnection * self)1290 tp_base_connection_create_interfaces_array (TpBaseConnection *self)
1291 {
1292 TpBaseConnectionPrivate *priv = self->priv;
1293 TpBaseConnectionClass *klass = TP_BASE_CONNECTION_GET_CLASS (self);
1294 GPtrArray *always;
1295 guint i;
1296
1297 g_assert (priv->interfaces == NULL);
1298
1299 always = klass->get_interfaces_always_present (self);
1300
1301 priv->interfaces = g_array_sized_new (TRUE, FALSE, sizeof (gchar *),
1302 always->len);
1303 for (i = 0; i < always->len; i++)
1304 {
1305 g_array_append_val (priv->interfaces,
1306 g_ptr_array_index (always, i));
1307 }
1308
1309 g_ptr_array_unref (always);
1310 }
1311
1312 static GObject *
tp_base_connection_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)1313 tp_base_connection_constructor (GType type, guint n_construct_properties,
1314 GObjectConstructParam *construct_params)
1315 {
1316 guint i;
1317 TpBaseConnection *self = TP_BASE_CONNECTION (
1318 G_OBJECT_CLASS (tp_base_connection_parent_class)->constructor (
1319 type, n_construct_properties, construct_params));
1320 TpBaseConnectionPrivate *priv = self->priv;
1321 TpBaseConnectionClass *cls = TP_BASE_CONNECTION_GET_CLASS (self);
1322
1323 g_assert (cls->create_handle_repos != NULL);
1324 g_assert (cls->create_channel_factories != NULL ||
1325 cls->create_channel_managers != NULL);
1326 g_assert (cls->shut_down != NULL);
1327 g_assert (cls->start_connecting != NULL);
1328
1329 /* if we fail to connect to D-Bus here, we'll return an error from
1330 * register */
1331 tp_base_connection_ensure_dbus (self, NULL);
1332
1333 (cls->create_handle_repos) (self, priv->handles);
1334
1335 /* a connection that doesn't support contacts is no use to anyone */
1336 g_assert (priv->handles[TP_HANDLE_TYPE_CONTACT] != NULL);
1337
1338 if (cls->create_channel_factories != NULL)
1339 priv->channel_factories = cls->create_channel_factories (self);
1340 else
1341 priv->channel_factories = g_ptr_array_sized_new (0);
1342
1343 if (cls->create_channel_managers != NULL)
1344 priv->channel_managers = cls->create_channel_managers (self);
1345 else
1346 priv->channel_managers = g_ptr_array_sized_new (0);
1347
1348 for (i = 0; i < priv->channel_factories->len; i++)
1349 {
1350 GObject *factory = g_ptr_array_index (priv->channel_factories, i);
1351 g_signal_connect (factory, "new-channel", G_CALLBACK
1352 (factory_new_channel_cb), self);
1353 g_signal_connect (factory, "channel-error", G_CALLBACK
1354 (factory_channel_error_cb), self);
1355 }
1356
1357 for (i = 0; i < priv->channel_managers->len; i++)
1358 {
1359 TpChannelManager *manager = TP_CHANNEL_MANAGER (
1360 g_ptr_array_index (priv->channel_managers, i));
1361
1362 g_signal_connect (manager, "new-channels",
1363 (GCallback) manager_new_channels_cb, self);
1364 g_signal_connect (manager, "request-already-satisfied",
1365 (GCallback) manager_request_already_satisfied_cb, self);
1366 g_signal_connect (manager, "request-failed",
1367 (GCallback) manager_request_failed_cb, self);
1368 g_signal_connect (manager, "channel-closed",
1369 (GCallback) manager_channel_closed_cb, self);
1370 }
1371
1372 tp_base_connection_create_interfaces_array (self);
1373
1374 priv->been_constructed = TRUE;
1375
1376 return (GObject *) self;
1377 }
1378
1379 /**
1380 * tp_base_connection_add_possible_client_interest:
1381 * @self: a connection
1382 * @token: a quark corresponding to a D-Bus interface, or a token
1383 * representing part of a D-Bus interface, for which this connection wishes
1384 * to be notified when clients register an interest
1385 *
1386 * Add @token to the set of tokens for which this connection will emit
1387 * #TpBaseConnection::clients-interested and
1388 * #TpBaseConnection::clients-uninterested.
1389 *
1390 * This method must be called from the #GObjectClass<!--
1391 * -->.constructed or #GObjectClass<!-- -->.constructor callback
1392 * (otherwise, it will run too late to be useful).
1393 */
1394 void
tp_base_connection_add_possible_client_interest(TpBaseConnection * self,GQuark token)1395 tp_base_connection_add_possible_client_interest (TpBaseConnection *self,
1396 GQuark token)
1397 {
1398 gpointer p = GUINT_TO_POINTER (token);
1399
1400 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
1401 g_return_if_fail (self->status == TP_INTERNAL_CONNECTION_STATUS_NEW);
1402
1403 if (g_hash_table_lookup (self->priv->client_interests, p) == NULL)
1404 g_hash_table_insert (self->priv->client_interests, p,
1405 g_hash_table_new (g_str_hash, g_str_equal));
1406 }
1407
1408 /* D-Bus properties for the Requests interface */
1409
1410 static void
factory_get_channel_details_foreach(TpChannelIface * chan,gpointer data)1411 factory_get_channel_details_foreach (TpChannelIface *chan,
1412 gpointer data)
1413 {
1414 GPtrArray *details = data;
1415
1416 g_ptr_array_add (details, get_channel_details (G_OBJECT (chan)));
1417 }
1418
1419
1420 static void
manager_get_channel_details_foreach(TpExportableChannel * chan,gpointer data)1421 manager_get_channel_details_foreach (TpExportableChannel *chan,
1422 gpointer data)
1423 {
1424 GPtrArray *details = data;
1425
1426 g_ptr_array_add (details, get_channel_details (G_OBJECT (chan)));
1427 }
1428
1429
1430 static GPtrArray *
conn_requests_get_channel_details(TpBaseConnection * self)1431 conn_requests_get_channel_details (TpBaseConnection *self)
1432 {
1433 TpBaseConnectionPrivate *priv = self->priv;
1434 /* guess that each ChannelManager and each ChannelFactory has two
1435 * channels, on average */
1436 GPtrArray *details = g_ptr_array_sized_new (priv->channel_managers->len * 2
1437 + priv->channel_factories->len * 2);
1438 guint i;
1439
1440 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1441 for (i = 0; i < priv->channel_factories->len; i++)
1442 {
1443 TpChannelFactoryIface *factory = TP_CHANNEL_FACTORY_IFACE (
1444 g_ptr_array_index (priv->channel_factories, i));
1445
1446 tp_channel_factory_iface_foreach (factory,
1447 factory_get_channel_details_foreach, details);
1448 }
1449 G_GNUC_END_IGNORE_DEPRECATIONS
1450
1451 for (i = 0; i < priv->channel_managers->len; i++)
1452 {
1453 TpChannelManager *manager = TP_CHANNEL_MANAGER (
1454 g_ptr_array_index (priv->channel_managers, i));
1455
1456 tp_channel_manager_foreach_channel (manager,
1457 manager_get_channel_details_foreach, details);
1458 }
1459
1460 return details;
1461 }
1462
1463
1464 static void
get_requestables_foreach(TpChannelManager * manager,GHashTable * fixed_properties,const gchar * const * allowed_properties,gpointer user_data)1465 get_requestables_foreach (TpChannelManager *manager,
1466 GHashTable *fixed_properties,
1467 const gchar * const *allowed_properties,
1468 gpointer user_data)
1469 {
1470 GPtrArray *details = user_data;
1471
1472 g_ptr_array_add (details, tp_value_array_build (2,
1473 TP_HASH_TYPE_CHANNEL_CLASS, fixed_properties,
1474 G_TYPE_STRV, allowed_properties,
1475 G_TYPE_INVALID));
1476 }
1477
1478
1479 static GPtrArray *
conn_requests_get_requestables(TpBaseConnection * self)1480 conn_requests_get_requestables (TpBaseConnection *self)
1481 {
1482 TpBaseConnectionPrivate *priv = self->priv;
1483 /* generously guess that each ChannelManager has about 2 ChannelClasses */
1484 GPtrArray *details = g_ptr_array_sized_new (priv->channel_managers->len * 2);
1485 guint i;
1486
1487 for (i = 0; i < priv->channel_managers->len; i++)
1488 {
1489 TpChannelManager *manager = TP_CHANNEL_MANAGER (
1490 g_ptr_array_index (priv->channel_managers, i));
1491
1492 tp_channel_manager_foreach_channel_class (manager,
1493 get_requestables_foreach, details);
1494 }
1495
1496 return details;
1497 }
1498
1499
1500 static void
conn_requests_get_dbus_property(GObject * object,GQuark interface,GQuark name,GValue * value,gpointer unused G_GNUC_UNUSED)1501 conn_requests_get_dbus_property (GObject *object,
1502 GQuark interface,
1503 GQuark name,
1504 GValue *value,
1505 gpointer unused G_GNUC_UNUSED)
1506 {
1507 TpBaseConnection *self = TP_BASE_CONNECTION (object);
1508
1509 g_return_if_fail (interface == TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS);
1510
1511 if (name == g_quark_from_static_string ("Channels"))
1512 {
1513 g_value_take_boxed (value, conn_requests_get_channel_details (self));
1514 }
1515 else if (name == g_quark_from_static_string ("RequestableChannelClasses"))
1516 {
1517 g_value_take_boxed (value, conn_requests_get_requestables (self));
1518 }
1519 else
1520 {
1521 g_return_if_reached ();
1522 }
1523 }
1524
1525 static GPtrArray *
tp_base_connection_get_interfaces_always_present(TpBaseConnection * self)1526 tp_base_connection_get_interfaces_always_present (TpBaseConnection *self)
1527 {
1528 GPtrArray *interfaces = g_ptr_array_new ();
1529 const gchar **ptr;
1530
1531 /* copy the klass->interfaces_always_present property for backwards
1532 * compatibility */
1533 for (ptr = TP_BASE_CONNECTION_GET_CLASS (self)->interfaces_always_present;
1534 ptr != NULL && *ptr != NULL;
1535 ptr++)
1536 {
1537 g_ptr_array_add (interfaces, (gchar *) *ptr);
1538 }
1539
1540 return interfaces;
1541 }
1542
1543 static void
tp_base_connection_class_init(TpBaseConnectionClass * klass)1544 tp_base_connection_class_init (TpBaseConnectionClass *klass)
1545 {
1546 static TpDBusPropertiesMixinPropImpl connection_properties[] = {
1547 { "SelfHandle", "self-handle", NULL },
1548 { "SelfID", "self-id", NULL },
1549 { "Status", "dbus-status", NULL },
1550 { "Interfaces", "interfaces", NULL },
1551 { "HasImmortalHandles", "has-immortal-handles", NULL },
1552 { NULL }
1553 };
1554 static TpDBusPropertiesMixinPropImpl requests_properties[] = {
1555 { "Channels", NULL, NULL },
1556 { "RequestableChannelClasses", NULL, NULL },
1557 { NULL }
1558 };
1559 GParamSpec *param_spec;
1560 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1561
1562 g_type_class_add_private (klass, sizeof (TpBaseConnectionPrivate));
1563 object_class->dispose = tp_base_connection_dispose;
1564 object_class->finalize = tp_base_connection_finalize;
1565 object_class->constructor = tp_base_connection_constructor;
1566 object_class->get_property = tp_base_connection_get_property;
1567 object_class->set_property = tp_base_connection_set_property;
1568
1569 klass->get_interfaces_always_present =
1570 tp_base_connection_get_interfaces_always_present;
1571
1572 /**
1573 * TpBaseConnection:protocol: (skip)
1574 *
1575 * Identifier used in the Telepathy protocol when this connection's protocol
1576 * name is required.
1577 */
1578 param_spec = g_param_spec_string ("protocol",
1579 "Telepathy identifier for protocol",
1580 "Identifier string used when the protocol name is required.",
1581 NULL,
1582 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1583 g_object_class_install_property (object_class, PROP_PROTOCOL, param_spec);
1584
1585 /**
1586 * TpBaseConnection:self-handle: (skip)
1587 *
1588 * The handle of type %TP_HANDLE_TYPE_CONTACT representing the local user.
1589 * Must be set nonzero by the subclass before moving to state CONNECTED.
1590 *
1591 * Since: 0.7.15
1592 */
1593 param_spec = g_param_spec_uint ("self-handle",
1594 "Connection.SelfHandle",
1595 "The handle of type %TP_HANDLE_TYPE_CONTACT representing the local user.",
1596 0, G_MAXUINT, 0,
1597 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1598 g_object_class_install_property (object_class, PROP_SELF_HANDLE, param_spec);
1599
1600 /**
1601 * TpBaseConnection:self-id: (skip)
1602 *
1603 * The identifier representing the local user. This is the result of
1604 * inspecting #TpBaseConnection:self-handle.
1605 *
1606 * Since: 0.21.2
1607 */
1608 param_spec = g_param_spec_string ("self-id",
1609 "Connection.SelfID",
1610 "The identifier representing the local user.",
1611 "",
1612 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1613 g_object_class_install_property (object_class, PROP_SELF_ID, param_spec);
1614
1615 /**
1616 * TpBaseConnection:interfaces: (skip)
1617 *
1618 * The set of D-Bus interfaces available on this Connection, other than
1619 * Connection itself.
1620 *
1621 * Since: 0.11.3
1622 */
1623 param_spec = g_param_spec_boxed ("interfaces",
1624 "Connection.Interfaces",
1625 "The set of D-Bus interfaces available on this Connection, other than "
1626 "Connection itself",
1627 G_TYPE_STRV,
1628 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1629 g_object_class_install_property (object_class, PROP_INTERFACES, param_spec);
1630
1631 /**
1632 * TpBaseConnection:dbus-status: (skip)
1633 *
1634 * The Connection.Status as visible on D-Bus, which is the same as
1635 * #TpBaseConnection<!-- -->.status except that
1636 * %TP_INTERNAL_CONNECTION_STATUS_NEW is replaced by
1637 * %TP_CONNECTION_STATUS_DISCONNECTED.
1638 *
1639 * The #GObject::notify signal is not currently emitted for this property.
1640 *
1641 * Since: 0.11.3
1642 */
1643 param_spec = g_param_spec_uint ("dbus-status",
1644 "Connection.Status",
1645 "The connection status as visible on D-Bus",
1646 TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_DISCONNECTED,
1647 TP_CONNECTION_STATUS_DISCONNECTED,
1648 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1649 g_object_class_install_property (object_class, PROP_DBUS_STATUS, param_spec);
1650
1651 /**
1652 * TpBaseConnection:dbus-daemon: (skip)
1653 *
1654 * #TpDBusDaemon object encapsulating this object's connection to D-Bus.
1655 * Read-only except during construction.
1656 *
1657 * If this property is %NULL or omitted during construction, the object will
1658 * automatically attempt to connect to the starter or session bus with
1659 * tp_dbus_daemon_dup() just after it is constructed; if this fails, this
1660 * property will remain %NULL, and tp_base_connection_register() will fail.
1661 *
1662 * Since: 0.11.3
1663 */
1664 g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
1665 g_param_spec_object ("dbus-daemon", "D-Bus daemon",
1666 "The D-Bus daemon used by this object", TP_TYPE_DBUS_DAEMON,
1667 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1668
1669 /**
1670 * TpBaseConnection:has-immortal-handles:
1671 *
1672 * This property is not useful to use directly. Its value is %TRUE, to
1673 * indicate that this version of telepathy-glib never unreferences handles
1674 * until the connection becomes disconnected.
1675 *
1676 * Since: 0.13.8
1677 */
1678 param_spec = g_param_spec_boolean ("has-immortal-handles",
1679 "Connection.HasImmortalHandles",
1680 "Always TRUE", TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1681 g_object_class_install_property (object_class, PROP_HAS_IMMORTAL_HANDLES,
1682 param_spec);
1683
1684 /**
1685 * TpBaseConnection:account-path-suffix:
1686 *
1687 * The suffix of the account object path such as
1688 * "gabble/jabber/chris_40example_2ecom0" for the account whose object path is
1689 * %TP_ACCOUNT_OBJECT_PATH_BASE + "gabble/jabber/chris_40example_2ecom0".
1690 * The same as returned by tp_account_get_path_suffix().
1691 *
1692 * It is given by the AccountManager in the connection parameters. Or %NULL if
1693 * the ConnectionManager or the AccountManager are too old.
1694 *
1695 * Since: 0.23.2
1696 */
1697 param_spec = g_param_spec_string ("account-path-suffix",
1698 "Account path suffix",
1699 "The suffix of the account path",
1700 NULL,
1701 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1702 g_object_class_install_property (object_class, PROP_ACCOUNT_PATH_SUFFIX,
1703 param_spec);
1704
1705 /* signal definitions */
1706
1707 /**
1708 * TpBaseConnection::shutdown-finished: (skip)
1709 * @connection: the #TpBaseConnection
1710 *
1711 * Emitted by tp_base_connection_finish_shutdown() when the underlying
1712 * network connection has been closed; #TpBaseConnectionManager listens
1713 * for this signal and removes connections from its table of active
1714 * connections when it is received.
1715 */
1716 signals[SHUTDOWN_FINISHED] =
1717 g_signal_new ("shutdown-finished",
1718 G_OBJECT_CLASS_TYPE (klass),
1719 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1720 0,
1721 NULL, NULL, NULL,
1722 G_TYPE_NONE, 0);
1723
1724 /**
1725 * TpBaseConnection::clients-interested:
1726 * @connection: the #TpBaseConnection
1727 * @token: the interface or part of an interface in which clients are newly
1728 * interested
1729 *
1730 * Emitted when a client becomes interested in any token that was added with
1731 * tp_base_connection_add_possible_client_interest().
1732 *
1733 * The "signal detail" is a GQuark representing @token. Modules implementing
1734 * an interface (Location, say) should typically connect to a detailed signal
1735 * like
1736 * "clients-interested::org.freedesktop.Telepathy.Connection.Interface.Location"
1737 * rather than receiving all emissions of this signal.
1738 */
1739 signals[CLIENTS_INTERESTED] =
1740 g_signal_new ("clients-interested",
1741 G_OBJECT_CLASS_TYPE (klass),
1742 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1743 0,
1744 NULL, NULL, NULL,
1745 G_TYPE_NONE, 1, G_TYPE_STRING);
1746
1747 /**
1748 * TpBaseConnection::clients-uninterested:
1749 * @connection: the #TpBaseConnection
1750 * @token: the interface or part of an interface in which clients are no
1751 * longer interested
1752 *
1753 * Emitted when no more clients are interested in an interface added with
1754 * tp_base_connection_add_possible_client_interest(), for which
1755 * #TpBaseConnection::clients-interested was previously emitted.
1756 *
1757 * As with #TpBaseConnection::clients-interested, the "signal detail" is a
1758 * GQuark representing @token. Modules implementing an interface (Location,
1759 * say) should typically connect to a detailed signal like
1760 * "clients-uninterested::org.freedesktop.Telepathy.Connection.Interface.Location"
1761 * rather than receiving all emissions of this signal.
1762 */
1763 signals[CLIENTS_UNINTERESTED] =
1764 g_signal_new ("clients-uninterested",
1765 G_OBJECT_CLASS_TYPE (klass),
1766 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1767 0,
1768 NULL, NULL, NULL,
1769 G_TYPE_NONE, 1, G_TYPE_STRING);
1770
1771 tp_dbus_properties_mixin_class_init (object_class, 0);
1772 tp_dbus_properties_mixin_implement_interface (object_class,
1773 TP_IFACE_QUARK_CONNECTION,
1774 tp_dbus_properties_mixin_getter_gobject_properties, NULL,
1775 connection_properties);
1776 tp_dbus_properties_mixin_implement_interface (object_class,
1777 TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS,
1778 conn_requests_get_dbus_property, NULL,
1779 requests_properties);
1780 }
1781
1782 static void
tp_base_connection_init(TpBaseConnection * self)1783 tp_base_connection_init (TpBaseConnection *self)
1784 {
1785 TpBaseConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
1786 TP_TYPE_BASE_CONNECTION, TpBaseConnectionPrivate);
1787 guint i;
1788
1789 self->priv = priv;
1790 self->status = TP_INTERNAL_CONNECTION_STATUS_NEW;
1791
1792 for (i = 0; i < TP_NUM_HANDLE_TYPES; i++)
1793 {
1794 priv->handles[i] = NULL;
1795 }
1796
1797 priv->channel_requests = g_ptr_array_new ();
1798 priv->client_interests = g_hash_table_new_full (NULL, NULL, NULL,
1799 (GDestroyNotify) g_hash_table_unref);
1800 priv->interested_clients = g_hash_table_new_full (g_str_hash, g_str_equal,
1801 g_free, NULL);
1802 }
1803
1804 static gchar *
squash_name(const gchar * name,guint length)1805 squash_name (const gchar *name, guint length)
1806 {
1807 GChecksum *checksum;
1808 gchar *squashed;
1809
1810 g_assert (length >= 10);
1811 checksum = g_checksum_new (G_CHECKSUM_MD5);
1812 g_checksum_update (checksum, (guchar *) name, -1);
1813 squashed = g_strdup_printf (
1814 "%.*s_%.8s", length - 9, name, g_checksum_get_string (checksum));
1815 g_checksum_free (checksum);
1816 return squashed;
1817 }
1818
1819 /**
1820 * tp_base_connection_register:
1821 * @self: A connection
1822 * @cm_name: The name of the connection manager in the Telepathy protocol
1823 * @bus_name: (out): Used to return the bus name corresponding to the connection
1824 * if %TRUE is returned. To be freed by the caller.
1825 * @object_path: (out): Used to return the object path of the connection if
1826 * %TRUE is returned. To be freed by the caller.
1827 * @error: Used to return an error if %FALSE is returned; may be %NULL
1828 *
1829 * Make the connection object appear on the bus, returning the bus
1830 * name and object path used. If %TRUE is returned, the connection owns the
1831 * bus name, and will release it when destroyed.
1832 *
1833 * Since 0.11.11, @bus_name and @object_path may be %NULL if the
1834 * strings are not needed.
1835 *
1836 * Returns: %TRUE on success, %FALSE on error.
1837 */
1838 gboolean
tp_base_connection_register(TpBaseConnection * self,const gchar * cm_name,gchar ** bus_name,gchar ** object_path,GError ** error)1839 tp_base_connection_register (TpBaseConnection *self,
1840 const gchar *cm_name,
1841 gchar **bus_name,
1842 gchar **object_path,
1843 GError **error)
1844 {
1845 TpBaseConnectionClass *cls = TP_BASE_CONNECTION_GET_CLASS (self);
1846 TpBaseConnectionPrivate *priv = self->priv;
1847 gchar *tmp;
1848 gchar *safe_proto;
1849 gchar *unique_name;
1850 guint prefix_length;
1851 const guint dbus_max_name_length = 255;
1852
1853 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), FALSE);
1854 g_return_val_if_fail (cm_name != NULL, FALSE);
1855 g_return_val_if_fail (!self->priv->been_registered, FALSE);
1856
1857 if (tp_connection_manager_check_valid_protocol_name (priv->protocol, NULL))
1858 {
1859 safe_proto = g_strdelimit (g_strdup (priv->protocol), "-", '_');
1860 }
1861 else
1862 {
1863 WARNING ("Protocol name %s is not valid - should match "
1864 "[A-Za-z][A-Za-z0-9-]+", priv->protocol);
1865 safe_proto = tp_escape_as_identifier (priv->protocol);
1866 }
1867
1868 /* Plus two for the dots. */
1869 prefix_length = strlen (TP_CONN_BUS_NAME_BASE) +
1870 strlen (cm_name) + strlen (safe_proto) + 2;
1871
1872 if (cls->get_unique_connection_name)
1873 {
1874
1875 tmp = cls->get_unique_connection_name (self);
1876 g_assert (tmp != NULL);
1877 unique_name = tp_escape_as_identifier (tmp);
1878 g_free (tmp);
1879
1880 if (prefix_length + strlen (unique_name) > dbus_max_name_length)
1881 {
1882 /* Is prefix is too long to make reasonable bus name? Ten = one
1883 * character of the original unique name plus underscore plus
1884 * 8-character hash.
1885 */
1886 if (prefix_length >= dbus_max_name_length - 10)
1887 {
1888 WARNING (
1889 "Couldn't fit CM name + protocol name + unique name into "
1890 "255 characters.");
1891 g_free (unique_name);
1892 return FALSE;
1893 }
1894
1895 tmp = unique_name;
1896 unique_name = squash_name (
1897 tmp, dbus_max_name_length - prefix_length);
1898 g_free (tmp);
1899 }
1900 }
1901 else
1902 {
1903 unique_name = g_strdup_printf ("_%p", self);
1904 }
1905
1906 if (!tp_base_connection_ensure_dbus (self, error))
1907 {
1908 g_free (safe_proto);
1909 g_free (unique_name);
1910 return FALSE;
1911 }
1912
1913 self->bus_name = g_strdup_printf (TP_CONN_BUS_NAME_BASE "%s.%s.%s",
1914 cm_name, safe_proto, unique_name);
1915 g_assert (strlen (self->bus_name) <= 255);
1916 self->object_path = g_strdup_printf (TP_CONN_OBJECT_PATH_BASE "%s/%s/%s",
1917 cm_name, safe_proto, unique_name);
1918
1919 g_free (safe_proto);
1920 g_free (unique_name);
1921
1922 if (!tp_dbus_daemon_request_name (priv->bus_proxy, self->bus_name, FALSE,
1923 error))
1924 {
1925 g_free (self->bus_name);
1926 self->bus_name = NULL;
1927 return FALSE;
1928 }
1929
1930 DEBUG ("%p: bus name %s; object path %s", self, self->bus_name,
1931 self->object_path);
1932 tp_dbus_daemon_register_object (priv->bus_proxy, self->object_path, self);
1933 self->priv->been_registered = TRUE;
1934
1935 if (bus_name != NULL)
1936 *bus_name = g_strdup (self->bus_name);
1937
1938 if (object_path != NULL)
1939 *object_path = g_strdup (self->object_path);
1940
1941 return TRUE;
1942 }
1943
1944 static void
tp_base_connection_close_all_channels(TpBaseConnection * self)1945 tp_base_connection_close_all_channels (TpBaseConnection *self)
1946 {
1947 TpBaseConnectionPrivate *priv = self->priv;
1948
1949 /* We deliberately don't iterate over channel managers here -
1950 * they don't need this, and are expected to listen to
1951 * TpSvcConnection::status-changed on the connection for themselves.
1952 */
1953
1954 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1955 /* trigger close_all on all channel factories */
1956 g_ptr_array_foreach (priv->channel_factories, (GFunc)
1957 tp_channel_factory_iface_close_all, NULL);
1958 G_GNUC_END_IGNORE_DEPRECATIONS
1959 }
1960
1961 /* D-Bus methods on Connection interface ----------------------------*/
1962
1963 static inline TpConnectionStatusReason
conn_status_reason_from_g_error(GError * error)1964 conn_status_reason_from_g_error (GError *error)
1965 {
1966 if (error->domain == TP_ERROR)
1967 {
1968 switch (error->code)
1969 {
1970 #define OBVIOUS_MAPPING(x) \
1971 case TP_ERROR_ ## x: \
1972 return TP_CONNECTION_STATUS_REASON_ ## x
1973
1974 OBVIOUS_MAPPING (NETWORK_ERROR);
1975 OBVIOUS_MAPPING (ENCRYPTION_ERROR);
1976 OBVIOUS_MAPPING (AUTHENTICATION_FAILED);
1977 OBVIOUS_MAPPING (CERT_NOT_PROVIDED);
1978 OBVIOUS_MAPPING (CERT_UNTRUSTED);
1979 OBVIOUS_MAPPING (CERT_EXPIRED);
1980 OBVIOUS_MAPPING (CERT_NOT_ACTIVATED);
1981 OBVIOUS_MAPPING (CERT_FINGERPRINT_MISMATCH);
1982 OBVIOUS_MAPPING (CERT_HOSTNAME_MISMATCH);
1983 OBVIOUS_MAPPING (CERT_SELF_SIGNED);
1984 #undef OBVIOUS_MAPPING
1985
1986 case TP_ERROR_PERMISSION_DENIED:
1987 case TP_ERROR_DOES_NOT_EXIST:
1988 return TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED;
1989
1990 case TP_ERROR_CERT_INVALID:
1991 return TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR;
1992
1993 case TP_ERROR_CANCELLED:
1994 return TP_CONNECTION_STATUS_REASON_REQUESTED;
1995
1996 case TP_ERROR_ENCRYPTION_NOT_AVAILABLE:
1997 return TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR;
1998
1999 case TP_ERROR_REGISTRATION_EXISTS:
2000 case TP_ERROR_ALREADY_CONNECTED:
2001 case TP_ERROR_CONNECTION_REPLACED:
2002 return TP_CONNECTION_STATUS_REASON_NAME_IN_USE;
2003
2004 case TP_ERROR_CONNECTION_REFUSED:
2005 case TP_ERROR_CONNECTION_FAILED:
2006 case TP_ERROR_CONNECTION_LOST:
2007 case TP_ERROR_SERVICE_BUSY:
2008 return TP_CONNECTION_STATUS_REASON_NETWORK_ERROR;
2009
2010 /* current status: all TP_ERRORs up to and including
2011 * TP_ERROR_RESOURCE_UNAVAILABLE have been looked at */
2012 }
2013 }
2014
2015 return TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED;
2016 }
2017
2018 static void
tp_base_connection_connect(TpSvcConnection * iface,DBusGMethodInvocation * context)2019 tp_base_connection_connect (TpSvcConnection *iface,
2020 DBusGMethodInvocation *context)
2021 {
2022 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2023 TpBaseConnectionClass *cls = TP_BASE_CONNECTION_GET_CLASS (self);
2024 GError *error = NULL;
2025
2026 g_assert (TP_IS_BASE_CONNECTION (self));
2027
2028 if (self->status == TP_INTERNAL_CONNECTION_STATUS_NEW)
2029 {
2030 if (cls->start_connecting (self, &error))
2031 {
2032 if (self->status == TP_INTERNAL_CONNECTION_STATUS_NEW)
2033 {
2034 tp_base_connection_change_status (self,
2035 TP_CONNECTION_STATUS_CONNECTING,
2036 TP_CONNECTION_STATUS_REASON_REQUESTED);
2037 }
2038 }
2039 else
2040 {
2041 if (self->status != TP_CONNECTION_STATUS_DISCONNECTED)
2042 {
2043 tp_base_connection_change_status (self,
2044 TP_CONNECTION_STATUS_DISCONNECTED,
2045 conn_status_reason_from_g_error (error));
2046 }
2047 dbus_g_method_return_error (context, error);
2048 g_error_free (error);
2049 return;
2050 }
2051 }
2052 tp_svc_connection_return_from_connect (context);
2053 }
2054
2055 static void
tp_base_connection_disconnect(TpSvcConnection * iface,DBusGMethodInvocation * context)2056 tp_base_connection_disconnect (TpSvcConnection *iface,
2057 DBusGMethodInvocation *context)
2058 {
2059 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2060
2061 g_assert (TP_IS_BASE_CONNECTION (self));
2062
2063 if (self->priv->disconnect_requests != NULL)
2064 {
2065 g_assert (self->status == TP_CONNECTION_STATUS_DISCONNECTED);
2066 g_ptr_array_add (self->priv->disconnect_requests, context);
2067 return;
2068 }
2069
2070 if (self->status == TP_CONNECTION_STATUS_DISCONNECTED)
2071 {
2072 /* status DISCONNECTED and disconnect_requests NULL => already dead */
2073 tp_svc_connection_return_from_disconnect (context);
2074 return;
2075 }
2076
2077 self->priv->disconnect_requests = g_ptr_array_sized_new (1);
2078 g_ptr_array_add (self->priv->disconnect_requests, context);
2079
2080 tp_base_connection_change_status (self,
2081 TP_CONNECTION_STATUS_DISCONNECTED,
2082 TP_CONNECTION_STATUS_REASON_REQUESTED);
2083 }
2084
2085 static const gchar * const *
tp_base_connection_get_interfaces(TpBaseConnection * self)2086 tp_base_connection_get_interfaces (TpBaseConnection *self)
2087 {
2088 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), NULL);
2089
2090 return (const gchar * const *)(self->priv->interfaces->data);
2091 }
2092
2093 static void
tp_base_connection_dbus_get_interfaces(TpSvcConnection * iface,DBusGMethodInvocation * context)2094 tp_base_connection_dbus_get_interfaces (TpSvcConnection *iface,
2095 DBusGMethodInvocation *context)
2096 {
2097 tp_svc_connection_return_from_get_interfaces (context,
2098 (const gchar **) tp_base_connection_get_interfaces (
2099 (TpBaseConnection *) iface));
2100 }
2101
2102 static void
tp_base_connection_get_protocol(TpSvcConnection * iface,DBusGMethodInvocation * context)2103 tp_base_connection_get_protocol (TpSvcConnection *iface,
2104 DBusGMethodInvocation *context)
2105 {
2106 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2107 TpBaseConnectionPrivate *priv;
2108
2109 g_assert (TP_IS_BASE_CONNECTION (self));
2110
2111 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2112
2113 priv = self->priv;
2114
2115 tp_svc_connection_return_from_get_protocol (context, priv->protocol);
2116 }
2117
2118 static void
tp_base_connection_dbus_get_self_handle(TpSvcConnection * iface,DBusGMethodInvocation * context)2119 tp_base_connection_dbus_get_self_handle (TpSvcConnection *iface,
2120 DBusGMethodInvocation *context)
2121 {
2122 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2123
2124 g_assert (TP_IS_BASE_CONNECTION (self));
2125
2126 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2127
2128 tp_svc_connection_return_from_get_self_handle (
2129 context, self->self_handle);
2130 }
2131
2132 /**
2133 * tp_base_connection_get_status:
2134 * @self: the connection
2135 *
2136 * Return the status of this connection, as set by
2137 * tp_base_connection_change_status() or similar functions like
2138 * tp_base_connection_disconnect_with_dbus_error().
2139 *
2140 * Like the corresponding D-Bus property, this method returns
2141 * %TP_CONNECTION_STATUS_DISCONNECTED in two situations:
2142 * either the connection is newly-created (and has never emitted
2143 * #TpSvcConnection::status-changed), or D-Bus clients have already been
2144 * told that it has been destroyed (by the Disconnect D-Bus method,
2145 * a failed attempt to connect, or loss of an established connection).
2146 * Use tp_base_connection_is_destroyed() to distinguish between the two.
2147 *
2148 * Returns: the value of #TpBaseConnection:dbus-status
2149 * Since: 0.19.1
2150 */
2151 TpConnectionStatus
tp_base_connection_get_status(TpBaseConnection * self)2152 tp_base_connection_get_status (TpBaseConnection *self)
2153 {
2154 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self),
2155 TP_CONNECTION_STATUS_DISCONNECTED);
2156
2157 if (self->status == TP_INTERNAL_CONNECTION_STATUS_NEW)
2158 {
2159 return TP_CONNECTION_STATUS_DISCONNECTED;
2160 }
2161 else
2162 {
2163 return self->status;
2164 }
2165 }
2166
2167 /**
2168 * tp_base_connection_is_destroyed:
2169 * @self: the connection
2170 *
2171 * Return whether this connection has already emitted the D-Bus signal
2172 * indicating that it has been destroyed.
2173 *
2174 * In particular, this can be used to distinguish between the two reasons
2175 * why tp_base_connection_get_status() would return
2176 * %TP_CONNECTION_STATUS_DISCONNECTED: it will return %FALSE if the
2177 * connection is newly-created, and %TRUE if the Disconnect D-Bus method
2178 * has been called, an attempt to connect has failed, or an established
2179 * connection has encountered an error.
2180 *
2181 * Returns: %TRUE if this connection is disappearing from D-Bus
2182 * Since: 0.19.1
2183 */
2184 gboolean
tp_base_connection_is_destroyed(TpBaseConnection * self)2185 tp_base_connection_is_destroyed (TpBaseConnection *self)
2186 {
2187 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), TRUE);
2188
2189 /* in particular return FALSE if the status is NEW */
2190 return (self->status == TP_CONNECTION_STATUS_DISCONNECTED);
2191 }
2192
2193 /**
2194 * tp_base_connection_check_connected:
2195 * @self: the connection
2196 * @error: used to raise %TP_ERROR_DISCONNECTED if %FALSE is returned
2197 *
2198 * Return whether this connection is fully active and connected.
2199 * If it is not, raise %TP_ERROR_DISCONNECTED.
2200 *
2201 * This is equivalent to checking whether tp_base_connection_get_status()
2202 * returns %TP_CONNECTION_STATUS_CONNECTED; it is provided because methods
2203 * on the connection often need to make this check, and return a
2204 * #GError if it fails.
2205 *
2206 * Returns: %TRUE if this connection is connected
2207 * Since: 0.19.1
2208 */
2209 gboolean
tp_base_connection_check_connected(TpBaseConnection * self,GError ** error)2210 tp_base_connection_check_connected (TpBaseConnection *self,
2211 GError **error)
2212 {
2213 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), FALSE);
2214
2215 if (self->status == TP_CONNECTION_STATUS_CONNECTED)
2216 return TRUE;
2217
2218 g_set_error_literal (error, TP_ERROR, TP_ERROR_DISCONNECTED,
2219 "Connection is disconnected");
2220 return FALSE;
2221 }
2222
2223 static void
tp_base_connection_dbus_get_status(TpSvcConnection * iface,DBusGMethodInvocation * context)2224 tp_base_connection_dbus_get_status (TpSvcConnection *iface,
2225 DBusGMethodInvocation *context)
2226 {
2227 tp_svc_connection_return_from_get_status (
2228 context, tp_base_connection_get_status (
2229 (TpBaseConnection *) iface));
2230 }
2231
2232 static void
tp_base_connection_hold_handles(TpSvcConnection * iface,guint handle_type,const GArray * handles,DBusGMethodInvocation * context)2233 tp_base_connection_hold_handles (TpSvcConnection *iface,
2234 guint handle_type,
2235 const GArray *handles,
2236 DBusGMethodInvocation *context)
2237 {
2238 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2239 TpBaseConnectionPrivate *priv;
2240 GError *error = NULL;
2241
2242 g_assert (TP_IS_BASE_CONNECTION (self));
2243
2244 priv = self->priv;
2245
2246 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2247
2248 if (!tp_handles_supported_and_valid (priv->handles,
2249 handle_type, handles, FALSE, &error))
2250 {
2251 dbus_g_method_return_error (context, error);
2252 g_error_free (error);
2253 return;
2254 }
2255
2256 tp_svc_connection_return_from_hold_handles (context);
2257 }
2258
2259 static void
tp_base_connection_inspect_handles(TpSvcConnection * iface,guint handle_type,const GArray * handles,DBusGMethodInvocation * context)2260 tp_base_connection_inspect_handles (TpSvcConnection *iface,
2261 guint handle_type,
2262 const GArray *handles,
2263 DBusGMethodInvocation *context)
2264 {
2265 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2266 TpBaseConnectionPrivate *priv = self->priv;
2267 GError *error = NULL;
2268 const gchar **ret;
2269 guint i;
2270
2271 g_assert (TP_IS_BASE_CONNECTION (self));
2272
2273 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2274
2275 if (!tp_handles_supported_and_valid (priv->handles,
2276 handle_type, handles, FALSE, &error))
2277 {
2278 dbus_g_method_return_error (context, error);
2279
2280 g_error_free (error);
2281
2282 return;
2283 }
2284
2285 ret = g_new (const gchar *, handles->len + 1);
2286
2287 for (i = 0; i < handles->len; i++)
2288 {
2289 TpHandle handle;
2290 const gchar *tmp;
2291
2292 handle = g_array_index (handles, TpHandle, i);
2293 tmp = tp_handle_inspect (priv->handles[handle_type], handle);
2294 g_assert (tmp != NULL);
2295
2296 ret[i] = tmp;
2297 }
2298
2299 ret[i] = NULL;
2300
2301 tp_svc_connection_return_from_inspect_handles (context, ret);
2302
2303 g_free (ret);
2304 }
2305
2306 /*
2307 * list_channel_factory_foreach_one:
2308 * @chan: a channel
2309 * @data: a GPtrArray in which channel information should be stored
2310 *
2311 * Called by the exported ListChannels function for each channel in a channel
2312 * factory, this should add to the GPtrArray (in the data pointer) a
2313 * GValueArray containing the following:
2314 * a D-Bus object path for the channel object on this service
2315 * a D-Bus interface name representing the channel type
2316 * an integer representing the handle type this channel communicates with,
2317 * or zero
2318 * an integer handle representing the contact, room or list this channel
2319 * communicates with, or zero
2320 */
2321 static void
list_channel_factory_foreach_one(TpChannelIface * chan,gpointer data)2322 list_channel_factory_foreach_one (TpChannelIface *chan,
2323 gpointer data)
2324 {
2325 GObject *channel = G_OBJECT (chan);
2326 GPtrArray *values = (GPtrArray *) data;
2327 gchar *path, *type;
2328 guint handle_type, handle;
2329 GValue *entry = tp_dbus_specialized_value_slice_new
2330 (TP_STRUCT_TYPE_CHANNEL_INFO);
2331
2332 g_object_get (channel,
2333 "object-path", &path,
2334 "channel-type", &type,
2335 "handle-type", &handle_type,
2336 "handle", &handle,
2337 NULL);
2338
2339 dbus_g_type_struct_set (entry,
2340 0, path,
2341 1, type,
2342 2, handle_type,
2343 3, handle,
2344 G_MAXUINT);
2345
2346 g_ptr_array_add (values, entry);
2347
2348 g_free (path);
2349 g_free (type);
2350 }
2351
2352
2353 /*
2354 * list_channel_manager_foreach_one:
2355 * @chan: a channel
2356 * @data: a GPtrArray in which channel information should be stored
2357 *
2358 * Called by the exported ListChannels function for each channel in a channel
2359 * manager, this should add to the GPtrArray (in the data pointer) a
2360 * GValueArray containing the following:
2361 * a D-Bus object path for the channel object on this service
2362 * a D-Bus interface name representing the channel type
2363 * an integer representing the handle type this channel communicates with,
2364 * or zero
2365 * an integer handle representing the contact, room or list this channel
2366 * communicates with, or zero
2367 */
2368 static void
list_channel_manager_foreach_one(TpExportableChannel * channel,gpointer data)2369 list_channel_manager_foreach_one (TpExportableChannel *channel,
2370 gpointer data)
2371 {
2372 GPtrArray *values = (GPtrArray *) data;
2373 gchar *path, *type;
2374 guint handle_type, handle;
2375 GValue *entry = tp_dbus_specialized_value_slice_new
2376 (TP_STRUCT_TYPE_CHANNEL_INFO);
2377
2378 g_assert (TP_IS_EXPORTABLE_CHANNEL (channel));
2379
2380 exportable_channel_get_old_info (channel, &path, &type, &handle_type,
2381 &handle);
2382
2383 dbus_g_type_struct_set (entry,
2384 0, path,
2385 1, type,
2386 2, handle_type,
2387 3, handle,
2388 G_MAXUINT);
2389
2390 g_ptr_array_add (values, entry);
2391
2392 g_free (path);
2393 g_free (type);
2394 }
2395
2396
2397 static void
tp_base_connection_list_channels(TpSvcConnection * iface,DBusGMethodInvocation * context)2398 tp_base_connection_list_channels (TpSvcConnection *iface,
2399 DBusGMethodInvocation *context)
2400 {
2401 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2402 TpBaseConnectionPrivate *priv;
2403 GPtrArray *channels, *values;
2404 guint i;
2405
2406 g_assert (TP_IS_BASE_CONNECTION (self));
2407
2408 priv = self->priv;
2409
2410 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2411
2412 /* I think on average, each factory will have 2 channels :D */
2413 values = g_ptr_array_sized_new (priv->channel_factories->len * 2
2414 + priv->channel_managers->len * 2);
2415
2416 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2417 for (i = 0; i < priv->channel_factories->len; i++)
2418 {
2419 TpChannelFactoryIface *factory = g_ptr_array_index
2420 (priv->channel_factories, i);
2421
2422 tp_channel_factory_iface_foreach (factory,
2423 list_channel_factory_foreach_one, values);
2424 }
2425 G_GNUC_END_IGNORE_DEPRECATIONS
2426
2427 for (i = 0; i < priv->channel_managers->len; i++)
2428 {
2429 TpChannelManager *manager = g_ptr_array_index
2430 (priv->channel_managers, i);
2431
2432 tp_channel_manager_foreach_channel (manager,
2433 list_channel_manager_foreach_one, values);
2434 }
2435
2436 channels = g_ptr_array_sized_new (values->len);
2437
2438 for (i = 0; i < values->len; i++)
2439 {
2440 g_ptr_array_add (channels, g_value_get_boxed (g_ptr_array_index
2441 (values, i)));
2442 }
2443
2444 tp_svc_connection_return_from_list_channels (context, channels);
2445
2446 g_ptr_array_unref (channels);
2447 for (i = 0; i < values->len; i++)
2448 {
2449 tp_g_value_slice_free (g_ptr_array_index (values, i));
2450 }
2451 g_ptr_array_unref (values);
2452 }
2453
2454 static void
tp_base_connection_request_channel(TpSvcConnection * iface,const gchar * type,guint handle_type,guint handle,gboolean suppress_handler,DBusGMethodInvocation * context)2455 tp_base_connection_request_channel (TpSvcConnection *iface,
2456 const gchar *type,
2457 guint handle_type,
2458 guint handle,
2459 gboolean suppress_handler,
2460 DBusGMethodInvocation *context)
2461 {
2462 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2463 TpBaseConnectionPrivate *priv;
2464 TpChannelFactoryRequestStatus status =
2465 TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED;
2466 GError *error = NULL;
2467 guint i;
2468 ChannelRequest *request;
2469 GHashTable *request_properties;
2470 gboolean claimed_by_channel_manager = FALSE;
2471 TpHandleRepoIface *handle_repo = NULL;
2472
2473 g_assert (TP_IS_BASE_CONNECTION (self));
2474
2475 priv = self->priv;
2476
2477 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2478
2479 if (handle_type == TP_HANDLE_TYPE_NONE)
2480 {
2481 if (handle != 0)
2482 {
2483 GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2484 "When handle type is NONE, handle must be 0" };
2485
2486 dbus_g_method_return_error (context, &e);
2487 return;
2488 }
2489 }
2490 else
2491 {
2492 handle_repo = tp_base_connection_get_handles (self,
2493 handle_type);
2494
2495 if (handle_repo == NULL)
2496 {
2497 GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE,
2498 "Handle type not supported by this connection manager" };
2499
2500 dbus_g_method_return_error (context, &e);
2501 return;
2502 }
2503
2504 if (!tp_handle_is_valid (handle_repo, handle, &error))
2505 {
2506 dbus_g_method_return_error (context, error);
2507 g_error_free (error);
2508 return;
2509 }
2510 }
2511
2512 request = channel_request_new (context, METHOD_REQUEST_CHANNEL,
2513 type, handle_type, handle, suppress_handler);
2514 g_ptr_array_add (priv->channel_requests, request);
2515
2516 /* First try the channel managers */
2517
2518 request_properties = tp_asv_new (
2519 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, type,
2520 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, handle_type,
2521 NULL);
2522
2523 if (handle != 0)
2524 {
2525 tp_asv_set_uint32 (request_properties,
2526 TP_PROP_CHANNEL_TARGET_HANDLE, handle);
2527 g_assert (handle_repo != NULL);
2528 tp_asv_set_string (request_properties,
2529 TP_PROP_CHANNEL_TARGET_ID,
2530 tp_handle_inspect (handle_repo, handle));
2531 }
2532
2533 for (i = 0; i < priv->channel_managers->len; i++)
2534 {
2535 TpChannelManager *manager = TP_CHANNEL_MANAGER (
2536 g_ptr_array_index (priv->channel_managers, i));
2537
2538 if (tp_channel_manager_request_channel (manager, request,
2539 request_properties))
2540 {
2541 claimed_by_channel_manager = TRUE;
2542 break;
2543 }
2544 }
2545
2546 g_hash_table_unref (request_properties);
2547
2548 if (claimed_by_channel_manager)
2549 return;
2550
2551 /* OK, none of them wanted it. Now try the channel factories */
2552
2553 for (i = 0; i < priv->channel_factories->len; i++)
2554 {
2555 TpChannelFactoryIface *factory = g_ptr_array_index
2556 (priv->channel_factories, i);
2557 TpChannelFactoryRequestStatus cur_status;
2558 TpChannelIface *chan = NULL;
2559
2560 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2561 cur_status = tp_channel_factory_iface_request (factory, type,
2562 (TpHandleType) handle_type, handle, request, &chan, &error);
2563 G_GNUC_END_IGNORE_DEPRECATIONS
2564
2565 switch (cur_status)
2566 {
2567 case TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING:
2568 {
2569 g_assert (NULL != chan);
2570 factory_satisfy_requests (self, factory, chan, request, FALSE);
2571 /* factory_satisfy_requests should remove the request */
2572 g_assert (!tp_g_ptr_array_contains (priv->channel_requests,
2573 request));
2574 return;
2575 }
2576 case TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED:
2577 g_assert (NULL != chan);
2578 /* the signal handler should have completed the queued request
2579 * and freed the ChannelRequest already */
2580 g_assert (!tp_g_ptr_array_contains (priv->channel_requests,
2581 request));
2582 return;
2583 case TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED:
2584 DEBUG ("queued request, channel_type=%s, handle_type=%u, "
2585 "handle=%u, suppress_handler=%u", type, handle_type,
2586 handle, suppress_handler);
2587 return;
2588 case TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR:
2589 /* pass through error */
2590 goto ERROR;
2591 default:
2592 /* always return the most specific error */
2593 if (cur_status > status)
2594 status = cur_status;
2595 }
2596 }
2597
2598 switch (status)
2599 {
2600 case TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE:
2601 DEBUG ("invalid handle %u", handle);
2602
2603 error = g_error_new (TP_ERROR, TP_ERROR_INVALID_HANDLE,
2604 "invalid handle %u", handle);
2605
2606 break;
2607
2608 case TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE:
2609 DEBUG ("requested channel is unavailable with "
2610 "handle type %u", handle_type);
2611
2612 error = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE,
2613 "requested channel is not available with "
2614 "handle type %u", handle_type);
2615
2616 break;
2617
2618 case TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED:
2619 DEBUG ("unsupported channel type %s", type);
2620
2621 error = g_error_new (TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
2622 "unsupported channel type %s", type);
2623
2624 break;
2625
2626 default:
2627 g_assert_not_reached ();
2628 }
2629
2630 ERROR:
2631 g_assert (error != NULL);
2632 dbus_g_method_return_error (request->context, error);
2633 request->context = NULL;
2634 g_error_free (error);
2635
2636 g_ptr_array_remove (priv->channel_requests, request);
2637 channel_request_free (request);
2638 }
2639
2640 static void
tp_base_connection_release_handles(TpSvcConnection * iface,guint handle_type,const GArray * handles,DBusGMethodInvocation * context)2641 tp_base_connection_release_handles (TpSvcConnection *iface,
2642 guint handle_type,
2643 const GArray * handles,
2644 DBusGMethodInvocation *context)
2645 {
2646 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2647 TpBaseConnectionPrivate *priv = self->priv;
2648 GError *error = NULL;
2649
2650 g_assert (TP_IS_BASE_CONNECTION (self));
2651
2652 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2653
2654 if (!tp_handles_supported_and_valid (priv->handles,
2655 handle_type, handles, FALSE, &error))
2656 {
2657 dbus_g_method_return_error (context, error);
2658 g_error_free (error);
2659 return;
2660 }
2661
2662 tp_svc_connection_return_from_release_handles (context);
2663 }
2664
2665 typedef struct
2666 {
2667 GArray *handles;
2668 guint n_pending;
2669 DBusGMethodInvocation *context;
2670 } RequestHandlesData;
2671
2672 typedef struct
2673 {
2674 RequestHandlesData *request;
2675 guint pos;
2676 } EnsureHandleData;
2677
2678 static void
ensure_handle_cb(GObject * source,GAsyncResult * result,gpointer user_data)2679 ensure_handle_cb (GObject *source,
2680 GAsyncResult *result,
2681 gpointer user_data)
2682 {
2683 TpHandleRepoIface *repo = (TpHandleRepoIface *) source;
2684 EnsureHandleData *data = user_data;
2685 RequestHandlesData *request = data->request;
2686 TpHandle handle;
2687 GError *error = NULL;
2688
2689 /* If another tp_handle_ensure_async() already failed, ignore others */
2690 if (request->context == NULL)
2691 goto out;
2692
2693 handle = tp_handle_ensure_finish (repo, result, &error);
2694 if (handle == 0)
2695 {
2696 dbus_g_method_return_error (request->context, error);
2697 request->context = NULL;
2698 g_clear_error (&error);
2699 }
2700 else
2701 {
2702 TpHandle *handle_p;
2703
2704 handle_p = &g_array_index (request->handles, TpHandle, data->pos);
2705 g_assert (*handle_p == 0);
2706 *handle_p = handle;
2707 }
2708
2709 out:
2710 request->n_pending--;
2711 if (request->n_pending == 0)
2712 {
2713 if (request->context != NULL)
2714 {
2715 tp_svc_connection_return_from_request_handles (request->context,
2716 request->handles);
2717 }
2718 g_array_unref (request->handles);
2719 g_slice_free (RequestHandlesData, request);
2720 }
2721
2722 g_slice_free (EnsureHandleData, data);
2723 }
2724
2725 /**
2726 * tp_base_connection_dbus_request_handles: (skip)
2727 * @iface: A pointer to #TpBaseConnection, cast to a pointer to
2728 * #TpSvcConnection
2729 * @handle_type: The handle type (#TpHandleType) as a guint
2730 * @names: A strv of handle names
2731 * @context: The dbus-glib method invocation context
2732 *
2733 * Implements D-Bus method RequestHandles on interface
2734 * org.freedesktop.Telepathy.Connection.
2735 *
2736 * This was exported so subclasses could use it as a basis for their
2737 * reimplementations, but reimplementing the method is now deprecated.
2738 *
2739 * Deprecated: 0.19.0
2740 */
2741 void
tp_base_connection_dbus_request_handles(TpSvcConnection * iface,guint handle_type,const gchar ** names,DBusGMethodInvocation * context)2742 tp_base_connection_dbus_request_handles (TpSvcConnection *iface,
2743 guint handle_type,
2744 const gchar **names,
2745 DBusGMethodInvocation *context)
2746 {
2747 TpBaseConnection *self = TP_BASE_CONNECTION (iface);
2748 TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (self,
2749 handle_type);
2750 guint count, i;
2751 GError *error = NULL;
2752 RequestHandlesData *request;
2753
2754 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
2755
2756 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
2757
2758 if (!tp_handle_type_is_valid (handle_type, &error))
2759 {
2760 g_assert (error != NULL);
2761 goto error;
2762 }
2763
2764 if (handle_repo == NULL)
2765 {
2766 DEBUG ("unimplemented handle type %u", handle_type);
2767
2768 error = g_error_new (TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
2769 "unimplemented handle type %u", handle_type);
2770 goto error;
2771 }
2772
2773 count = g_strv_length ((GStrv) names);
2774
2775 if (count == 0)
2776 {
2777 GArray *tmp = g_array_sized_new (FALSE, TRUE, sizeof (guint), 0);
2778 tp_svc_connection_return_from_request_handles (context, tmp);
2779 g_array_unref (tmp);
2780 return;
2781 }
2782
2783 request = g_slice_new0 (RequestHandlesData);
2784 request->handles = g_array_sized_new (FALSE, TRUE, sizeof (guint), count);
2785 request->n_pending = count;
2786 request->context = context;
2787
2788 /* _sized_new() just pre-allocates memory, but handles->len is still 0 */
2789 g_array_set_size (request->handles, count);
2790
2791 for (i = 0; i < count; i++)
2792 {
2793 EnsureHandleData *data;
2794
2795 data = g_slice_new0 (EnsureHandleData);
2796 data->request = request;
2797 data->pos = i;
2798
2799 tp_handle_ensure_async (handle_repo, self, names[i], NULL,
2800 ensure_handle_cb, data);
2801 }
2802
2803 return;
2804
2805 error:
2806 dbus_g_method_return_error (context, error);
2807 g_error_free (error);
2808 }
2809
2810 /**
2811 * tp_base_connection_get_handles:
2812 * @self: A connection
2813 * @handle_type: The handle type
2814 *
2815 * <!---->
2816 *
2817 * Returns: (transfer none): the handle repository corresponding to the given
2818 * handle type, or #NULL if it's unsupported or invalid.
2819 */
2820 TpHandleRepoIface *
tp_base_connection_get_handles(TpBaseConnection * self,TpHandleType handle_type)2821 tp_base_connection_get_handles (TpBaseConnection *self,
2822 TpHandleType handle_type)
2823 {
2824 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), NULL);
2825
2826 if (handle_type >= TP_NUM_HANDLE_TYPES)
2827 return NULL;
2828
2829 return self->priv->handles[handle_type];
2830 }
2831
2832
2833 /**
2834 * tp_base_connection_get_self_handle: (skip)
2835 * @self: A connection
2836 *
2837 * Returns the #TpBaseConnection:self-handle property, which is guaranteed not
2838 * to be 0 once the connection has moved to the CONNECTED state.
2839 *
2840 * Returns: the current self handle of the connection.
2841 *
2842 * Since: 0.7.15
2843 */
2844 TpHandle
tp_base_connection_get_self_handle(TpBaseConnection * self)2845 tp_base_connection_get_self_handle (TpBaseConnection *self)
2846 {
2847 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), 0);
2848
2849 return self->self_handle;
2850 }
2851
2852 /**
2853 * tp_base_connection_set_self_handle:
2854 * @self: A connection
2855 * @self_handle: The new self handle for the connection.
2856 *
2857 * Sets the #TpBaseConnection:self-handle property. self_handle may not be 0
2858 * once the connection has moved to the CONNECTED state.
2859 *
2860 * Since: 0.7.15
2861 */
2862 void
tp_base_connection_set_self_handle(TpBaseConnection * self,TpHandle self_handle)2863 tp_base_connection_set_self_handle (TpBaseConnection *self,
2864 TpHandle self_handle)
2865 {
2866 if (self->status == TP_CONNECTION_STATUS_CONNECTED)
2867 g_return_if_fail (self_handle != 0);
2868
2869 if (self->self_handle == self_handle)
2870 return;
2871
2872 self->self_handle = self_handle;
2873 self->priv->self_id = NULL;
2874
2875 if (self_handle != 0)
2876 {
2877 self->priv->self_id = tp_handle_inspect (
2878 self->priv->handles[TP_HANDLE_TYPE_CONTACT], self_handle);
2879 }
2880
2881 tp_svc_connection_emit_self_handle_changed (self, self->self_handle);
2882
2883 tp_svc_connection_emit_self_contact_changed (self,
2884 self->self_handle, self->priv->self_id);
2885
2886 g_object_notify ((GObject *) self, "self-handle");
2887 g_object_notify ((GObject *) self, "self-id");
2888 }
2889
2890
2891 /**
2892 * tp_base_connection_finish_shutdown: (skip)
2893 * @self: The connection
2894 *
2895 * Tell the connection manager that this Connection has been disconnected,
2896 * has emitted StatusChanged and is ready to be removed from D-Bus.
2897 */
tp_base_connection_finish_shutdown(TpBaseConnection * self)2898 void tp_base_connection_finish_shutdown (TpBaseConnection *self)
2899 {
2900 GPtrArray *contexts;
2901 guint i;
2902
2903 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
2904 g_return_if_fail (self->status == TP_CONNECTION_STATUS_DISCONNECTED);
2905 g_return_if_fail (self->priv->disconnect_requests != NULL);
2906
2907 contexts = self->priv->disconnect_requests;
2908 self->priv->disconnect_requests = NULL;
2909
2910 for (i = 0; i < contexts->len; i++)
2911 {
2912 tp_svc_connection_return_from_disconnect (g_ptr_array_index (contexts,
2913 i));
2914 }
2915
2916 g_ptr_array_unref (contexts);
2917
2918 g_signal_emit (self, signals[SHUTDOWN_FINISHED], 0);
2919 }
2920
2921 /**
2922 * tp_base_connection_disconnect_with_dbus_error: (skip)
2923 * @self: The connection
2924 * @error_name: The D-Bus error with which the connection changed status to
2925 * Disconnected
2926 * @details: Further details of the error, as a hash table where the keys
2927 * are strings as defined in the Telepathy specification, and the
2928 * values are #GValue<!-- -->s. %NULL is allowed, and treated as
2929 * an empty hash table.
2930 * @reason: The reason code to use in the StatusChanged signal
2931 * (a less specific, non-extensible version of @error_name)
2932 *
2933 * Changes the #TpBaseConnection<!-- -->.status of @self to
2934 * %TP_CONNECTION_STATUS_DISCONNECTED, as if by a call to
2935 * tp_base_connection_change_status(), but additionally emits the
2936 * <code>ConnectionError</code> D-Bus signal to provide more details about the
2937 * error.
2938 *
2939 * Well-known keys for @details are documented in the Telepathy specification's
2940 * <ulink url='http://telepathy.freedesktop.org/spec/Connection.html#Signal:ConnectionError'>definition
2941 * of the ConnectionError signal</ulink>, and include:
2942 *
2943 * <itemizedlist>
2944 * <listitem><code>"debug-message"</code>, whose value should have type
2945 * #G_TYPE_STRING, for debugging information about the
2946 * disconnection which should not be shown to the user</listitem>
2947 * <listitem><code>"server-message"</code>, whose value should also have type
2948 * #G_TYPE_STRING, for a human-readable error message from the server (in an
2949 * unspecified language) explaining why the user was
2950 * disconnected.</listitem>
2951 * </itemizedlist>
2952 *
2953 * Since: 0.7.24
2954 */
2955 void
tp_base_connection_disconnect_with_dbus_error(TpBaseConnection * self,const gchar * error_name,GHashTable * details,TpConnectionStatusReason reason)2956 tp_base_connection_disconnect_with_dbus_error (TpBaseConnection *self,
2957 const gchar *error_name,
2958 GHashTable *details,
2959 TpConnectionStatusReason reason)
2960 {
2961 GHashTable *dup_ = NULL;
2962
2963 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
2964 g_return_if_fail (tp_dbus_check_valid_interface_name (error_name, NULL));
2965
2966 if (details == NULL)
2967 {
2968 dup_ = g_hash_table_new (g_str_hash, g_str_equal);
2969 details = dup_;
2970 }
2971
2972 tp_svc_connection_emit_connection_error (self, error_name, details);
2973 tp_base_connection_change_status (self, TP_CONNECTION_STATUS_DISCONNECTED,
2974 reason);
2975
2976 if (dup_ != NULL)
2977 g_hash_table_unref (dup_);
2978 }
2979
2980 /**
2981 * tp_base_connection_disconnect_with_dbus_error_vardict: (skip)
2982 * @self: The connection
2983 * @error_name: The D-Bus error with which the connection changed status to
2984 * Disconnected
2985 * @details: Further details of the error, as a variant of
2986 * type %G_VARIANT_TYPE_VARDICT. The keys
2987 * are strings as defined in the Telepathy specification, and the
2988 * values are of type %G_VARIANT_TYPE_VARIANT.
2989 * %NULL is allowed, and treated as an empty dictionary.
2990 * @reason: The reason code to use in the StatusChanged signal
2991 * (a less specific, non-extensible version of @error_name)
2992 *
2993 * Changes the #TpBaseConnection<!-- -->.status of @self to
2994 * %TP_CONNECTION_STATUS_DISCONNECTED, as if by a call to
2995 * tp_base_connection_change_status(), but additionally emits the
2996 * <code>ConnectionError</code> D-Bus signal to provide more details about the
2997 * error.
2998 *
2999 * Well-known keys for @details are documented in the Telepathy specification's
3000 * <ulink url='http://telepathy.freedesktop.org/spec/Connection.html#Signal:ConnectionError'>definition
3001 * of the ConnectionError signal</ulink>, and include:
3002 *
3003 * <itemizedlist>
3004 * <listitem><code>"debug-message"</code>, whose value should have type
3005 * #G_TYPE_STRING, for debugging information about the
3006 * disconnection which should not be shown to the user</listitem>
3007 * <listitem><code>"server-message"</code>, whose value should also have type
3008 * #G_TYPE_STRING, for a human-readable error message from the server (in an
3009 * unspecified language) explaining why the user was
3010 * disconnected.</listitem>
3011 * </itemizedlist>
3012 *
3013 * Since: 0.7.24
3014 */
3015 void
tp_base_connection_disconnect_with_dbus_error_vardict(TpBaseConnection * self,const gchar * error_name,GVariant * details,TpConnectionStatusReason reason)3016 tp_base_connection_disconnect_with_dbus_error_vardict (TpBaseConnection *self,
3017 const gchar *error_name,
3018 GVariant *details,
3019 TpConnectionStatusReason reason)
3020 {
3021 GHashTable *hash;
3022
3023 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
3024 g_return_if_fail (tp_dbus_check_valid_interface_name (error_name, NULL));
3025
3026 if (details == NULL)
3027 {
3028 hash = g_hash_table_new (g_str_hash, g_str_equal);
3029 }
3030 else
3031 {
3032 hash = _tp_asv_from_vardict (details);
3033 }
3034
3035 tp_svc_connection_emit_connection_error (self, error_name, hash);
3036 tp_base_connection_change_status (self, TP_CONNECTION_STATUS_DISCONNECTED,
3037 reason);
3038
3039 g_hash_table_unref (hash);
3040 }
3041
3042 /**
3043 * tp_base_connection_change_status:
3044 * @self: The connection
3045 * @status: The new status
3046 * @reason: The reason for the status change
3047 *
3048 * Change the status of the connection. The allowed state transitions are:
3049 *
3050 * <itemizedlist>
3051 * <listitem>#TP_INTERNAL_CONNECTION_STATUS_NEW →
3052 * #TP_CONNECTION_STATUS_CONNECTING</listitem>
3053 * <listitem>#TP_CONNECTION_STATUS_CONNECTING →
3054 * #TP_CONNECTION_STATUS_CONNECTED</listitem>
3055 * <listitem>#TP_INTERNAL_CONNECTION_STATUS_NEW →
3056 * #TP_CONNECTION_STATUS_CONNECTED (exactly equivalent to both of the above
3057 * one after the other; see below)</listitem>
3058 * <listitem>anything except #TP_CONNECTION_STATUS_DISCONNECTED →
3059 * #TP_CONNECTION_STATUS_DISCONNECTED</listitem>
3060 * </itemizedlist>
3061 *
3062 * Before the transition to #TP_CONNECTION_STATUS_CONNECTED, the implementation
3063 * must have discovered the handle for the local user and passed it to
3064 * tp_base_connection_set_self_handle().
3065 *
3066 * Changing from NEW to CONNECTED is implemented by doing the transition from
3067 * NEW to CONNECTING, followed by the transition from CONNECTING to CONNECTED;
3068 * it's exactly equivalent to calling tp_base_connection_change_status for
3069 * those two transitions one after the other.
3070 *
3071 * Any other valid transition does the following, in this order:
3072 *
3073 * <itemizedlist>
3074 * <listitem>Update #TpBaseConnection<!-- -->.status;</listitem>
3075 * <listitem>If the new state is #TP_CONNECTION_STATUS_DISCONNECTED, call
3076 * tp_channel_factory_iface_close_all() on all channel factories</listitem>
3077 * <listitem>Emit the D-Bus StatusChanged signal;</listitem>
3078 * <listitem>Call #TpBaseConnectionClass.connecting,
3079 * #TpBaseConnectionClass.connected or #TpBaseConnectionClass.disconnected
3080 * as appropriate;</listitem>
3081 * <listitem>Call the channel factories' status change callbacks;</listitem>
3082 * <listitem>If the new state is #TP_CONNECTION_STATUS_DISCONNECTED, call the
3083 * subclass' #TpBaseConnectionClass.shut_down callback.</listitem>
3084 * </itemizedlist>
3085 *
3086 * To provide more details about what happened when moving to @status
3087 * #TP_CONNECTION_STATUS_DISCONNECTED due to an error, consider calling
3088 * tp_base_connection_disconnect_with_dbus_error() instead of this function.
3089 *
3090 * Changed in 0.7.35: the @self_handle member of #TpBaseConnection was
3091 * previously set to 0 at this stage. It now remains non-zero until the object
3092 * is disposed.
3093 */
3094 void
tp_base_connection_change_status(TpBaseConnection * self,TpConnectionStatus status,TpConnectionStatusReason reason)3095 tp_base_connection_change_status (TpBaseConnection *self,
3096 TpConnectionStatus status,
3097 TpConnectionStatusReason reason)
3098 {
3099 TpBaseConnectionPrivate *priv;
3100 TpBaseConnectionClass *klass;
3101 TpConnectionStatus prev_status;
3102
3103 g_assert (TP_IS_BASE_CONNECTION (self));
3104
3105 priv = self->priv;
3106 klass = TP_BASE_CONNECTION_GET_CLASS (self);
3107
3108 if (self->status == TP_INTERNAL_CONNECTION_STATUS_NEW
3109 && status == TP_CONNECTION_STATUS_CONNECTED)
3110 {
3111 /* going straight from NEW to CONNECTED would cause confusion, so before
3112 * we do anything else, go via CONNECTING */
3113 DEBUG("from NEW to CONNECTED: going via CONNECTING first");
3114 tp_base_connection_change_status (self, TP_CONNECTION_STATUS_CONNECTING,
3115 reason);
3116 }
3117
3118 DEBUG("was %u, now %u, for reason %u", self->status, status, reason);
3119 g_return_if_fail (status != TP_INTERNAL_CONNECTION_STATUS_NEW);
3120
3121 if (self->status == status)
3122 {
3123 WARNING ("attempted to re-emit the current status %u, reason %u",
3124 status, reason);
3125 return;
3126 }
3127
3128 prev_status = self->status;
3129
3130 /* make appropriate assertions about our state */
3131 switch (status)
3132 {
3133 case TP_CONNECTION_STATUS_DISCONNECTED:
3134 /* you can go from any state to DISCONNECTED, except DISCONNECTED;
3135 * and we already warned and returned if that was the case, so
3136 * nothing to do here */
3137 break;
3138 case TP_CONNECTION_STATUS_CONNECTED:
3139 /* you can only go to CONNECTED if you're CONNECTING (or NEW, but we
3140 * covered that by forcing a transition to CONNECTING above) */
3141 g_return_if_fail (prev_status == TP_CONNECTION_STATUS_CONNECTING);
3142 /* by the time we go CONNECTED we must have the self handle */
3143 g_return_if_fail (self->self_handle != 0);
3144 break;
3145 case TP_CONNECTION_STATUS_CONNECTING:
3146 /* you can't go CONNECTING if a connection attempt has been made
3147 * before */
3148 g_return_if_fail (prev_status == TP_INTERNAL_CONNECTION_STATUS_NEW);
3149 break;
3150 default:
3151 CRITICAL ("invalid connection status %d", status);
3152 return;
3153 }
3154
3155 /* now that we've finished return_if_fail'ing, we can start to make
3156 * the actual changes */
3157 self->status = status;
3158
3159 /* ref self in case user callbacks unref us */
3160 g_object_ref (self);
3161
3162 if (status == TP_CONNECTION_STATUS_DISCONNECTED)
3163 {
3164 /* the presence of this array indicates that we are shutting down */
3165 if (self->priv->disconnect_requests == NULL)
3166 self->priv->disconnect_requests = g_ptr_array_sized_new (0);
3167
3168 /* remove all channels and shut down all factories, so we don't get
3169 * any race conditions where method calls are delivered to a channel
3170 * after we've started disconnecting
3171 */
3172 tp_base_connection_close_all_channels (self);
3173 }
3174
3175 DEBUG("emitting status-changed to %u, for reason %u", status, reason);
3176 tp_svc_connection_emit_status_changed (self, status, reason);
3177
3178 /* tell subclass and factories about the state change. In the case of
3179 * disconnection, shut down afterwards */
3180 switch (status)
3181 {
3182 case TP_CONNECTION_STATUS_CONNECTING:
3183 if (klass->connecting)
3184 (klass->connecting) (self);
3185 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3186 g_ptr_array_foreach (priv->channel_factories, (GFunc)
3187 tp_channel_factory_iface_connecting, NULL);
3188 G_GNUC_END_IGNORE_DEPRECATIONS
3189 break;
3190
3191 case TP_CONNECTION_STATUS_CONNECTED:
3192 /* the implementation should have ensured we have a valid self_handle
3193 * before changing the state to CONNECTED */
3194
3195 g_assert (self->self_handle != 0);
3196 g_assert (tp_handle_is_valid (priv->handles[TP_HANDLE_TYPE_CONTACT],
3197 self->self_handle, NULL));
3198 if (klass->connected)
3199 (klass->connected) (self);
3200 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3201 g_ptr_array_foreach (priv->channel_factories, (GFunc)
3202 tp_channel_factory_iface_connected, NULL);
3203 G_GNUC_END_IGNORE_DEPRECATIONS
3204 break;
3205
3206 case TP_CONNECTION_STATUS_DISCONNECTED:
3207 /* cancel all queued channel requests that weren't already cancelled by
3208 * the channel managers.
3209 */
3210 if (priv->channel_requests->len > 0)
3211 {
3212 g_ptr_array_foreach (priv->channel_requests, (GFunc)
3213 channel_request_cancel, NULL);
3214 g_ptr_array_remove_range (priv->channel_requests, 0,
3215 priv->channel_requests->len);
3216 }
3217
3218 if (prev_status != TP_INTERNAL_CONNECTION_STATUS_NEW)
3219 {
3220 if (klass->disconnected)
3221 (klass->disconnected) (self);
3222 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3223 g_ptr_array_foreach (priv->channel_factories, (GFunc)
3224 tp_channel_factory_iface_disconnected, NULL);
3225 G_GNUC_END_IGNORE_DEPRECATIONS
3226 }
3227 (klass->shut_down) (self);
3228 tp_base_connection_unregister (self);
3229 break;
3230
3231 default:
3232 g_assert_not_reached ();
3233 }
3234
3235 g_object_unref (self);
3236 }
3237
3238
3239 /**
3240 * tp_base_connection_add_interfaces: (skip)
3241 * @self: A TpBaseConnection in state #TP_INTERNAL_CONNECTION_STATUS_NEW
3242 * or #TP_CONNECTION_STATUS_CONNECTING
3243 * @interfaces: A %NULL-terminated array of D-Bus interface names, which
3244 * must remain valid at least until the connection enters state
3245 * #TP_CONNECTION_STATUS_DISCONNECTED (in practice, you should either
3246 * use static strings, or use strdup'd strings and free them in the dispose
3247 * callback).
3248 *
3249 * Add some interfaces to the list supported by this Connection. If you're
3250 * going to call this function at all, you must do so before moving to state
3251 * CONNECTED (or DISCONNECTED); if you don't call it, only the set of
3252 * interfaces always present (@get_interfaces_always_present in
3253 * #TpBaseConnectionClass) will be supported.
3254 */
3255 void
tp_base_connection_add_interfaces(TpBaseConnection * self,const gchar ** interfaces)3256 tp_base_connection_add_interfaces (TpBaseConnection *self,
3257 const gchar **interfaces)
3258 {
3259 TpBaseConnectionPrivate *priv = self->priv;
3260
3261 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
3262 g_return_if_fail (self->status != TP_CONNECTION_STATUS_CONNECTED);
3263 g_return_if_fail (self->status != TP_CONNECTION_STATUS_DISCONNECTED);
3264
3265 for (; interfaces != NULL && *interfaces != NULL; interfaces++)
3266 g_array_append_val (priv->interfaces, *interfaces);
3267 }
3268
3269 static void
tp_base_connection_interested_name_owner_changed_cb(TpDBusDaemon * it G_GNUC_UNUSED,const gchar * unique_name,const gchar * new_owner,gpointer user_data)3270 tp_base_connection_interested_name_owner_changed_cb (
3271 TpDBusDaemon *it G_GNUC_UNUSED,
3272 const gchar *unique_name,
3273 const gchar *new_owner,
3274 gpointer user_data)
3275 {
3276 TpBaseConnection *self = user_data;
3277 GHashTableIter iter;
3278 gpointer q, hash;
3279
3280 /* We don't care about the initial report that :1.42 is owned by :1.42. */
3281 if (!tp_str_empty (new_owner))
3282 return;
3283
3284 /* Failing that, @unique_name must have crashed... */
3285
3286 g_hash_table_iter_init (&iter, self->priv->client_interests);
3287
3288 while (g_hash_table_iter_next (&iter, &q, &hash))
3289 {
3290 if (g_hash_table_remove (hash, unique_name) &&
3291 g_hash_table_size (hash) == 0)
3292 {
3293 const gchar *s = g_quark_to_string (GPOINTER_TO_UINT (q));
3294
3295 DEBUG ("%s was the last client interested in %s", unique_name, s);
3296 g_signal_emit (self, signals[CLIENTS_UNINTERESTED],
3297 (GQuark) GPOINTER_TO_UINT (q), s);
3298 }
3299 }
3300
3301 /* this has to be done last, because the keys in the other hashes are
3302 * borrowed from here */
3303 g_hash_table_remove (self->priv->interested_clients, unique_name);
3304
3305 tp_dbus_daemon_cancel_name_owner_watch (self->priv->bus_proxy,
3306 unique_name, tp_base_connection_interested_name_owner_changed_cb, self);
3307 }
3308
3309 static void
tp_base_connection_add_client_interest_impl(TpBaseConnection * self,const gchar * unique_name,const gchar * const * interests,gboolean only_if_uninterested)3310 tp_base_connection_add_client_interest_impl (TpBaseConnection *self,
3311 const gchar *unique_name,
3312 const gchar * const *interests,
3313 gboolean only_if_uninterested)
3314 {
3315 gpointer name_in_hash, count_p;
3316 gsize total;
3317 const gchar * const *interest;
3318 gboolean was_there;
3319
3320 if (g_hash_table_lookup_extended (self->priv->interested_clients,
3321 unique_name, &name_in_hash, &count_p))
3322 {
3323 total = GPOINTER_TO_SIZE (count_p);
3324 was_there = TRUE;
3325 }
3326 else
3327 {
3328 name_in_hash = g_strdup (unique_name);
3329 total = 0;
3330 g_hash_table_insert (self->priv->interested_clients, name_in_hash,
3331 GSIZE_TO_POINTER (total));
3332 was_there = FALSE;
3333 }
3334
3335 for (interest = interests; *interest != NULL; interest++)
3336 {
3337 GQuark q = g_quark_try_string (*interest);
3338 GHashTable *clients;
3339 gsize count;
3340
3341 if (q == 0)
3342 {
3343 /* we can only declare an interest in known quarks, so clearly this
3344 * one is not useful */
3345 continue;
3346 }
3347
3348 clients = g_hash_table_lookup (self->priv->client_interests,
3349 GUINT_TO_POINTER (q));
3350
3351 if (clients == NULL)
3352 {
3353 /* declaring an interest in this token has no effect */
3354 continue;
3355 }
3356
3357 count = GPOINTER_TO_SIZE (g_hash_table_lookup (clients, unique_name));
3358
3359 if (count > 0 && only_if_uninterested)
3360 {
3361 /* that client is already interested - nothing to do */
3362 continue;
3363 }
3364
3365 /* name_in_hash is borrowed from interested_clients so we have to
3366 * keep using the same one */
3367 g_hash_table_insert (clients, name_in_hash,
3368 GSIZE_TO_POINTER (++count));
3369 total++;
3370
3371 if (count == 1 && g_hash_table_size (clients) == 1)
3372 {
3373 /* Transition from 0 to 1 interests in total; the signal detail is
3374 * the token. */
3375 DEBUG ("%s is the first to be interested in %s", unique_name,
3376 *interest);
3377 g_signal_emit (self, signals[CLIENTS_INTERESTED], q, *interest);
3378 }
3379 }
3380
3381 if (total > 0)
3382 {
3383 /* name_in_hash is borrowed by client_interests so we have to keep
3384 * using the same one */
3385 g_hash_table_steal (self->priv->interested_clients, name_in_hash);
3386 g_hash_table_insert (self->priv->interested_clients, name_in_hash,
3387 GSIZE_TO_POINTER (total));
3388
3389 if (!was_there)
3390 {
3391 tp_dbus_daemon_watch_name_owner (self->priv->bus_proxy, unique_name,
3392 tp_base_connection_interested_name_owner_changed_cb, self, NULL);
3393 }
3394 }
3395 else
3396 {
3397 g_hash_table_remove (self->priv->interested_clients, unique_name);
3398
3399 tp_dbus_daemon_cancel_name_owner_watch (self->priv->bus_proxy,
3400 unique_name, tp_base_connection_interested_name_owner_changed_cb,
3401 self);
3402 }
3403 }
3404
3405 /**
3406 * tp_base_connection_add_client_interest:
3407 * @self: a #TpBaseConnection
3408 * @unique_name: the unique bus name of a D-Bus client
3409 * @token: a D-Bus interface or a token representing part of an interface,
3410 * added with tp_base_connection_add_possible_client_interest()
3411 * @only_if_uninterested: only add to the interest count if the client is not
3412 * already interested (appropriate for APIs that implicitly subscribe on first
3413 * use if this has not been done already, like Location)
3414 *
3415 * Add a "client interest" for @token on behalf of the given client.
3416 *
3417 * This emits #TpBaseConnection::clients-interested if this was the first
3418 * time a client expressed an interest in this token.
3419 */
3420 void
tp_base_connection_add_client_interest(TpBaseConnection * self,const gchar * unique_name,const gchar * token,gboolean only_if_uninterested)3421 tp_base_connection_add_client_interest (TpBaseConnection *self,
3422 const gchar *unique_name,
3423 const gchar *token,
3424 gboolean only_if_uninterested)
3425 {
3426 const gchar * tokens[2] = { NULL, NULL };
3427
3428 tokens[0] = token;
3429 tp_base_connection_add_client_interest_impl (self, unique_name, tokens,
3430 only_if_uninterested);
3431 }
3432
3433 static void
tp_base_connection_dbus_add_client_interest(TpSvcConnection * svc,const gchar ** interests,DBusGMethodInvocation * context)3434 tp_base_connection_dbus_add_client_interest (TpSvcConnection *svc,
3435 const gchar **interests,
3436 DBusGMethodInvocation *context)
3437 {
3438 TpBaseConnection *self = (TpBaseConnection *) svc;
3439 gchar *unique_name = NULL;
3440
3441 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
3442 g_return_if_fail (self->priv->bus_proxy != NULL);
3443
3444 if (interests == NULL || interests[0] == NULL)
3445 goto finally;
3446
3447 unique_name = dbus_g_method_get_sender (context);
3448
3449 tp_base_connection_add_client_interest_impl (self, unique_name,
3450 (const gchar * const *) interests, FALSE);
3451
3452 finally:
3453 tp_svc_connection_return_from_add_client_interest (context);
3454 g_free (unique_name);
3455 }
3456
3457 static void
tp_base_connection_dbus_remove_client_interest(TpSvcConnection * svc,const gchar ** interests,DBusGMethodInvocation * context)3458 tp_base_connection_dbus_remove_client_interest (TpSvcConnection *svc,
3459 const gchar **interests,
3460 DBusGMethodInvocation *context)
3461 {
3462 gchar *unique_name = NULL;
3463 const gchar **interest;
3464 TpBaseConnection *self = (TpBaseConnection *) svc;
3465 gpointer name_in_hash, count_p;
3466 gsize total;
3467
3468 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
3469 g_return_if_fail (self->priv->bus_proxy != NULL);
3470
3471 if (interests == NULL || interests[0] == NULL)
3472 goto finally;
3473
3474 unique_name = dbus_g_method_get_sender (context);
3475
3476 /* this method isn't really meant to fail, so we might as well return now */
3477
3478 if (!g_hash_table_lookup_extended (self->priv->interested_clients,
3479 unique_name, &name_in_hash, &count_p))
3480 {
3481 /* unique_name doesn't own any client interests. Strictly speaking this
3482 * is an error, but it's probably ignoring the reply anyway, so we
3483 * won't tell it. */
3484 goto finally;
3485 }
3486
3487 total = GPOINTER_TO_SIZE (count_p);
3488
3489 for (interest = interests; *interest != NULL; interest++)
3490 {
3491 GQuark q = g_quark_try_string (*interest);
3492 GHashTable *clients;
3493 gsize count;
3494
3495 if (q == 0)
3496 {
3497 /* we can only declare an interest in known quarks, so clearly this
3498 * one is not useful */
3499 continue;
3500 }
3501
3502 clients = g_hash_table_lookup (self->priv->client_interests,
3503 GUINT_TO_POINTER (q));
3504
3505 if (clients == NULL)
3506 {
3507 /* declaring an interest in this token has no effect */
3508 continue;
3509 }
3510
3511 count = GPOINTER_TO_SIZE (g_hash_table_lookup (clients, unique_name));
3512
3513 if (count == 0)
3514 {
3515 /* strictly speaking, this is an error, but nobody will be waiting
3516 * for a reply anyway */
3517 DEBUG ("unable to decrement %s interest in %s past zero",
3518 unique_name, *interest);
3519 continue;
3520 }
3521
3522 total--;
3523
3524 if (count == 1)
3525 {
3526 g_hash_table_remove (clients, unique_name);
3527
3528 if (g_hash_table_size (clients) == 0)
3529 {
3530 /* transition from 1 to 0 total interest-counts */
3531 DEBUG ("%s was the last client interested in %s", unique_name,
3532 *interest);
3533 g_signal_emit (self, signals[CLIENTS_UNINTERESTED], q,
3534 *interest);
3535 }
3536 }
3537 else
3538 {
3539 /* name_in_hash is borrowed from interested_clients so we have to
3540 * keep using the same one */
3541 g_hash_table_insert (clients, name_in_hash,
3542 GSIZE_TO_POINTER (--count));
3543 }
3544 }
3545
3546 if (total == 0)
3547 {
3548 g_hash_table_remove (self->priv->interested_clients, unique_name);
3549
3550 tp_dbus_daemon_cancel_name_owner_watch (self->priv->bus_proxy,
3551 unique_name, tp_base_connection_interested_name_owner_changed_cb,
3552 self);
3553 }
3554
3555 finally:
3556 tp_svc_connection_return_from_remove_client_interest (context);
3557 g_free (unique_name);
3558 }
3559
3560 static void
conn_iface_init(gpointer g_iface,gpointer iface_data)3561 conn_iface_init (gpointer g_iface, gpointer iface_data)
3562 {
3563 TpSvcConnectionClass *klass = g_iface;
3564
3565 #define IMPLEMENT(prefix,x) tp_svc_connection_implement_##x (klass, \
3566 tp_base_connection_##prefix##x)
3567 IMPLEMENT(,connect);
3568 IMPLEMENT(,disconnect);
3569 IMPLEMENT(dbus_,get_interfaces);
3570 IMPLEMENT(,get_protocol);
3571 IMPLEMENT(dbus_,get_self_handle);
3572 IMPLEMENT(dbus_,get_status);
3573 IMPLEMENT(,hold_handles);
3574 IMPLEMENT(,inspect_handles);
3575 IMPLEMENT(,list_channels);
3576 IMPLEMENT(,request_channel);
3577 IMPLEMENT(,release_handles);
3578 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
3579 IMPLEMENT(dbus_,request_handles);
3580 G_GNUC_END_IGNORE_DEPRECATIONS
3581 IMPLEMENT(dbus_,add_client_interest);
3582 IMPLEMENT(dbus_,remove_client_interest);
3583 #undef IMPLEMENT
3584 }
3585
3586
3587 /* The handling of calls to Connection.Interface.Requests.CreateChannel is
3588 * split into three chained functions, which each call the next function in
3589 * the chain unless an error has occured.
3590 */
3591 static void conn_requests_check_basic_properties (TpBaseConnection *self,
3592 GHashTable *requested_properties, ChannelRequestMethod method,
3593 DBusGMethodInvocation *context);
3594
3595 static void
3596 conn_requests_requestotron_validate_handle (TpBaseConnection *self,
3597 GHashTable *requested_properties, ChannelRequestMethod method,
3598 const gchar *type, TpHandleType target_handle_type,
3599 TpHandle target_handle, const gchar *target_id,
3600 DBusGMethodInvocation *context);
3601
3602 static void conn_requests_offer_request (TpBaseConnection *self,
3603 GHashTable *requested_properties, ChannelRequestMethod method,
3604 const gchar *type, TpHandleType target_handle_type,
3605 TpHandle target_handle, DBusGMethodInvocation *context);
3606
3607
3608 #define RETURN_INVALID_ARGUMENT(message) \
3609 G_STMT_START { \
3610 GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, message }; \
3611 dbus_g_method_return_error (context, &e); \
3612 return; \
3613 } G_STMT_END
3614
3615
3616 static void
conn_requests_requestotron(TpBaseConnection * self,GHashTable * requested_properties,ChannelRequestMethod method,DBusGMethodInvocation * context)3617 conn_requests_requestotron (TpBaseConnection *self,
3618 GHashTable *requested_properties,
3619 ChannelRequestMethod method,
3620 DBusGMethodInvocation *context)
3621 {
3622 TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context);
3623
3624 /* Call the first function in the chain handling incoming requests; it will
3625 * call the next steps.
3626 */
3627 conn_requests_check_basic_properties (self, requested_properties, method,
3628 context);
3629 }
3630
3631
3632 static void
conn_requests_check_basic_properties(TpBaseConnection * self,GHashTable * requested_properties,ChannelRequestMethod method,DBusGMethodInvocation * context)3633 conn_requests_check_basic_properties (TpBaseConnection *self,
3634 GHashTable *requested_properties,
3635 ChannelRequestMethod method,
3636 DBusGMethodInvocation *context)
3637 {
3638 /* Step 1:
3639 * Check that ChannelType, TargetHandleType, TargetHandle, TargetID have
3640 * the correct types, and that ChannelType is not omitted.
3641 */
3642 const gchar *type;
3643 TpHandleType target_handle_type;
3644 TpHandle target_handle;
3645 const gchar *target_id;
3646 gboolean valid;
3647
3648 type = tp_asv_get_string (requested_properties,
3649 TP_PROP_CHANNEL_CHANNEL_TYPE);
3650
3651 if (type == NULL)
3652 RETURN_INVALID_ARGUMENT ("ChannelType is required");
3653
3654 target_handle_type = tp_asv_get_uint32 (requested_properties,
3655 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid);
3656
3657 /* Allow TargetHandleType to be missing, but not to be otherwise broken */
3658 if (!valid && tp_asv_lookup (requested_properties,
3659 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE) != NULL)
3660 RETURN_INVALID_ARGUMENT (
3661 "TargetHandleType must be an integer in range 0 to 2**32-1");
3662
3663 target_handle = tp_asv_get_uint32 (requested_properties,
3664 TP_PROP_CHANNEL_TARGET_HANDLE, &valid);
3665
3666 /* Allow TargetHandle to be missing, but not to be otherwise broken */
3667 if (!valid && tp_asv_lookup (requested_properties,
3668 TP_PROP_CHANNEL_TARGET_HANDLE) != NULL)
3669 RETURN_INVALID_ARGUMENT (
3670 "TargetHandle must be an integer in range 1 to 2**32-1");
3671
3672 /* TargetHandle may not be 0 */
3673 if (valid && target_handle == 0)
3674 RETURN_INVALID_ARGUMENT ("TargetHandle may not be 0");
3675
3676 target_id = tp_asv_get_string (requested_properties,
3677 TP_PROP_CHANNEL_TARGET_ID);
3678
3679 /* Allow TargetID to be missing, but not to be otherwise broken */
3680 if (target_id == NULL && tp_asv_lookup (requested_properties,
3681 TP_PROP_CHANNEL_TARGET_ID) != NULL)
3682 RETURN_INVALID_ARGUMENT ("TargetID must be a string");
3683
3684 if (tp_asv_lookup (requested_properties, TP_PROP_CHANNEL_INITIATOR_HANDLE)
3685 != NULL)
3686 RETURN_INVALID_ARGUMENT ("InitiatorHandle may not be requested");
3687
3688 if (tp_asv_lookup (requested_properties, TP_PROP_CHANNEL_INITIATOR_ID)
3689 != NULL)
3690 RETURN_INVALID_ARGUMENT ("InitiatorID may not be requested");
3691
3692 if (tp_asv_lookup (requested_properties, TP_PROP_CHANNEL_REQUESTED)
3693 != NULL)
3694 RETURN_INVALID_ARGUMENT ("Requested may not be requested");
3695
3696 conn_requests_requestotron_validate_handle (self,
3697 requested_properties, method,
3698 type, target_handle_type, target_handle, target_id,
3699 context);
3700 }
3701
3702
3703 /*
3704 * @target_handle: non-zero if a TargetHandle property was in the request;
3705 * zero if TargetHandle was not in the request.
3706 */
3707 static void
conn_requests_requestotron_validate_handle(TpBaseConnection * self,GHashTable * requested_properties,ChannelRequestMethod method,const gchar * type,TpHandleType target_handle_type,TpHandle target_handle,const gchar * target_id,DBusGMethodInvocation * context)3708 conn_requests_requestotron_validate_handle (TpBaseConnection *self,
3709 GHashTable *requested_properties,
3710 ChannelRequestMethod method,
3711 const gchar *type,
3712 TpHandleType target_handle_type,
3713 TpHandle target_handle,
3714 const gchar *target_id,
3715 DBusGMethodInvocation *context)
3716 {
3717 /* Step 2: Validate the supplied set of Handle properties */
3718 TpHandleRepoIface *handles = NULL;
3719 GHashTable *altered_properties = NULL;
3720 GValue *target_handle_value = NULL;
3721 GValue *target_id_value = NULL;
3722
3723 /* Handle type 0 cannot have a handle */
3724 if (target_handle_type == TP_HANDLE_TYPE_NONE && target_handle != 0)
3725 RETURN_INVALID_ARGUMENT (
3726 "When TargetHandleType is NONE, TargetHandle must be omitted");
3727
3728 /* Handle type 0 cannot have a target id */
3729 if (target_handle_type == TP_HANDLE_TYPE_NONE && target_id != NULL)
3730 RETURN_INVALID_ARGUMENT (
3731 "When TargetHandleType is NONE, TargetID must be omitted");
3732
3733 if (target_handle_type != TP_HANDLE_TYPE_NONE)
3734 {
3735 GError *error = NULL;
3736
3737 if (target_handle == 0 && target_id == NULL)
3738 RETURN_INVALID_ARGUMENT ("When TargetHandleType is not None, either "
3739 "TargetHandle or TargetID must also be given");
3740
3741 if (target_handle != 0 && target_id != NULL)
3742 RETURN_INVALID_ARGUMENT (
3743 "TargetHandle and TargetID must not both be given");
3744
3745 handles = tp_base_connection_get_handles (self, target_handle_type);
3746
3747 if (handles == NULL)
3748 {
3749 GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE,
3750 "Handle type not supported by this connection manager" };
3751
3752 dbus_g_method_return_error (context, &e);
3753 return;
3754 }
3755
3756 if (target_handle == 0)
3757 {
3758 /* Turn TargetID into TargetHandle */
3759 target_handle = tp_handle_ensure (handles, target_id, NULL, &error);
3760
3761 if (target_handle == 0)
3762 {
3763 /* tp_handle_ensure can return any error in any domain; force
3764 * the domain and code to be as documented for CreateChannel.
3765 */
3766 error->domain = TP_ERROR;
3767 error->code = TP_ERROR_INVALID_HANDLE;
3768 dbus_g_method_return_error (context, error);
3769 g_error_free (error);
3770 return;
3771 }
3772
3773 altered_properties = g_hash_table_new_full (g_str_hash, g_str_equal,
3774 NULL, NULL);
3775 tp_g_hash_table_update (altered_properties, requested_properties,
3776 NULL, NULL);
3777
3778 target_handle_value = tp_g_value_slice_new_uint (target_handle);
3779 g_hash_table_insert (altered_properties,
3780 TP_PROP_CHANNEL_TARGET_HANDLE, target_handle_value);
3781
3782 requested_properties = altered_properties;
3783 }
3784 else
3785 {
3786 /* Check the supplied TargetHandle is valid */
3787 if (!tp_handle_is_valid (handles, target_handle, &error))
3788 {
3789 error->domain = TP_ERROR;
3790 error->code = TP_ERROR_INVALID_HANDLE;
3791 dbus_g_method_return_error (context, error);
3792 g_error_free (error);
3793 return;
3794 }
3795
3796 altered_properties = g_hash_table_new_full (g_str_hash, g_str_equal,
3797 NULL, NULL);
3798 tp_g_hash_table_update (altered_properties, requested_properties,
3799 NULL, NULL);
3800
3801 target_id_value = tp_g_value_slice_new_string (
3802 tp_handle_inspect (handles, target_handle));
3803 g_hash_table_insert (altered_properties,
3804 TP_PROP_CHANNEL_TARGET_ID,
3805 target_id_value);
3806
3807 requested_properties = altered_properties;
3808 }
3809 }
3810
3811 conn_requests_offer_request (self, requested_properties, method, type,
3812 target_handle_type, target_handle, context);
3813
3814 /* If we made a new table, we should destroy it, and whichever of the GValues
3815 * holding TargetHandle or TargetID we filled in. The other GValues are
3816 * borrowed from the supplied requested_properties table.
3817 */
3818 if (altered_properties != NULL)
3819 {
3820 g_hash_table_unref (altered_properties);
3821
3822 if (target_handle_value != NULL)
3823 tp_g_value_slice_free (target_handle_value);
3824
3825 if (target_id_value != NULL)
3826 tp_g_value_slice_free (target_id_value);
3827 }
3828 }
3829
3830
3831 static void
conn_requests_offer_request(TpBaseConnection * self,GHashTable * requested_properties,ChannelRequestMethod method,const gchar * type,TpHandleType target_handle_type,TpHandle target_handle,DBusGMethodInvocation * context)3832 conn_requests_offer_request (TpBaseConnection *self,
3833 GHashTable *requested_properties,
3834 ChannelRequestMethod method,
3835 const gchar *type,
3836 TpHandleType target_handle_type,
3837 TpHandle target_handle,
3838 DBusGMethodInvocation *context)
3839 {
3840 /* Step 3: offer the incoming, vaguely sanitized request to the channel
3841 * managers.
3842 */
3843 TpBaseConnectionPrivate *priv = self->priv;
3844 TpChannelManagerRequestFunc func;
3845 ChannelRequest *request;
3846 gboolean suppress_handler;
3847 guint i;
3848
3849 switch (method)
3850 {
3851 case METHOD_CREATE_CHANNEL:
3852 func = tp_channel_manager_create_channel;
3853 suppress_handler = TRUE;
3854 break;
3855
3856 case METHOD_ENSURE_CHANNEL:
3857 func = tp_channel_manager_ensure_channel;
3858 suppress_handler = FALSE;
3859 break;
3860
3861 default:
3862 g_assert_not_reached ();
3863 }
3864
3865 request = channel_request_new (context, method,
3866 type, target_handle_type, target_handle, suppress_handler);
3867 g_ptr_array_add (priv->channel_requests, request);
3868
3869 for (i = 0; i < priv->channel_managers->len; i++)
3870 {
3871 TpChannelManager *manager = TP_CHANNEL_MANAGER (
3872 g_ptr_array_index (priv->channel_managers, i));
3873
3874 if (func (manager, request, requested_properties))
3875 return;
3876 }
3877
3878 /* Nobody accepted the request */
3879 tp_dbus_g_method_return_not_implemented (context);
3880 request->context = NULL;
3881
3882 g_ptr_array_remove (priv->channel_requests, request);
3883 channel_request_free (request);
3884 }
3885
3886
3887 static void
conn_requests_create_channel(TpSvcConnectionInterfaceRequests * svc,GHashTable * requested_properties,DBusGMethodInvocation * context)3888 conn_requests_create_channel (TpSvcConnectionInterfaceRequests *svc,
3889 GHashTable *requested_properties,
3890 DBusGMethodInvocation *context)
3891 {
3892 TpBaseConnection *self = TP_BASE_CONNECTION (svc);
3893
3894 conn_requests_requestotron (self, requested_properties,
3895 METHOD_CREATE_CHANNEL, context);
3896 }
3897
3898
3899 static void
conn_requests_ensure_channel(TpSvcConnectionInterfaceRequests * svc,GHashTable * requested_properties,DBusGMethodInvocation * context)3900 conn_requests_ensure_channel (TpSvcConnectionInterfaceRequests *svc,
3901 GHashTable *requested_properties,
3902 DBusGMethodInvocation *context)
3903 {
3904 TpBaseConnection *self = TP_BASE_CONNECTION (svc);
3905
3906 conn_requests_requestotron (self, requested_properties,
3907 METHOD_ENSURE_CHANNEL, context);
3908 }
3909
3910
3911 static void
requests_iface_init(gpointer g_iface,gpointer iface_data G_GNUC_UNUSED)3912 requests_iface_init (gpointer g_iface,
3913 gpointer iface_data G_GNUC_UNUSED)
3914 {
3915 TpSvcConnectionInterfaceRequestsClass *iface = g_iface;
3916
3917 #define IMPLEMENT(x) \
3918 tp_svc_connection_interface_requests_implement_##x (\
3919 iface, conn_requests_##x)
3920 IMPLEMENT (create_channel);
3921 IMPLEMENT (ensure_channel);
3922 #undef IMPLEMENT
3923 }
3924
3925
3926 /**
3927 * tp_base_connection_channel_manager_iter_init: (skip)
3928 * @iter: an uninitialized #TpChannelManagerIter
3929 * @self: a connection
3930 *
3931 * Initializes an iterator over the #TpChannelManager objects known to
3932 * @self. It is intended to be used as followed:
3933 *
3934 * <informalexample><programlisting>
3935 * TpChannelManagerIter iter;
3936 * TpChannelManager *manager;
3937 *
3938 * tp_base_connection_channel_manager_iter_init (&iter, base_conn);
3939 * while (tp_base_connection_channel_manager_iter_next (&iter, &manager))
3940 * {
3941 * ...do something with manager...
3942 * }
3943 * </programlisting></informalexample>
3944 *
3945 * Since: 0.7.15
3946 */
3947 void
tp_base_connection_channel_manager_iter_init(TpChannelManagerIter * iter,TpBaseConnection * self)3948 tp_base_connection_channel_manager_iter_init (TpChannelManagerIter *iter,
3949 TpBaseConnection *self)
3950 {
3951 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
3952
3953 iter->self = self;
3954 iter->index = 0;
3955 }
3956
3957
3958 /**
3959 * tp_base_connection_channel_manager_iter_next: (skip)
3960 * @iter: an initialized #TpChannelManagerIter
3961 * @manager_out: a location to store the channel manager, or %NULL.
3962 *
3963 * Advances @iter, and retrieves the #TpChannelManager it now points to. If
3964 * there are no more channel managers, @manager_out is not set and %FALSE is
3965 * returned.
3966 *
3967 * Returns: %FALSE if there are no more channel managers; else %TRUE.
3968 *
3969 * Since: 0.7.15
3970 */
3971 gboolean
tp_base_connection_channel_manager_iter_next(TpChannelManagerIter * iter,TpChannelManager ** manager_out)3972 tp_base_connection_channel_manager_iter_next (TpChannelManagerIter *iter,
3973 TpChannelManager **manager_out)
3974 {
3975 TpBaseConnectionPrivate *priv;
3976
3977 /* Check the caller initialized the iterator properly. */
3978 g_assert (TP_IS_BASE_CONNECTION (iter->self));
3979
3980 priv = iter->self->priv;
3981
3982 /* Be noisy if something's gone really wrong */
3983 g_return_val_if_fail (iter->index <= priv->channel_managers->len, FALSE);
3984
3985 if (iter->index == priv->channel_managers->len)
3986 return FALSE;
3987
3988 if (manager_out != NULL)
3989 *manager_out = TP_CHANNEL_MANAGER (
3990 g_ptr_array_index (priv->channel_managers, iter->index));
3991
3992 iter->index++;
3993 return TRUE;
3994 }
3995
3996
3997 static void
tp_base_connection_fill_contact_attributes(GObject * obj,const GArray * contacts,GHashTable * attributes_hash)3998 tp_base_connection_fill_contact_attributes (GObject *obj,
3999 const GArray *contacts, GHashTable *attributes_hash)
4000 {
4001 TpBaseConnection *self = TP_BASE_CONNECTION (obj);
4002 TpBaseConnectionPrivate *priv = self->priv;
4003 guint i;
4004
4005 for (i = 0; i < contacts->len; i++)
4006 {
4007 TpHandle handle;
4008 const gchar *tmp;
4009
4010 handle = g_array_index (contacts, TpHandle, i);
4011 tmp = tp_handle_inspect (priv->handles[TP_HANDLE_TYPE_CONTACT], handle);
4012 g_assert (tmp != NULL);
4013
4014 tp_contacts_mixin_set_contact_attribute (attributes_hash,
4015 handle, TP_TOKEN_CONNECTION_CONTACT_ID,
4016 tp_g_value_slice_new_string (tmp));
4017 }
4018 }
4019
4020 /**
4021 * tp_base_connection_register_with_contacts_mixin: (skip)
4022 * @self: An instance of the #TpBaseConnections that uses the Contacts
4023 * mixin
4024 *
4025 * Register the Connection interface with the Contacts interface to make it
4026 * inspectable. The Contacts mixin should be initialized before this function
4027 * is called
4028 */
4029 void
tp_base_connection_register_with_contacts_mixin(TpBaseConnection * self)4030 tp_base_connection_register_with_contacts_mixin (TpBaseConnection *self)
4031 {
4032 g_return_if_fail (TP_IS_BASE_CONNECTION (self));
4033
4034 tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self),
4035 TP_IFACE_CONNECTION,
4036 tp_base_connection_fill_contact_attributes);
4037 }
4038
4039 /**
4040 * tp_base_connection_get_dbus_daemon: (skip)
4041 * @self: the connection manager
4042 *
4043 * <!-- -->
4044 *
4045 * Returns: (transfer none): the value of the
4046 * #TpBaseConnectionManager:dbus-daemon property. The caller must reference
4047 * the returned object with g_object_ref() if it will be kept.
4048 *
4049 * Since: 0.11.3
4050 */
4051 TpDBusDaemon *
tp_base_connection_get_dbus_daemon(TpBaseConnection * self)4052 tp_base_connection_get_dbus_daemon (TpBaseConnection *self)
4053 {
4054 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), NULL);
4055
4056 return self->priv->bus_proxy;
4057 }
4058
4059 gpointer
_tp_base_connection_find_channel_manager(TpBaseConnection * self,GType type)4060 _tp_base_connection_find_channel_manager (TpBaseConnection *self,
4061 GType type)
4062 {
4063 guint i;
4064
4065 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), NULL);
4066
4067 for (i = 0; i < self->priv->channel_managers->len; i++)
4068 {
4069 gpointer manager = g_ptr_array_index (self->priv->channel_managers, i);
4070
4071 if (g_type_is_a (G_OBJECT_TYPE (manager), type))
4072 {
4073 return manager;
4074 }
4075 }
4076
4077 return NULL;
4078 }
4079
4080 /**
4081 * tp_base_connection_get_bus_name:
4082 * @self: the connection
4083 *
4084 * Return the bus name starting with %TP_CONN_BUS_NAME_BASE that represents
4085 * this connection on D-Bus.
4086 *
4087 * The returned string belongs to the #TpBaseConnection and must be copied
4088 * by the caller if it will be kept.
4089 *
4090 * If this connection has never been present on D-Bus
4091 * (tp_base_connection_register() has never been called), return %NULL
4092 * instead.
4093 *
4094 * Returns: (allow-none) (transfer none): the bus name of this connection,
4095 * or %NULL
4096 * Since: 0.19.1
4097 */
4098 const gchar *
tp_base_connection_get_bus_name(TpBaseConnection * self)4099 tp_base_connection_get_bus_name (TpBaseConnection *self)
4100 {
4101 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), NULL);
4102
4103 return self->bus_name;
4104 }
4105
4106 /**
4107 * tp_base_connection_get_object_path:
4108 * @self: the connection
4109 *
4110 * Return the object path starting with %TP_CONN_OBJECT_PATH_BASE that
4111 * represents this connection on D-Bus.
4112 *
4113 * The returned string belongs to the #TpBaseConnection and must be copied
4114 * by the caller if it will be kept.
4115 *
4116 * If this connection has never been present on D-Bus
4117 * (tp_base_connection_register() has never been called), return %NULL
4118 * instead.
4119 *
4120 * Returns: (allow-none) (transfer none): the object path of this connection,
4121 * or %NULL
4122 * Since: 0.19.1
4123 */
4124 const gchar *
tp_base_connection_get_object_path(TpBaseConnection * self)4125 tp_base_connection_get_object_path (TpBaseConnection *self)
4126 {
4127 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), NULL);
4128
4129 return self->object_path;
4130 }
4131
4132 /**
4133 * tp_base_connection_get_account_path_suffix:
4134 * @self: the connection
4135 *
4136 * <!-- -->
4137 *
4138 * Returns: the same value has the #TpBaseConnection:account-path-suffix
4139 * property.
4140 * Since: 0.23.2
4141 */
4142 const gchar *
tp_base_connection_get_account_path_suffix(TpBaseConnection * self)4143 tp_base_connection_get_account_path_suffix (TpBaseConnection *self)
4144 {
4145 g_return_val_if_fail (TP_IS_BASE_CONNECTION (self), NULL);
4146
4147 return self->priv->account_path_suffix;
4148 }
4149