1 /*
2  * proxy.c - Base class for Telepathy client proxies
3  *
4  * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/>
5  * Copyright (C) 2007-2008 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/proxy-subclass.h"
25 #include "telepathy-glib/proxy-internal.h"
26 
27 #include <string.h>
28 
29 #include <telepathy-glib/interfaces.h>
30 #include <telepathy-glib/automatic-client-factory.h>
31 #include <telepathy-glib/util.h>
32 
33 #include "dbus-internal.h"
34 #define DEBUG_FLAG TP_DEBUG_PROXY
35 #include "debug-internal.h"
36 #include "simple-client-factory-internal.h"
37 #include "util-internal.h"
38 
39 #include "_gen/tp-cli-generic-body.h"
40 
41 #if 0
42 #define MORE_DEBUG DEBUG
43 #else
44 #define MORE_DEBUG(...) G_STMT_START {} G_STMT_END
45 #endif
46 
47 /**
48  * TP_DBUS_ERRORS:
49  *
50  * #GError domain representing D-Bus errors not directly related to
51  * Telepathy, for use by #TpProxy. The @code in a #GError with this
52  * domain must be a member of #TpDBusError.
53  *
54  * This macro expands to a function call returning a #GQuark.
55  *
56  * Since: 0.7.1
57  */
58 GQuark
tp_dbus_errors_quark(void)59 tp_dbus_errors_quark (void)
60 {
61   static GQuark q = 0;
62 
63   if (q == 0)
64     q = g_quark_from_static_string ("tp_dbus_errors_quark");
65 
66   return q;
67 }
68 
69 /**
70  * TpDBusError:
71  * @TP_DBUS_ERROR_UNKNOWN_REMOTE_ERROR: Raised if the error raised by
72  *  a remote D-Bus object is not recognised
73  * @TP_DBUS_ERROR_PROXY_UNREFERENCED: Emitted in #TpProxy::invalidated
74  *  when the #TpProxy has lost its last reference
75  * @TP_DBUS_ERROR_NO_INTERFACE: Raised by #TpProxy methods if the remote
76  *  object does not appear to have the required interface
77  * @TP_DBUS_ERROR_NAME_OWNER_LOST: Emitted in #TpProxy::invalidated if the
78  *  remote process loses ownership of its bus name, and raised by
79  *  any #TpProxy methods that have not had a reply at that time or are called
80  *  after the proxy becomes invalid in this way (usually meaning it crashed)
81  * @TP_DBUS_ERROR_INVALID_BUS_NAME: Raised if a D-Bus bus name given is not
82  *  valid, or is of an unacceptable type (e.g. well-known vs. unique)
83  * @TP_DBUS_ERROR_INVALID_INTERFACE_NAME: Raised if a D-Bus interface or
84  *  error name given is not valid
85  * @TP_DBUS_ERROR_INVALID_OBJECT_PATH: Raised if a D-Bus object path
86  *  given is not valid
87  * @TP_DBUS_ERROR_INVALID_MEMBER_NAME: Raised if a D-Bus method or signal
88  *  name given is not valid
89  * @TP_DBUS_ERROR_OBJECT_REMOVED: A generic error which can be used with
90  *  #TpProxy::invalidated to indicate an application-specific indication
91  *  that the remote object no longer exists, if no more specific error
92  *  is available.
93  * @TP_DBUS_ERROR_CANCELLED: Raised from calls that re-enter the main
94  *  loop (*_run_*) if they are cancelled
95  * @TP_DBUS_ERROR_INCONSISTENT: Raised if information received from a remote
96  *  object is inconsistent or otherwise obviously wrong (added in 0.7.17).
97  *  See also %TP_ERROR_CONFUSED.
98  *
99  * #GError codes for use with the %TP_DBUS_ERRORS domain.
100  *
101  * Since 0.11.5, there is a corresponding #GEnumClass type,
102  * %TP_TYPE_DBUS_ERROR.
103  *
104  * Since: 0.7.1
105  */
106 
107 /**
108  * NUM_TP_DBUS_ERRORS: (skip)
109  *
110  * 1 more than the highest valid #TpDBusError at the time of compilation.
111  * In new code, use %TP_NUM_DBUS_ERRORS instead.
112  *
113  * Since: 0.7.1
114  */
115 
116 /**
117  * TP_NUM_DBUS_ERRORS:
118  *
119  * 1 more than the highest valid #TpDBusError at the time of compilation
120  *
121  * Since: 0.19.0
122  */
123 
124 /**
125  * TP_TYPE_DBUS_ERROR:
126  *
127  * The #GEnumClass type of a #TpDBusError.
128  *
129  * Since: 0.11.5
130  */
131 
132 /**
133  * SECTION:proxy
134  * @title: TpProxy
135  * @short_description: base class for Telepathy client proxy objects
136  * @see_also: #TpChannel, #TpConnection, #TpConnectionManager
137  *
138  * #TpProxy is a base class for Telepathy client-side proxies, which represent
139  * an object accessed via D-Bus and provide access to its methods and signals.
140  *
141  * Since: 0.7.1
142  */
143 
144 /**
145  * SECTION:proxy-dbus-core
146  * @title: TpProxy D-Bus core methods
147  * @short_description: The D-Bus Introspectable, Peer and Properties interfaces
148  * @see_also: #TpProxy
149  *
150  * All D-Bus objects support the Peer interface, and many support the
151  * Introspectable and Properties interfaces.
152  *
153  * Since: 0.7.2
154  */
155 
156 /**
157  * SECTION:proxy-tp-properties
158  * @title: TpProxy Telepathy Properties
159  * @short_description: The Telepathy Properties interface
160  * @see_also: #TpProxy
161  *
162  * As well as #TpProxy, proxy.h includes auto-generated client wrappers for the
163  * Telepathy Properties interface, which can be implemented by any type of
164  * object.
165  *
166  * The Telepathy Properties interface should not be confused with the D-Bus
167  * core Properties interface.
168  *
169  * Since: 0.7.1
170  */
171 
172 /**
173  * SECTION:proxy-subclass
174  * @title: TpProxy subclasses and mixins
175  * @short_description: Providing extra functionality for a #TpProxy or
176  *  subclass, or subclassing it
177  * @see_also: #TpProxy
178  *
179  * The implementations of #TpProxy subclasses and "mixin" functions need
180  * access to the underlying dbus-glib objects used to implement the
181  * #TpProxy API.
182  *
183  * Mixin functions to implement particular D-Bus interfaces should usually
184  * be auto-generated, by copying tools/glib-client-gen.py from telepathy-glib.
185  *
186  * Since: 0.7.1
187  */
188 
189 /**
190  * TpProxy:
191  *
192  * Structure representing a Telepathy client-side proxy.
193  *
194  * Since: 0.7.1
195  */
196 
197 /**
198  * TpProxyInterfaceAddedCb:
199  * @self: the proxy
200  * @quark: a quark whose string value is the interface being added
201  * @proxy: the #DBusGProxy for the added interface
202  * @unused: unused
203  *
204  * The signature of a #TpProxy::interface-added signal callback.
205  *
206  * Since: 0.7.1
207  */
208 
209 /**
210  * TpProxyClass:
211  * @parent_class: The parent class structure
212  * @interface: If set non-zero by a subclass, #TpProxy will
213  *    automatically add this interface in its constructor
214  * @must_have_unique_name: If set %TRUE by a subclass, the #TpProxy
215  *    constructor will fail if a well-known bus name is given
216  *
217  * The class of a #TpProxy. The struct fields not documented here are reserved.
218  *
219  * Since: 0.7.1
220  */
221 
222 /**
223  * TpProxyPrepareAsync:
224  * @proxy: the object on which @feature has to be prepared
225  * @feature: a #GQuark representing the feature to prepare
226  * @callback: called when the feature has been prepared, or the preparation
227  * failed
228  * @user_data: data to pass to @callback
229  *
230  * Function called when @feature has to be prepared for @proxy.
231  */
232 
233 /**
234  * TpProxyFeature:
235  * @name: a #GQuark representing the name of the feature
236  * @core: if %TRUE, every non-core feature of the class depends on this one,
237  * and every feature (core or not) in subclasses depends on this one
238  * @prepare_async: called when the feature has to be prepared
239  * @prepare_before_signalling_connected_async: only relevant for
240  * TpConnection sub-classes; same as @prepare_async but for
241  * features wanting to have a chance to prepare themself before the
242  * TpConnection object announce its %TP_CONNECTION_STATUS_CONNECTED status
243  * @interfaces_needed: an array of #GQuark representing interfaces which have
244  * to be implemented on the object in order to be able to prepare the feature
245  * @depends_on: an array of #GQuark representing other features which have to
246  * be prepared before trying to prepare this feature
247  * @can_retry: If %TRUE, allow retrying preparation of this feature even if it
248  * failed once already; if %FALSE any attempt of preparing the feature after
249  * the preparation already failed once will immediately fail with re-calling
250  * @prepare_async
251  *
252  * Structure representing a feature.
253  *
254  * Since: 0.11.3
255  */
256 
257 /**
258  * TpProxyClassFeatureListFunc:
259  * @cls: a subclass of #TpProxyClass
260  *
261  * A function called to list the features supported by
262  * tp_proxy_prepare_async(). Currently, only code inside telepathy-glib can
263  * implement this.
264  *
265  * Returns: an array of feature descriptions
266  *
267  * Since: 0.11.3
268  */
269 
270 typedef struct _TpProxyErrorMappingLink TpProxyErrorMappingLink;
271 
272 struct _TpProxyErrorMappingLink {
273     const gchar *prefix;
274     GQuark domain;
275     GEnumClass *code_enum_class;
276     TpProxyErrorMappingLink *next;
277 };
278 
279 typedef struct _TpProxyInterfaceAddLink TpProxyInterfaceAddLink;
280 
281 struct _TpProxyInterfaceAddLink {
282     TpProxyInterfaceAddedCb callback;
283     TpProxyInterfaceAddLink *next;
284 };
285 
286 struct _TpProxyFeaturePrivate
287 {
288   gpointer unused;
289 };
290 
291 /**
292  * TpProxyInvokeFunc:
293  * @self: the #TpProxy on which the D-Bus method was invoked
294  * @error: %NULL if the method call succeeded, or a non-%NULL error if the
295  *  method call failed
296  * @args: array of "out" arguments (return values) for the D-Bus method,
297  *  or %NULL if an error occurred or if there were no "out" arguments
298  * @callback: the callback that should be invoked, as passed to
299  *  tp_proxy_pending_call_v0_new()
300  * @user_data: user-supplied data to pass to the callback, as passed to
301  *  tp_proxy_pending_call_v0_new()
302  * @weak_object: user-supplied object to pass to the callback, as passed to
303  *  tp_proxy_pending_call_v0_new()
304  *
305  * Signature of a callback invoked by the #TpProxy machinery after a D-Bus
306  * method call has succeeded or failed. It is responsible for calling the
307  * user-supplied callback.
308  *
309  * Because parts of dbus-glib aren't reentrant, this callback may be called
310  * from an idle handler shortly after the method call reply is received,
311  * rather than from the callback for the reply.
312  *
313  * At most one of @args and @error can be non-%NULL (implementations may
314  * assert this). @args and @error may both be %NULL if a method with no
315  * "out" arguments (i.e. a method that returns nothing) was called
316  * successfully.
317  *
318  * The #TpProxyInvokeFunc must call callback with @user_data, @weak_object,
319  * and appropriate arguments derived from @error and @args. It is responsible
320  * for freeing @error and @args, if their ownership has not been transferred.
321  *
322  * Since: 0.7.1
323  */
324 
325 typedef enum {
326     /* Not a feature */
327     FEATURE_STATE_INVALID = GPOINTER_TO_INT (NULL),
328     /* Nobody cares */
329     FEATURE_STATE_UNWANTED,
330     /* Want to prepare, waiting for dependencies to be satisfied (or maybe
331      * just poll_features being called) */
332     FEATURE_STATE_WANTED,
333     /* Want to prepare, have called prepare_async */
334     FEATURE_STATE_TRYING,
335     /* Couldn't prepare because a required interface on the connection
336      * was missing and the connection wasn't connected yet. We'll retry to
337      * prepare once the connection is connected.
338      * This state is only used when preparing Connection features */
339     FEATURE_STATE_MISSING_IFACE,
340     /* Couldn't prepare, gave up */
341     FEATURE_STATE_FAILED,
342     /* Prepared */
343     FEATURE_STATE_READY
344 } FeatureState;
345 
346 typedef struct {
347     GSimpleAsyncResult *result;
348     GArray *features;
349     gboolean core;
350 } TpProxyPrepareRequest;
351 
352 static TpProxyPrepareRequest *
tp_proxy_prepare_request_new(GSimpleAsyncResult * result,const GQuark * features)353 tp_proxy_prepare_request_new (GSimpleAsyncResult *result,
354     const GQuark *features)
355 {
356   TpProxyPrepareRequest *req = g_slice_new0 (TpProxyPrepareRequest);
357 
358   if (result != NULL)
359     req->result = g_object_ref (result);
360 
361   req->features = _tp_quark_array_copy (features);
362   g_assert (req->features != NULL);
363   return req;
364 }
365 
366 static void
tp_proxy_prepare_request_finish(TpProxyPrepareRequest * req,const GError * error)367 tp_proxy_prepare_request_finish (TpProxyPrepareRequest *req,
368     const GError *error)
369 {
370   DEBUG ("%p", req);
371 
372   if (req->result != NULL)
373     {
374       if (error != NULL)
375         g_simple_async_result_set_from_error (req->result, error);
376 
377       g_simple_async_result_complete_in_idle (req->result);
378       g_object_unref (req->result);
379     }
380 
381   g_array_unref (req->features);
382   g_slice_free (TpProxyPrepareRequest, req);
383 }
384 
385 struct _TpProxyPrivate {
386     /* GQuark for interface => either a ref'd DBusGProxy *,
387      * or the TpProxy itself used as a dummy value to indicate that
388      * the DBusGProxy has not been needed yet */
389     GData *interfaces;
390 
391     /* feature => FeatureState */
392     GData *features;
393 
394     /* Queue of TpProxyPrepareRequest. The first requests are the core one,
395      * sorted from the most upper super class to the subclass core features.
396      * This is needed to guarantee than subclass features are not prepared
397      * until the super class features have been prepared. */
398     GQueue *prepare_requests;
399 
400     GSimpleAsyncResult *will_announce_connected_result;
401     /* Number of pending calls blocking will_announce_connected_result to be
402      * completed */
403     guint pending_will_announce_calls;
404 
405     gboolean dispose_has_run;
406 
407     TpSimpleClientFactory *factory;
408 };
409 
410 G_DEFINE_TYPE (TpProxy, tp_proxy, G_TYPE_OBJECT)
411 
412 enum
413 {
414   PROP_DBUS_DAEMON = 1,
415   PROP_DBUS_CONNECTION,
416   PROP_BUS_NAME,
417   PROP_OBJECT_PATH,
418   PROP_INTERFACES,
419   PROP_FACTORY,
420   N_PROPS
421 };
422 
423 enum {
424     SIGNAL_INTERFACE_ADDED,
425     SIGNAL_INVALIDATED,
426     N_SIGNALS
427 };
428 
429 static guint signals[N_SIGNALS] = {0};
430 
431 static void tp_proxy_iface_destroyed_cb (DBusGProxy *dgproxy, TpProxy *self);
432 
433 /**
434  * tp_proxy_borrow_interface_by_id: (skip)
435  * @self: the TpProxy
436  * @iface: quark representing the interface required
437  * @error: used to raise an error in the #TP_DBUS_ERRORS domain if @iface
438  *         is invalid, @self has been invalidated or @self does not implement
439  *         @iface
440  *
441  * <!-- -->
442  *
443  * Returns: a borrowed reference to a #DBusGProxy
444  * for which the bus name and object path are the same as for @self, but the
445  * interface is as given (or %NULL if an @error is raised).
446  * The reference is only valid as long as @self is.
447  *
448  * Since: 0.7.1
449  * Deprecated: Since 0.19.9. New code should use
450  *  tp_proxy_get_interface_by_id() instead.
451  */
452 DBusGProxy *
tp_proxy_borrow_interface_by_id(TpProxy * self,GQuark iface,GError ** error)453 tp_proxy_borrow_interface_by_id (TpProxy *self,
454                                  GQuark iface,
455                                  GError **error)
456 {
457   return tp_proxy_get_interface_by_id (self, iface, error);
458 }
459 
460 /**
461  * tp_proxy_get_interface_by_id: (skip)
462  * @self: the TpProxy
463  * @iface: quark representing the interface required
464  * @error: used to raise an error in the #TP_DBUS_ERRORS domain if @iface
465  *         is invalid, @self has been invalidated or @self does not implement
466  *         @iface
467  *
468  * <!-- -->
469  *
470  * Returns: a borrowed reference to a #DBusGProxy
471  * for which the bus name and object path are the same as for @self, but the
472  * interface is as given (or %NULL if an @error is raised).
473  * The reference is only valid as long as @self is.
474  *
475  * Since: 0.19.9
476  */
477 DBusGProxy *
tp_proxy_get_interface_by_id(TpProxy * self,GQuark iface,GError ** error)478 tp_proxy_get_interface_by_id (TpProxy *self,
479                               GQuark iface,
480                               GError **error)
481 {
482   gpointer dgproxy;
483 
484   if (self->invalidated != NULL)
485     {
486       g_set_error (error, self->invalidated->domain, self->invalidated->code,
487           "%s", self->invalidated->message);
488       return NULL;
489     }
490 
491   if (!tp_dbus_check_valid_interface_name (g_quark_to_string (iface),
492         error))
493       return NULL;
494 
495   dgproxy = g_datalist_id_get_data (&self->priv->interfaces, iface);
496 
497   if (dgproxy == self)
498     {
499       /* dummy value - we've never actually needed the interface, so we
500        * didn't create it, to avoid binding to all the signals */
501 
502       dgproxy = dbus_g_proxy_new_for_name (self->dbus_connection,
503           self->bus_name, self->object_path, g_quark_to_string (iface));
504       DEBUG ("%p: %s DBusGProxy is %p", self, g_quark_to_string (iface),
505           dgproxy);
506 
507       g_signal_connect (dgproxy, "destroy",
508           G_CALLBACK (tp_proxy_iface_destroyed_cb), self);
509 
510       g_datalist_id_set_data_full (&self->priv->interfaces, iface,
511           dgproxy, g_object_unref);
512 
513       g_signal_emit (self, signals[SIGNAL_INTERFACE_ADDED], 0,
514           (guint) iface, dgproxy);
515     }
516 
517   if (dgproxy != NULL)
518     {
519       return dgproxy;
520     }
521 
522   g_set_error (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_NO_INTERFACE,
523       "Object %s does not have interface %s",
524       self->object_path, g_quark_to_string (iface));
525 
526   return NULL;
527 }
528 
529 /**
530  * tp_proxy_has_interface_by_id:
531  * @self: the #TpProxy (or subclass)
532  * @iface: quark representing the D-Bus interface required
533  *
534  * Return whether this proxy is known to have a particular interface, by its
535  * quark ID. This is equivalent to using g_quark_to_string() followed by
536  * tp_proxy_has_interface(), but more efficient.
537  *
538  * Returns: %TRUE if this proxy implements the given interface.
539  *
540  * Since: 0.7.1
541  */
542 gboolean
tp_proxy_has_interface_by_id(gpointer self,GQuark iface)543 tp_proxy_has_interface_by_id (gpointer self,
544                               GQuark iface)
545 {
546   TpProxy *proxy = self;
547 
548   g_return_val_if_fail (TP_IS_PROXY (self), FALSE);
549 
550   return (g_datalist_id_get_data (&proxy->priv->interfaces, iface)
551       != NULL);
552 }
553 
554 /**
555  * tp_proxy_has_interface:
556  * @self: the #TpProxy (or subclass)
557  * @iface: the D-Bus interface required, as a string
558  *
559  * Return whether this proxy is known to have a particular interface. In
560  * versions older than 0.11.11, this was a macro wrapper around
561  * tp_proxy_has_interface_by_id().
562  *
563  * For objects that discover their interfaces at runtime, this method will
564  * indicate that interfaces are missing until they are known to be present.
565  * In subclasses that define features for use with tp_proxy_prepare_async(),
566  * successfully preparing the "core" feature for that subclass (such as
567  * %TP_CHANNEL_FEATURE_CORE or %TP_CONNECTION_FEATURE_CORE) implies that the
568  * interfaces are known.
569  *
570  * Returns: %TRUE if this proxy implements the given interface.
571  * Since: 0.7.1
572  */
573 gboolean
tp_proxy_has_interface(gpointer self,const gchar * iface)574 tp_proxy_has_interface (gpointer self,
575     const gchar *iface)
576 {
577   TpProxy *proxy = self;
578   GQuark q = g_quark_try_string (iface);
579 
580   g_return_val_if_fail (TP_IS_PROXY (self), FALSE);
581 
582   return (q != 0 &&
583     g_datalist_id_get_data (&proxy->priv->interfaces, q) != NULL);
584 }
585 
586 static void
tp_proxy_lose_interface(GQuark unused,gpointer dgproxy_or_self,gpointer self)587 tp_proxy_lose_interface (GQuark unused,
588                          gpointer dgproxy_or_self,
589                          gpointer self)
590 {
591   if (dgproxy_or_self != self)
592     g_signal_handlers_disconnect_by_func (dgproxy_or_self,
593         G_CALLBACK (tp_proxy_iface_destroyed_cb), self);
594 }
595 
596 static void
tp_proxy_lose_interfaces(TpProxy * self)597 tp_proxy_lose_interfaces (TpProxy *self)
598 {
599   g_datalist_foreach (&self->priv->interfaces,
600       tp_proxy_lose_interface, self);
601 
602   g_datalist_clear (&self->priv->interfaces);
603 }
604 
605 static void tp_proxy_poll_features (TpProxy *self, const GError *error);
606 
607 /* This signature is chosen to match GSourceFunc */
608 static gboolean
tp_proxy_emit_invalidated(gpointer p)609 tp_proxy_emit_invalidated (gpointer p)
610 {
611   TpProxy *self = TP_PROXY (p);
612 
613   g_signal_emit (self, signals[SIGNAL_INVALIDATED], 0,
614       self->invalidated->domain, self->invalidated->code,
615       self->invalidated->message);
616 
617   /* make all pending tp_proxy_prepare_async calls fail */
618   tp_proxy_poll_features (self, NULL);
619   g_assert_cmpuint (g_queue_get_length (self->priv->prepare_requests), ==, 0);
620 
621   /* Don't clear the datalist until after we've emitted the signal, so
622    * the pending call and signal connection friend classes can still get
623    * to the proxies */
624   tp_proxy_lose_interfaces (self);
625 
626   if (self->dbus_connection != NULL)
627     {
628       dbus_g_connection_unref (self->dbus_connection);
629       self->dbus_connection = NULL;
630     }
631 
632   return FALSE;
633 }
634 
635 /**
636  * tp_proxy_invalidate:
637  * @self: a proxy
638  * @error: an error causing the invalidation
639  *
640  * Mark @self as having been invalidated - no further calls will work, and
641  * if not already invalidated, the #TpProxy::invalidated signal will be emitted
642  * with the given error.
643  *
644  * Since: 0.7.1
645  */
646 void
tp_proxy_invalidate(TpProxy * self,const GError * error)647 tp_proxy_invalidate (TpProxy *self, const GError *error)
648 {
649   g_return_if_fail (self != NULL);
650   g_return_if_fail (error != NULL);
651 
652   if (self->invalidated == NULL)
653     {
654       DEBUG ("%p: %s", self, error->message);
655       self->invalidated = g_error_copy (error);
656 
657       tp_proxy_emit_invalidated (self);
658     }
659 }
660 
661 static void
tp_proxy_iface_destroyed_cb(DBusGProxy * dgproxy,TpProxy * self)662 tp_proxy_iface_destroyed_cb (DBusGProxy *dgproxy,
663                              TpProxy *self)
664 {
665   /* We can't call any API on the proxy now. Because the proxies are all
666    * for the same bus name, we can assume that all of them are equally
667    * useless now */
668   tp_proxy_lose_interfaces (self);
669 
670   /* We need to be able to delay emitting the invalidated signal, so that
671    * any queued-up method calls and signal handlers will run first, and so
672    * it doesn't try to reenter libdbus.
673    */
674   if (self->invalidated == NULL)
675     {
676       DEBUG ("%p", self);
677       self->invalidated = g_error_new_literal (TP_DBUS_ERRORS,
678           TP_DBUS_ERROR_NAME_OWNER_LOST, "Name owner lost (service crashed?)");
679 
680       g_idle_add_full (G_PRIORITY_HIGH, tp_proxy_emit_invalidated,
681           g_object_ref (self), g_object_unref);
682     }
683 }
684 
685 /**
686  * tp_proxy_add_interface_by_id: (skip)
687  * @self: the TpProxy, which must not have become #TpProxy::invalidated.
688  * @iface: quark representing the interface to be added
689  *
690  * Declare that this proxy supports a given interface.
691  *
692  * To use methods and signals of that interface, either call
693  * tp_proxy_get_interface_by_id() to get the #DBusGProxy, or use the
694  * tp_cli_* wrapper functions (strongly recommended).
695  *
696  * If the interface is the proxy's "main interface", or has already been
697  * added, then do nothing.
698  *
699  * Returns: either %NULL or a borrowed #DBusGProxy corresponding to @iface,
700  * depending on implementation details. To reliably borrow the #DBusGProxy, use
701  * tp_proxy_get_interface_by_id(). (This method should probably have
702  * returned void; sorry.)
703  *
704  * Since: 0.7.1
705  */
706 DBusGProxy *
tp_proxy_add_interface_by_id(TpProxy * self,GQuark iface)707 tp_proxy_add_interface_by_id (TpProxy *self,
708                               GQuark iface)
709 {
710   DBusGProxy *iface_proxy = g_datalist_id_get_data (&self->priv->interfaces,
711       iface);
712 
713   g_return_val_if_fail
714       (tp_dbus_check_valid_interface_name (g_quark_to_string (iface),
715           NULL),
716        NULL);
717 
718   g_return_val_if_fail (tp_proxy_get_invalidated (self) == NULL, NULL);
719 
720   if (iface_proxy == NULL)
721     {
722       /* we don't want to actually create it just yet - dbus-glib will
723        * helpfully wake us up on every signal, if we do. So we set a
724        * dummy value (self), and replace it with the real value in
725        * tp_proxy_get_interface_by_id */
726       g_datalist_id_set_data_full (&self->priv->interfaces, iface,
727           self, NULL);
728     }
729 
730   return iface_proxy;
731 }
732 
733 /**
734  * tp_proxy_add_interfaces: (skip)
735  * @self: the TpProxy, which must not have become #TpProxy::invalidated.
736  * @interfaces: the names of the interfaces to be added
737  *
738  * Declare that this proxy supports the given interfaces. Equivalent to calling
739  * g_quark_from_string () followed by tp_proxy_add_interface_by_id () for each
740  * of the interface names.
741  *
742  * Since: 0.14.4
743  */
744 void
tp_proxy_add_interfaces(TpProxy * self,const gchar * const * interfaces)745 tp_proxy_add_interfaces (TpProxy *self,
746     const gchar * const *interfaces)
747 {
748   const gchar * const *iter;
749 
750   if (G_UNLIKELY (interfaces == NULL))
751     return;
752 
753   for (iter = interfaces; *iter != NULL; iter++)
754     {
755       if (tp_dbus_check_valid_interface_name (*iter, NULL))
756         {
757           GQuark q = g_quark_from_string (*iter);
758 
759           tp_proxy_add_interface_by_id (self, q);
760         }
761       else
762         {
763           DEBUG ("Ignoring invalid interface on %s: %s",
764               tp_proxy_get_object_path (self), *iter);
765         }
766     }
767 }
768 
769 static GQuark
error_mapping_quark(void)770 error_mapping_quark (void)
771 {
772   static GQuark q = 0;
773 
774   if (G_UNLIKELY (q == 0))
775     {
776       q = g_quark_from_static_string ("TpProxyErrorMappingCb_0.7.1");
777     }
778 
779   return q;
780 }
781 
782 /**
783  * tp_proxy_dbus_error_to_gerror:
784  * @self: a #TpProxy or subclass
785  * @dbus_error: a D-Bus error name, for instance from the callback for
786  *              tp_cli_connection_connect_to_connection_error()
787  * @debug_message: a debug message that accompanied the error name, or %NULL
788  * @error: used to return the corresponding #GError
789  *
790  * Convert a D-Bus error name into a GError as if it was returned by a method
791  * on this proxy. This method is useful when D-Bus error names are emitted in
792  * signals, such as Connection.ConnectionError and
793  * Group.MembersChangedDetailed.
794  *
795  * Since: 0.7.24
796  */
797 void
tp_proxy_dbus_error_to_gerror(gpointer self,const char * dbus_error,const char * debug_message,GError ** error)798 tp_proxy_dbus_error_to_gerror (gpointer self,
799                                const char *dbus_error,
800                                const char *debug_message,
801                                GError **error)
802 {
803   GType proxy_type = TP_TYPE_PROXY;
804   GType type;
805 
806   g_return_if_fail (TP_IS_PROXY (self));
807 
808   if (error == NULL)
809     return;
810 
811   g_return_if_fail (*error == NULL);
812 
813   if (!tp_dbus_check_valid_interface_name (dbus_error, error))
814     {
815       return;
816     }
817 
818   if (debug_message == NULL)
819     debug_message = "";
820 
821   for (type = G_TYPE_FROM_INSTANCE (self);
822        type != proxy_type;
823        type = g_type_parent (type))
824     {
825       TpProxyErrorMappingLink *iter;
826 
827       for (iter = g_type_get_qdata (type, error_mapping_quark ());
828            iter != NULL;
829            iter = iter->next)
830         {
831           size_t prefix_len = strlen (iter->prefix);
832 
833           if (!strncmp (dbus_error, iter->prefix, prefix_len)
834               && dbus_error[prefix_len] == '.')
835             {
836               GEnumValue *code =
837                 g_enum_get_value_by_nick (iter->code_enum_class,
838                     dbus_error + prefix_len + 1);
839 
840               if (code != NULL)
841                 {
842                   g_set_error (error, iter->domain, code->value,
843                       "%s", debug_message);
844                   return;
845                 }
846             }
847         }
848     }
849 
850   /* we don't have an error mapping - so let's just paste the
851    * error name and message into TP_DBUS_ERROR_UNKNOWN_REMOTE_ERROR */
852   g_set_error (error, TP_DBUS_ERRORS,
853       TP_DBUS_ERROR_UNKNOWN_REMOTE_ERROR, "%s: %s", dbus_error, debug_message);
854 }
855 
856 GError *
_tp_proxy_take_and_remap_error(TpProxy * self,GError * error)857 _tp_proxy_take_and_remap_error (TpProxy *self,
858                                 GError *error)
859 {
860   if (error == NULL ||
861       error->domain != DBUS_GERROR ||
862       error->code != DBUS_GERROR_REMOTE_EXCEPTION)
863     {
864       return error;
865     }
866   else
867     {
868       GError *replacement = NULL;
869       const gchar *dbus = dbus_g_error_get_name (error);
870 
871       tp_proxy_dbus_error_to_gerror (self, dbus, error->message, &replacement);
872       g_error_free (error);
873       return replacement;
874     }
875 }
876 
877 static void
dup_quark_into_ptr_array(GQuark q,gpointer unused,gpointer user_data)878 dup_quark_into_ptr_array (GQuark q,
879                           gpointer unused,
880                           gpointer user_data)
881 {
882   GPtrArray *strings = user_data;
883 
884   g_ptr_array_add (strings, g_strdup (g_quark_to_string (q)));
885 }
886 
887 static void
tp_proxy_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)888 tp_proxy_get_property (GObject *object,
889                        guint property_id,
890                        GValue *value,
891                        GParamSpec *pspec)
892 {
893   TpProxy *self = TP_PROXY (object);
894 
895   switch (property_id)
896     {
897     case PROP_DBUS_DAEMON:
898       if (TP_IS_DBUS_DAEMON (self))
899         {
900           g_value_set_object (value, self);
901         }
902       else
903         {
904           g_value_set_object (value, self->dbus_daemon);
905         }
906       break;
907     case PROP_DBUS_CONNECTION:
908       g_value_set_boxed (value, self->dbus_connection);
909       break;
910     case PROP_BUS_NAME:
911       g_value_set_string (value, self->bus_name);
912       break;
913     case PROP_OBJECT_PATH:
914       g_value_set_string (value, self->object_path);
915       break;
916     case PROP_INTERFACES:
917         {
918           GPtrArray *strings = g_ptr_array_new ();
919 
920           g_datalist_foreach (&self->priv->interfaces,
921               dup_quark_into_ptr_array, strings);
922           g_ptr_array_add (strings, NULL);
923           g_value_take_boxed (value, g_ptr_array_free (strings, FALSE));
924         }
925       break;
926     case PROP_FACTORY:
927       g_value_set_object (value, self->priv->factory);
928       break;
929     default:
930       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
931       break;
932   }
933 }
934 
935 static void
tp_proxy_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)936 tp_proxy_set_property (GObject *object,
937                        guint property_id,
938                        const GValue *value,
939                        GParamSpec *pspec)
940 {
941   TpProxy *self = TP_PROXY (object);
942 
943   switch (property_id)
944     {
945     case PROP_DBUS_DAEMON:
946       if (TP_IS_DBUS_DAEMON (self))
947         {
948           g_assert (g_value_get_object (value) == NULL);
949         }
950       else
951         {
952           TpProxy *daemon_as_proxy = TP_PROXY (g_value_get_object (value));
953 
954           g_assert (self->dbus_daemon == NULL);
955 
956           if (daemon_as_proxy != NULL)
957             self->dbus_daemon = TP_DBUS_DAEMON (g_object_ref
958                 (daemon_as_proxy));
959 
960           if (daemon_as_proxy != NULL)
961             {
962               g_assert (self->dbus_connection == NULL ||
963                   self->dbus_connection == daemon_as_proxy->dbus_connection);
964 
965               if (self->dbus_connection == NULL)
966                 self->dbus_connection =
967                     dbus_g_connection_ref (daemon_as_proxy->dbus_connection);
968             }
969         }
970       break;
971     case PROP_DBUS_CONNECTION:
972         {
973           DBusGConnection *conn = g_value_get_boxed (value);
974 
975           /* if we're given a NULL dbus-connection, but we've got a
976            * DBusGConnection from the dbus-daemon, we want to keep it */
977           if (conn == NULL)
978             return;
979 
980           if (self->dbus_connection == NULL)
981             self->dbus_connection = g_value_dup_boxed (value);
982 
983           g_assert (self->dbus_connection == g_value_get_boxed (value));
984         }
985       break;
986     case PROP_BUS_NAME:
987       g_assert (self->bus_name == NULL);
988       self->bus_name = g_value_dup_string (value);
989       break;
990     case PROP_OBJECT_PATH:
991       g_assert (self->object_path == NULL);
992       self->object_path = g_value_dup_string (value);
993       break;
994     case PROP_FACTORY:
995       g_assert (self->priv->factory == NULL);
996       self->priv->factory = g_value_dup_object (value);
997       break;
998     default:
999       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1000       break;
1001   }
1002 }
1003 
1004 static void
tp_proxy_init(TpProxy * self)1005 tp_proxy_init (TpProxy *self)
1006 {
1007   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_PROXY,
1008       TpProxyPrivate);
1009 
1010   self->priv->prepare_requests = g_queue_new ();
1011 }
1012 
1013 static GQuark
interface_added_cb_quark(void)1014 interface_added_cb_quark (void)
1015 {
1016   static GQuark q = 0;
1017 
1018   if (G_UNLIKELY (q == 0))
1019     {
1020       q = g_quark_from_static_string ("TpProxyInterfaceAddedCb_0.7.1");
1021     }
1022 
1023   return q;
1024 }
1025 
1026 static FeatureState
tp_proxy_get_feature_state(TpProxy * self,GQuark feature)1027 tp_proxy_get_feature_state (TpProxy *self,
1028     GQuark feature)
1029 {
1030   return GPOINTER_TO_INT (g_datalist_id_get_data (&self->priv->features,
1031         feature));
1032 }
1033 
1034 static void
tp_proxy_set_feature_state(TpProxy * self,GQuark feature,FeatureState state)1035 tp_proxy_set_feature_state (TpProxy *self,
1036     GQuark feature,
1037     FeatureState state)
1038 {
1039   g_datalist_id_set_data (&self->priv->features, feature,
1040       GINT_TO_POINTER (state));
1041 }
1042 
1043 static void
assert_feature_validity(TpProxy * self,const TpProxyFeature * feature)1044 assert_feature_validity (TpProxy *self,
1045     const TpProxyFeature *feature)
1046 {
1047   g_assert (feature != NULL);
1048 
1049   /* Core features can't have depends, their depends are implicit */
1050   if (feature->core)
1051     g_assert (feature->depends_on == NULL || feature->depends_on[0] == 0);
1052 
1053   /* prepare_before_signalling_connected_async only make sense for
1054    * TpConnection subclasses */
1055   if (feature->prepare_before_signalling_connected_async != NULL)
1056     g_assert (TP_IS_CONNECTION (self));
1057 }
1058 
1059 static GObject *
tp_proxy_constructor(GType type,guint n_params,GObjectConstructParam * params)1060 tp_proxy_constructor (GType type,
1061                       guint n_params,
1062                       GObjectConstructParam *params)
1063 {
1064   GObjectClass *object_class = (GObjectClass *) tp_proxy_parent_class;
1065   TpProxy *self = TP_PROXY (object_class->constructor (type,
1066         n_params, params));
1067   TpProxyClass *klass = TP_PROXY_GET_CLASS (self);
1068   TpProxyInterfaceAddLink *iter;
1069   GType proxy_parent_type = G_TYPE_FROM_CLASS (tp_proxy_parent_class);
1070   GType ancestor_type;
1071 
1072   _tp_register_dbus_glib_marshallers ();
1073 
1074   for (ancestor_type = type;
1075        ancestor_type != proxy_parent_type && ancestor_type != 0;
1076        ancestor_type = g_type_parent (ancestor_type))
1077     {
1078       TpProxyClass *ancestor = g_type_class_peek (ancestor_type);
1079       const TpProxyFeature *features;
1080       guint i;
1081       GArray *core_features;
1082 
1083       for (iter = g_type_get_qdata (ancestor_type,
1084               interface_added_cb_quark ());
1085            iter != NULL;
1086            iter = iter->next)
1087         g_signal_connect (self, "interface-added", G_CALLBACK (iter->callback),
1088             NULL);
1089 
1090       if (ancestor == NULL || ancestor->list_features == NULL)
1091         continue;
1092 
1093       features = ancestor->list_features (ancestor);
1094 
1095       if (features == NULL)
1096         continue;
1097 
1098       core_features = g_array_new (TRUE, FALSE, sizeof (GQuark));
1099 
1100       for (i = 0; features[i].name != 0; i++)
1101         {
1102           assert_feature_validity (self, &features[i]);
1103 
1104           tp_proxy_set_feature_state (self, features[i].name,
1105               FEATURE_STATE_UNWANTED);
1106 
1107           if (features[i].core)
1108             {
1109               g_array_append_val (core_features, features[i].name);
1110             }
1111         }
1112 
1113       if (core_features->len > 0)
1114         {
1115           TpProxyPrepareRequest *req;
1116 
1117           req = tp_proxy_prepare_request_new (NULL,
1118               (const GQuark *) core_features->data);
1119           req->core = TRUE;
1120 
1121           g_queue_push_head (self->priv->prepare_requests, req);
1122 
1123           DEBUG ("%p: request %p represents core features on %s", self, req,
1124               g_type_name (ancestor_type));
1125         }
1126 
1127       g_array_unref (core_features);
1128     }
1129 
1130   g_return_val_if_fail (self->dbus_connection != NULL, NULL);
1131   g_return_val_if_fail (self->object_path != NULL, NULL);
1132   g_return_val_if_fail (self->bus_name != NULL, NULL);
1133 
1134   g_return_val_if_fail (tp_dbus_check_valid_object_path (self->object_path,
1135         NULL), NULL);
1136   g_return_val_if_fail (tp_dbus_check_valid_bus_name (self->bus_name,
1137         TP_DBUS_NAME_TYPE_ANY, NULL), NULL);
1138 
1139   tp_proxy_add_interface_by_id (self, TP_IFACE_QUARK_DBUS_INTROSPECTABLE);
1140   tp_proxy_add_interface_by_id (self, TP_IFACE_QUARK_DBUS_PEER);
1141   tp_proxy_add_interface_by_id (self, TP_IFACE_QUARK_DBUS_PROPERTIES);
1142 
1143   if (klass->interface != 0)
1144     {
1145       tp_proxy_add_interface_by_id (self, klass->interface);
1146     }
1147 
1148   /* Some interfaces are stateful, so we only allow binding to a unique
1149    * name, like in dbus_g_proxy_new_for_name_owner() */
1150   if (klass->must_have_unique_name)
1151     {
1152       g_return_val_if_fail (self->bus_name[0] == ':', NULL);
1153     }
1154 
1155   return (GObject *) self;
1156 }
1157 
1158 static GQuark const no_quarks[] = { 0 };
1159 
1160 static void
tp_proxy_dispose(GObject * object)1161 tp_proxy_dispose (GObject *object)
1162 {
1163   TpProxy *self = TP_PROXY (object);
1164   GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_PROXY_UNREFERENCED,
1165       "Proxy unreferenced" };
1166 
1167   if (self->priv->dispose_has_run)
1168     return;
1169   self->priv->dispose_has_run = TRUE;
1170 
1171   DEBUG ("%p", self);
1172 
1173   tp_proxy_invalidate (self, &e);
1174 
1175   tp_clear_object (&self->dbus_daemon);
1176   tp_clear_object (&self->priv->factory);
1177 
1178   G_OBJECT_CLASS (tp_proxy_parent_class)->dispose (object);
1179 }
1180 
1181 static void
tp_proxy_finalize(GObject * object)1182 tp_proxy_finalize (GObject *object)
1183 {
1184   TpProxy *self = TP_PROXY (object);
1185 
1186   DEBUG ("%p", self);
1187 
1188   if (self->priv->features != NULL)
1189     g_datalist_clear (&self->priv->features);
1190 
1191   g_assert (self->invalidated != NULL);
1192   g_error_free (self->invalidated);
1193 
1194   /* invalidation ensures that these have gone away */
1195   g_assert_cmpuint (g_queue_get_length (self->priv->prepare_requests), ==, 0);
1196   tp_clear_pointer (&self->priv->prepare_requests, g_queue_free);
1197 
1198   g_free (self->bus_name);
1199   g_free (self->object_path);
1200 
1201   G_OBJECT_CLASS (tp_proxy_parent_class)->finalize (object);
1202 }
1203 
1204 /**
1205  * tp_proxy_or_subclass_hook_on_interface_add:
1206  * @proxy_or_subclass: The #GType of #TpProxy or a subclass
1207  * @callback: A signal handler for #TpProxy::interface-added
1208  *
1209  * Arrange for @callback to be connected to #TpProxy::interface-added
1210  * during the #TpProxy constructor. This is done sufficiently early that
1211  * it will see the signal for the default interface (@interface member of
1212  * #TpProxyClass), if any, being added. The intended use is for the callback
1213  * to call dbus_g_proxy_add_signal() on the new #DBusGProxy.
1214  *
1215  * Since 0.7.6, to ensure correct overriding of interfaces that might be
1216  * added to telepathy-glib, before calling this function you should
1217  * call tp_proxy_init_known_interfaces, tp_connection_init_known_interfaces,
1218  * tp_channel_init_known_interfaces etc. as appropriate for the subclass.
1219  *
1220  * Since: 0.7.1
1221  */
1222 void
tp_proxy_or_subclass_hook_on_interface_add(GType proxy_or_subclass,TpProxyInterfaceAddedCb callback)1223 tp_proxy_or_subclass_hook_on_interface_add (GType proxy_or_subclass,
1224     TpProxyInterfaceAddedCb callback)
1225 {
1226   GQuark q = interface_added_cb_quark ();
1227   TpProxyInterfaceAddLink *old_link = g_type_get_qdata (proxy_or_subclass, q);
1228   TpProxyInterfaceAddLink *new_link;
1229 
1230   g_return_if_fail (g_type_is_a (proxy_or_subclass, TP_TYPE_PROXY));
1231   g_return_if_fail (callback != NULL);
1232 
1233   /* never freed, suppressed in telepathy-glib.supp */
1234   new_link = g_slice_new0 (TpProxyInterfaceAddLink);
1235   new_link->callback = callback;
1236   new_link->next = old_link;    /* may be NULL */
1237   g_type_set_qdata (proxy_or_subclass, q, new_link);
1238 }
1239 
1240 /**
1241  * tp_proxy_subclass_add_error_mapping:
1242  * @proxy_subclass: The #GType of a subclass of #TpProxy (which must not be
1243  *  #TpProxy itself)
1244  * @static_prefix: A prefix for D-Bus error names, not including the trailing
1245  *  dot (which must remain valid forever, and should usually be in static
1246  *  storage)
1247  * @domain: A quark representing the corresponding #GError domain
1248  * @code_enum_type: The type of a subclass of #GEnumClass
1249  *
1250  * Register a mapping from D-Bus errors received from the given proxy
1251  * subclass to #GError instances.
1252  *
1253  * When a D-Bus error is received, the #TpProxy code checks for error
1254  * mappings registered for the class of the proxy receiving the error,
1255  * then for all of its parent classes.
1256  *
1257  * If there is an error mapping for which the D-Bus error name
1258  * starts with the mapping's @static_prefix, the proxy will check the
1259  * corresponding @code_enum_type for a value whose @value_nick is
1260  * the rest of the D-Bus error name (with the leading dot removed). If there
1261  * isn't such a value, it will continue to try other error mappings.
1262  *
1263  * If a suitable error mapping and code are found, the #GError that is raised
1264  * will have its error domain set to the @domain from the error mapping,
1265  * and its error code taken from the enum represented by the @code_enum_type.
1266  *
1267  * If no suitable error mapping or code is found, the #GError will have
1268  * error domain %TP_DBUS_ERRORS and error code
1269  * %TP_DBUS_ERROR_UNKNOWN_REMOTE_ERROR.
1270  *
1271  * Since: 0.7.1
1272  */
1273 void
tp_proxy_subclass_add_error_mapping(GType proxy_subclass,const gchar * static_prefix,GQuark domain,GType code_enum_type)1274 tp_proxy_subclass_add_error_mapping (GType proxy_subclass,
1275                                      const gchar *static_prefix,
1276                                      GQuark domain,
1277                                      GType code_enum_type)
1278 {
1279   GQuark q = error_mapping_quark ();
1280   TpProxyErrorMappingLink *old_link = g_type_get_qdata (proxy_subclass, q);
1281   TpProxyErrorMappingLink *new_link;
1282   GType tp_type_proxy = TP_TYPE_PROXY;
1283 
1284   g_return_if_fail (proxy_subclass != tp_type_proxy);
1285   g_return_if_fail (g_type_is_a (proxy_subclass, tp_type_proxy));
1286   g_return_if_fail (static_prefix != NULL);
1287   g_return_if_fail (domain != 0);
1288   g_return_if_fail (code_enum_type != G_TYPE_INVALID);
1289 
1290   new_link = g_slice_new0 (TpProxyErrorMappingLink);
1291   new_link->prefix = static_prefix;
1292   new_link->domain = domain;
1293   /* We never unref the enum type - intentional one-per-process leak.
1294    * See "tp_proxy_subclass_add_error_mapping refs the enum" in our valgrind
1295    * suppressions file */
1296   new_link->code_enum_class = g_type_class_ref (code_enum_type);
1297   new_link->next = old_link;    /* may be NULL */
1298   g_type_set_qdata (proxy_subclass, q, new_link);
1299 }
1300 
1301 static void
tp_proxy_class_init(TpProxyClass * klass)1302 tp_proxy_class_init (TpProxyClass *klass)
1303 {
1304   GParamSpec *param_spec;
1305   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1306 
1307   tp_proxy_init_known_interfaces ();
1308 
1309   g_type_class_add_private (klass, sizeof (TpProxyPrivate));
1310 
1311   object_class->constructor = tp_proxy_constructor;
1312   object_class->get_property = tp_proxy_get_property;
1313   object_class->set_property = tp_proxy_set_property;
1314   object_class->dispose = tp_proxy_dispose;
1315   object_class->finalize = tp_proxy_finalize;
1316 
1317   /**
1318    * TpProxy:dbus-daemon:
1319    *
1320    * The D-Bus daemon for this object (this object itself, if it is a
1321    * TpDBusDaemon). Read-only except during construction.
1322    */
1323   param_spec = g_param_spec_object ("dbus-daemon", "D-Bus daemon",
1324       "The D-Bus daemon used by this object, or this object itself if it's "
1325       "a TpDBusDaemon", TP_TYPE_DBUS_DAEMON,
1326       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1327   g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
1328       param_spec);
1329 
1330   /**
1331    * TpProxy:dbus-connection: (skip)
1332    *
1333    * The D-Bus connection for this object. Read-only except during
1334    * construction.
1335    */
1336   param_spec = g_param_spec_boxed ("dbus-connection", "D-Bus connection",
1337       "The D-Bus connection used by this object", DBUS_TYPE_G_CONNECTION,
1338       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1339   g_object_class_install_property (object_class, PROP_DBUS_CONNECTION,
1340       param_spec);
1341 
1342   /**
1343    * TpProxy:bus-name:
1344    *
1345    * The D-Bus bus name for this object. Read-only except during construction.
1346    */
1347   param_spec = g_param_spec_string ("bus-name", "D-Bus bus name",
1348       "The D-Bus bus name for this object", NULL,
1349       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1350   g_object_class_install_property (object_class, PROP_BUS_NAME,
1351       param_spec);
1352 
1353   /**
1354    * TpProxy:object-path:
1355    *
1356    * The D-Bus object path for this object. Read-only except during
1357    * construction.
1358    */
1359   param_spec = g_param_spec_string ("object-path", "D-Bus object path",
1360       "The D-Bus object path for this object", NULL,
1361       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1362   g_object_class_install_property (object_class, PROP_OBJECT_PATH,
1363       param_spec);
1364 
1365   /**
1366    * TpProxy:interfaces:
1367    *
1368    * Known D-Bus interface names for this object.
1369    */
1370   param_spec = g_param_spec_boxed ("interfaces", "D-Bus interfaces",
1371       "Known D-Bus interface names for this object", G_TYPE_STRV,
1372       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1373   g_object_class_install_property (object_class, PROP_INTERFACES,
1374       param_spec);
1375 
1376   /**
1377    * TpProxy:factory:
1378    *
1379    * The #TpSimpleClientFactory used to create this proxy,
1380    * or %NULL if this proxy was not created through a factory.
1381    */
1382   param_spec = g_param_spec_object ("factory", "Simple Client Factory",
1383       "The TpSimpleClientFactory used to create this proxy",
1384       TP_TYPE_SIMPLE_CLIENT_FACTORY,
1385       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1386   g_object_class_install_property (object_class, PROP_FACTORY,
1387       param_spec);
1388 
1389   /**
1390    * TpProxy::interface-added: (skip)
1391    * @self: the proxy object
1392    * @id: the GQuark representing the interface
1393    * @proxy: the dbus-glib proxy representing the interface
1394    *
1395    * Emitted when this proxy has gained an interface. It is not guaranteed
1396    * to be emitted immediately, but will be emitted before the interface is
1397    * first used (at the latest: before it's returned from
1398    * tp_proxy_get_interface_by_id(), any signal is connected, or any
1399    * method is called).
1400    *
1401    * The intended use is to call dbus_g_proxy_add_signals(). This signal
1402    * should only be used by TpProy implementations
1403    */
1404   signals[SIGNAL_INTERFACE_ADDED] = g_signal_new ("interface-added",
1405       G_OBJECT_CLASS_TYPE (klass),
1406       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1407       0,
1408       NULL, NULL, NULL,
1409       G_TYPE_NONE, 2, G_TYPE_UINT, DBUS_TYPE_G_PROXY);
1410 
1411   /**
1412    * TpProxy::invalidated:
1413    * @self: the proxy object
1414    * @domain: domain of a GError indicating why this proxy was invalidated
1415    * @code: error code of a GError indicating why this proxy was invalidated
1416    * @message: a message associated with the error
1417    *
1418    * Emitted when this proxy has been become invalid for
1419    * whatever reason. Any more specific signal should be emitted first.
1420    *
1421    * An invalidated proxy is one which can make no more method calls and will
1422    * emit no more D-Bus signals. This is typically because the D-Bus object
1423    * represented by the proxy ceased to exist, or there was some error
1424    * obtaining the initial state.
1425    *
1426    * Any pending or future method calls made on this proxy will fail gracefully
1427    * with the same error as returned by tp_proxy_get_invalidated().
1428    */
1429   signals[SIGNAL_INVALIDATED] = g_signal_new ("invalidated",
1430       G_OBJECT_CLASS_TYPE (klass),
1431       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1432       0,
1433       NULL, NULL, NULL,
1434       G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
1435 }
1436 
1437 /**
1438  * tp_proxy_get_factory:
1439  * @self: a #TpProxy or subclass
1440  *
1441  * <!-- -->
1442  *
1443  * Returns: (transfer none): the same value as #TpProxy:factory property
1444  *
1445  * Since: 0.15.5
1446  */
1447 TpSimpleClientFactory *
tp_proxy_get_factory(gpointer self)1448 tp_proxy_get_factory (gpointer self)
1449 {
1450   TpProxy *proxy = self;
1451 
1452   g_return_val_if_fail (TP_IS_PROXY (self), NULL);
1453 
1454   return proxy->priv->factory;
1455 }
1456 
1457 void
_tp_proxy_ensure_factory(gpointer proxy,TpSimpleClientFactory * factory)1458 _tp_proxy_ensure_factory (gpointer proxy,
1459     TpSimpleClientFactory *factory)
1460 {
1461   TpProxy *self = TP_PROXY (proxy);
1462 
1463   if (self->priv->factory != NULL)
1464     return;
1465 
1466   if (factory != NULL)
1467     {
1468       self->priv->factory = g_object_ref (factory);
1469     }
1470   else
1471     {
1472       self->priv->factory = (TpSimpleClientFactory *)
1473           tp_automatic_client_factory_new (self->dbus_daemon);
1474     }
1475 
1476   _tp_simple_client_factory_insert_proxy (self->priv->factory, self);
1477 }
1478 
1479 /**
1480  * tp_proxy_get_dbus_daemon:
1481  * @self: a #TpProxy or subclass
1482  *
1483  * <!-- -->
1484  *
1485  * Returns: (transfer none): a borrowed reference to the #TpDBusDaemon for
1486  *  this object, if any; always %NULL if this object is itself a
1487  *  #TpDBusDaemon. The caller must reference the returned object with
1488  *  g_object_ref() if it will be kept.
1489  *
1490  * Since: 0.7.17
1491  */
1492 TpDBusDaemon *
tp_proxy_get_dbus_daemon(gpointer self)1493 tp_proxy_get_dbus_daemon (gpointer self)
1494 {
1495   TpProxy *proxy = TP_PROXY (self);
1496 
1497   return proxy->dbus_daemon;
1498 }
1499 
1500 /**
1501  * tp_proxy_get_dbus_connection: (skip)
1502  * @self: a #TpProxy or subclass
1503  *
1504  * <!-- -->
1505  *
1506  * Returns: a borrowed reference to the D-Bus connection used by this object.
1507  *  The caller must reference the returned pointer with
1508  *  dbus_g_connection_ref() if it will be kept.
1509  *
1510  * Since: 0.7.17
1511  */
1512 DBusGConnection *
tp_proxy_get_dbus_connection(gpointer self)1513 tp_proxy_get_dbus_connection (gpointer self)
1514 {
1515   TpProxy *proxy = TP_PROXY (self);
1516 
1517   return proxy->dbus_connection;
1518 }
1519 
1520 /**
1521  * tp_proxy_get_bus_name:
1522  * @self: a #TpProxy or subclass
1523  *
1524  * <!-- -->
1525  *
1526  * Returns: the bus name of the application exporting the object. The caller
1527  *  must copy the string with g_strdup() if it will be kept.
1528  *
1529  * Since: 0.7.17
1530  */
1531 const gchar *
tp_proxy_get_bus_name(gpointer self)1532 tp_proxy_get_bus_name (gpointer self)
1533 {
1534   TpProxy *proxy = TP_PROXY (self);
1535 
1536   return proxy->bus_name;
1537 }
1538 
1539 /**
1540  * tp_proxy_get_object_path:
1541  * @self: a #TpProxy or subclass
1542  *
1543  * <!-- -->
1544  *
1545  * Returns: the object path of the remote object. The caller must copy the
1546  *  string with g_strdup() if it will be kept.
1547  *
1548  * Since: 0.7.17
1549  */
1550 const gchar *
tp_proxy_get_object_path(gpointer self)1551 tp_proxy_get_object_path (gpointer self)
1552 {
1553   TpProxy *proxy = TP_PROXY (self);
1554 
1555   return proxy->object_path;
1556 }
1557 
1558 /**
1559  * tp_proxy_get_invalidated:
1560  * @self: a #TpProxy or subclass
1561  *
1562  * <!-- -->
1563  *
1564  * Returns: the reason this proxy was invalidated, or %NULL if has not been
1565  *  invalidated. The caller must copy the error, for instance with
1566  *  g_error_copy(), if it will be kept.
1567  *
1568  * Since: 0.7.17
1569  */
1570 const GError *
tp_proxy_get_invalidated(gpointer self)1571 tp_proxy_get_invalidated (gpointer self)
1572 {
1573   TpProxy *proxy = TP_PROXY (self);
1574 
1575   return proxy->invalidated;
1576 }
1577 
1578 /**
1579  * tp_proxy_dbus_g_proxy_claim_for_signal_adding:
1580  * @proxy: a #DBusGProxy
1581  *
1582  * Attempt to "claim" a #DBusGProxy for addition of signal signatures.
1583  * If this function has not been called on @proxy before, %TRUE is
1584  * returned, and the caller may safely call dbus_g_proxy_add_signal()
1585  * on @proxy. If this function has already been caled, %FALSE is
1586  * returned, and the caller may not safely call dbus_g_proxy_add_signal().
1587  *
1588  * This is intended for use by auto-generated signal-adding functions,
1589  * to allow interfaces provided as local extensions to override those in
1590  * telepathy-glib without causing assertion failures.
1591  *
1592  * Returns: %TRUE if it is safe to call dbus_g_proxy_add_signal()
1593  * Since: 0.7.6
1594  */
1595 gboolean
tp_proxy_dbus_g_proxy_claim_for_signal_adding(DBusGProxy * proxy)1596 tp_proxy_dbus_g_proxy_claim_for_signal_adding (DBusGProxy *proxy)
1597 {
1598   static GQuark q = 0;
1599 
1600   g_return_val_if_fail (proxy != NULL, FALSE);
1601 
1602   if (G_UNLIKELY (q == 0))
1603     {
1604       q = g_quark_from_static_string (
1605           "tp_proxy_dbus_g_proxy_claim_for_signal_adding@0.7.6");
1606     }
1607 
1608   if (g_object_get_qdata ((GObject *) proxy, q) != NULL)
1609     {
1610       /* Someone else has already added signal signatures for this interface.
1611        * We can't do it again or it'll cause an assertion */
1612       return FALSE;
1613     }
1614 
1615   /* the proxy is just used as qdata here because it's a convenient
1616    * non-NULL pointer */
1617   g_object_set_qdata ((GObject *) proxy, q, proxy);
1618   return TRUE;
1619 }
1620 
1621 static gpointer
tp_proxy_once(gpointer data G_GNUC_UNUSED)1622 tp_proxy_once (gpointer data G_GNUC_UNUSED)
1623 {
1624   GType type = TP_TYPE_PROXY;
1625 
1626   tp_proxy_or_subclass_hook_on_interface_add (type,
1627       tp_cli_generic_add_signals);
1628 
1629   return NULL;
1630 }
1631 
1632 /**
1633  * tp_proxy_init_known_interfaces:
1634  *
1635  * Ensure that the known interfaces for TpProxy have been set up.
1636  * This is done automatically when necessary, but for correct
1637  * overriding of library interfaces by local extensions, you should
1638  * call this function before calling
1639  * tp_proxy_or_subclass_hook_on_interface_add().
1640  *
1641  * Functions like tp_connection_init_known_interfaces and
1642  * tp_channel_init_known_interfaces do this automatically.
1643  *
1644  * Since: 0.7.6
1645  */
1646 void
tp_proxy_init_known_interfaces(void)1647 tp_proxy_init_known_interfaces (void)
1648 {
1649   static GOnce once = G_ONCE_INIT;
1650 
1651   g_once (&once, tp_proxy_once, NULL);
1652 }
1653 
1654 static const TpProxyFeature *
tp_proxy_subclass_get_feature(GType type,GQuark feature)1655 tp_proxy_subclass_get_feature (GType type,
1656     GQuark feature)
1657 {
1658   GType proxy_type = TP_TYPE_PROXY;
1659 
1660   g_return_val_if_fail (g_type_is_a (type, proxy_type), NULL);
1661 
1662   /* we stop at proxy_type since we know that TpProxy has no features */
1663   for ( ; type != proxy_type; type = g_type_parent (type))
1664     {
1665       guint i;
1666       TpProxyClass *cls = g_type_class_ref (type);
1667       const TpProxyFeature *features;
1668 
1669       if (cls->list_features == NULL)
1670         goto cont;
1671 
1672       features = cls->list_features (cls);
1673 
1674       if (features == NULL)
1675         goto cont;
1676 
1677       for (i = 0; features[i].name != 0; i++)
1678         {
1679           if (features[i].name == feature)
1680             {
1681               g_type_class_unref (cls);
1682               return features + i;
1683             }
1684         }
1685 
1686 cont:
1687       g_type_class_unref (cls);
1688     }
1689 
1690   return FALSE;
1691 }
1692 
1693 /**
1694  * tp_proxy_is_prepared:
1695  * @self: an instance of a #TpProxy subclass
1696  * @feature: a feature that is supported by @self's class
1697  *
1698  * Return %TRUE if @feature has been prepared successfully, or %FALSE if
1699  * @feature has not been requested, has not been prepared yet, or is not
1700  * available on this object at all.
1701  *
1702  * (For instance, if @feature is %TP_CHANNEL_FEATURE_CHAT_STATES and @self
1703  * is a #TpChannel in a protocol that doesn't actually implement chat states,
1704  * or is not a #TpChannel at all, then this method will return %FALSE.)
1705  *
1706  * To prepare features, call tp_proxy_prepare_async().
1707  *
1708  * Returns: %TRUE if @feature has been prepared successfully
1709  *
1710  * Since: 0.11.3
1711  */
1712 gboolean
tp_proxy_is_prepared(gpointer self,GQuark feature)1713 tp_proxy_is_prepared (gpointer self,
1714     GQuark feature)
1715 {
1716   FeatureState state;
1717 
1718   g_return_val_if_fail (TP_IS_PROXY (self), FALSE);
1719 
1720   if (tp_proxy_get_invalidated (self) != NULL)
1721     return FALSE;
1722 
1723   state = tp_proxy_get_feature_state (self, feature);
1724 
1725   return (state == FEATURE_STATE_READY);
1726 }
1727 
1728 /*
1729  * _tp_proxy_is_preparing:
1730  * @self: an instance of a #TpProxy subclass
1731  * @feature: a feature that is supported by @self's class
1732  *
1733  * Return %TRUE if @feature has been requested, but has not been prepared
1734  * successfully or unsuccessfully yet.
1735  *
1736  * It is an error to use a @feature not specifically supported by @self - for
1737  * instance, it is an error to use %TP_CHANNEL_FEATURE_CHAT_STATES on any
1738  * #TpProxy that is not also a #TpChannel.
1739  *
1740  * Subclasses of #TpProxy should use this method to check whether to take
1741  * action for a particular feature. For instance, #TpChannel could call this
1742  * method for %TP_CHANNEL_CHAT_STATES when it discovers that the ChatStates
1743  * interface is supported, to decide whether to fetch the state of that
1744  * interface.
1745  *
1746  * Returns: %TRUE if @feature has been requested, but preparing it has neither
1747  *  succeeded nor failed yet
1748  */
1749 gboolean
_tp_proxy_is_preparing(gpointer self,GQuark feature)1750 _tp_proxy_is_preparing (gpointer self,
1751     GQuark feature)
1752 {
1753   FeatureState state;
1754 
1755   g_return_val_if_fail (TP_IS_PROXY (self), FALSE);
1756 
1757   if (tp_proxy_get_invalidated (self) != NULL)
1758     return FALSE;
1759 
1760   state = tp_proxy_get_feature_state (self, feature);
1761   g_return_val_if_fail (state != FEATURE_STATE_INVALID, FALSE);
1762   return (state == FEATURE_STATE_WANTED || state == FEATURE_STATE_TRYING);
1763 }
1764 
1765 static gboolean
check_feature_interfaces(TpProxy * self,GQuark name)1766 check_feature_interfaces (TpProxy *self,
1767     GQuark name)
1768 {
1769   const TpProxyFeature *feature = tp_proxy_subclass_get_feature (
1770       G_OBJECT_TYPE (self), name);
1771   guint i;
1772 
1773   if (feature->interfaces_needed == NULL)
1774     return TRUE;
1775 
1776   for (i = 0; feature->interfaces_needed[i] != 0; i++)
1777     {
1778       if (!tp_proxy_has_interface_by_id (self, feature->interfaces_needed[i]))
1779         {
1780           DEBUG ("Proxy doesn't implement %s, can't prepare feature %s",
1781               g_quark_to_string (feature->interfaces_needed[i]),
1782               g_quark_to_string (name));
1783 
1784           return FALSE;
1785         }
1786     }
1787 
1788   return TRUE;
1789 }
1790 
1791 /* Returns %TRUE if all the deps of @name are ready
1792  * @can_retry: if %TRUE dependencies which have failed but have
1793  * TpProxyFeature.can_retry won't be considered as having failed so we'll
1794  * still have a change to retry preparing those.
1795  * @failed: (out): %TRUE if one of @name's dep can't be prepared and so
1796  * @name can't be either
1797  */
1798 static gboolean
check_depends_ready(TpProxy * self,GQuark name,gboolean can_retry,gboolean * failed)1799 check_depends_ready (TpProxy *self,
1800     GQuark name,
1801     gboolean can_retry,
1802     gboolean *failed)
1803 {
1804   const TpProxyFeature *feature = tp_proxy_subclass_get_feature (
1805       G_OBJECT_TYPE (self), name);
1806   guint i;
1807   gboolean ready = TRUE;
1808 
1809   g_assert (failed != NULL);
1810   *failed = FALSE;
1811 
1812   if (feature->depends_on == NULL)
1813     return TRUE;
1814 
1815   for (i = 0; feature->depends_on[i] != 0; i++)
1816     {
1817       GQuark dep = feature->depends_on[i];
1818       const TpProxyFeature *dep_feature = tp_proxy_subclass_get_feature (
1819           G_OBJECT_TYPE (self), dep);
1820       FeatureState dep_state;
1821 
1822       dep_state = tp_proxy_get_feature_state (self, dep);
1823       switch (dep_state)
1824         {
1825           case FEATURE_STATE_INVALID:
1826             DEBUG ("Can't prepare %s, because %s (a dependency) is "
1827                 "invalid", g_quark_to_string (name), g_quark_to_string (dep));
1828 
1829             *failed = TRUE;
1830             return FALSE;
1831 
1832           case FEATURE_STATE_FAILED:
1833           case FEATURE_STATE_MISSING_IFACE:
1834             if (!can_retry || !dep_feature->can_retry)
1835               {
1836                 DEBUG ("Can't prepare %s, because %s (a dependency) is "
1837                     "failed to prepare",
1838                     g_quark_to_string (name), g_quark_to_string (dep));
1839 
1840                 *failed = TRUE;
1841                 return FALSE;
1842               }
1843 
1844             DEBUG ("retry preparing dep: %s", g_quark_to_string (dep));
1845             tp_proxy_set_feature_state (self, dep, FEATURE_STATE_WANTED);
1846             ready = FALSE;
1847             break;
1848 
1849           case FEATURE_STATE_UNWANTED:
1850           case FEATURE_STATE_WANTED:
1851           case FEATURE_STATE_TRYING:
1852             ready = FALSE;
1853             break;
1854 
1855           case FEATURE_STATE_READY:
1856             break;
1857         }
1858     }
1859 
1860   return ready;
1861 }
1862 
1863 static void
depends_prepare_cb(GObject * source,GAsyncResult * result,gpointer user_data)1864 depends_prepare_cb (GObject *source,
1865     GAsyncResult *result,
1866     gpointer user_data)
1867 {
1868   TpProxy *self = TP_PROXY (source);
1869 
1870   tp_proxy_poll_features (self, NULL);
1871 }
1872 
1873 static void
prepare_depends(TpProxy * self,GQuark name)1874 prepare_depends (TpProxy *self,
1875     GQuark name)
1876 {
1877   const TpProxyFeature *feature;
1878 
1879   feature = tp_proxy_subclass_get_feature (G_OBJECT_TYPE (self), name);
1880   g_assert (feature->depends_on != NULL);
1881 
1882   tp_proxy_prepare_async (self, feature->depends_on, depends_prepare_cb, NULL);
1883 }
1884 
1885 /**
1886  * tp_proxy_prepare_async:
1887  * @self: an instance of a #TpProxy subclass
1888  * @features: (transfer none) (array zero-terminated=1) (allow-none): an array
1889  *  of desired features, ending with 0; %NULL is equivalent to an array
1890  *  containing only 0
1891  * @callback: if not %NULL, called exactly once, when the features have all
1892  *  been prepared or failed to prepare, or after the proxy is invalidated
1893  * @user_data: user data for @callback
1894  *
1895  * #TpProxy itself does not support any features, but subclasses like
1896  * #TpChannel can support features, which can either be core functionality like
1897  * %TP_CHANNEL_FEATURE_CORE, or extended functionality like
1898  * %TP_CHANNEL_FEATURE_CHAT_STATES.
1899  *
1900  * Proxy instances start with no features prepared. When features are
1901  * requested via tp_proxy_prepare_async(), the proxy starts to do the
1902  * necessary setup to use those features.
1903  *
1904  * tp_proxy_prepare_async() always waits for core functionality of the proxy's
1905  * class to be prepared, even if it is not specifically requested: for
1906  * instance, because %TP_CHANNEL_FEATURE_CORE is core functionality of a
1907  * #TpChannel,
1908  *
1909  * |[
1910  * TpChannel *channel = ...;
1911  *
1912  * tp_proxy_prepare_async (channel, NULL, callback, user_data);
1913  * ]|
1914  *
1915  * is equivalent to
1916  *
1917  * |[
1918  * TpChannel *channel = ...;
1919  * GQuark features[] = { TP_CHANNEL_FEATURE_CORE, 0 };
1920  *
1921  * tp_proxy_prepare_async (channel, features, callback, user_data);
1922  * ]|
1923  *
1924  * If a feature represents core functionality (like %TP_CHANNEL_FEATURE_CORE),
1925  * failure to prepare it will result in tp_proxy_prepare_async() finishing
1926  * unsuccessfully: if failure to prepare the feature indicates that the proxy
1927  * is no longer useful, it will also emit #TpProxy::invalidated.
1928  *
1929  * If a feature represents non-essential functionality
1930  * (like %TP_CHANNEL_FEATURE_CHAT_STATES), or is not supported by the object
1931  * at all, then failure to prepare it is not fatal:
1932  * tp_proxy_prepare_async() will complete successfully, but
1933  * tp_proxy_is_prepared() will still return %FALSE for the feature, and
1934  * accessor methods for the feature will typically return a dummy value.
1935  *
1936  * Some #TpProxy subclasses automatically start to prepare their core
1937  * features when instantiated, and features will sometimes become prepared as
1938  * a side-effect of other actions, but to ensure that a feature is present you
1939  * must generally call tp_proxy_prepare_async() and wait for the result.
1940  *
1941  * Since: 0.11.3
1942  */
1943 void
tp_proxy_prepare_async(gpointer self,const GQuark * features,GAsyncReadyCallback callback,gpointer user_data)1944 tp_proxy_prepare_async (gpointer self,
1945     const GQuark *features,
1946     GAsyncReadyCallback callback,
1947     gpointer user_data)
1948 {
1949   TpProxy *proxy = self;
1950   GSimpleAsyncResult *result = NULL;
1951   guint i;
1952 
1953   g_return_if_fail (TP_IS_PROXY (self));
1954 
1955   if (features == NULL)
1956     features = no_quarks;
1957 
1958   for (i = 0; features[i] != 0; i++)
1959     {
1960       FeatureState state = tp_proxy_get_feature_state (self, features[i]);
1961       const TpProxyFeature *feature = tp_proxy_subclass_get_feature (
1962           G_OBJECT_TYPE (self), features[i]);
1963 
1964       /* We just skip unknown features, which have state FEATURE_STATE_INVALID
1965        * (this doesn't seem ideal, but is
1966        * consistent with TpAccountManager's existing behaviour) */
1967       if (state == FEATURE_STATE_INVALID)
1968         {
1969           continue;
1970         }
1971       else if (state == FEATURE_STATE_UNWANTED ||
1972           (state == FEATURE_STATE_FAILED && feature->can_retry))
1973         {
1974           gboolean failed;
1975 
1976           /* Check deps. We only offer there the chance to retry a previously
1977            * failed dependency. Doing it in tp_proxy_poll_features() could
1978            * result in an infinite loop if we'd depends on 2 features which
1979            * are constantly failing. */
1980           if (!check_depends_ready (self, features[i], TRUE, &failed))
1981             {
1982               if (failed)
1983                 {
1984                   /* We can't prepare the feature because of its deps */
1985                   tp_proxy_set_feature_state (self, features[i],
1986                       FEATURE_STATE_FAILED);
1987                   continue;
1988                 }
1989 
1990               prepare_depends (self, features[i]);
1991             }
1992 
1993           tp_proxy_set_feature_state (self, features[i], FEATURE_STATE_WANTED);
1994         }
1995     }
1996 
1997   if (callback != NULL)
1998     result = g_simple_async_result_new (self, callback, user_data,
1999         tp_proxy_prepare_async);
2000 
2001   if (proxy->invalidated != NULL)
2002     {
2003       if (result != NULL)
2004         {
2005           g_simple_async_result_set_from_error (result, proxy->invalidated);
2006           g_simple_async_result_complete_in_idle (result);
2007         }
2008 
2009       goto finally;
2010     }
2011 
2012   g_queue_push_tail (proxy->priv->prepare_requests,
2013       tp_proxy_prepare_request_new (result, features));
2014   tp_proxy_poll_features (proxy, NULL);
2015 
2016 finally:
2017   if (result != NULL)
2018     g_object_unref (result);
2019 }
2020 
2021 /**
2022  * tp_proxy_prepare_finish:
2023  * @self: an instance of a #TpProxy subclass
2024  * @result: the result passed to the callback of tp_proxy_prepare_async()
2025  * @error: used to return an error if %FALSE is returned
2026  *
2027  * Check for error in a call to tp_proxy_prepare_async(). An error here
2028  * generally indicates that either the asynchronous call was cancelled,
2029  * or @self has emitted #TpProxy::invalidated.
2030  *
2031  * Returns: %FALSE (setting @error) if tp_proxy_prepare_async() failed
2032  *  or was cancelled
2033  *
2034  * Since: 0.11.3
2035  */
2036 gboolean
tp_proxy_prepare_finish(gpointer self,GAsyncResult * result,GError ** error)2037 tp_proxy_prepare_finish (gpointer self,
2038     GAsyncResult *result,
2039     GError **error)
2040 {
2041   _tp_implement_finish_void (self, tp_proxy_prepare_async);
2042 }
2043 
2044 static gboolean
prepare_finish(TpProxy * self,GAsyncResult * result,gpointer source,GError ** error)2045 prepare_finish (TpProxy *self,
2046     GAsyncResult *result,
2047     gpointer source,
2048     GError **error)
2049 {
2050   _tp_implement_finish_void (self, source);
2051 }
2052 
2053 static void
feature_prepared_cb(GObject * source,GAsyncResult * result,gpointer user_data)2054 feature_prepared_cb (GObject *source,
2055     GAsyncResult *result,
2056     gpointer user_data)
2057 {
2058   TpProxy *self = TP_PROXY (source);
2059   TpProxyFeature *feature = user_data;
2060   GError *error = NULL;
2061   gboolean prepared = TRUE;
2062 
2063   if (!prepare_finish (self, result, feature->prepare_async, &error))
2064     {
2065       DEBUG ("Failed to prepare %s: %s", g_quark_to_string (feature->name),
2066           error->message);
2067 
2068       prepared = FALSE;
2069       g_error_free (error);
2070     }
2071 
2072   _tp_proxy_set_feature_prepared (self, feature->name, prepared);
2073 }
2074 
2075 static void
prepare_feature(TpProxy * self,const TpProxyFeature * feature)2076 prepare_feature (TpProxy *self,
2077     const TpProxyFeature *feature)
2078 {
2079   /* If no function is set, then subclass is supposed to call
2080    * _tp_proxy_set_feature_prepared() itself. This is used by features prepared
2081    * from constructed. */
2082   if (feature->prepare_async == NULL)
2083     return;
2084 
2085   feature->prepare_async (self, feature, feature_prepared_cb,
2086       (gpointer) feature);
2087 }
2088 
2089 static gboolean
core_prepared(TpProxy * self)2090 core_prepared (TpProxy *self)
2091 {
2092   /* All the core features have been prepared if the head of the
2093    * prepare_requests queue is NOT a core feature */
2094   TpProxyPrepareRequest *req = g_queue_peek_head (self->priv->prepare_requests);
2095 
2096   if (req == NULL)
2097     return TRUE;
2098 
2099   return !req->core;
2100 }
2101 
2102 /* Returns %TRUE if all the features requested in @req have complete their
2103  * preparation */
2104 static gboolean
request_is_complete(TpProxy * self,TpProxyPrepareRequest * req)2105 request_is_complete (TpProxy *self,
2106     TpProxyPrepareRequest *req)
2107 {
2108   guint i;
2109   gboolean complete = TRUE;
2110 
2111   for (i = 0; i < req->features->len; i++)
2112     {
2113       GQuark feature = g_array_index (req->features, GQuark, i);
2114       FeatureState state = tp_proxy_get_feature_state (self, feature);
2115       const TpProxyFeature *feat_struct = tp_proxy_subclass_get_feature (
2116           G_OBJECT_TYPE (self), feature);
2117 
2118       switch (state)
2119         {
2120           case FEATURE_STATE_UNWANTED:
2121             /* this can only happen in the special pseudo-request for the
2122              * core features, which blocks everything */
2123             g_assert (req->core);
2124             complete = FALSE;
2125 
2126             /* fall through to treat it as WANTED */
2127           case FEATURE_STATE_WANTED:
2128             if (core_prepared (self) ||
2129                 req->core)
2130               {
2131                 gboolean failed;
2132 
2133                 /* Check if we have the required interfaces. We can't do that
2134                  * in tp_proxy_prepare_async() as CORE have to be prepared */
2135                 if (!check_feature_interfaces (self, feature))
2136                   {
2137                     if (TP_IS_CONNECTION (self) &&
2138                         tp_connection_get_status ((TpConnection *) self, NULL)
2139                         != TP_CONNECTION_STATUS_CONNECTED)
2140                       {
2141                         /* Give a chance to retry preparing the feature once
2142                          * the Connection is connected as it may still gain
2143                          * the interface. */
2144                         tp_proxy_set_feature_state (self, feature,
2145                             FEATURE_STATE_MISSING_IFACE);
2146                       }
2147                     else
2148                       {
2149                         tp_proxy_set_feature_state (self, feature,
2150                             FEATURE_STATE_FAILED);
2151                       }
2152                     continue;
2153                   }
2154 
2155                 if (check_depends_ready (self, feature, FALSE, &failed))
2156                   {
2157                     /* We can prepare it now */
2158                     DEBUG ("%p: calling callback for %s", self,
2159                         g_quark_to_string (feature));
2160 
2161                     tp_proxy_set_feature_state (self, feature,
2162                         FEATURE_STATE_TRYING);
2163 
2164                     prepare_feature (self, feat_struct);
2165                     complete = FALSE;
2166                   }
2167                 else if (failed)
2168                   {
2169                     tp_proxy_set_feature_state (self, feature,
2170                         FEATURE_STATE_FAILED);
2171                   }
2172                 else
2173                   {
2174                     /* We have to wait until the deps finish their
2175                      * preparation. */
2176                     complete = FALSE;
2177                   }
2178               }
2179             break;
2180 
2181           case FEATURE_STATE_TRYING:
2182             complete = FALSE;
2183             break;
2184 
2185           case FEATURE_STATE_INVALID:
2186           case FEATURE_STATE_FAILED:
2187           case FEATURE_STATE_MISSING_IFACE:
2188           case FEATURE_STATE_READY:
2189             /* nothing more to do */
2190             break;
2191         }
2192     }
2193 
2194   return complete;
2195 }
2196 
2197 static void
finish_all_requests(TpProxy * self,const GError * error)2198 finish_all_requests (TpProxy *self,
2199     const GError *error)
2200 {
2201   GList *iter;
2202   GQueue *tmp = g_queue_copy (self->priv->prepare_requests);
2203 
2204   g_queue_clear (self->priv->prepare_requests);
2205 
2206   for (iter = tmp->head; iter != NULL; iter = g_list_next (iter))
2207     {
2208       tp_proxy_prepare_request_finish (iter->data, error);
2209     }
2210 
2211   g_queue_free (tmp);
2212 }
2213 
2214 /*
2215  * tp_proxy_poll_features:
2216  * @self: a proxy
2217  * @error: if not %NULL, fail all feature requests with this error
2218  *
2219  * For each feature in state WANTED, if its dependencies have been satisfied,
2220  * call the callback and advance it to state TRYING.
2221  *
2222  * For each feature request, see if it's finished yet.
2223  *
2224  * Called every time the set of prepared/failed features changes,
2225  * when a temporary error causes introspection to fail, and when
2226  * #TpProxy.invalidated changes.
2227  *
2228  * If @error is %NULL, #TpProxy.invalidated is also checked.
2229  */
2230 static void
tp_proxy_poll_features(TpProxy * self,const GError * error)2231 tp_proxy_poll_features (TpProxy *self,
2232     const GError *error)
2233 {
2234   const gchar *error_source = "temporarily failed";
2235   GList *iter;
2236   GList *next;
2237 
2238   if (g_queue_get_length (self->priv->prepare_requests) == 0)
2239     return;
2240 
2241   g_object_ref (self);
2242 
2243   for (iter = self->priv->prepare_requests->head; iter != NULL; iter = next)
2244     {
2245       TpProxyPrepareRequest *req = iter->data;
2246       TpProxyPrepareRequest *head = g_queue_peek_head (
2247           self->priv->prepare_requests);
2248 
2249       if (error == NULL)
2250         {
2251           error_source = "invalidated";
2252           error = self->invalidated;
2253         }
2254 
2255       if (error != NULL)
2256         {
2257           DEBUG ("%p: %s, ending all requests", self, error_source);
2258 
2259           finish_all_requests (self, error);
2260           break;
2261         }
2262 
2263       next = iter->next;
2264 
2265       /* Core features have to be prepared first, in superclass-to-subclass
2266        * order. The next core feature to be prepared, if any, is always at the
2267        * head of prepare_requests. */
2268       if (!core_prepared (self) &&
2269           req != head)
2270         {
2271           DEBUG ("%p: core features not ready yet, nothing prepared", self);
2272           continue;
2273         }
2274 
2275       if (request_is_complete (self, req))
2276         {
2277           DEBUG ("%p: request %p prepared", self, req);
2278           g_queue_delete_link (self->priv->prepare_requests, iter);
2279 
2280           tp_proxy_prepare_request_finish (req, NULL);
2281         }
2282     }
2283 
2284   g_object_unref (self);
2285 }
2286 
2287 /*
2288  * _tp_proxy_set_feature_prepared:
2289  * @self: a proxy
2290  * @feature: a feature made available by @self's class
2291  * @succeeded: %TRUE if the feature was prepared successfully
2292  *
2293  * Record that @self has attempted to prepare @feature. No further
2294  * attempts will be made to prepare it. If @succeeded is %TRUE,
2295  * tp_proxy_is_prepared() will return %TRUE for @self and @feature.
2296  * Whether @succeeded is %TRUE or %FALSE, any calls to
2297  * tp_proxy_prepare_async() that were only waiting for @feature will
2298  * finish successfully.
2299  *
2300  * If @feature represents core functionality of the class that should
2301  * always have worked (such as the GetAll method call for a #TpAccount's
2302  * properties), the subclass should instead call either
2303  * _tp_proxy_set_features_failed() (if it might still be possible to use @self
2304  * later, as for a #TpConnectionManager) or tp_proxy_invalidate() (if not)
2305  * instead; either of these will cause all calls to tp_proxy_prepare_async()
2306  * to finish with an error.
2307  */
2308 void
_tp_proxy_set_feature_prepared(TpProxy * self,GQuark feature,gboolean succeeded)2309 _tp_proxy_set_feature_prepared (TpProxy *self,
2310     GQuark feature,
2311     gboolean succeeded)
2312 {
2313   g_return_if_fail (TP_IS_PROXY (self));
2314   g_return_if_fail (tp_proxy_get_feature_state (self, feature) !=
2315       FEATURE_STATE_INVALID);
2316   tp_proxy_set_feature_state (self, feature,
2317       succeeded ? FEATURE_STATE_READY : FEATURE_STATE_FAILED);
2318   tp_proxy_poll_features (self, NULL);
2319 }
2320 
2321 /*
2322  * _tp_proxy_set_features_failed:
2323  * @self: a proxy
2324  * @error: an error
2325  *
2326  * Record that @self has been unable to prepare any features, but is still
2327  * potentially usable. Any pending calls to tp_proxy_prepare_async() will
2328  * finish unsuccessfully with @error, but @self will *not* be invalidated.
2329  */
2330 
2331 void
_tp_proxy_set_features_failed(TpProxy * self,const GError * error)2332 _tp_proxy_set_features_failed (TpProxy *self,
2333     const GError *error)
2334 {
2335   g_return_if_fail (TP_IS_PROXY (self));
2336   g_return_if_fail (error != NULL);
2337   tp_proxy_poll_features (self, error);
2338 }
2339 
2340 static void
check_announce_connected(TpProxy * self,gboolean in_idle)2341 check_announce_connected (TpProxy *self,
2342     gboolean in_idle)
2343 {
2344   if (self->priv->pending_will_announce_calls != 0)
2345     return;
2346 
2347   if (in_idle)
2348     {
2349       g_simple_async_result_complete_in_idle (
2350           self->priv->will_announce_connected_result);
2351     }
2352   else
2353     {
2354       g_simple_async_result_complete (
2355           self->priv->will_announce_connected_result);
2356     }
2357 
2358   tp_clear_object (&self->priv->will_announce_connected_result);
2359 }
2360 
2361 static void
prepare_before_signalling_connected_cb(GObject * source,GAsyncResult * result,gpointer user_data)2362 prepare_before_signalling_connected_cb (GObject *source,
2363     GAsyncResult *result,
2364     gpointer user_data)
2365 {
2366   TpProxy *self = TP_PROXY (user_data);
2367 
2368   /* We don't care if the call succeeded or not as it was already prepared */
2369   self->priv->pending_will_announce_calls--;
2370 
2371   check_announce_connected (self, FALSE);
2372 }
2373 
foreach_feature(GQuark name,gpointer data,gpointer user_data)2374 static void foreach_feature (GQuark name,
2375     gpointer data,
2376     gpointer user_data)
2377 {
2378   FeatureState state = GPOINTER_TO_INT (data);
2379   TpProxy *self = user_data;
2380 
2381   if (state == FEATURE_STATE_MISSING_IFACE)
2382     {
2383       GQuark features[] = { 0, 0};
2384 
2385       tp_proxy_set_feature_state (self, name, FEATURE_STATE_UNWANTED);
2386 
2387       self->priv->pending_will_announce_calls++;
2388 
2389       features[0] = name;
2390 
2391       tp_proxy_prepare_async (self, features,
2392           prepare_before_signalling_connected_cb, self);
2393     }
2394   else if (state == FEATURE_STATE_READY)
2395     {
2396       const TpProxyFeature *feature;
2397 
2398       feature = tp_proxy_subclass_get_feature (G_OBJECT_TYPE (self), name);
2399 
2400       if (feature->prepare_before_signalling_connected_async == NULL)
2401         return;
2402 
2403       self->priv->pending_will_announce_calls++;
2404 
2405       feature->prepare_before_signalling_connected_async (self, feature,
2406           prepare_before_signalling_connected_cb, self);
2407     }
2408 }
2409 
2410 /*
2411  * _tp_proxy_will_announce_connected_async:
2412  *
2413  * Called by connection.c when the connection became connected and we're about
2414  * to announce it. But before we have to wait for all the prepared features to
2415  * process their prepare_before_signalling_connected_async, if any.
2416  */
2417 void
_tp_proxy_will_announce_connected_async(TpProxy * self,GAsyncReadyCallback callback,gpointer user_data)2418 _tp_proxy_will_announce_connected_async (TpProxy *self,
2419     GAsyncReadyCallback callback,
2420     gpointer user_data)
2421 {
2422   g_assert (TP_IS_CONNECTION (self));
2423   g_assert (self->priv->will_announce_connected_result == NULL);
2424 
2425   self->priv->will_announce_connected_result = g_simple_async_result_new (
2426       (GObject *) self, callback, user_data,
2427       _tp_proxy_will_announce_connected_async);
2428 
2429   g_datalist_foreach (&self->priv->features, foreach_feature, self);
2430 
2431   check_announce_connected (self, TRUE);
2432 }
2433 
2434 gboolean
_tp_proxy_will_announce_connected_finish(TpProxy * self,GAsyncResult * result,GError ** error)2435 _tp_proxy_will_announce_connected_finish (TpProxy *self,
2436     GAsyncResult *result,
2437     GError **error)
2438 {
2439   _tp_implement_finish_void (self, _tp_proxy_will_announce_connected_async)
2440 }
2441