1 /*
2  * connection.c - proxy for a Telepathy connection
3  *
4  * Copyright (C) 2007-2011 Collabora Ltd. <http://www.collabora.co.uk/>
5  * Copyright (C) 2007-2011 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 #include "config.h"
23 
24 #include "telepathy-glib/connection.h"
25 
26 #include <string.h>
27 
28 #include <dbus/dbus-protocol.h>
29 
30 #include <telepathy-glib/connection-manager.h>
31 #include <telepathy-glib/dbus.h>
32 #include <telepathy-glib/defs.h>
33 #include <telepathy-glib/enums.h>
34 #include <telepathy-glib/errors.h>
35 #include <telepathy-glib/gtypes.h>
36 #include <telepathy-glib/handle.h>
37 #include <telepathy-glib/interfaces.h>
38 #include <telepathy-glib/proxy-subclass.h>
39 #include <telepathy-glib/util.h>
40 
41 #define DEBUG_FLAG TP_DEBUG_CONNECTION
42 #include "telepathy-glib/capabilities-internal.h"
43 #include "telepathy-glib/connection-internal.h"
44 #include "telepathy-glib/connection-contact-list.h"
45 #include "telepathy-glib/dbus-internal.h"
46 #include "telepathy-glib/debug-internal.h"
47 #include "telepathy-glib/proxy-internal.h"
48 #include "telepathy-glib/simple-client-factory-internal.h"
49 #include "telepathy-glib/contact-internal.h"
50 #include "telepathy-glib/util-internal.h"
51 #include "telepathy-glib/variant-util-internal.h"
52 
53 #include "_gen/tp-cli-connection-body.h"
54 
55 /**
56  * SECTION:connection
57  * @title: TpConnection
58  * @short_description: proxy object for a Telepathy connection
59  * @see_also: #TpConnectionManager, #TpChannel
60  *
61  * #TpConnection objects represent Telepathy instant messaging connections
62  * accessed via D-Bus.
63  *
64  * #TpConnection objects should be obtained from a #TpAccount, unless you
65  * are implementing a lower-level Telepathy component (such as the account
66  * manager service itself).
67  *
68  * Since 0.16, #TpConnection always has a non-%NULL #TpProxy:factory, and its
69  * #TpProxy:factory will be propagated to its #TpChannel objects
70  * (if any). Similarly, the #TpProxy:factory<!-- -->'s features
71  * will be used for #TpContact objects.
72  * If a #TpConnection is created without going via the
73  * #TpAccount or specifying a #TpProxy:factory, the default
74  * is to use a new #TpAutomaticClientFactory.
75  *
76  * Since: 0.7.1
77  */
78 
79 /**
80  * TP_CONNECTION_FEATURE_CORE:
81  *
82  * Expands to a call to a function that returns a quark for the "core" feature
83  * on a #TpConnection.
84  *
85  * When this feature is prepared, the basic properties of the Connection have
86  * been retrieved and are available for use, and change-notification has been
87  * set up for those that can change.
88  *
89  * Specifically, this implies that:
90  *
91  * <itemizedlist>
92  *  <listitem>#TpConnection:status has a value other than
93  *    %TP_UNKNOWN_CONNECTION_STATUS, and #TpConnection:status-reason is
94  *    the reason for changing to that status</listitem>
95  *  <listitem>interfaces that are always available have been added to the
96  *    #TpProxy:interfaces (although the set of interfaces is not guaranteed
97  *    to be complete until #TpConnection:status becomes
98  *    %TP_CONNECTION_STATUS_CONNECTED))</listitem>
99  * </itemizedlist>
100  *
101  * <note>
102  *  <title>prepared does not imply connected</title>
103  *  <para>Unlike the older #TpConnection:connection-ready mechanism, this
104  *    feature does not imply that the connection has successfully connected.
105  *    It only implies that an initial status (disconnected, connecting or
106  *    connected) has been discovered. %TP_CONNECTION_FEATURE_CONNECTED
107  *    is the closest equivalent of #TpConnection:connection-ready.</para>
108  * </note>
109  *
110  * One can ask for a feature to be prepared using the
111  * tp_proxy_prepare_async() function, and waiting for it to callback.
112  *
113  * Since: 0.11.3
114  */
115 
116 GQuark
tp_connection_get_feature_quark_core(void)117 tp_connection_get_feature_quark_core (void)
118 {
119   return g_quark_from_static_string ("tp-connection-feature-core");
120 }
121 
122 /**
123  * TP_CONNECTION_FEATURE_CONNECTED:
124  *
125  * Expands to a call to a function that returns a #GQuark representing the
126  * "connected" feature.
127  *
128  * When this feature is prepared, it means that the connection has become
129  * connected to the appropriate real-time communications service, and all
130  * information requested via other features has been updated accordingly.
131  * In particular, the following aspects of %TP_CONNECTION_FEATURE_CORE
132  * will be up to date:
133  *
134  * <itemizedlist>
135  *  <listitem>#TpConnection:status is
136  *    %TP_CONNECTION_STATUS_CONNECTED</listitem>
137  *  <listitem>#TpConnection:self-handle is valid and non-zero</listitem>
138  *  <listitem>#TpConnection:self-contact is non-%NULL</listitem>
139  *  <listitem>all interfaces have been added to the set of
140  *    #TpProxy:interfaces, and that set will not change again</listitem>
141  * </itemizedlist>
142  *
143  * <note>
144  *   <title>Someone still has to call Connect()</title>
145  *   <para>Requesting this feature via tp_proxy_prepare_async() means that
146  *     you want to wait for the connection to connect, but it doesn't actually
147  *     start the process of connecting. For connections associated with
148  *     a #TpAccount, the account manager service is responsible for
149  *     doing that, but if you are constructing connections directly
150  *     (e.g. if you are implementing an account manager), you must
151  *     tp_cli_connection_call_connect() separately.
152  *     </para>
153  * </note>
154  *
155  * One can ask for a feature to be prepared using the
156  * tp_proxy_prepare_async() function, and waiting for it to callback.
157  *
158  * Since: 0.11.3
159  */
160 
161 GQuark
tp_connection_get_feature_quark_connected(void)162 tp_connection_get_feature_quark_connected (void)
163 {
164   return g_quark_from_static_string ("tp-connection-feature-connected");
165 }
166 
167 /**
168  * TP_CONNECTION_FEATURE_CAPABILITIES:
169  *
170  * Expands to a call to a function that returns a #GQuark representing the
171  * "capabilities" feature.
172  *
173  * When this feature is prepared, the Requests.RequestableChannelClasses
174  * property of the Connection has been retrieved.
175  * In particular, the %TpConnection:capabilities property has been set.
176  *
177  * One can ask for a feature to be prepared using the
178  * tp_proxy_prepare_async() function, and waiting for it to callback.
179  *
180  * Since: 0.11.3
181  */
182 
183 GQuark
tp_connection_get_feature_quark_capabilities(void)184 tp_connection_get_feature_quark_capabilities (void)
185 {
186   return g_quark_from_static_string ("tp-connection-feature-capabilities");
187 }
188 
189 /**
190  * TP_CONNECTION_FEATURE_BALANCE:
191  *
192  * Expands to a call to a function that returns a #GQuark representing the
193  * "balance" feature.
194  *
195  * When this feature is prepared, the Balance.AccountBalance and
196  * Balance.ManageCreditURI properties of the Connection have been retrieved.
197  * In particular, the %TpConnection:balance, %TpConnection:balance-scale,
198  * %TpConnection:balance-currency and %TpConnection:balance-uri properties
199  * have been set and the TpConnection::balance-changed: will be emitted
200  * when they are changed.
201  *
202  * One can ask for a feature to be prepared using the
203  * tp_proxy_prepare_async() function, and waiting for it to callback.
204  *
205  * Since: 0.15.1
206  */
207 
208 GQuark
tp_connection_get_feature_quark_balance(void)209 tp_connection_get_feature_quark_balance (void)
210 {
211   return g_quark_from_static_string ("tp-connection-feature-balance");
212 }
213 
214 /**
215  * TP_ERRORS_DISCONNECTED:
216  *
217  * #GError domain representing a Telepathy connection becoming disconnected.
218  * The @code in a #GError with this domain must be a member of
219  * #TpConnectionStatusReason.
220  *
221  * This macro expands to a function call returning a #GQuark.
222  *
223  * Since 0.7.24, this error domain is only used if a connection manager emits
224  * a #TpConnectionStatusReason not known to telepathy-glib.
225  *
226  * Since: 0.7.1
227  */
228 GQuark
tp_errors_disconnected_quark(void)229 tp_errors_disconnected_quark (void)
230 {
231   static GQuark q = 0;
232 
233   if (q == 0)
234     q = g_quark_from_static_string ("tp_errors_disconnected_quark");
235 
236   return q;
237 }
238 
239 /**
240  * TP_UNKNOWN_CONNECTION_STATUS:
241  *
242  * An invalid connection status used in #TpConnection to indicate that the
243  * status has not yet been discovered.
244  *
245  * Since: 0.7.1
246  */
247 
248 /**
249  * TpConnectionClass:
250  * @parent_class: the parent class
251  *
252  * The class of a #TpConnection. In addition to @parent_class there are four
253  * pointers reserved for possible future use.
254  *
255  * (Changed in 0.7.12: the layout of the structure is visible, allowing
256  * subclassing.)
257  *
258  * Since: 0.7.1
259  */
260 
261 /**
262  * TpConnection:
263  *
264  * A proxy object for a Telepathy connection. There are no interesting
265  * public struct fields.
266  *
267  * (Changed in 0.7.12: the layout of the structure is visible, allowing
268  * subclassing.)
269  *
270  * Since: 0.7.1
271  */
272 
273 /* properties */
274 enum
275 {
276   PROP_STATUS = 1,
277   PROP_STATUS_REASON,
278   PROP_CONNECTION_MANAGER_NAME,
279   PROP_CM_NAME,
280   PROP_PROTOCOL_NAME,
281   PROP_CONNECTION_READY,
282   PROP_SELF_CONTACT,
283   PROP_SELF_HANDLE,
284   PROP_CAPABILITIES,
285   PROP_BALANCE,
286   PROP_BALANCE_SCALE,
287   PROP_BALANCE_CURRENCY,
288   PROP_BALANCE_URI,
289   PROP_CONTACT_LIST_STATE,
290   PROP_CONTACT_LIST_PERSISTS,
291   PROP_CAN_CHANGE_CONTACT_LIST,
292   PROP_REQUEST_USES_MESSAGE,
293   PROP_DISJOINT_GROUPS,
294   PROP_GROUP_STORAGE,
295   PROP_CONTACT_GROUPS,
296   PROP_CAN_REPORT_ABUSIVE,
297   PROP_BLOCKED_CONTACTS,
298   N_PROPS
299 };
300 
301 enum {
302   SIGNAL_BALANCE_CHANGED,
303   SIGNAL_GROUPS_CREATED,
304   SIGNAL_GROUPS_REMOVED,
305   SIGNAL_GROUP_RENAMED,
306   SIGNAL_CONTACT_LIST_CHANGED,
307   SIGNAL_BLOCKED_CONTACTS_CHANGED,
308   N_SIGNALS
309 };
310 
311 static guint signals[N_SIGNALS] = { 0 };
312 
G_DEFINE_TYPE(TpConnection,tp_connection,TP_TYPE_PROXY)313 G_DEFINE_TYPE (TpConnection,
314     tp_connection,
315     TP_TYPE_PROXY)
316 
317 static void
318 tp_connection_get_property (GObject *object,
319                             guint property_id,
320                             GValue *value,
321                             GParamSpec *pspec)
322 {
323   TpConnection *self = TP_CONNECTION (object);
324 
325   /* Deprecated properties uses deprecated getters */
326   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
327   switch (property_id)
328     {
329     case PROP_CONNECTION_MANAGER_NAME:
330       g_value_set_string (value, self->priv->cm_name);
331       break;
332     case PROP_CM_NAME:
333       g_value_set_string (value, self->priv->cm_name);
334       break;
335     case PROP_PROTOCOL_NAME:
336       g_value_set_string (value, self->priv->proto_name);
337       break;
338     case PROP_CONNECTION_READY:
339       g_value_set_boolean (value, self->priv->ready);
340       break;
341     case PROP_STATUS:
342       g_value_set_uint (value, self->priv->status);
343       break;
344     case PROP_STATUS_REASON:
345       g_value_set_uint (value, self->priv->status_reason);
346       break;
347     case PROP_SELF_CONTACT:
348       g_value_set_object (value, tp_connection_get_self_contact (self));
349       break;
350     case PROP_SELF_HANDLE:
351       g_value_set_uint (value, tp_connection_get_self_handle (self));
352       break;
353     case PROP_CAPABILITIES:
354       g_value_set_object (value, self->priv->capabilities);
355       break;
356     case PROP_BALANCE:
357       g_value_set_int (value, self->priv->balance);
358       break;
359     case PROP_BALANCE_SCALE:
360       g_value_set_uint (value, self->priv->balance_scale);
361       break;
362     case PROP_BALANCE_CURRENCY:
363       g_value_set_string (value, self->priv->balance_currency);
364       break;
365     case PROP_BALANCE_URI:
366       g_value_set_string (value, self->priv->balance_uri);
367       break;
368     case PROP_CONTACT_LIST_STATE:
369       g_value_set_uint (value, self->priv->contact_list_state);
370       break;
371     case PROP_CONTACT_LIST_PERSISTS:
372       g_value_set_boolean (value, self->priv->contact_list_persists);
373       break;
374     case PROP_CAN_CHANGE_CONTACT_LIST:
375       g_value_set_boolean (value, self->priv->can_change_contact_list);
376       break;
377     case PROP_REQUEST_USES_MESSAGE:
378       g_value_set_boolean (value, self->priv->request_uses_message);
379       break;
380     case PROP_DISJOINT_GROUPS:
381       g_value_set_boolean (value, self->priv->disjoint_groups);
382       break;
383     case PROP_GROUP_STORAGE:
384       g_value_set_uint (value, self->priv->group_storage);
385       break;
386     case PROP_CONTACT_GROUPS:
387       g_value_set_boxed (value, self->priv->contact_groups);
388       break;
389     case PROP_CAN_REPORT_ABUSIVE:
390       g_value_set_boolean (value, tp_connection_can_report_abusive (self));
391       break;
392     case PROP_BLOCKED_CONTACTS:
393       g_value_set_boxed (value, tp_connection_get_blocked_contacts (self));
394       break;
395     default:
396       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
397       break;
398     }
399   G_GNUC_END_IGNORE_DEPRECATIONS
400 }
401 
402 static void
tp_connection_unpack_balance(TpConnection * self,GValueArray * balance_s)403 tp_connection_unpack_balance (TpConnection *self,
404     GValueArray *balance_s)
405 {
406   gint balance = 0;
407   guint scale = G_MAXUINT32;
408   const char *currency = "";
409   gboolean changed = FALSE;
410 
411   if (balance_s == NULL)
412     goto finally;
413 
414   tp_value_array_unpack (balance_s, 3,
415       &balance, &scale, &currency);
416 
417 finally:
418 
419   g_object_freeze_notify ((GObject *) self);
420 
421   if (self->priv->balance != balance)
422     {
423       self->priv->balance = balance;
424       g_object_notify ((GObject *) self, "balance");
425       changed = TRUE;
426     }
427 
428   if (self->priv->balance_scale != scale)
429     {
430       self->priv->balance_scale = scale;
431       g_object_notify ((GObject *) self, "balance-scale");
432       changed = TRUE;
433     }
434 
435   if (tp_strdiff (self->priv->balance_currency, currency))
436     {
437       g_free (self->priv->balance_currency);
438       self->priv->balance_currency = g_strdup (currency);
439       g_object_notify ((GObject *) self, "balance-currency");
440       changed = TRUE;
441     }
442 
443   g_object_thaw_notify ((GObject *) self);
444 
445   if (changed)
446     {
447       g_signal_emit (self, signals[SIGNAL_BALANCE_CHANGED], 0,
448           balance, scale, currency);
449     }
450 }
451 
452 static void
tp_connection_get_balance_cb(TpProxy * proxy,GHashTable * props,const GError * in_error,gpointer user_data,GObject * weak_obj)453 tp_connection_get_balance_cb (TpProxy *proxy,
454     GHashTable *props,
455     const GError *in_error,
456     gpointer user_data,
457     GObject *weak_obj)
458 {
459   TpConnection *self = (TpConnection *) proxy;
460   GSimpleAsyncResult *result = user_data;
461   GValueArray *balance = NULL;
462 
463   if (in_error != NULL)
464     {
465       DEBUG ("Failed to get Balance properties: %s", in_error->message);
466       g_simple_async_result_set_from_error (result, in_error);
467       goto finally;
468     }
469 
470   balance =
471     tp_asv_get_boxed (props, "AccountBalance", TP_STRUCT_TYPE_CURRENCY_AMOUNT);
472   self->priv->balance_uri =
473     g_strdup (tp_asv_get_string (props, "ManageCreditURI"));
474 
475   g_object_freeze_notify ((GObject *) self);
476 
477   tp_connection_unpack_balance (self, balance);
478 
479   _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_BALANCE,
480       TRUE);
481 
482   g_object_notify ((GObject *) self, "balance-uri");
483 
484   g_object_thaw_notify ((GObject *) self);
485 
486 finally:
487   g_simple_async_result_complete_in_idle (result);
488 }
489 
490 static void
tp_connection_balance_changed_cb(TpConnection * self,const GValueArray * balance,gpointer user_data,GObject * weak_obj)491 tp_connection_balance_changed_cb (TpConnection *self,
492     const GValueArray *balance,
493     gpointer user_data,
494     GObject *weak_obj)
495 {
496   tp_connection_unpack_balance (self, (GValueArray *) balance);
497 }
498 
499 static void
tp_connection_prepare_balance_async(TpProxy * proxy,const TpProxyFeature * feature,GAsyncReadyCallback callback,gpointer user_data)500 tp_connection_prepare_balance_async (TpProxy *proxy,
501     const TpProxyFeature *feature,
502     GAsyncReadyCallback callback,
503     gpointer user_data)
504 {
505   TpConnection *self = (TpConnection *) proxy;
506   GSimpleAsyncResult *result;
507 
508   result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
509       tp_connection_prepare_balance_async);
510 
511   g_assert (self->priv->balance_currency == NULL);
512 
513   tp_cli_dbus_properties_call_get_all (self, -1,
514       TP_IFACE_CONNECTION_INTERFACE_BALANCE,
515       tp_connection_get_balance_cb, result, g_object_unref, NULL);
516 
517   tp_cli_connection_interface_balance_connect_to_balance_changed (self,
518       tp_connection_balance_changed_cb,
519       NULL, NULL, NULL, NULL);
520 }
521 
522 static void
tp_connection_get_rcc_cb(TpProxy * proxy,const GValue * value,const GError * error,gpointer user_data,GObject * weak_object)523 tp_connection_get_rcc_cb (TpProxy *proxy,
524     const GValue *value,
525     const GError *error,
526     gpointer user_data,
527     GObject *weak_object)
528 {
529   TpConnection *self = (TpConnection *) proxy;
530   GSimpleAsyncResult *result;
531 
532   if (error != NULL)
533     {
534       DEBUG ("Failed to get RequestableChannelClasses property, using an "
535           "empty set: %s", error->message);
536 
537       /* it's NULL-safe */
538       self->priv->capabilities = _tp_capabilities_new (NULL, FALSE);
539       goto finally;
540     }
541 
542   g_assert (self->priv->capabilities == NULL);
543 
544   if (!G_VALUE_HOLDS (value, TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST))
545     {
546       DEBUG ("RequestableChannelClasses is not of type a(a{sv}as), using an "
547           "empty set: %s", G_VALUE_TYPE_NAME (value));
548 
549       /* it's NULL-safe */
550       self->priv->capabilities = _tp_capabilities_new (NULL, FALSE);
551       goto finally;
552     }
553 
554   DEBUG ("CAPABILITIES ready");
555 
556   self->priv->capabilities = _tp_capabilities_new (g_value_get_boxed (value),
557       FALSE);
558 
559 finally:
560   while ((result = g_queue_pop_head (&self->priv->capabilities_queue)) != NULL)
561     {
562       g_simple_async_result_complete_in_idle (result);
563       g_object_unref (result);
564     }
565 
566   g_object_notify ((GObject *) self, "capabilities");
567 }
568 
569 static void
_tp_connection_do_get_capabilities_async(TpConnection * self,GSimpleAsyncResult * result)570 _tp_connection_do_get_capabilities_async (TpConnection *self,
571     GSimpleAsyncResult *result)
572 {
573   if (self->priv->capabilities != NULL)
574     {
575       /* been there, done that, bored now */
576       g_simple_async_result_complete_in_idle (result);
577       g_object_unref (result);
578     }
579   else
580     {
581       g_queue_push_tail (&self->priv->capabilities_queue, result);
582       if (g_queue_get_length (&self->priv->capabilities_queue) == 1)
583         {
584           DEBUG ("%s: Retrieving capabilities",
585             tp_proxy_get_object_path (self));
586 
587           /* We don't check whether we actually have this interface here. The
588            * quark is dbus properties quark is guaranteed to be on every
589            * TpProxy and only very very old CMs won't have Requests, in case
590            * someone still has such a relic we'll we'll just handle it when
591            * they reply to us with an error */
592           tp_cli_dbus_properties_call_get (self, -1,
593             TP_IFACE_CONNECTION_INTERFACE_REQUESTS,
594               "RequestableChannelClasses",
595             tp_connection_get_rcc_cb, NULL, NULL, NULL);
596         }
597     }
598 }
599 
600 void
_tp_connection_get_capabilities_async(TpConnection * self,GAsyncReadyCallback callback,gpointer user_data)601 _tp_connection_get_capabilities_async (TpConnection *self,
602   GAsyncReadyCallback callback,
603   gpointer user_data)
604 {
605   GSimpleAsyncResult *result;
606 
607   result = g_simple_async_result_new ((GObject *) self, callback, user_data,
608       _tp_connection_get_capabilities_async);
609   _tp_connection_do_get_capabilities_async (self, result);
610 }
611 
612 gboolean
_tp_connection_get_capabilities_finish(TpConnection * self,GAsyncResult * result,GError ** error)613 _tp_connection_get_capabilities_finish (TpConnection *self,
614   GAsyncResult *result, GError **error)
615 {
616   _tp_implement_finish_void (self, _tp_connection_get_capabilities_async);
617 }
618 
619 static void
tp_connection_prepare_capabilities_async(TpProxy * proxy,const TpProxyFeature * feature,GAsyncReadyCallback callback,gpointer user_data)620 tp_connection_prepare_capabilities_async (TpProxy *proxy,
621     const TpProxyFeature *feature,
622     GAsyncReadyCallback callback,
623     gpointer user_data)
624 {
625   TpConnection *self = (TpConnection *) proxy;
626   GSimpleAsyncResult *result;
627 
628   DEBUG ("%s: Preparing capabilities", tp_proxy_get_object_path (self));
629 
630   result = g_simple_async_result_new ((GObject *) self, callback, user_data,
631       tp_connection_prepare_capabilities_async);
632 
633   _tp_connection_do_get_capabilities_async (self, result);
634 }
635 
636 static void
signal_connected(TpConnection * self)637 signal_connected (TpConnection *self)
638 {
639   /* we shouldn't have gone to status CONNECTED for any reason
640    * that isn't REQUESTED :-) */
641   DEBUG ("%s (%p): CORE and CONNECTED ready",
642     tp_proxy_get_object_path (self), self);
643   self->priv->status = TP_CONNECTION_STATUS_CONNECTED;
644   self->priv->status_reason = TP_CONNECTION_STATUS_REASON_REQUESTED;
645   self->priv->ready = TRUE;
646 
647   _tp_proxy_set_feature_prepared ((TpProxy *) self,
648       TP_CONNECTION_FEATURE_CONNECTED, TRUE);
649   _tp_proxy_set_feature_prepared ((TpProxy *) self,
650       TP_CONNECTION_FEATURE_CORE, TRUE);
651 
652   g_object_notify ((GObject *) self, "status");
653   g_object_notify ((GObject *) self, "status-reason");
654   g_object_notify ((GObject *) self, "connection-ready");
655 }
656 
657 static void
will_announced_connected_cb(GObject * source,GAsyncResult * result,gpointer user_data)658 will_announced_connected_cb (GObject *source,
659     GAsyncResult *result,
660     gpointer user_data)
661 {
662   TpConnection *self = (TpConnection *) source;
663   GError *error = NULL;
664 
665   if (!_tp_proxy_will_announce_connected_finish ((TpProxy *) self, result,
666         &error))
667     {
668       DEBUG ("_tp_connection_prepare_contact_info_async failed: %s",
669           error->message);
670 
671       g_error_free (error);
672     }
673 
674   if (tp_proxy_get_invalidated (self) != NULL)
675     {
676       DEBUG ("Connection has been invalidated; we're done");
677       return;
678     }
679 
680   signal_connected (self);
681 }
682 
683 static void
tp_connection_continue_introspection(TpConnection * self)684 tp_connection_continue_introspection (TpConnection *self)
685 {
686   if (tp_proxy_get_invalidated (self) != NULL)
687     {
688       DEBUG ("Already invalidated: not becoming ready");
689       return;
690     }
691 
692   if (self->priv->introspect_needed == NULL)
693     {
694       if (!self->priv->introspecting_after_connected)
695         {
696           /* Introspection will restart when we become CONNECTED */
697           DEBUG ("CORE ready, but not CONNECTED");
698           _tp_proxy_set_feature_prepared ((TpProxy *) self,
699             TP_CONNECTION_FEATURE_CORE, TRUE);
700           return;
701         }
702 
703       /* We'll announce CONNECTED state soon, but first give a chance to
704        * prepared feature to be updated, if needed */
705       _tp_proxy_will_announce_connected_async ((TpProxy *) self,
706           will_announced_connected_cb, NULL);
707     }
708   else
709     {
710       TpConnectionProc next = self->priv->introspect_needed->data;
711 
712       self->priv->introspect_needed = g_list_delete_link (
713           self->priv->introspect_needed,
714           self->priv->introspect_needed);
715       next (self);
716     }
717 }
718 
719 static void
got_contact_attribute_interfaces(TpProxy * proxy,const GValue * value,const GError * error,gpointer user_data G_GNUC_UNUSED,GObject * weak_object G_GNUC_UNUSED)720 got_contact_attribute_interfaces (TpProxy *proxy,
721                                   const GValue *value,
722                                   const GError *error,
723                                   gpointer user_data G_GNUC_UNUSED,
724                                   GObject *weak_object G_GNUC_UNUSED)
725 {
726   TpConnection *self = TP_CONNECTION (proxy);
727   GArray *arr;
728 
729   g_assert (self->priv->introspection_call != NULL);
730   self->priv->introspection_call = NULL;
731 
732   if (error == NULL && G_VALUE_HOLDS (value, G_TYPE_STRV))
733     {
734       gchar **interfaces = g_value_get_boxed (value);
735       gchar **iter;
736 
737       arr = g_array_sized_new (FALSE, FALSE, sizeof (GQuark),
738           interfaces == NULL ? 0 : g_strv_length (interfaces));
739 
740       if (interfaces != NULL)
741         {
742           for (iter = interfaces; *iter != NULL; iter++)
743             {
744               if (tp_dbus_check_valid_interface_name (*iter, NULL))
745                 {
746                   GQuark q = g_quark_from_string (*iter);
747 
748                   DEBUG ("%p: ContactAttributeInterfaces has %s", self,
749                       *iter);
750                   g_array_append_val (arr, q);
751                 }
752               else
753                 {
754                   DEBUG ("%p: ignoring invalid interface: %s", self,
755                       *iter);
756                 }
757             }
758         }
759     }
760   else
761     {
762       if (error == NULL)
763         DEBUG ("%p: ContactAttributeInterfaces had wrong type %s, "
764             "ignoring", self, G_VALUE_TYPE_NAME (value));
765       else
766         DEBUG ("%p: Get(Contacts, ContactAttributeInterfaces) failed with "
767             "%s %d: %s", self, g_quark_to_string (error->domain), error->code,
768             error->message);
769 
770       arr = g_array_sized_new (FALSE, FALSE, sizeof (GQuark), 0);
771     }
772 
773   g_assert (self->priv->contact_attribute_interfaces == NULL);
774   self->priv->contact_attribute_interfaces = arr;
775   self->priv->ready_enough_for_contacts = TRUE;
776 
777   tp_connection_continue_introspection (self);
778 }
779 
780 static void
introspect_contacts(TpConnection * self)781 introspect_contacts (TpConnection *self)
782 {
783   /* "This cannot change during the lifetime of the Connection." -- tp-spec */
784   if (self->priv->contact_attribute_interfaces != NULL)
785     {
786       tp_connection_continue_introspection (self);
787       return;
788     }
789 
790   g_assert (self->priv->introspection_call == NULL);
791   self->priv->introspection_call = tp_cli_dbus_properties_call_get (self, -1,
792        TP_IFACE_CONNECTION_INTERFACE_CONTACTS, "ContactAttributeInterfaces",
793        got_contact_attribute_interfaces, NULL, NULL, NULL);
794 }
795 
796 static void
tp_connection_set_self_contact(TpConnection * self,TpContact * contact)797 tp_connection_set_self_contact (TpConnection *self,
798     TpContact *contact)
799 {
800   if (contact != self->priv->self_contact)
801     {
802       TpContact *tmp = self->priv->self_contact;
803 
804       self->priv->self_contact = g_object_ref (contact);
805       tp_clear_object (&tmp);
806       g_object_notify ((GObject *) self, "self-contact");
807       g_object_notify ((GObject *) self, "self-handle");
808     }
809 
810   if (self->priv->introspecting_self_contact)
811     {
812       self->priv->introspecting_self_contact = FALSE;
813       tp_connection_continue_introspection (self);
814     }
815 }
816 
817 static void
tp_connection_got_self_contact_cb(TpConnection * self,guint n_contacts,TpContact * const * contacts,guint n_failed,const TpHandle * failed,const GError * error,gpointer unused_data G_GNUC_UNUSED,GObject * unused_object G_GNUC_UNUSED)818 tp_connection_got_self_contact_cb (TpConnection *self,
819     guint n_contacts,
820     TpContact * const *contacts,
821     guint n_failed,
822     const TpHandle *failed,
823     const GError *error,
824     gpointer unused_data G_GNUC_UNUSED,
825     GObject *unused_object G_GNUC_UNUSED)
826 {
827   if (n_contacts != 0)
828     {
829       g_assert (n_contacts == 1);
830       g_assert (n_failed == 0);
831       g_assert (error == NULL);
832 
833       if (tp_contact_get_handle (contacts[0]) ==
834           self->priv->last_known_self_handle)
835         {
836           tp_connection_set_self_contact (self, contacts[0]);
837         }
838       else
839         {
840           DEBUG ("SelfHandle is now %u, ignoring contact object for %u",
841               self->priv->last_known_self_handle,
842               tp_contact_get_handle (contacts[0]));
843         }
844     }
845   else if (error != NULL)
846     {
847       /* Unrecoverable error: we were probably invalidated, but in case
848        * we weren't... */
849       DEBUG ("Failed to hold the handle from GetSelfHandle(): %s",
850           error->message);
851       tp_proxy_invalidate ((TpProxy *) self, error);
852     }
853   else if (n_failed == 1 && failed[0] != self->priv->last_known_self_handle)
854     {
855       /* Since we tried to make the TpContact, our self-handle has changed,
856        * so it doesn't matter that we couldn't make a TpContact for the old
857        * one - carry on and make a TpContact for the new one instead. */
858       DEBUG ("Failed to hold handle %u from GetSelfHandle(), but it's "
859           "changed to %u anyway, so never mind", failed[0],
860           self->priv->last_known_self_handle);
861     }
862   else
863     {
864       GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
865           "The handle from GetSelfHandle() was considered invalid" };
866 
867       DEBUG ("%s", e.message);
868       tp_proxy_invalidate ((TpProxy *) self, &e);
869     }
870 }
871 
872 static void
get_self_contact(TpConnection * self)873 get_self_contact (TpConnection *self)
874 {
875   TpSimpleClientFactory *factory;
876   GArray *features;
877 
878   factory = tp_proxy_get_factory (self);
879   features = tp_simple_client_factory_dup_contact_features (factory, self);
880 
881   /* FIXME: We should use tp_simple_client_factory_ensure_contact(), but that would
882    * require immortal-handles and spec change to give the self identifier. */
883   /* This relies on the special case in tp_connection_get_contacts_by_handle()
884    * which makes it start working slightly early. */
885    G_GNUC_BEGIN_IGNORE_DEPRECATIONS
886    tp_connection_get_contacts_by_handle (self,
887        1, &self->priv->last_known_self_handle,
888       features->len, (TpContactFeature *) features->data,
889       tp_connection_got_self_contact_cb, NULL, NULL, NULL);
890    G_GNUC_END_IGNORE_DEPRECATIONS
891 
892   g_array_unref (features);
893 }
894 
895 static void
introspect_self_contact(TpConnection * self)896 introspect_self_contact (TpConnection *self)
897 {
898   self->priv->introspecting_self_contact = TRUE;
899   get_self_contact (self);
900 }
901 
902 static void
got_self_handle(TpConnection * self,guint self_handle,const GError * error,gpointer user_data G_GNUC_UNUSED,GObject * user_object G_GNUC_UNUSED)903 got_self_handle (TpConnection *self,
904                  guint self_handle,
905                  const GError *error,
906                  gpointer user_data G_GNUC_UNUSED,
907                  GObject *user_object G_GNUC_UNUSED)
908 {
909   g_assert (self->priv->introspection_call != NULL);
910   self->priv->introspection_call = NULL;
911 
912   if (error != NULL)
913     {
914       DEBUG ("%p: GetSelfHandle() failed: %s", self, error->message);
915       tp_proxy_invalidate ((TpProxy *) self, error);
916       return;
917     }
918 
919   if (self_handle == 0)
920     {
921       GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
922           "GetSelfHandle() returned 0" };
923       DEBUG ("%s", e.message);
924       tp_proxy_invalidate ((TpProxy *) self, &e);
925       return;
926     }
927 
928   self->priv->last_known_self_handle = self_handle;
929   self->priv->introspect_needed = g_list_append (self->priv->introspect_needed,
930     introspect_self_contact);
931   tp_connection_continue_introspection (self);
932 }
933 
934 static void
on_self_handle_changed(TpConnection * self,guint self_handle,gpointer user_data G_GNUC_UNUSED,GObject * user_object G_GNUC_UNUSED)935 on_self_handle_changed (TpConnection *self,
936                         guint self_handle,
937                         gpointer user_data G_GNUC_UNUSED,
938                         GObject *user_object G_GNUC_UNUSED)
939 {
940   if (self_handle == 0)
941     {
942       DEBUG ("Ignoring alleged change of self-handle to %u", self_handle);
943       return;
944     }
945 
946   if (self->priv->last_known_self_handle == 0)
947     {
948       /* We're going to call GetAll(Connection) anyway, or if the CM
949        * is sufficiently deficient, GetSelfHandle(). */
950       DEBUG ("Ignoring early self-handle change to %u, we'll pick it up later",
951           self_handle);
952       return;
953     }
954 
955   DEBUG ("SelfHandleChanged to %u, I wonder what that means?", self_handle);
956   self->priv->last_known_self_handle = self_handle;
957   get_self_contact (self);
958 }
959 
960 static void
introspect_self_handle(TpConnection * self)961 introspect_self_handle (TpConnection *self)
962 {
963   if (!self->priv->introspecting_after_connected)
964     {
965       tp_connection_continue_introspection (self);
966       return;
967     }
968 
969   g_assert (self->priv->introspection_call == NULL);
970   self->priv->introspection_call = tp_cli_connection_call_get_self_handle (
971       self, -1, got_self_handle, NULL, NULL, NULL);
972 }
973 
974 /* Appending callbacks to self->priv->introspect_needed relies on this */
975 G_STATIC_ASSERT (sizeof (TpConnectionProc) <= sizeof (gpointer));
976 
977 static void
tp_connection_add_interfaces_from_introspection(TpConnection * self,const gchar ** interfaces)978 tp_connection_add_interfaces_from_introspection (TpConnection *self,
979                                                  const gchar **interfaces)
980 {
981   TpProxy *proxy = (TpProxy *) self;
982 
983   tp_proxy_add_interfaces (proxy, interfaces);
984 
985   if (tp_proxy_has_interface_by_id (proxy,
986         TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS))
987     {
988       self->priv->introspect_needed = g_list_append (
989           self->priv->introspect_needed, introspect_contacts);
990     }
991   else
992     {
993       self->priv->ready_enough_for_contacts = TRUE;
994     }
995 }
996 
997 static void
tp_connection_got_interfaces_cb(TpConnection * self,const gchar ** interfaces,const GError * error,gpointer user_data,GObject * user_object)998 tp_connection_got_interfaces_cb (TpConnection *self,
999                                  const gchar **interfaces,
1000                                  const GError *error,
1001                                  gpointer user_data,
1002                                  GObject *user_object)
1003 {
1004   g_assert (self->priv->introspection_call != NULL);
1005   self->priv->introspection_call = NULL;
1006 
1007   if (error != NULL)
1008     {
1009       DEBUG ("%p: GetInterfaces() failed, assuming no interfaces: %s",
1010           self, error->message);
1011       interfaces = NULL;
1012     }
1013 
1014   DEBUG ("%p: Introspected interfaces", self);
1015 
1016   if (tp_proxy_get_invalidated (self) != NULL)
1017     {
1018       DEBUG ("%p: already invalidated, not trying to become ready: %s",
1019           self, tp_proxy_get_invalidated (self)->message);
1020       return;
1021     }
1022 
1023   g_assert (self->priv->introspect_needed == NULL);
1024 
1025   if (interfaces != NULL)
1026     tp_connection_add_interfaces_from_introspection (self, interfaces);
1027 
1028   self->priv->introspect_needed = g_list_append (self->priv->introspect_needed,
1029     introspect_self_handle);
1030 
1031   /* FIXME: give subclasses a chance to influence the definition of "ready"
1032    * now that we have our interfaces? */
1033 
1034   tp_connection_continue_introspection (self);
1035 }
1036 
1037 static void
1038 _tp_connection_got_properties (TpProxy *proxy,
1039     GHashTable *asv,
1040     const GError *error,
1041     gpointer unused G_GNUC_UNUSED,
1042     GObject *unused_object G_GNUC_UNUSED);
1043 
1044 static void
tp_connection_status_changed(TpConnection * self,guint status,guint reason)1045 tp_connection_status_changed (TpConnection *self,
1046                               guint status,
1047                               guint reason)
1048 {
1049   DEBUG ("%p: %d -> %d because %d", self, self->priv->status, status, reason);
1050 
1051   if (status == TP_CONNECTION_STATUS_CONNECTED)
1052     {
1053       if (self->priv->introspection_call != NULL &&
1054           !self->priv->introspecting_after_connected)
1055         {
1056           /* We thought we knew what was going on, but now the connection has
1057            * gone to CONNECTED and all bets are off. Start again! */
1058           DEBUG ("Cancelling pre-CONNECTED introspection and starting again");
1059           tp_proxy_pending_call_cancel (self->priv->introspection_call);
1060           self->priv->introspection_call = NULL;
1061           g_list_free (self->priv->introspect_needed);
1062           self->priv->introspect_needed = NULL;
1063         }
1064 
1065       self->priv->introspecting_after_connected = TRUE;
1066 
1067       /* we defer the perceived change to CONNECTED until ready */
1068       if (self->priv->introspection_call == NULL)
1069         {
1070           self->priv->introspection_call =
1071             tp_cli_dbus_properties_call_get_all (self, -1,
1072               TP_IFACE_CONNECTION, _tp_connection_got_properties, NULL, NULL, NULL);
1073         }
1074     }
1075   else
1076     {
1077       self->priv->status = status;
1078       self->priv->status_reason = reason;
1079       g_object_notify ((GObject *) self, "status");
1080       g_object_notify ((GObject *) self, "status-reason");
1081     }
1082 }
1083 
1084 static void
tp_connection_connection_error_cb(TpConnection * self,const gchar * error_name,GHashTable * details,gpointer user_data,GObject * weak_object)1085 tp_connection_connection_error_cb (TpConnection *self,
1086                                    const gchar *error_name,
1087                                    GHashTable *details,
1088                                    gpointer user_data,
1089                                    GObject *weak_object)
1090 {
1091   g_free (self->priv->connection_error);
1092   self->priv->connection_error = g_strdup (error_name);
1093 
1094   if (self->priv->connection_error_details != NULL)
1095     g_hash_table_unref (self->priv->connection_error_details);
1096 
1097   self->priv->connection_error_details = g_boxed_copy (
1098       TP_HASH_TYPE_STRING_VARIANT_MAP, details);
1099 }
1100 
1101 void
_tp_connection_status_reason_to_gerror(TpConnectionStatusReason reason,TpConnectionStatus prev_status,const gchar ** ret_str,GError ** error)1102 _tp_connection_status_reason_to_gerror (TpConnectionStatusReason reason,
1103     TpConnectionStatus prev_status,
1104     const gchar **ret_str,
1105     GError **error)
1106 {
1107   TpError code;
1108   const gchar *message;
1109 
1110   switch (reason)
1111     {
1112     case TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED:
1113       code = TP_ERROR_DISCONNECTED;
1114       message = "Disconnected for unspecified reason";
1115       break;
1116 
1117     case TP_CONNECTION_STATUS_REASON_REQUESTED:
1118       code = TP_ERROR_CANCELLED;
1119       message = "User requested disconnection";
1120       break;
1121 
1122     case TP_CONNECTION_STATUS_REASON_NETWORK_ERROR:
1123       code = TP_ERROR_NETWORK_ERROR;
1124       message = "Network error";
1125       break;
1126 
1127     case TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR:
1128       code = TP_ERROR_ENCRYPTION_ERROR;
1129       message = "Encryption error";
1130       break;
1131 
1132     case TP_CONNECTION_STATUS_REASON_NAME_IN_USE:
1133       if (prev_status == TP_CONNECTION_STATUS_CONNECTED)
1134         {
1135           code = TP_ERROR_CONNECTION_REPLACED;
1136           message = "Connection replaced";
1137         }
1138       else
1139         {
1140           /* If the connection was with register=TRUE, we should ideally use
1141            * REGISTRATION_EXISTS; but we can't actually tell that from here,
1142            * so we'll have to rely on CMs supporting in-band registration
1143            * (Gabble) to emit ConnectionError */
1144           code = TP_ERROR_ALREADY_CONNECTED;
1145           message = "Already connected (or if registering, registration "
1146             "already exists)";
1147         }
1148       break;
1149 
1150     case TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED:
1151       code = TP_ERROR_CERT_NOT_PROVIDED;
1152       message = "Server certificate not provided";
1153       break;
1154 
1155     case TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED:
1156       code = TP_ERROR_CERT_UNTRUSTED;
1157       message = "Server certificate CA not trusted";
1158       break;
1159 
1160     case TP_CONNECTION_STATUS_REASON_CERT_EXPIRED:
1161       code = TP_ERROR_CERT_EXPIRED;
1162       message = "Server certificate expired";
1163       break;
1164 
1165     case TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED:
1166       code = TP_ERROR_CERT_NOT_ACTIVATED;
1167       message = "Server certificate not valid yet";
1168       break;
1169 
1170     case TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH:
1171       code = TP_ERROR_CERT_HOSTNAME_MISMATCH;
1172       message = "Server certificate has wrong hostname";
1173       break;
1174 
1175     case TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH:
1176       code = TP_ERROR_CERT_FINGERPRINT_MISMATCH;
1177       message = "Server certificate fingerprint mismatch";
1178       break;
1179 
1180     case TP_CONNECTION_STATUS_REASON_CERT_SELF_SIGNED:
1181       code = TP_ERROR_CERT_SELF_SIGNED;
1182       message = "Server certificate is self-signed";
1183       break;
1184 
1185     case TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR:
1186       code = TP_ERROR_CERT_INVALID;
1187       message = "Unspecified server certificate error";
1188       break;
1189 
1190     default:
1191       g_set_error (error, TP_ERRORS_DISCONNECTED, reason,
1192           "Unknown disconnection reason");
1193 
1194       if (ret_str != NULL)
1195         *ret_str = TP_ERROR_STR_DISCONNECTED;
1196 
1197       return;
1198     }
1199 
1200   g_set_error (error, TP_ERROR, code, "%s", message);
1201 
1202   if (ret_str != NULL)
1203     *ret_str = tp_error_get_dbus_name (code);
1204 }
1205 
1206 static void
tp_connection_status_changed_cb(TpConnection * self,guint status,guint reason,gpointer user_data,GObject * weak_object)1207 tp_connection_status_changed_cb (TpConnection *self,
1208                                  guint status,
1209                                  guint reason,
1210                                  gpointer user_data,
1211                                  GObject *weak_object)
1212 {
1213   TpConnectionStatus prev_status = self->priv->status;
1214 
1215   /* The status is initially attempted to be discovered starting in the
1216    * constructor. If we don't have the reply for that call yet, ignore this
1217    * signal StatusChanged in order to run the interface introspection only one
1218    * time. We will get the initial introspection reply later anyway. */
1219   if (self->priv->status != TP_UNKNOWN_CONNECTION_STATUS)
1220     {
1221       tp_connection_status_changed (self, status, reason);
1222     }
1223 
1224   /* we only want to run this in response to a StatusChanged signal,
1225    * not if the initial status is DISCONNECTED */
1226 
1227   if (status == TP_CONNECTION_STATUS_DISCONNECTED)
1228     {
1229       GError *error = NULL;
1230 
1231       if (self->priv->connection_error == NULL)
1232         {
1233           _tp_connection_status_reason_to_gerror (reason, prev_status,
1234               NULL, &error);
1235         }
1236       else
1237         {
1238           g_assert (self->priv->connection_error_details != NULL);
1239           tp_proxy_dbus_error_to_gerror (self, self->priv->connection_error,
1240               tp_asv_get_string (self->priv->connection_error_details,
1241                 "debug-message"), &error);
1242 
1243           /* ... but if we don't know anything about that D-Bus error
1244            * name, we can still be more helpful by deriving an error code from
1245            * TpConnectionStatusReason */
1246           if (g_error_matches (error, TP_DBUS_ERRORS,
1247                 TP_DBUS_ERROR_UNKNOWN_REMOTE_ERROR))
1248             {
1249               GError *from_csr = NULL;
1250 
1251               _tp_connection_status_reason_to_gerror (reason, prev_status,
1252                   NULL, &from_csr);
1253               error->domain = from_csr->domain;
1254               error->code = from_csr->code;
1255               g_error_free (from_csr);
1256             }
1257         }
1258 
1259       tp_proxy_invalidate ((TpProxy *) self, error);
1260       g_error_free (error);
1261     }
1262 }
1263 
1264 static void
tp_connection_got_status_cb(TpConnection * self,guint status,const GError * error,gpointer unused,GObject * user_object)1265 tp_connection_got_status_cb (TpConnection *self,
1266                              guint status,
1267                              const GError *error,
1268                              gpointer unused,
1269                              GObject *user_object)
1270 {
1271   DEBUG ("%p", self);
1272 
1273   g_assert (self->priv->introspection_call != NULL);
1274   self->priv->introspection_call = NULL;
1275 
1276   if (error == NULL)
1277     {
1278       DEBUG ("%p: Initial status is %d", self, status);
1279       tp_connection_status_changed (self, status,
1280           TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED);
1281 
1282       /* try introspecting before CONNECTED - it might work... */
1283       if (status != TP_CONNECTION_STATUS_CONNECTED &&
1284           self->priv->introspection_call == NULL)
1285         {
1286           self->priv->introspection_call =
1287             tp_cli_connection_call_get_interfaces (self, -1,
1288                 tp_connection_got_interfaces_cb, NULL, NULL, NULL);
1289         }
1290     }
1291   else
1292     {
1293       DEBUG ("%p: GetStatus() failed with %s %d \"%s\"",
1294           self, g_quark_to_string (error->domain), error->code,
1295           error->message);
1296     }
1297 }
1298 
1299 static void
tp_connection_invalidated(TpConnection * self)1300 tp_connection_invalidated (TpConnection *self)
1301 {
1302   if (self->priv->introspection_call != NULL)
1303     {
1304       DEBUG ("Cancelling introspection");
1305       tp_proxy_pending_call_cancel (self->priv->introspection_call);
1306       self->priv->introspection_call = NULL;
1307     }
1308 
1309   /* Drop the ref we have on all roster contacts, this is to break the refcycle
1310    * we have between TpConnection and TpContact, otherwise self would never
1311    * run dispose.
1312    * Note that invalidated is also called from dispose, so self->priv->roster
1313    * could already be NULL.
1314    *
1315    * FIXME: When we decide to break tp-glib API/guarantees, we should stop
1316    * TpContact taking a strong ref on its TpConnection and force user to keep
1317    * a ref on the TpConnection to use its TpContact, this would avoid the
1318    * refcycle completely. */
1319   if (self->priv->roster != NULL)
1320     g_hash_table_remove_all (self->priv->roster);
1321   g_clear_object (&self->priv->self_contact);
1322   tp_clear_pointer (&self->priv->blocked_contacts, g_ptr_array_unref);
1323 }
1324 
1325 static gboolean
_tp_connection_extract_properties(TpConnection * self,GHashTable * asv,guint32 * status,guint32 * self_handle,const gchar *** interfaces)1326 _tp_connection_extract_properties (TpConnection *self,
1327     GHashTable *asv,
1328     guint32 *status,
1329     guint32 *self_handle,
1330     const gchar ***interfaces)
1331 {
1332   gboolean sufficient;
1333 
1334   /* has_immortal_handles is a bitfield, so we can't pass a pointer to it */
1335   if (tp_asv_get_boolean (asv, "HasImmortalHandles", NULL))
1336     self->priv->has_immortal_handles = TRUE;
1337 
1338   *status = tp_asv_get_uint32 (asv, "Status", &sufficient);
1339 
1340   if (!sufficient
1341       || *status > TP_CONNECTION_STATUS_DISCONNECTED)
1342     return FALSE;
1343 
1344   *interfaces = (const gchar **) tp_asv_get_strv (asv, "Interfaces");
1345 
1346   if (*interfaces == NULL)
1347     return FALSE;
1348 
1349   if (*status == TP_CONNECTION_STATUS_CONNECTED)
1350     {
1351       *self_handle = tp_asv_get_uint32 (asv, "SelfHandle", &sufficient);
1352 
1353       if (!sufficient || *self_handle == 0)
1354         return FALSE;
1355     }
1356   else
1357     {
1358       *self_handle = 0;
1359     }
1360 
1361   return TRUE;
1362 }
1363 
1364 static void
_tp_connection_got_properties(TpProxy * proxy,GHashTable * asv,const GError * error,gpointer unused G_GNUC_UNUSED,GObject * unused_object G_GNUC_UNUSED)1365 _tp_connection_got_properties (TpProxy *proxy,
1366     GHashTable *asv,
1367     const GError *error,
1368     gpointer unused G_GNUC_UNUSED,
1369     GObject *unused_object G_GNUC_UNUSED)
1370 {
1371   TpConnection *self = TP_CONNECTION (proxy);
1372   guint32 status;
1373   guint32 self_handle;
1374   const gchar **interfaces;
1375 
1376   if (tp_proxy_get_invalidated (self) != NULL)
1377     {
1378       DEBUG ("%p: already invalidated, not trying to become ready: %s",
1379           self, tp_proxy_get_invalidated (self)->message);
1380       return;
1381     }
1382 
1383   if (self->priv->introspection_call)
1384     self->priv->introspection_call = NULL;
1385 
1386   if (error == NULL &&
1387       _tp_connection_extract_properties (
1388         self,
1389         asv,
1390         &status,
1391         &self_handle,
1392         &interfaces))
1393     {
1394       tp_connection_add_interfaces_from_introspection (self, interfaces);
1395 
1396       if (status == TP_CONNECTION_STATUS_CONNECTED)
1397         {
1398           self->priv->introspecting_after_connected = TRUE;
1399           self->priv->last_known_self_handle = self_handle;
1400 
1401           self->priv->introspect_needed = g_list_append (
1402               self->priv->introspect_needed, introspect_self_contact);
1403         }
1404       else
1405         {
1406           tp_connection_status_changed (self, status,
1407               TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED);
1408         }
1409 
1410       tp_connection_continue_introspection (self);
1411       return;
1412     }
1413   else if (error != NULL)
1414     {
1415       DEBUG ("GetAll failed: %s", error->message);
1416     }
1417 
1418 
1419   DEBUG ("Could not extract all required properties from GetAll return, "
1420          "will use 0.18 API instead");
1421 
1422   if (self->priv->introspection_call == NULL)
1423     {
1424       if (self->priv->status == TP_UNKNOWN_CONNECTION_STATUS &&
1425           !self->priv->introspecting_after_connected)
1426         {
1427           /* get my initial status */
1428           DEBUG ("Calling GetStatus");
1429           self->priv->introspection_call =
1430             tp_cli_connection_call_get_status (self, -1,
1431               tp_connection_got_status_cb, NULL, NULL, NULL);
1432         }
1433       else
1434         {
1435           self->priv->introspection_call =
1436             tp_cli_connection_call_get_interfaces (self, -1,
1437                 tp_connection_got_interfaces_cb, NULL, NULL, NULL);
1438         }
1439     }
1440 }
1441 
1442 static gboolean _tp_connection_parse (const gchar *path_or_bus_name,
1443     char delimiter,
1444     gchar **protocol,
1445     gchar **cm_name);
1446 
1447 static void
tp_connection_constructed(GObject * object)1448 tp_connection_constructed (GObject *object)
1449 {
1450   GObjectClass *object_class = (GObjectClass *) tp_connection_parent_class;
1451   TpConnection *self = TP_CONNECTION (object);
1452   const gchar *object_path;
1453 
1454   if (object_class->constructed != NULL)
1455     object_class->constructed (object);
1456 
1457   DEBUG ("%s (%p) constructed", tp_proxy_get_object_path (object), object);
1458 
1459   _tp_proxy_ensure_factory (self, NULL);
1460 
1461   /* Connect to my own StatusChanged signal.
1462    * The connection hasn't had a chance to become invalid yet, so we can
1463    * assume that this signal connection will work */
1464   tp_cli_connection_connect_to_status_changed (self,
1465       tp_connection_status_changed_cb, NULL, NULL, NULL, NULL);
1466   tp_cli_connection_connect_to_connection_error (self,
1467       tp_connection_connection_error_cb, NULL, NULL, NULL, NULL);
1468 
1469   /* We need to connect to SelfHandleChanged early, too, so that we're
1470    * already connected before we GetAll */
1471   tp_cli_connection_connect_to_self_handle_changed (self,
1472       on_self_handle_changed, NULL, NULL, NULL, NULL);
1473 
1474   object_path = tp_proxy_get_object_path (TP_PROXY (self));
1475   g_assert (_tp_connection_parse (object_path, '/',
1476       &(self->priv->proto_name), &(self->priv->cm_name)));
1477 
1478   tp_cli_dbus_properties_call_get_all (self, -1,
1479       TP_IFACE_CONNECTION, _tp_connection_got_properties, NULL, NULL, NULL);
1480 
1481   /* Give a chance to TpAccount to know about invalidated connection before we
1482    * unref all roster contacts. This is to let applications properly remove all
1483    * contacts at once instead of getting weak notify on each. */
1484   g_signal_connect_after (self, "invalidated",
1485       G_CALLBACK (tp_connection_invalidated), NULL);
1486 }
1487 
1488 static void
tp_connection_init(TpConnection * self)1489 tp_connection_init (TpConnection *self)
1490 {
1491   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_CONNECTION,
1492       TpConnectionPrivate);
1493 
1494   self->priv->status = TP_UNKNOWN_CONNECTION_STATUS;
1495   self->priv->status_reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED;
1496   self->priv->contacts = g_hash_table_new (g_direct_hash, g_direct_equal);
1497   self->priv->introspection_call = NULL;
1498   self->priv->interests = tp_intset_new ();
1499   self->priv->contact_groups = g_ptr_array_new_with_free_func (g_free);
1500   g_ptr_array_add (self->priv->contact_groups, NULL);
1501   self->priv->roster = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1502       NULL, g_object_unref);
1503   self->priv->contacts_changed_queue = g_queue_new ();
1504 
1505   g_queue_init (&self->priv->capabilities_queue);
1506 
1507   self->priv->blocked_contacts = g_ptr_array_new_with_free_func (
1508       g_object_unref);
1509 
1510   self->priv->blocked_changed_queue = g_queue_new ();
1511 }
1512 
1513 static void
tp_connection_finalize(GObject * object)1514 tp_connection_finalize (GObject *object)
1515 {
1516   TpConnection *self = TP_CONNECTION (object);
1517 
1518   DEBUG ("%p", self);
1519 
1520   tp_clear_pointer (&self->priv->cm_name, g_free);
1521   tp_clear_pointer (&self->priv->proto_name, g_free);
1522 
1523   /* not true unless we were finalized before we were ready */
1524   if (self->priv->introspect_needed != NULL)
1525     {
1526       g_list_free (self->priv->introspect_needed);
1527       self->priv->introspect_needed = NULL;
1528     }
1529 
1530   if (self->priv->contact_attribute_interfaces != NULL)
1531     {
1532       g_array_unref (self->priv->contact_attribute_interfaces);
1533       self->priv->contact_attribute_interfaces = NULL;
1534     }
1535 
1536   g_free (self->priv->connection_error);
1537   self->priv->connection_error = NULL;
1538 
1539   if (self->priv->connection_error_details != NULL)
1540     {
1541       g_hash_table_unref (self->priv->connection_error_details);
1542       self->priv->connection_error_details = NULL;
1543     }
1544 
1545   if (self->priv->avatar_request_queue != NULL)
1546     {
1547       g_array_unref (self->priv->avatar_request_queue);
1548       self->priv->avatar_request_queue = NULL;
1549     }
1550 
1551   if (self->priv->avatar_request_idle_id != 0)
1552     {
1553       g_source_remove (self->priv->avatar_request_idle_id);
1554       self->priv->avatar_request_idle_id = 0;
1555     }
1556 
1557   tp_contact_info_spec_list_free (self->priv->contact_info_supported_fields);
1558   self->priv->contact_info_supported_fields = NULL;
1559 
1560   tp_clear_pointer (&self->priv->balance_currency, g_free);
1561   tp_clear_pointer (&self->priv->balance_uri, g_free);
1562   tp_clear_pointer (&self->priv->cm_name, g_free);
1563   tp_clear_pointer (&self->priv->proto_name, g_free);
1564 
1565   ((GObjectClass *) tp_connection_parent_class)->finalize (object);
1566 }
1567 
1568 static void
contact_notify_disposed(gpointer k G_GNUC_UNUSED,gpointer v,gpointer d G_GNUC_UNUSED)1569 contact_notify_disposed (gpointer k G_GNUC_UNUSED,
1570     gpointer v,
1571     gpointer d G_GNUC_UNUSED)
1572 {
1573   _tp_contact_connection_disposed (v);
1574 }
1575 
1576 
1577 static void
tp_connection_dispose(GObject * object)1578 tp_connection_dispose (GObject *object)
1579 {
1580   TpConnection *self = TP_CONNECTION (object);
1581 
1582   DEBUG ("%p", object);
1583 
1584   if (self->priv->account != NULL)
1585     {
1586       g_object_remove_weak_pointer ((GObject *) self->priv->account,
1587           (gpointer) &self->priv->account);
1588       self->priv->account = NULL;
1589     }
1590 
1591   tp_clear_pointer (&self->priv->contact_groups, g_ptr_array_unref);
1592   tp_clear_pointer (&self->priv->roster, g_hash_table_unref);
1593   tp_clear_pointer (&self->priv->contacts_changed_queue,
1594       _tp_connection_contacts_changed_queue_free);
1595   tp_clear_pointer (&self->priv->blocked_changed_queue,
1596       _tp_connection_blocked_changed_queue_free);
1597 
1598   if (self->priv->contacts != NULL)
1599     {
1600       g_hash_table_foreach (self->priv->contacts, contact_notify_disposed,
1601           NULL);
1602       tp_clear_pointer (&self->priv->contacts, g_hash_table_unref);
1603     }
1604 
1605   tp_clear_object (&self->priv->capabilities);
1606   tp_clear_pointer (&self->priv->avatar_requirements,
1607       tp_avatar_requirements_destroy);
1608 
1609   if (self->priv->interests != NULL)
1610     {
1611       guint size = tp_intset_size (self->priv->interests);
1612 
1613       /* Before freeing the set of tokens in which we declared an
1614        * interest, cancel those interests. We'll still get the signals
1615        * if there's another interested TpConnection in this process,
1616        * because the CM uses distributed refcounting. */
1617       if (size > 0)
1618         {
1619           TpIntsetFastIter iter;
1620           GPtrArray *strings;
1621           guint element;
1622 
1623           strings = g_ptr_array_sized_new (size + 1);
1624 
1625           tp_intset_fast_iter_init (&iter, self->priv->interests);
1626 
1627           while (tp_intset_fast_iter_next (&iter, &element))
1628             g_ptr_array_add (strings,
1629                 (gchar *) g_quark_to_string (element));
1630 
1631           g_ptr_array_add (strings, NULL);
1632 
1633           /* no callback - if the CM replies, we'll ignore it anyway */
1634           tp_cli_connection_call_remove_client_interest (self, -1,
1635               (const gchar **) strings->pdata, NULL, NULL, NULL, NULL);
1636           g_ptr_array_unref (strings);
1637         }
1638 
1639       tp_intset_destroy (self->priv->interests);
1640       self->priv->interests = NULL;
1641     }
1642 
1643   tp_clear_pointer (&self->priv->blocked_contacts, g_ptr_array_unref);
1644   g_clear_object (&self->priv->self_contact);
1645 
1646   ((GObjectClass *) tp_connection_parent_class)->dispose (object);
1647 }
1648 
1649 enum {
1650     FEAT_CORE,
1651     FEAT_CONNECTED,
1652     FEAT_CAPABILITIES,
1653     FEAT_AVATAR_REQUIREMENTS,
1654     FEAT_CONTACT_INFO,
1655     FEAT_BALANCE,
1656     FEAT_CONTACT_LIST,
1657     FEAT_CONTACT_LIST_PROPS,
1658     FEAT_CONTACT_GROUPS,
1659     FEAT_CONTACT_BLOCKING,
1660     FEAT_ALIASING,
1661     N_FEAT
1662 };
1663 
1664 static const TpProxyFeature *
tp_connection_list_features(TpProxyClass * cls G_GNUC_UNUSED)1665 tp_connection_list_features (TpProxyClass *cls G_GNUC_UNUSED)
1666 {
1667   static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
1668   static GQuark need_requests[2] = {0, 0};
1669   static GQuark need_avatars[2] = {0, 0};
1670   static GQuark need_contact_info[2] = {0, 0};
1671   static GQuark need_balance[2] = {0, 0};
1672   static GQuark need_contact_list[3] = {0, 0, 0};
1673   static GQuark need_contact_groups[2] = {0, 0};
1674   static GQuark need_contact_blocking[2] = {0, 0};
1675   static GQuark depends_contact_list[2] = {0, 0};
1676   static GQuark need_aliasing[2] = {0, 0};
1677 
1678   if (G_LIKELY (features[0].name != 0))
1679     return features;
1680 
1681   features[FEAT_CORE].name = TP_CONNECTION_FEATURE_CORE;
1682   features[FEAT_CORE].core = TRUE;
1683 
1684   features[FEAT_CONNECTED].name = TP_CONNECTION_FEATURE_CONNECTED;
1685 
1686   features[FEAT_CAPABILITIES].name = TP_CONNECTION_FEATURE_CAPABILITIES;
1687   features[FEAT_CAPABILITIES].prepare_async =
1688       tp_connection_prepare_capabilities_async;
1689   need_requests[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS;
1690   features[FEAT_CAPABILITIES].interfaces_needed = need_requests;
1691 
1692   features[FEAT_AVATAR_REQUIREMENTS].name = TP_CONNECTION_FEATURE_AVATAR_REQUIREMENTS;
1693   features[FEAT_AVATAR_REQUIREMENTS].prepare_async =
1694     _tp_connection_prepare_avatar_requirements_async;
1695   need_avatars[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_AVATARS;
1696   features[FEAT_AVATAR_REQUIREMENTS].interfaces_needed = need_avatars;
1697 
1698   features[FEAT_CONTACT_INFO].name = TP_CONNECTION_FEATURE_CONTACT_INFO;
1699   features[FEAT_CONTACT_INFO].prepare_async =
1700     _tp_connection_prepare_contact_info_async;
1701   need_contact_info[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO;
1702   features[FEAT_CONTACT_INFO].interfaces_needed = need_contact_info;
1703 
1704   features[FEAT_BALANCE].name = TP_CONNECTION_FEATURE_BALANCE;
1705   features[FEAT_BALANCE].prepare_async = tp_connection_prepare_balance_async;
1706   need_balance[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_BALANCE;
1707   features[FEAT_BALANCE].interfaces_needed = need_balance;
1708 
1709   features[FEAT_CONTACT_LIST].name = TP_CONNECTION_FEATURE_CONTACT_LIST;
1710   features[FEAT_CONTACT_LIST].prepare_async = _tp_connection_prepare_contact_list_async;
1711   need_contact_list[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_LIST;
1712   need_contact_list[1] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACTS;
1713   features[FEAT_CONTACT_LIST].interfaces_needed = need_contact_list;
1714   depends_contact_list[0] = TP_CONNECTION_FEATURE_CONTACT_LIST_PROPERTIES;
1715   features[FEAT_CONTACT_LIST].depends_on = depends_contact_list;
1716 
1717   features[FEAT_CONTACT_LIST_PROPS].name = TP_CONNECTION_FEATURE_CONTACT_LIST_PROPERTIES;
1718   features[FEAT_CONTACT_LIST_PROPS].prepare_async = _tp_connection_prepare_contact_list_props_async;
1719   features[FEAT_CONTACT_LIST_PROPS].interfaces_needed = need_contact_list;
1720 
1721   features[FEAT_CONTACT_GROUPS].name = TP_CONNECTION_FEATURE_CONTACT_GROUPS;
1722   features[FEAT_CONTACT_GROUPS].prepare_async = _tp_connection_prepare_contact_groups_async;
1723   need_contact_groups[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_GROUPS;
1724   features[FEAT_CONTACT_GROUPS].interfaces_needed = need_contact_groups;
1725 
1726   features[FEAT_CONTACT_BLOCKING].name = TP_CONNECTION_FEATURE_CONTACT_BLOCKING;
1727   features[FEAT_CONTACT_BLOCKING].prepare_async = _tp_connection_prepare_contact_blocking_async;
1728   need_contact_blocking[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_BLOCKING;
1729   features[FEAT_CONTACT_BLOCKING].interfaces_needed = need_contact_blocking;
1730 
1731   features[FEAT_ALIASING].name = TP_CONNECTION_FEATURE_ALIASING;
1732   features[FEAT_ALIASING].prepare_async = _tp_connection_prepare_aliasing_async;
1733   need_aliasing[0] = TP_IFACE_QUARK_CONNECTION_INTERFACE_ALIASING;
1734   features[FEAT_ALIASING].interfaces_needed = need_aliasing;
1735 
1736   /* assert that the terminator at the end is there */
1737   g_assert (features[N_FEAT].name == 0);
1738 
1739   return features;
1740 }
1741 
1742 static void
tp_connection_class_init(TpConnectionClass * klass)1743 tp_connection_class_init (TpConnectionClass *klass)
1744 {
1745   GParamSpec *param_spec;
1746   TpProxyClass *proxy_class = (TpProxyClass *) klass;
1747   GObjectClass *object_class = (GObjectClass *) klass;
1748 
1749   tp_connection_init_known_interfaces ();
1750 
1751   g_type_class_add_private (klass, sizeof (TpConnectionPrivate));
1752 
1753   object_class->constructed = tp_connection_constructed;
1754   object_class->get_property = tp_connection_get_property;
1755   object_class->dispose = tp_connection_dispose;
1756   object_class->finalize = tp_connection_finalize;
1757 
1758   proxy_class->interface = TP_IFACE_QUARK_CONNECTION;
1759   /* If you change this, you must also change TpChannel to stop asserting
1760    * that its connection has a unique name */
1761   proxy_class->must_have_unique_name = TRUE;
1762   proxy_class->list_features = tp_connection_list_features;
1763 
1764   /**
1765    * TpConnection:status:
1766    *
1767    * This connection's status, or %TP_UNKNOWN_CONNECTION_STATUS if we don't
1768    * know yet.
1769    *
1770    * To wait for a valid status (and other properties), call
1771    * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_CORE.
1772    *
1773    * Since version 0.11.3, the change to status
1774    * %TP_CONNECTION_STATUS_CONNECTED is delayed slightly, until introspection
1775    * of the connection has finished.
1776    */
1777   param_spec = g_param_spec_uint ("status", "Status",
1778       "The status of this connection", 0, G_MAXUINT32,
1779       TP_UNKNOWN_CONNECTION_STATUS,
1780       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1781   g_object_class_install_property (object_class, PROP_STATUS,
1782       param_spec);
1783 
1784   /**
1785    * TpConnection:connection-manager-name:
1786    *
1787    * This connection's connection manager name.
1788    *
1789    * Since: 0.13.16
1790    * Deprecated: Use #TpConnection:cm-name instead.
1791    */
1792   g_object_class_install_property (object_class, PROP_CONNECTION_MANAGER_NAME,
1793       g_param_spec_string ("connection-manager-name",
1794           "Connection manager name",
1795           "The connection's connection manager name",
1796           NULL,
1797           G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
1798 
1799   /**
1800    * TpConnection:cm-name:
1801    *
1802    * This connection's connection manager name.
1803    *
1804    * Since: 0.19.3
1805    */
1806   g_object_class_install_property (object_class, PROP_CM_NAME,
1807       g_param_spec_string ("cm-name",
1808           "Connection manager name",
1809           "The connection's connection manager name",
1810           NULL,
1811           G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
1812 
1813   /**
1814    * TpConnection:protocol-name:
1815    *
1816    * The connection's machine-readable protocol name, such as "jabber",
1817    * "msn" or "local-xmpp". Recommended names for most protocols can be
1818    * found in the Telepathy D-Bus Interface Specification.
1819    *
1820    * Since: 0.13.16
1821    *
1822    */
1823   g_object_class_install_property (object_class, PROP_PROTOCOL_NAME,
1824       g_param_spec_string ("protocol-name",
1825           "Protocol name",
1826           "The connection's protocol name",
1827           NULL,
1828           G_PARAM_STATIC_STRINGS | G_PARAM_READABLE));
1829 
1830   /**
1831    * TpConnection:self-handle:
1832    *
1833    * The %TP_HANDLE_TYPE_CONTACT handle of the local user on this connection,
1834    * or 0 if we don't know yet or if the connection has become invalid.
1835    *
1836    * This may change if the local user's unique identifier changes (for
1837    * instance by using /nick on IRC), in which case #GObject::notify will be
1838    * emitted.
1839    *
1840    * To wait for a valid self-handle (and other properties), call
1841    * tp_proxy_prepare_async() with the feature
1842    * %TP_CONNECTION_FEATURE_CONNECTED.
1843    *
1844    * Deprecated: Use #TpConnection:self-contact instead.
1845    */
1846   param_spec = g_param_spec_uint ("self-handle", "Self handle",
1847       "The local user's Contact handle on this connection", 0, G_MAXUINT32,
1848       0,
1849       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1850   g_object_class_install_property (object_class, PROP_SELF_HANDLE,
1851       param_spec);
1852 
1853   /**
1854    * TpConnection:self-contact:
1855    *
1856    * A #TpContact representing the local user on this connection,
1857    * or %NULL if not yet available.
1858    *
1859    * If the local user's unique identifier changes (for instance by using
1860    * /nick on IRC), this property will change to a different #TpContact object
1861    * representing the new identifier, and #GObject::notify will be emitted.
1862    *
1863    * The #TpContact object is guaranteed to have all of the features previously
1864    * passed to tp_simple_client_factory_add_contact_features() prepared.
1865    *
1866    * To wait for a non-%NULL self-contact (and other properties), call
1867    * tp_proxy_prepare_async() with the feature
1868    * %TP_CONNECTION_FEATURE_CONNECTED.
1869    *
1870    * Since: 0.13.9
1871    */
1872   param_spec = g_param_spec_object ("self-contact", "Self contact",
1873       "The local user's Contact object on this connection", TP_TYPE_CONTACT,
1874       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1875   g_object_class_install_property (object_class, PROP_SELF_CONTACT,
1876       param_spec);
1877 
1878   /**
1879    * TpConnection:status-reason:
1880    *
1881    * To wait for a valid status (and other properties), call
1882    * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_CORE.
1883    *
1884    * The reason why #TpConnection:status changed to its current value,
1885    * or TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED if unknown.
1886    * know yet.
1887    */
1888   param_spec = g_param_spec_uint ("status-reason", "Last status change reason",
1889       "The reason why #TpConnection:status changed to its current value",
1890       0, G_MAXUINT32, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
1891       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1892   g_object_class_install_property (object_class, PROP_STATUS_REASON,
1893       param_spec);
1894 
1895   /**
1896    * TpConnection:connection-ready:
1897    *
1898    * Initially %FALSE; changes to %TRUE when the connection has gone to
1899    * CONNECTED status, introspection has finished and it's ready for use.
1900    *
1901    * By the time this property becomes %TRUE, any extra interfaces will
1902    * have been set up and the #TpProxy:interfaces property will have been
1903    * populated.
1904    *
1905    * This is similar to %TP_CONNECTION_FEATURE_CONNECTED, except that once
1906    * it has changed to %TRUE, it remains %TRUE even if the connection has
1907    * been invalidated.
1908    *
1909    * Deprecated: 0.17.6: use tp_proxy_is_prepared() with
1910    *  %TP_CHANNEL_FEATURE_CONNECTED for checks, or tp_proxy_prepare_async() for
1911    *  notification
1912    */
1913   param_spec = g_param_spec_boolean ("connection-ready", "Connection ready?",
1914       "Initially FALSE; changes to TRUE when introspection finishes", FALSE,
1915       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED);
1916   g_object_class_install_property (object_class, PROP_CONNECTION_READY,
1917       param_spec);
1918 
1919  /**
1920    * TpConnection:capabilities:
1921    *
1922    * The %TpCapabilities object representing the capabilities of this
1923    * connection, or NULL if we don't know yet.
1924    *
1925    * To wait for valid capability information, call tp_proxy_prepare_async()
1926    * with the feature %TP_CONNECTION_FEATURE_CAPABILITIES.
1927    */
1928   param_spec = g_param_spec_object ("capabilities", "Capabilities",
1929       "A TpCapabilities object representing the capabilities of the connection",
1930       TP_TYPE_CAPABILITIES,
1931       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1932   g_object_class_install_property (object_class, PROP_CAPABILITIES,
1933       param_spec);
1934 
1935   /**
1936    * TpConnection:balance:
1937    *
1938    * The Amount field of the Balance.AccountBalance property.
1939    *
1940    * For this property to be valid, you must first call
1941    * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
1942    *
1943    * See Also: tp_connection_get_balance()
1944    */
1945   param_spec = g_param_spec_int ("balance", "Balance Amount",
1946       "The Amount field of the Account Balance",
1947       G_MININT32, G_MAXINT32, 0,
1948       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1949   g_object_class_install_property (object_class, PROP_BALANCE,
1950       param_spec);
1951 
1952   /**
1953    * TpConnection:balance-scale:
1954    *
1955    * The Scale field of the Balance.AccountBalance property.
1956    *
1957    * For this property to be valid, you must first call
1958    * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
1959    *
1960    * See Also: tp_connection_get_balance()
1961    */
1962   param_spec = g_param_spec_uint ("balance-scale", "Balance Scale",
1963       "The Scale field of the Account Balance",
1964       0, G_MAXUINT32, G_MAXUINT32,
1965       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1966   g_object_class_install_property (object_class, PROP_BALANCE_SCALE,
1967       param_spec);
1968 
1969   /**
1970    * TpConnection:balance-currency:
1971    *
1972    * The Currency field of the Balance.AccountBalance property.
1973    *
1974    * For this property to be valid, you must first call
1975    * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
1976    *
1977    * See Also: tp_connection_get_balance()
1978    */
1979   param_spec = g_param_spec_string ("balance-currency", "Balance Currency",
1980       "The Currency field of the Account Balance",
1981       NULL,
1982       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1983   g_object_class_install_property (object_class, PROP_BALANCE_CURRENCY,
1984       param_spec);
1985 
1986   /**
1987    * TpConnection:balance-uri:
1988    *
1989    * The Balance.ManageCreditURI property.
1990    *
1991    * For this property to be valid, you must first call
1992    * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
1993    */
1994   param_spec = g_param_spec_string ("balance-uri", "Balance URI",
1995       "The URI for managing the account balance",
1996       NULL,
1997       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1998   g_object_class_install_property (object_class, PROP_BALANCE_URI,
1999       param_spec);
2000 
2001   /**
2002    * TpConnection::balance-changed:
2003    * @self: a channel
2004    * @balance: the value of the #TpConnection:balance property
2005    * @balance_scale: the value of the #TpConnection:balance-scale property
2006    * @balance_currency: the value of the #TpConnection:balance-currency property
2007    *
2008    * Emitted when at least one of the #TpConnection:balance,
2009    * #TpConnection:balance-scale or #TpConnection:balance-currency
2010    * property is changed.
2011    *
2012    * For this signal to be emitted, you must first call
2013    * tp_proxy_prepare_async() with the feature %TP_CONNECTION_FEATURE_BALANCE.
2014    *
2015    * Since: 0.15.1
2016    */
2017   signals[SIGNAL_BALANCE_CHANGED] = g_signal_new ("balance-changed",
2018       G_OBJECT_CLASS_TYPE (klass),
2019       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
2020       0,
2021       NULL, NULL, NULL,
2022       G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_STRING);
2023 
2024   /**
2025    * TpConnection:contact-list-state:
2026    *
2027    * The progress made in retrieving the contact list.
2028    *
2029    * For this property to be valid, you must first call
2030    * tp_proxy_prepare_async() with the feature
2031    * %TP_CONNECTION_FEATURE_CONTACT_LIST_PROPERTIES or
2032    * %TP_CONNECTION_FEATURE_CONTACT_LIST.
2033    *
2034    * Since: 0.15.5
2035    */
2036   param_spec = g_param_spec_uint ("contact-list-state", "ContactList state",
2037       "The state of the contact list",
2038       0, G_MAXUINT, TP_CONTACT_LIST_STATE_NONE,
2039       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2040   g_object_class_install_property (object_class, PROP_CONTACT_LIST_STATE,
2041       param_spec);
2042 
2043   /**
2044    * TpConnection:contact-list-persists:
2045    *
2046    * If true, presence subscriptions (in both directions) on this connection are
2047    * stored by the server or other infrastructure.
2048    *
2049    * If false, presence subscriptions on this connection are not stored.
2050    *
2051    * For this property to be valid, you must first call
2052    * tp_proxy_prepare_async() with the feature
2053    * %TP_CONNECTION_FEATURE_CONTACT_LIST_PROPERTIES or
2054    * %TP_CONNECTION_FEATURE_CONTACT_LIST.
2055    *
2056    * Since: 0.15.5
2057    */
2058   param_spec = g_param_spec_boolean ("contact-list-persists",
2059       "ContactList persists", "Whether the contact list persists",
2060       FALSE,
2061       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2062   g_object_class_install_property (object_class, PROP_CONTACT_LIST_PERSISTS,
2063       param_spec);
2064 
2065   /**
2066    * TpConnection:can-change-contact-list:
2067    *
2068    * If true, presence subscription and publication can be changed using the
2069    * RequestSubscription, AuthorizePublication and RemoveContacts methods.
2070    *
2071    * Rational: link-local XMPP, presence is implicitly published to everyone in
2072    * the local subnet, so the user cannot control their presence publication.
2073    *
2074    * For this property to be valid, you must first call
2075    * tp_proxy_prepare_async() with the feature
2076    * %TP_CONNECTION_FEATURE_CONTACT_LIST_PROPERTIES or
2077    * %TP_CONNECTION_FEATURE_CONTACT_LIST.
2078    *
2079    * Since: 0.15.5
2080    */
2081   param_spec = g_param_spec_boolean ("can-change-contact-list",
2082       "ContactList can change", "Whether the contact list can change",
2083       FALSE,
2084       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2085   g_object_class_install_property (object_class, PROP_CAN_CHANGE_CONTACT_LIST,
2086       param_spec);
2087 
2088   /**
2089    * TpConnection:request-uses-message:
2090    *
2091    * If true, the Message parameter to RequestSubscription is likely to be
2092    * significant, and user interfaces SHOULD prompt the user for a message to
2093    * send with the request; a message such as "I would like to add you to my
2094    * contact list", translated into the local user's language, might make a
2095    * suitable default.
2096    *
2097    * For this property to be valid, you must first call
2098    * tp_proxy_prepare_async() with the feature
2099    * %TP_CONNECTION_FEATURE_CONTACT_LIST_PROPERTIES or
2100    * %TP_CONNECTION_FEATURE_CONTACT_LIST.
2101    *
2102    * Since: 0.15.5
2103    */
2104   param_spec = g_param_spec_boolean ("request-uses-message",
2105       "Request Uses Message", "Whether request uses message",
2106       FALSE,
2107       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2108   g_object_class_install_property (object_class, PROP_REQUEST_USES_MESSAGE,
2109       param_spec);
2110 
2111   /**
2112    * TpConnection:disjoint-groups:
2113    *
2114    * True if each contact can be in at most one group; false if each contact
2115    * can be in many groups.
2116    *
2117    * This property cannot change after the connection has moved to the
2118    * %TP_CONNECTION_STATUS_CONNECTED state. Until then, its value is undefined,
2119    * and it may change at any time, without notification.
2120    *
2121    * For this property to be valid, you must first call
2122    * tp_proxy_prepare_async() with the feature
2123    * %TP_CONNECTION_FEATURE_CONTACT_GROUPS.
2124    *
2125    * Since: 0.15.5
2126    */
2127   param_spec = g_param_spec_boolean ("disjoint-groups",
2128       "Disjoint Groups", "Whether groups are disjoint",
2129       FALSE,
2130       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2131   g_object_class_install_property (object_class, PROP_DISJOINT_GROUPS,
2132       param_spec);
2133 
2134   /**
2135    * TpConnection:group-storage:
2136    *
2137    * Indicates the extent to which contacts' groups can be set and stored.
2138    *
2139    * This property cannot change after the connection has moved to the
2140    * %TP_CONNECTION_STATUS_CONNECTED state. Until then, its value is undefined,
2141    * and it may change at any time, without notification.
2142    *
2143    * For this property to be valid, you must first call
2144    * tp_proxy_prepare_async() with the feature
2145    * %TP_CONNECTION_FEATURE_CONTACT_GROUPS.
2146    *
2147    * Since: 0.15.5
2148    */
2149   param_spec = g_param_spec_uint ("group-storage",
2150       "Group Storage", "Group storage capabilities",
2151       0, G_MAXUINT, TP_CONTACT_METADATA_STORAGE_TYPE_NONE,
2152       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2153   g_object_class_install_property (object_class, PROP_GROUP_STORAGE,
2154       param_spec);
2155 
2156   /**
2157    * TpConnection:contact-groups:
2158    *
2159    * The names of all groups that currently exist. This may be a larger set than
2160    * the union of all #TpContact:contact-groups properties, if the connection
2161    * allows groups to be empty.
2162    *
2163    * This property's value is not meaningful until the
2164    * #TpConnection:contact-list-state property has become
2165    * %TP_CONTACT_LIST_STATE_SUCCESS.
2166    *
2167    * For this property to be valid, you must first call
2168    * tp_proxy_prepare_async() with the feature
2169    * %TP_CONNECTION_FEATURE_CONTACT_GROUPS.
2170    *
2171    * Since: 0.15.5
2172    */
2173   param_spec = g_param_spec_boxed ("contact-groups",
2174       "Contact Groups",
2175       "All existing contact groups",
2176       G_TYPE_STRV,
2177       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2178   g_object_class_install_property (object_class, PROP_CONTACT_GROUPS,
2179       param_spec);
2180 
2181   /**
2182    * TpConnection:can-report-abusive:
2183    *
2184    * If this property is %TRUE, contacts may be reported as abusive to the
2185    * server administrators by setting report_abusive to %TRUE when calling
2186    * tp_connection_block_contacts_async().
2187    *
2188    * For this property to be valid, you must first call
2189    * tp_proxy_prepare_async() with the feature
2190    * %TP_CONNECTION_FEATURE_CONTACT_BLOCKING.
2191    *
2192    * Since: 0.17.0
2193    */
2194   param_spec = g_param_spec_boolean ("can-report-abusive",
2195       "Can report abusive",
2196       "Can report abusive",
2197       FALSE,
2198       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2199   g_object_class_install_property (object_class, PROP_CAN_REPORT_ABUSIVE,
2200       param_spec);
2201 
2202   /**
2203    * TpConnection:blocked-contacts:
2204    *
2205    * A #GPtrArray of blocked #TpContact. Changes are notified using the
2206    * #TpConnection::blocked-contacts-changed signal.
2207    *
2208    * These TpContact objects have been prepared with the desired features.
2209    * See tp_simple_client_factory_add_contact_features() to define which
2210    * features needs to be prepared on them.
2211    *
2212    * For this property to be valid, you must first call
2213    * tp_proxy_prepare_async() with the feature
2214    * %TP_CONNECTION_FEATURE_CONTACT_BLOCKING.
2215    *
2216    * Since: 0.17.0
2217    */
2218   param_spec = g_param_spec_boxed ("blocked-contacts",
2219       "blocked contacts",
2220       "Blocked contacts",
2221       G_TYPE_PTR_ARRAY,
2222       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2223   g_object_class_install_property (object_class, PROP_BLOCKED_CONTACTS,
2224       param_spec);
2225 
2226   /**
2227    * TpConnection::groups-created:
2228    * @self: a #TpConnection
2229    * @added: a #GStrv with the names of the new groups.
2230    *
2231    * Emitted when new, empty groups are created. This will often be followed by
2232    * #TpContact::contact-groups-changed signals that add some members. When this
2233    * signal is emitted, #TpConnection:contact-groups property is already
2234    * updated.
2235    *
2236    * For this signal to be emited, you must first call
2237    * tp_proxy_prepare_async() with the feature
2238    * %TP_CONNECTION_FEATURE_CONTACT_GROUPS.
2239    *
2240    * Since: 0.15.5
2241    */
2242   signals[SIGNAL_GROUPS_CREATED] = g_signal_new (
2243       "groups-created",
2244       G_TYPE_FROM_CLASS (object_class),
2245       G_SIGNAL_RUN_LAST,
2246       0,
2247       NULL, NULL, NULL,
2248       G_TYPE_NONE, 1, G_TYPE_STRV);
2249 
2250   /**
2251    * TpConnection::groups-removed:
2252    * @self: A #TpConnection
2253    * @added: A #GStrv with the names of the groups.
2254    *
2255    * Emitted when one or more groups are removed. If they had members at the
2256    * time that they were removed, then immediately after this signal is emitted,
2257    * #TpContact::contact-groups-changed signals that their members were removed.
2258    * When this signal is emitted, #TpConnection:contact-groups property is
2259    * already updated.
2260    *
2261    * For this signal to be emited, you must first call
2262    * tp_proxy_prepare_async() with the feature
2263    * %TP_CONNECTION_FEATURE_CONTACT_GROUPS.
2264    *
2265    * Since: 0.15.5
2266    */
2267   signals[SIGNAL_GROUPS_REMOVED] = g_signal_new (
2268       "groups-removed",
2269       G_TYPE_FROM_CLASS (object_class),
2270       G_SIGNAL_RUN_LAST,
2271       0,
2272       NULL, NULL, NULL,
2273       G_TYPE_NONE, 1, G_TYPE_STRV);
2274 
2275   /**
2276    * TpConnection::group-renamed:
2277    * @self: a #TpConnection
2278    * @old_name: the old name of the group.
2279    * @new_name: the new name of the group.
2280    *
2281    * Emitted when a group is renamed, in protocols where this can be
2282    * distinguished from group creation, removal and membership changes.
2283    *
2284    * Immediately after this signal is emitted, #TpConnection::groups-created
2285    * signal the creation of a group with the new name, and
2286    * #TpConnection::groups-removed signal the removal of a group with the old
2287    * name.
2288    * If the group was not empty, immediately after those signals are emitted,
2289    * #TpContact::contact-groups-changed signal that the members of that group
2290    * were removed from the old name and added to the new name.
2291    *
2292    * When this signal is emitted, #TpConnection:contact-groups property is
2293    * already updated.
2294    *
2295    * For this signal to be emited, you must first call
2296    * tp_proxy_prepare_async() with the feature
2297    * %TP_CONNECTION_FEATURE_CONTACT_GROUPS.
2298    *
2299    * Since: 0.15.5
2300    */
2301   signals[SIGNAL_GROUP_RENAMED] = g_signal_new (
2302       "group-renamed",
2303       G_TYPE_FROM_CLASS (object_class),
2304       G_SIGNAL_RUN_LAST,
2305       0,
2306       NULL, NULL, NULL,
2307       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
2308   /**
2309    * TpConnection::contact-list-changed:
2310    * @self: a #TpConnection
2311    * @added: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
2312    *  a #GPtrArray of #TpContact added to contacts list
2313    * @removed: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
2314    *  a #GPtrArray of #TpContact removed from contacts list
2315    *
2316    * Notify of changes in the list of contacts as returned by
2317    * tp_connection_dup_contact_list(). It is guaranteed that all contacts have
2318    * desired features prepared. See
2319    * tp_simple_client_factory_add_contact_features() to define which features
2320    * needs to be prepared.
2321    *
2322    * This signal is also emitted for the initial set of contacts once retrieved.
2323    *
2324    * For this signal to be emitted, you must first call
2325    * tp_proxy_prepare_async() with the feature
2326    * %TP_CONNECTION_FEATURE_CONTACT_LIST.
2327    *
2328    * Since: 0.15.5
2329    */
2330   signals[SIGNAL_CONTACT_LIST_CHANGED] = g_signal_new (
2331       "contact-list-changed",
2332       G_OBJECT_CLASS_TYPE (klass),
2333       G_SIGNAL_RUN_LAST,
2334       0,
2335       NULL, NULL, NULL,
2336       G_TYPE_NONE, 2, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY);
2337 
2338   /**
2339    * TpConnection::blocked-contacts-changed:
2340    * @self: a #TpConnection
2341    * @added: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
2342    *  a #GPtrArray of #TpContact which have been blocked
2343    * @removed: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
2344    *  a #GPtrArray of #TpContact which are no longer blocked
2345    *
2346    * Notify of changes in #TpConnection:blocked-contacts.
2347    *  It is guaranteed that all contacts have desired features prepared. See
2348    * tp_simple_client_factory_add_contact_features() to define which features
2349    * needs to be prepared.
2350    *
2351    * This signal is also emitted for the initial set of blocked contacts once
2352    * retrieved.
2353    *
2354    * For this signal to be emitted, you must first call
2355    * tp_proxy_prepare_async() with the feature
2356    * %TP_CONNECTION_FEATURE_CONTACT_BLOCKING.
2357    *
2358    * Since: 0.17.0
2359    */
2360   signals[SIGNAL_BLOCKED_CONTACTS_CHANGED] = g_signal_new (
2361       "blocked-contacts-changed",
2362       G_OBJECT_CLASS_TYPE (klass),
2363       G_SIGNAL_RUN_LAST,
2364       0,
2365       NULL, NULL, NULL,
2366       G_TYPE_NONE, 2, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY);
2367 
2368 }
2369 
2370 /**
2371  * tp_connection_new:
2372  * @dbus: a D-Bus daemon; may not be %NULL
2373  * @bus_name: (allow-none): the well-known or unique name of the connection
2374  *  process; if well-known, this function will make a blocking call to the bus
2375  *  daemon to resolve the unique name. May be %NULL if @object_path is not, in
2376  *  which case a well-known name will be derived from @object_path.
2377  * @object_path: (allow-none): the object path of the connection process.
2378  *  May be %NULL if @bus_name is a well-known name, in which case the object
2379  *  path will be derived from @bus_name.
2380  * @error: used to indicate the error if %NULL is returned
2381  *
2382  * <!-- -->
2383  *
2384  * Returns: a new connection proxy, or %NULL if unique-name resolution
2385  *  fails or on invalid arguments
2386  *
2387  * Since: 0.7.1
2388  * Deprecated: Use tp_simple_client_factory_ensure_connection() instead.
2389  */
2390 TpConnection *
tp_connection_new(TpDBusDaemon * dbus,const gchar * bus_name,const gchar * object_path,GError ** error)2391 tp_connection_new (TpDBusDaemon *dbus,
2392                    const gchar *bus_name,
2393                    const gchar *object_path,
2394                    GError **error)
2395 {
2396   return _tp_connection_new_with_factory (NULL, dbus, bus_name, object_path,
2397       error);
2398 }
2399 
2400 TpConnection *
_tp_connection_new_with_factory(TpSimpleClientFactory * factory,TpDBusDaemon * dbus,const gchar * bus_name,const gchar * object_path,GError ** error)2401 _tp_connection_new_with_factory (TpSimpleClientFactory *factory,
2402     TpDBusDaemon *dbus,
2403     const gchar *bus_name,
2404     const gchar *object_path,
2405     GError **error)
2406 {
2407   gchar *dup_path = NULL;
2408   gchar *dup_name = NULL;
2409   gchar *dup_unique_name = NULL;
2410   TpConnection *ret = NULL;
2411 
2412   g_return_val_if_fail (TP_IS_DBUS_DAEMON (dbus), NULL);
2413   g_return_val_if_fail (object_path != NULL ||
2414                         (bus_name != NULL && bus_name[0] != ':'), NULL);
2415 
2416   if (object_path == NULL)
2417     {
2418       dup_path = g_strdelimit (g_strdup_printf ("/%s", bus_name), ".", '/');
2419       object_path = dup_path;
2420     }
2421   else if (bus_name == NULL)
2422     {
2423       dup_name = g_strdelimit (g_strdup (object_path + 1), "/", '.');
2424       bus_name = dup_name;
2425     }
2426 
2427   if (!_tp_connection_parse (object_path, '/', NULL, NULL))
2428     {
2429       g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_INVALID_OBJECT_PATH,
2430           "Connection object path is not in the right format");
2431       goto finally;
2432     }
2433 
2434   if (!tp_dbus_check_valid_bus_name (bus_name,
2435         TP_DBUS_NAME_TYPE_NOT_BUS_DAEMON, error))
2436     goto finally;
2437 
2438   /* Resolve unique name if necessary */
2439   if (bus_name[0] != ':')
2440     {
2441       if (!_tp_dbus_daemon_get_name_owner (dbus, 2000, bus_name,
2442           &dup_unique_name, error))
2443         goto finally;
2444 
2445       bus_name = dup_unique_name;
2446 
2447       if (!tp_dbus_check_valid_bus_name (bus_name,
2448           TP_DBUS_NAME_TYPE_UNIQUE, error))
2449         goto finally;
2450     }
2451 
2452   if (!tp_dbus_check_valid_object_path (object_path, error))
2453     goto finally;
2454 
2455   ret = TP_CONNECTION (g_object_new (TP_TYPE_CONNECTION,
2456         "dbus-daemon", dbus,
2457         "bus-name", bus_name,
2458         "object-path", object_path,
2459         "factory", factory,
2460         NULL));
2461 
2462 finally:
2463   g_free (dup_path);
2464   g_free (dup_name);
2465   g_free (dup_unique_name);
2466 
2467   return ret;
2468 }
2469 
2470 /**
2471  * tp_connection_get_account:
2472  * @self: a connection
2473  *
2474  * Return the the #TpAccount associated with this connection. Will return %NULL
2475  * if @self was not acquired from a #TpAccount via tp_account_get_connection(),
2476  * or if the account object got finalized in the meantime (#TpConnection does
2477  * not keep a strong ref on its #TpAccount).
2478  *
2479  * Returns: (transfer none): the account associated with this connection, or
2480  * %NULL.
2481  *
2482  * Since: 0.15.5
2483  */
2484 TpAccount *
tp_connection_get_account(TpConnection * self)2485 tp_connection_get_account (TpConnection *self)
2486 {
2487   g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
2488 
2489   return self->priv->account;
2490 }
2491 
2492 void
_tp_connection_set_account(TpConnection * self,TpAccount * account)2493 _tp_connection_set_account (TpConnection *self,
2494     TpAccount *account)
2495 {
2496   if (self->priv->account == account)
2497     return;
2498 
2499   g_assert (self->priv->account == NULL);
2500   g_assert (account != NULL);
2501 
2502   self->priv->account = account;
2503   g_object_add_weak_pointer ((GObject *) account,
2504       (gpointer) &self->priv->account);
2505 }
2506 
2507 /**
2508  * tp_connection_get_self_handle:
2509  * @self: a connection
2510  *
2511  * Return the %TP_HANDLE_TYPE_CONTACT handle of the local user on this
2512  * connection, or 0 if the self-handle is not known yet or the connection
2513  * has become invalid (the TpProxy::invalidated signal).
2514  *
2515  * The returned handle is not necessarily valid forever (the
2516  * notify::self-handle signal will be emitted if it changes, which can happen
2517  * on protocols such as IRC). Construct a #TpContact object if you want to
2518  * track the local user's identifier in the protocol, or other information
2519  * like their presence status, over time.
2520  *
2521  * Returns: the value of the TpConnection:self-handle property
2522  *
2523  * Since: 0.7.26
2524  * Deprecated: Use tp_connection_get_self_contact() instead.
2525  */
2526 TpHandle
tp_connection_get_self_handle(TpConnection * self)2527 tp_connection_get_self_handle (TpConnection *self)
2528 {
2529   g_return_val_if_fail (TP_IS_CONNECTION (self), 0);
2530 
2531   if (self->priv->self_contact == NULL)
2532     return 0;
2533 
2534   return tp_contact_get_handle (self->priv->self_contact);
2535 }
2536 
2537 /**
2538  * tp_connection_get_status:
2539  * @self: a connection
2540  * @reason: (out): a TpConnectionStatusReason, or %NULL
2541  *
2542  * If @reason is not %NULL it is set to the reason why "status" changed to its
2543  * current value, or %TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED if unknown.
2544  *
2545  * Returns: This connection's status, or %TP_UNKNOWN_CONNECTION_STATUS if we
2546  * don't know yet.
2547  *
2548  * Since: 0.7.14
2549  */
2550 TpConnectionStatus
tp_connection_get_status(TpConnection * self,TpConnectionStatusReason * reason)2551 tp_connection_get_status (TpConnection *self,
2552                           TpConnectionStatusReason *reason)
2553 {
2554   g_return_val_if_fail (TP_IS_CONNECTION (self), TP_UNKNOWN_CONNECTION_STATUS);
2555 
2556   if (reason != NULL)
2557     *reason = self->priv->status_reason;
2558 
2559   return self->priv->status;
2560 }
2561 
2562 /**
2563  * tp_connection_get_connection_manager_name:
2564  * @self: a #TpConnection
2565  *
2566  * <!-- -->
2567  *
2568  * Returns: the same as the #TpConnection:connection-manager-name property
2569  *
2570  * Since: 0.13.16
2571  * Deprecated: Use tp_connection_get_cm_name() instead.
2572  *
2573  */
2574 const gchar *
tp_connection_get_connection_manager_name(TpConnection * self)2575 tp_connection_get_connection_manager_name (TpConnection *self)
2576 {
2577     g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
2578 
2579     return self->priv->cm_name;
2580 }
2581 
2582 /**
2583  * tp_connection_get_cm_name:
2584  * @self: a #TpConnection
2585  *
2586  * <!-- -->
2587  *
2588  * Returns: the same as the #TpConnection:cm-name property
2589  *
2590  * Since: 0.19.3
2591  *
2592  */
2593 const gchar *
tp_connection_get_cm_name(TpConnection * self)2594 tp_connection_get_cm_name (TpConnection *self)
2595 {
2596     g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
2597 
2598     return self->priv->cm_name;
2599 }
2600 
2601 /**
2602  * tp_connection_get_protocol_name:
2603  * @self: a #TpConnection
2604  *
2605  * <!-- -->
2606  *
2607  * Returns: the same as the #TpConnection:protocol-name property
2608  *
2609  * Since: 0.13.16
2610  *
2611  */
2612 const gchar *
tp_connection_get_protocol_name(TpConnection * self)2613 tp_connection_get_protocol_name (TpConnection *self)
2614 {
2615     g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
2616 
2617     return self->priv->proto_name;
2618 }
2619 
2620 /**
2621  * tp_connection_run_until_ready: (skip)
2622  * @self: a connection
2623  * @connect: if %TRUE, call Connect() if it appears to be necessary;
2624  *  if %FALSE, rely on Connect() to be called by another client
2625  * @error: if not %NULL and %FALSE is returned, used to raise an error
2626  * @loop: if not %NULL, a #GMainLoop is placed here while it is being run
2627  *  (so calling code can call g_main_loop_quit() to abort), and %NULL is
2628  *  placed here after the loop has been run
2629  *
2630  * If @self is connected and ready for use, return immediately. Otherwise,
2631  * call Connect() (unless @connect is %FALSE) and re-enter the main loop
2632  * until the connection becomes invalid, the connection connects successfully
2633  * and is introspected, or the main loop stored via @loop is cancelled.
2634  *
2635  * Returns: %TRUE if the connection is now connected and ready for use,
2636  *  %FALSE if the connection has become invalid.
2637  *
2638  * Since: 0.7.1
2639  * Deprecated: 0.11.0: Use tp_proxy_prepare_async() and re-enter the main
2640  *  loop yourself, or restructure your program in such a way as to avoid
2641  *  re-entering the main loop.
2642  */
2643 
2644 typedef struct {
2645     GMainLoop *loop;
2646     TpProxyPendingCall *pc;
2647     GError *connect_error /* gets initialized */;
2648 } RunUntilReadyData;
2649 
2650 static void
run_until_ready_ret(TpConnection * self,const GError * error,gpointer user_data,GObject * weak_object)2651 run_until_ready_ret (TpConnection *self,
2652                      const GError *error,
2653                      gpointer user_data,
2654                      GObject *weak_object)
2655 {
2656   RunUntilReadyData *data = user_data;
2657 
2658   if (error != NULL)
2659     {
2660       g_main_loop_quit (data->loop);
2661       data->connect_error = g_error_copy (error);
2662     }
2663 }
2664 
2665 static void
run_until_ready_destroy(gpointer p)2666 run_until_ready_destroy (gpointer p)
2667 {
2668   RunUntilReadyData *data = p;
2669 
2670   data->pc = NULL;
2671 }
2672 
2673 gboolean
tp_connection_run_until_ready(TpConnection * self,gboolean connect,GError ** error,GMainLoop ** loop)2674 tp_connection_run_until_ready (TpConnection *self,
2675                                gboolean connect,
2676                                GError **error,
2677                                GMainLoop **loop)
2678 {
2679   TpProxy *as_proxy = (TpProxy *) self;
2680   gulong invalidated_id, ready_id;
2681   RunUntilReadyData data = { NULL, NULL, NULL };
2682 
2683   g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
2684 
2685   if (as_proxy->invalidated)
2686     goto raise_invalidated;
2687 
2688   if (self->priv->ready)
2689     return TRUE;
2690 
2691   data.loop = g_main_loop_new (NULL, FALSE);
2692 
2693   invalidated_id = g_signal_connect_swapped (self, "invalidated",
2694       G_CALLBACK (g_main_loop_quit), data.loop);
2695   ready_id = g_signal_connect_swapped (self, "notify::connection-ready",
2696       G_CALLBACK (g_main_loop_quit), data.loop);
2697 
2698   if (self->priv->status != TP_CONNECTION_STATUS_CONNECTED &&
2699       connect)
2700     {
2701       data.pc = tp_cli_connection_call_connect (self, -1,
2702           run_until_ready_ret, &data,
2703           run_until_ready_destroy, NULL);
2704     }
2705 
2706   if (data.connect_error == NULL)
2707     {
2708       if (loop != NULL)
2709         *loop = data.loop;
2710 
2711       g_main_loop_run (data.loop);
2712 
2713       if (loop != NULL)
2714         *loop = NULL;
2715     }
2716 
2717   if (data.pc != NULL)
2718     tp_proxy_pending_call_cancel (data.pc);
2719 
2720   g_signal_handler_disconnect (self, invalidated_id);
2721   g_signal_handler_disconnect (self, ready_id);
2722   g_main_loop_unref (data.loop);
2723 
2724   if (data.connect_error != NULL)
2725     {
2726       g_propagate_error (error, data.connect_error);
2727       return FALSE;
2728     }
2729 
2730   if (as_proxy->invalidated != NULL)
2731     goto raise_invalidated;
2732 
2733   if (self->priv->ready)
2734     return TRUE;
2735 
2736   g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_CANCELLED,
2737       "tp_connection_run_until_ready() cancelled");
2738   return FALSE;
2739 
2740 raise_invalidated:
2741   if (error != NULL)
2742     {
2743       g_return_val_if_fail (*error == NULL, FALSE);
2744       *error = g_error_copy (as_proxy->invalidated);
2745     }
2746 
2747   return FALSE;
2748 }
2749 
2750 /**
2751  * TpConnectionNameListCb:
2752  * @names: (array zero-terminated=1): %NULL-terminated array of @n
2753  *  connection bus names, or %NULL on error
2754  * @n: number of names (not including the final %NULL), or 0 on error
2755  * @cms: (array zero-terminated=1): %NULL-terminated array of @n
2756  *  connection manager names (e.g. "gabble") in the same order as @names, or
2757  *  %NULL on error
2758  * @protocols: (array zero-terminated=1): %NULL-terminated array of
2759  *  @n protocol names as defined in the Telepathy spec (e.g. "jabber") in the
2760  *  same order as @names, or %NULL on error
2761  * @error: %NULL on success, or an error that occurred
2762  * @user_data: user-supplied data
2763  * @weak_object: user-supplied weakly referenced object
2764  *
2765  * Signature of the callback supplied to tp_list_connection_names().
2766  *
2767  * Since: 0.7.1
2768  */
2769 
2770 typedef struct {
2771     TpConnectionNameListCb callback;
2772     gpointer user_data;
2773     GDestroyNotify destroy;
2774 } _ListContext;
2775 
2776 static gboolean
_tp_connection_parse(const gchar * path_or_bus_name,char delimiter,gchar ** protocol,gchar ** cm_name)2777 _tp_connection_parse (const gchar *path_or_bus_name,
2778                       char delimiter,
2779                       gchar **protocol,
2780                       gchar **cm_name)
2781 {
2782   const gchar *prefix;
2783   const gchar *cm_name_start;
2784   const gchar *protocol_start;
2785   const gchar *account_start;
2786   gchar *dup_cm_name = NULL;
2787   gchar *dup_protocol = NULL;
2788 
2789   g_return_val_if_fail (delimiter == '.' || delimiter == '/', FALSE);
2790 
2791   /* If CM respects the spec, object path and bus name should be in the form:
2792    * /org/freedesktop/Telepathy/Connection/cmname/proto/account
2793    * org.freedesktop.Telepathy.Connection.cmname.proto.account
2794    */
2795   if (delimiter == '.')
2796     prefix = TP_CONN_BUS_NAME_BASE;
2797   else
2798     prefix = TP_CONN_OBJECT_PATH_BASE;
2799 
2800   if (!g_str_has_prefix (path_or_bus_name, prefix))
2801     goto OUT;
2802 
2803   cm_name_start = path_or_bus_name + strlen (prefix);
2804   protocol_start = strchr (cm_name_start, delimiter);
2805   if (protocol_start == NULL)
2806     goto OUT;
2807   protocol_start++;
2808 
2809   account_start = strchr (protocol_start, delimiter);
2810   if (account_start == NULL)
2811     goto OUT;
2812   account_start++;
2813 
2814   dup_cm_name = g_strndup (cm_name_start, protocol_start - cm_name_start - 1);
2815   if (!tp_connection_manager_check_valid_name (dup_cm_name, NULL))
2816     {
2817       g_free (dup_cm_name);
2818       dup_cm_name = NULL;
2819       goto OUT;
2820     }
2821 
2822   dup_protocol = g_strndup (protocol_start, account_start - protocol_start - 1);
2823   if (!tp_strdiff (dup_protocol, "local_2dxmpp"))
2824     {
2825       /* the CM's telepathy-glib is older than 0.7.x, work around it.
2826        * FIXME: Remove this workaround in 0.9.x */
2827       g_free (dup_protocol);
2828       dup_protocol = g_strdup ("local-xmpp");
2829     }
2830   else
2831     {
2832       /* the real protocol name may have "-" in; bus names may not, but
2833        * they may have "_", so the Telepathy spec specifies replacement.
2834        * Here we need to undo that replacement */
2835       g_strdelimit (dup_protocol, "_", '-');
2836     }
2837 
2838   if (!tp_connection_manager_check_valid_protocol_name (dup_protocol, NULL))
2839     {
2840       g_free (dup_protocol);
2841       dup_protocol = NULL;
2842       goto OUT;
2843     }
2844 
2845 OUT:
2846 
2847   if (dup_protocol == NULL || dup_cm_name == NULL)
2848     {
2849       g_free (dup_protocol);
2850       g_free (dup_cm_name);
2851       return FALSE;
2852     }
2853 
2854   if (cm_name != NULL)
2855     *cm_name = dup_cm_name;
2856   else
2857     g_free (dup_cm_name);
2858 
2859   if (protocol != NULL)
2860     *protocol = dup_protocol;
2861   else
2862     g_free (dup_protocol);
2863 
2864   return TRUE;
2865 }
2866 
2867 static void
tp_list_connection_names_helper(TpDBusDaemon * bus_daemon,const gchar * const * names,const GError * error,gpointer user_data,GObject * user_object)2868 tp_list_connection_names_helper (TpDBusDaemon *bus_daemon,
2869                                  const gchar * const *names,
2870                                  const GError *error,
2871                                  gpointer user_data,
2872                                  GObject *user_object)
2873 {
2874   _ListContext *list_context = user_data;
2875   const gchar * const *iter;
2876   /* array of borrowed strings */
2877   GPtrArray *bus_names;
2878   /* array of dup'd strings */
2879   GPtrArray *cms;
2880   /* array of borrowed strings */
2881   GPtrArray *protocols;
2882 
2883   if (error != NULL)
2884     {
2885       list_context->callback (NULL, 0, NULL, NULL, error,
2886           list_context->user_data, user_object);
2887       return;
2888     }
2889 
2890   bus_names = g_ptr_array_new ();
2891   cms = g_ptr_array_new ();
2892   protocols = g_ptr_array_new ();
2893 
2894   for (iter = names; iter != NULL && *iter != NULL; iter++)
2895     {
2896       gchar *proto, *cm_name;
2897 
2898       if (_tp_connection_parse (*iter, '.', &proto, &cm_name))
2899         {
2900           /* the casts here are because g_ptr_array contains non-const pointers -
2901            * but in this case I'll only be passing pdata to a callback with const
2902            * arguments, so it's fine */
2903           g_ptr_array_add (bus_names, (gpointer) *iter);
2904           g_ptr_array_add (cms, cm_name);
2905           g_ptr_array_add (protocols, proto);
2906           continue;
2907         }
2908     }
2909 
2910   g_ptr_array_add (bus_names, NULL);
2911   g_ptr_array_add (cms, NULL);
2912   g_ptr_array_add (protocols, NULL);
2913 
2914   list_context->callback ((const gchar * const *) bus_names->pdata,
2915       bus_names->len - 1, (const gchar * const *) cms->pdata,
2916       (const gchar * const *) protocols->pdata,
2917       NULL, list_context->user_data, user_object);
2918 
2919   g_ptr_array_unref (bus_names);
2920   g_strfreev ((char **) g_ptr_array_free (cms, FALSE));
2921   g_strfreev ((char **) g_ptr_array_free (protocols, FALSE));
2922 }
2923 
2924 static void
list_context_free(gpointer p)2925 list_context_free (gpointer p)
2926 {
2927   _ListContext *list_context = p;
2928 
2929   if (list_context->destroy != NULL)
2930     list_context->destroy (list_context->user_data);
2931 
2932   g_slice_free (_ListContext, list_context);
2933 }
2934 
2935 /**
2936  * tp_list_connection_names:
2937  * @bus_daemon: proxy for the D-Bus daemon
2938  * @callback: callback to be called when listing the connections succeeds or
2939  *   fails; not called if the D-Bus connection fails completely or if the
2940  *   @weak_object goes away
2941  * @user_data: user-supplied data for the callback
2942  * @destroy: callback to destroy the user-supplied data, called after
2943  *   @callback, but also if the D-Bus connection fails or if the @weak_object
2944  *   goes away
2945  * @weak_object: (allow-none): if not %NULL, will be weakly referenced; the callback will
2946  *   not be called if the object has vanished
2947  *
2948  * List the bus names of all the connections that currently exist, together
2949  * with the connection manager name and the protocol name for each connection.
2950  * Call the callback when done.
2951  *
2952  * The bus names passed to the callback can be used to construct #TpConnection
2953  * objects for any connections that are of interest.
2954  *
2955  * Since: 0.7.1
2956  */
2957 void
tp_list_connection_names(TpDBusDaemon * bus_daemon,TpConnectionNameListCb callback,gpointer user_data,GDestroyNotify destroy,GObject * weak_object)2958 tp_list_connection_names (TpDBusDaemon *bus_daemon,
2959                           TpConnectionNameListCb callback,
2960                           gpointer user_data,
2961                           GDestroyNotify destroy,
2962                           GObject *weak_object)
2963 {
2964   _ListContext *list_context = g_slice_new0 (_ListContext);
2965 
2966   g_return_if_fail (TP_IS_DBUS_DAEMON (bus_daemon));
2967   g_return_if_fail (callback != NULL);
2968 
2969   list_context->callback = callback;
2970   list_context->user_data = user_data;
2971 
2972   tp_dbus_daemon_list_names (bus_daemon, 2000,
2973       tp_list_connection_names_helper, list_context,
2974       list_context_free, weak_object);
2975 }
2976 
2977 static gpointer
tp_connection_once(gpointer data G_GNUC_UNUSED)2978 tp_connection_once (gpointer data G_GNUC_UNUSED)
2979 {
2980   GType type = TP_TYPE_CONNECTION;
2981 
2982   tp_proxy_init_known_interfaces ();
2983 
2984   tp_proxy_or_subclass_hook_on_interface_add (type,
2985       tp_cli_connection_add_signals);
2986   tp_proxy_subclass_add_error_mapping (type,
2987       TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR);
2988 
2989   return NULL;
2990 }
2991 
2992 /**
2993  * tp_connection_init_known_interfaces:
2994  *
2995  * Ensure that the known interfaces for TpConnection have been set up.
2996  * This is done automatically when necessary, but for correct
2997  * overriding of library interfaces by local extensions, you should
2998  * call this function before calling
2999  * tp_proxy_or_subclass_hook_on_interface_add() with first argument
3000  * %TP_TYPE_CONNECTION.
3001  *
3002  * Since: 0.7.6
3003  */
3004 void
tp_connection_init_known_interfaces(void)3005 tp_connection_init_known_interfaces (void)
3006 {
3007   static GOnce once = G_ONCE_INIT;
3008 
3009   g_once (&once, tp_connection_once, NULL);
3010 }
3011 
3012 typedef struct {
3013     TpConnectionWhenReadyCb callback;
3014     gpointer user_data;
3015     gulong invalidated_id;
3016     gulong ready_id;
3017 } CallWhenReadyContext;
3018 
3019 static void
cwr_invalidated(TpConnection * self,guint domain,gint code,gchar * message,gpointer user_data)3020 cwr_invalidated (TpConnection *self,
3021                  guint domain,
3022                  gint code,
3023                  gchar *message,
3024                  gpointer user_data)
3025 {
3026   CallWhenReadyContext *ctx = user_data;
3027   GError e = { domain, code, message };
3028 
3029   DEBUG ("enter");
3030 
3031   g_assert (ctx->callback != NULL);
3032 
3033   ctx->callback (self, &e, ctx->user_data);
3034 
3035   g_signal_handler_disconnect (self, ctx->invalidated_id);
3036   g_signal_handler_disconnect (self, ctx->ready_id);
3037 
3038   ctx->callback = NULL;   /* poison it to detect errors */
3039   g_slice_free (CallWhenReadyContext, ctx);
3040 }
3041 
3042 static void
cwr_ready(TpConnection * self,GParamSpec * unused G_GNUC_UNUSED,gpointer user_data)3043 cwr_ready (TpConnection *self,
3044            GParamSpec *unused G_GNUC_UNUSED,
3045            gpointer user_data)
3046 {
3047   CallWhenReadyContext *ctx = user_data;
3048 
3049   DEBUG ("enter");
3050 
3051   g_assert (ctx->callback != NULL);
3052 
3053   ctx->callback (self, NULL, ctx->user_data);
3054 
3055   g_signal_handler_disconnect (self, ctx->invalidated_id);
3056   g_signal_handler_disconnect (self, ctx->ready_id);
3057 
3058   ctx->callback = NULL;   /* poison it to detect errors */
3059   g_slice_free (CallWhenReadyContext, ctx);
3060 }
3061 
3062 /**
3063  * TpConnectionWhenReadyCb:
3064  * @connection: the connection (which may be in the middle of being disposed,
3065  *  if error is non-%NULL, error->domain is TP_DBUS_ERRORS and error->code is
3066  *  TP_DBUS_ERROR_PROXY_UNREFERENCED)
3067  * @error: %NULL if the connection is ready for use, or the error with which
3068  *  it was invalidated if it is now invalid
3069  * @user_data: whatever was passed to tp_connection_call_when_ready()
3070  *
3071  * Signature of a callback passed to tp_connection_call_when_ready(), which
3072  * will be called exactly once, when the connection becomes ready or
3073  * invalid (whichever happens first)
3074  *
3075  * Deprecated: 0.17.6
3076  */
3077 
3078 /**
3079  * tp_connection_call_when_ready: (skip)
3080  * @self: a connection
3081  * @callback: called when the connection becomes ready or invalidated,
3082  *  whichever happens first
3083  * @user_data: arbitrary user-supplied data passed to the callback
3084  *
3085  * If @self is ready for use or has been invalidated, call @callback
3086  * immediately, then return. Otherwise, arrange
3087  * for @callback to be called when @self either becomes ready for use
3088  * or becomes invalid.
3089  *
3090  * Note that if the connection is not in state CONNECTED, the callback will
3091  * not be called until the connection either goes to state CONNECTED
3092  * or is invalidated (e.g. by going to state DISCONNECTED or by becoming
3093  * unreferenced). In particular, this method does not call Connect().
3094  * Call tp_cli_connection_call_connect() too, if you want to do that.
3095  *
3096  * Since: 0.7.7
3097  * Deprecated: 0.17.6: Use tp_proxy_prepare_async()
3098  */
3099 void
tp_connection_call_when_ready(TpConnection * self,TpConnectionWhenReadyCb callback,gpointer user_data)3100 tp_connection_call_when_ready (TpConnection *self,
3101                                TpConnectionWhenReadyCb callback,
3102                                gpointer user_data)
3103 {
3104   TpProxy *as_proxy = (TpProxy *) self;
3105 
3106   g_return_if_fail (TP_IS_CONNECTION (self));
3107   g_return_if_fail (callback != NULL);
3108 
3109   if (self->priv->ready || as_proxy->invalidated != NULL)
3110     {
3111       DEBUG ("already ready or invalidated");
3112       callback (self, as_proxy->invalidated, user_data);
3113     }
3114   else
3115     {
3116       CallWhenReadyContext *ctx = g_slice_new (CallWhenReadyContext);
3117 
3118       DEBUG ("arranging callback later");
3119 
3120       ctx->callback = callback;
3121       ctx->user_data = user_data;
3122       ctx->invalidated_id = g_signal_connect (self, "invalidated",
3123           G_CALLBACK (cwr_invalidated), ctx);
3124       ctx->ready_id = g_signal_connect (self, "notify::connection-ready",
3125           G_CALLBACK (cwr_ready), ctx);
3126     }
3127 }
3128 
3129 static guint
get_presence_type_availability(TpConnectionPresenceType type)3130 get_presence_type_availability (TpConnectionPresenceType type)
3131 {
3132   switch (type)
3133     {
3134       case TP_CONNECTION_PRESENCE_TYPE_UNSET:
3135         return 0;
3136       case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
3137         return 1;
3138       case TP_CONNECTION_PRESENCE_TYPE_ERROR:
3139         return 2;
3140       case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
3141         return 3;
3142       case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
3143         return 4;
3144       case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
3145         return 5;
3146       case TP_CONNECTION_PRESENCE_TYPE_AWAY:
3147         return 6;
3148       case TP_CONNECTION_PRESENCE_TYPE_BUSY:
3149         return 7;
3150       case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
3151         return 8;
3152     }
3153 
3154   /* This is an unexpected presence type, treat it like UNKNOWN */
3155   return 1;
3156 }
3157 
3158 /**
3159  * tp_connection_presence_type_cmp_availability:
3160  * @p1: a #TpConnectionPresenceType
3161  * @p2: a #TpConnectionPresenceType
3162  *
3163  * Compares @p1 and @p2 like strcmp(). @p1 > @p2 means @p1 is more available
3164  * than @p2.
3165  *
3166  * The order used is: available > busy > away > xa > hidden > offline > error >
3167  * unknown > unset
3168  *
3169  * Returns: -1, 0 or 1, if @p1 is <, == or > than @p2.
3170  *
3171  * Since: 0.7.16
3172  */
3173 gint
tp_connection_presence_type_cmp_availability(TpConnectionPresenceType p1,TpConnectionPresenceType p2)3174 tp_connection_presence_type_cmp_availability (TpConnectionPresenceType p1,
3175                                               TpConnectionPresenceType p2)
3176 {
3177   guint availability1;
3178   guint availability2;
3179 
3180   availability1 = get_presence_type_availability (p1);
3181   availability2 = get_presence_type_availability (p2);
3182 
3183   if (availability1 < availability2)
3184     return -1;
3185 
3186   if (availability1 > availability2)
3187     return +1;
3188 
3189   return 0;
3190 }
3191 
3192 
3193 /**
3194  * tp_connection_parse_object_path:
3195  * @self: a connection
3196  * @protocol: (out) (transfer full): If not NULL, used to return the protocol
3197  *  of the connection
3198  * @cm_name: (out) (transfer full): If not NULL, used to return the connection
3199  *  manager name of the connection
3200  *
3201  * If the object path of @connection is in the correct form, set
3202  * @protocol and @cm_name, return TRUE. Otherwise leave them unchanged and
3203  * return FALSE.
3204  *
3205  * Returns: TRUE if the object path was correctly parsed, FALSE otherwise.
3206  *
3207  * Since: 0.7.27
3208  * Deprecated: Use tp_connection_get_protocol_name() and
3209  *  tp_connection_get_connection_manager_name() instead.
3210  */
3211 gboolean
tp_connection_parse_object_path(TpConnection * self,gchar ** protocol,gchar ** cm_name)3212 tp_connection_parse_object_path (TpConnection *self,
3213                                  gchar **protocol,
3214                                  gchar **cm_name)
3215 {
3216   g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
3217 
3218   if (protocol != NULL)
3219     *protocol = g_strdup (self->priv->proto_name);
3220 
3221   if (cm_name != NULL)
3222     *cm_name = g_strdup (self->priv->cm_name);
3223 
3224   return TRUE;
3225 }
3226 
3227 /* Can return a contact that's not meant to be visible to library users
3228  * because it lacks an identifier */
3229 TpContact *
_tp_connection_lookup_contact(TpConnection * self,TpHandle handle)3230 _tp_connection_lookup_contact (TpConnection *self,
3231                                TpHandle handle)
3232 {
3233   g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
3234 
3235   return g_hash_table_lookup (self->priv->contacts, GUINT_TO_POINTER (handle));
3236 }
3237 
3238 
3239 /* this could be done with proper weak references, but we know that every
3240  * connection will weakly reference all its contacts, so we can just do this
3241  * explicitly in tp_contact_dispose */
3242 void
_tp_connection_remove_contact(TpConnection * self,TpHandle handle,TpContact * contact)3243 _tp_connection_remove_contact (TpConnection *self,
3244                                TpHandle handle,
3245                                TpContact *contact)
3246 {
3247   TpContact *mine;
3248 
3249   g_return_if_fail (TP_IS_CONNECTION (self));
3250   g_return_if_fail (TP_IS_CONTACT (contact));
3251 
3252   mine = g_hash_table_lookup (self->priv->contacts, GUINT_TO_POINTER (handle));
3253   g_return_if_fail (mine == contact);
3254   g_hash_table_remove (self->priv->contacts, GUINT_TO_POINTER (handle));
3255 }
3256 
3257 
3258 void
_tp_connection_add_contact(TpConnection * self,TpHandle handle,TpContact * contact)3259 _tp_connection_add_contact (TpConnection *self,
3260                             TpHandle handle,
3261                             TpContact *contact)
3262 {
3263   g_return_if_fail (TP_IS_CONNECTION (self));
3264   g_return_if_fail (TP_IS_CONTACT (contact));
3265   g_return_if_fail (g_hash_table_lookup (self->priv->contacts,
3266         GUINT_TO_POINTER (handle)) == NULL);
3267 
3268   g_hash_table_insert (self->priv->contacts, GUINT_TO_POINTER (handle),
3269       contact);
3270 
3271   /* Set TP_CONTACT_FEATURE_CONTACT_BLOCKING if possible */
3272   if (tp_proxy_is_prepared (self, TP_CONNECTION_FEATURE_CONTACT_BLOCKING))
3273     {
3274       _tp_connection_set_contact_blocked (self, contact);
3275     }
3276 }
3277 
3278 
3279 /**
3280  * tp_connection_is_ready: (skip)
3281  * @self: a connection
3282  *
3283  * Returns the same thing as the #TpConnection:connection-ready property.
3284  *
3285  * Returns: %TRUE if introspection has completed
3286  * Since: 0.7.17
3287  * Deprecated: 0.17.6: use tp_proxy_is_prepared() with
3288  *  %TP_CONNECTION_FEATURE_CONNECTED
3289  */
3290 gboolean
tp_connection_is_ready(TpConnection * self)3291 tp_connection_is_ready (TpConnection *self)
3292 {
3293   g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
3294 
3295   return self->priv->ready;
3296 }
3297 
3298 /**
3299  * tp_connection_get_capabilities:
3300  * @self: a connection
3301  *
3302  * <!-- -->
3303  *
3304  * Returns: (transfer none): the same #TpCapabilities as the
3305  * #TpConnection:capabilities property
3306  * Since: 0.11.3
3307  */
3308 TpCapabilities *
tp_connection_get_capabilities(TpConnection * self)3309 tp_connection_get_capabilities (TpConnection *self)
3310 {
3311   g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
3312 
3313   return self->priv->capabilities;
3314 }
3315 
3316 /**
3317  * tp_connection_get_detailed_error:
3318  * @self: a connection
3319  * @details: (out) (allow-none) (element-type utf8 GObject.Value) (transfer none):
3320  *  optionally used to return a map from string to #GValue, which must not be
3321  *  modified or destroyed by the caller
3322  *
3323  * If the connection has disconnected, return the D-Bus error name with which
3324  * it disconnected (in particular, this is %TP_ERROR_STR_CANCELLED if it was
3325  * disconnected by a user request).
3326  *
3327  * Otherwise, return %NULL, without altering @details.
3328  *
3329  * Returns: (transfer none) (allow-none): a D-Bus error name, or %NULL.
3330  *
3331  * Since: 0.11.4
3332  */
3333 const gchar *
tp_connection_get_detailed_error(TpConnection * self,const GHashTable ** details)3334 tp_connection_get_detailed_error (TpConnection *self,
3335     const GHashTable **details)
3336 {
3337   TpProxy *proxy = (TpProxy *) self;
3338 
3339   if (proxy->invalidated == NULL)
3340     return NULL;
3341 
3342   if (self->priv->connection_error != NULL)
3343     {
3344       g_assert (self->priv->connection_error_details != NULL);
3345 
3346       if (details != NULL)
3347         *details = self->priv->connection_error_details;
3348 
3349       return self->priv->connection_error;
3350     }
3351   else
3352     {
3353       /* no detailed error, but we *have* been invalidated - guess one based
3354        * on the invalidation reason */
3355 
3356       if (details != NULL)
3357         {
3358           if (self->priv->connection_error_details == NULL)
3359             {
3360               self->priv->connection_error_details = tp_asv_new (
3361                   "debug-message", G_TYPE_STRING, proxy->invalidated->message,
3362                   NULL);
3363             }
3364 
3365           *details = self->priv->connection_error_details;
3366         }
3367 
3368       if (proxy->invalidated->domain == TP_ERROR)
3369         {
3370           return tp_error_get_dbus_name (proxy->invalidated->code);
3371         }
3372       else if (proxy->invalidated->domain == TP_DBUS_ERRORS)
3373         {
3374           switch (proxy->invalidated->code)
3375             {
3376             case TP_DBUS_ERROR_NAME_OWNER_LOST:
3377               /* the CM probably crashed */
3378               return DBUS_ERROR_NO_REPLY;
3379               break;
3380 
3381             case TP_DBUS_ERROR_OBJECT_REMOVED:
3382             case TP_DBUS_ERROR_UNKNOWN_REMOTE_ERROR:
3383             case TP_DBUS_ERROR_INCONSISTENT:
3384             /* ... and all other cases up to and including
3385              * TP_DBUS_ERROR_INCONSISTENT don't make sense in this context, so
3386              * just use the generic one for them too */
3387             default:
3388               return TP_ERROR_STR_DISCONNECTED;
3389             }
3390         }
3391       else
3392         {
3393           /* no idea what that means */
3394           return TP_ERROR_STR_DISCONNECTED;
3395         }
3396     }
3397 }
3398 
3399 /**
3400  * tp_connection_dup_detailed_error_vardict:
3401  * @self: a connection
3402  * @details: (out) (allow-none) (transfer full):
3403  *  optionally used to return a %G_VARIANT_TYPE_VARDICT with details
3404  *  of the error
3405  *
3406  * If the connection has disconnected, return the D-Bus error name with which
3407  * it disconnected (in particular, this is %TP_ERROR_STR_CANCELLED if it was
3408  * disconnected by a user request).
3409  *
3410  * Otherwise, return %NULL, without altering @details.
3411  *
3412  * Returns: (transfer full) (allow-none): a D-Bus error name, or %NULL.
3413  *
3414  * Since: 0.19.0
3415  */
3416 gchar *
tp_connection_dup_detailed_error_vardict(TpConnection * self,GVariant ** details)3417 tp_connection_dup_detailed_error_vardict (TpConnection *self,
3418     GVariant **details)
3419 {
3420   const GHashTable *asv;
3421   const gchar *error = tp_connection_get_detailed_error (self, &asv);
3422 
3423   if (error == NULL)
3424     return NULL;
3425 
3426   if (details != NULL)
3427     *details = _tp_asv_to_vardict (asv);
3428 
3429   return g_strdup (error);
3430 }
3431 
3432 /**
3433  * tp_connection_add_client_interest:
3434  * @self: a connection
3435  * @interested_in: a string identifying an interface or part of an interface
3436  *  to which this connection will subscribe
3437  *
3438  * Subscribe to any opt-in change notifications for @interested_in.
3439  *
3440  * For contact information, use #TpContact instead, which will call this
3441  * automatically.
3442  *
3443  * Since: 0.11.3
3444  */
3445 void
tp_connection_add_client_interest(TpConnection * self,const gchar * interested_in)3446 tp_connection_add_client_interest (TpConnection *self,
3447     const gchar *interested_in)
3448 {
3449   tp_connection_add_client_interest_by_id (self,
3450       g_quark_from_string (interested_in));
3451 }
3452 
3453 /**
3454  * tp_connection_add_client_interest_by_id: (skip)
3455  * @self: a connection
3456  * @interested_in: a quark identifying an interface or part of an interface
3457  *  to which this connection will subscribe
3458  *
3459  * Subscribe to any opt-in change notifications for @interested_in.
3460  *
3461  * Equivalent to, but a little more efficient than, calling
3462  * tp_connection_add_client_interest() for the string value of @interested_in.
3463  *
3464  * Since: 0.11.3
3465  */
3466 void
tp_connection_add_client_interest_by_id(TpConnection * self,GQuark interested_in)3467 tp_connection_add_client_interest_by_id (TpConnection *self,
3468     GQuark interested_in)
3469 {
3470   TpProxy *proxy = (TpProxy *) self;
3471   const gchar *interest = g_quark_to_string (interested_in);
3472   const gchar *strv[2] = { interest, NULL };
3473 
3474   g_return_if_fail (TP_IS_CONNECTION (self));
3475   g_return_if_fail (interest != NULL);
3476 
3477   if (proxy->invalidated != NULL ||
3478       tp_intset_is_member (self->priv->interests, interested_in))
3479     return;
3480 
3481   tp_intset_add (self->priv->interests, interested_in);
3482 
3483   /* no-reply flag set, and we ignore any reply */
3484   tp_cli_connection_call_add_client_interest (self, -1,
3485       strv, NULL, NULL, NULL, NULL);
3486 }
3487 
3488 /**
3489  * tp_connection_has_immortal_handles:
3490  * @self: a connection
3491  *
3492  * Return %TRUE if this connection is known to not destroy handles
3493  * (#TpHandle) until it disconnects.
3494  *
3495  * On such connections, if you know that a handle maps to a particular
3496  * identifier now, then you can rely on that handle mapping to that
3497  * identifier for the whole lifetime of the connection.
3498  *
3499  * Returns: %TRUE if handles last as long as the connection itself
3500  */
3501 gboolean
tp_connection_has_immortal_handles(TpConnection * self)3502 tp_connection_has_immortal_handles (TpConnection *self)
3503 {
3504   g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
3505 
3506   return self->priv->has_immortal_handles;
3507 }
3508 
3509 /**
3510  * tp_connection_get_self_contact:
3511  * @self: a connection
3512  *
3513  * Return a #TpContact representing the local user on this connection.
3514  *
3515  * The returned object is not necessarily valid after the main loop is
3516  * re-entered; ref it with g_object_ref() if you want to keep it.
3517  *
3518  * Returns: (transfer none): the value of the TpConnection:self-contact
3519  *  property, which may be %NULL
3520  *
3521  * Since: 0.13.9
3522  */
3523 TpContact *
tp_connection_get_self_contact(TpConnection * self)3524 tp_connection_get_self_contact (TpConnection *self)
3525 {
3526   g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
3527   return self->priv->self_contact;
3528 }
3529 
3530 /**
3531  * tp_connection_bind_connection_status_to_property:
3532  * @self: a #TpConnection
3533  * @target: the target #GObject
3534  * @target_property: the property on @target to bind (must be %G_TYPE_BOOLEAN)
3535  * @invert: %TRUE if you wish to invert the value of @target_property
3536  *   (i.e. %FALSE if connected)
3537  *
3538  * Binds the :status of @self to the boolean property of another
3539  * object using a #GBinding such that the @target_property will be set to
3540  * %TRUE when @self is connected (and @invert is %FALSE).
3541  *
3542  * @target_property will be synchronised immediately (%G_BINDING_SYNC_CREATE).
3543  * @invert can be interpreted as analogous to %G_BINDING_INVERT_BOOLEAN.
3544  *
3545  * For instance, this function can be used to bind the GtkWidget:sensitive
3546  * property to only make a widget sensitive when the account is connected.
3547  *
3548  * See g_object_bind_property() for more information.
3549  *
3550  * Returns: (transfer none): the #GBinding instance representing the binding
3551  *   between the @self and the @target. The binding is released whenever the
3552  *   #GBinding reference count reaches zero.
3553  * Since: 0.13.16
3554  */
3555 GBinding *
tp_connection_bind_connection_status_to_property(TpConnection * self,gpointer target,const char * target_property,gboolean invert)3556 tp_connection_bind_connection_status_to_property (TpConnection *self,
3557     gpointer target,
3558     const char *target_property,
3559     gboolean invert)
3560 {
3561   g_return_val_if_fail (TP_IS_CONNECTION (self), NULL);
3562 
3563   return g_object_bind_property_full (self, "status",
3564       target, target_property,
3565       G_BINDING_SYNC_CREATE,
3566       _tp_bind_connection_status_to_boolean,
3567       NULL, GUINT_TO_POINTER (invert), NULL);
3568 }
3569 
3570 /**
3571  * tp_connection_get_balance:
3572  * @self: a #TpConnection
3573  * @balance: (out): a pointer to store the account balance (or %NULL)
3574  * @scale: (out): a pointer to store the balance scale (or %NULL)
3575  * @currency: (out) (transfer none): a pointer to store the balance
3576  *   currency (or %NULL)
3577  *
3578  * If @self has a valid account balance, returns %TRUE and sets the variables
3579  * pointed to by @balance, @scale and @currency to the appropriate fields
3580  * of the Balance.AccountBalance property.
3581  *
3582  * The monetary value of the balance is expressed as a fixed-point number,
3583  * @balance, with a decimal scale defined by @scale; for instance a @balance
3584  * of 1234 with @scale of 2 represents a value of "12.34" in the currency
3585  * represented by @currency.
3586  *
3587  * Requires %TP_CONNECTION_FEATURE_BALANCE to be prepared.
3588  *
3589  * Returns: %TRUE if the balance is valid (and the values set), %FALSE if the
3590  *   balance is invalid.
3591  * Since: 0.15.1
3592  */
3593 gboolean
tp_connection_get_balance(TpConnection * self,gint * balance,guint * scale,const gchar ** currency)3594 tp_connection_get_balance (TpConnection *self,
3595     gint *balance,
3596     guint *scale,
3597     const gchar **currency)
3598 {
3599   g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
3600 
3601   if (self->priv->balance_currency == NULL)
3602     return FALSE;
3603 
3604   if (self->priv->balance == 0 &&
3605       self->priv->balance_scale == G_MAXUINT32 &&
3606       tp_str_empty (self->priv->balance_currency))
3607     return FALSE;
3608 
3609   if (balance != NULL)
3610     *balance = self->priv->balance;
3611 
3612   if (scale != NULL)
3613     *scale = self->priv->balance_scale;
3614 
3615   if (currency != NULL)
3616     *currency = self->priv->balance_currency;
3617 
3618   return TRUE;
3619 }
3620 
3621 /**
3622  * tp_connection_get_balance_uri:
3623  * @self: a #TpConnection
3624  *
3625  * The value of Balance.ManageCreditURI.
3626  *
3627  * Requires %TP_CONNECTION_FEATURE_BALANCE to be prepared.
3628  *
3629  * Returns: (transfer none): the #TpConnection:balance-uri property.
3630  * Since: 0.15.1
3631  */
3632 const gchar *
tp_connection_get_balance_uri(TpConnection * self)3633 tp_connection_get_balance_uri (TpConnection *self)
3634 {
3635   g_return_val_if_fail (TP_IS_CONNECTION (self), FALSE);
3636 
3637   return self->priv->balance_uri;
3638 }
3639 
3640 static void
_tp_connection_void_cb(TpConnection * proxy,const GError * error,gpointer user_data,GObject * weak_object)3641 _tp_connection_void_cb (TpConnection *proxy,
3642     const GError *error,
3643     gpointer user_data,
3644     GObject *weak_object)
3645 {
3646   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
3647 
3648   if (error != NULL)
3649     g_simple_async_result_set_from_error (result, error);
3650 
3651   g_simple_async_result_complete_in_idle (result);
3652   g_object_unref (G_OBJECT (result));
3653 }
3654 
3655 /**
3656  * tp_connection_disconnect_async:
3657  * @self: a #TpConnection
3658  * @callback: a callback to call when the request is satisfied
3659  * @user_data: data to pass to @callback
3660  *
3661  * Disconnect the connection.
3662  *
3663  * This method is intended for use by AccountManager implementations,
3664  * such as Mission Control. To disconnect a connection managed by an
3665  * AccountManager, either use tp_account_request_presence_async()
3666  * or tp_account_set_enabled_async(), depending whether the intention is
3667  * to put the account offline temporarily, or disable it longer-term.
3668  *
3669  * Since: 0.17.5
3670  */
3671 void
tp_connection_disconnect_async(TpConnection * self,GAsyncReadyCallback callback,gpointer user_data)3672 tp_connection_disconnect_async (TpConnection *self,
3673     GAsyncReadyCallback callback,
3674     gpointer user_data)
3675 {
3676   GSimpleAsyncResult *result;
3677 
3678   g_return_if_fail (TP_IS_CONNECTION (self));
3679 
3680   result = g_simple_async_result_new (G_OBJECT (self), callback,
3681       user_data, tp_connection_disconnect_async);
3682 
3683   tp_cli_connection_call_disconnect (self, -1, _tp_connection_void_cb, result,
3684       NULL, NULL);
3685 }
3686 
3687 /**
3688  * tp_connection_disconnect_finish:
3689  * @self: a #TpConnection
3690  * @result: a #GAsyncResult
3691  * @error: a #GError to fill
3692  *
3693  * Interpret the result of tp_connection_disconnect_async().
3694  *
3695  * Returns: %TRUE if the call was successful, otherwise %FALSE
3696  *
3697  * Since: 0.17.5
3698  */
3699 gboolean
tp_connection_disconnect_finish(TpConnection * self,GAsyncResult * result,GError ** error)3700 tp_connection_disconnect_finish (TpConnection *self,
3701     GAsyncResult *result,
3702     GError **error)
3703 {
3704   _tp_implement_finish_void (self, tp_connection_disconnect_async);
3705 }
3706