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, ¤cy);
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