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