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 (&amp;iter, base_conn);
3939  * while (tp_base_connection_channel_manager_iter_next (&amp;iter, &amp;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