1 /*
2 * connection-manager.c - proxy for a Telepathy connection manager
3 *
4 * Copyright (C) 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
5 * Copyright (C) 2007-2009 Nokia Corporation
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "config.h"
23
24 #include "telepathy-glib/connection-manager.h"
25
26 #include <string.h>
27
28 #include "telepathy-glib/defs.h"
29 #include "telepathy-glib/enums.h"
30 #include "telepathy-glib/errors.h"
31 #include "telepathy-glib/gtypes.h"
32 #include <telepathy-glib/interfaces.h>
33 #include <telepathy-glib/proxy-internal.h>
34 #include <telepathy-glib/proxy-subclass.h>
35 #include "telepathy-glib/util.h"
36
37 #define DEBUG_FLAG TP_DEBUG_MANAGER
38 #include "telepathy-glib/debug-internal.h"
39 #include "telepathy-glib/protocol-internal.h"
40 #include "telepathy-glib/util-internal.h"
41
42 #include "telepathy-glib/_gen/tp-cli-connection-manager-body.h"
43
44 /**
45 * SECTION:connection-manager
46 * @title: TpConnectionManager
47 * @short_description: proxy object for a Telepathy connection manager
48 * @see_also: #TpConnection
49 *
50 * #TpConnectionManager objects represent Telepathy connection managers. They
51 * can be used to open connections.
52 *
53 * Since: 0.7.1
54 */
55
56 /**
57 * TpConnectionManagerListCb:
58 * @cms: (array zero-terminated=1): %NULL-terminated array of
59 * #TpConnectionManager (the objects will
60 * be unreferenced and the array will be freed after the callback returns,
61 * so the callback must reference any CMs it stores a pointer to),
62 * or %NULL on error
63 * @n_cms: number of connection managers in @cms (not including the final
64 * %NULL)
65 * @error: %NULL on success, or an error that occurred
66 * @user_data: user-supplied data
67 * @weak_object: user-supplied weakly referenced object
68 *
69 * Signature of the callback supplied to tp_list_connection_managers().
70 *
71 * Since 0.11.3, tp_list_connection_managers() will
72 * wait for %TP_CONNECTION_MANAGER_FEATURE_CORE to be prepared on each
73 * connection manager passed to @callback, unless an error occurred while
74 * launching that connection manager.
75 *
76 * Since: 0.7.1
77 */
78
79 /**
80 * TP_CONNECTION_MANAGER_FEATURE_CORE:
81 *
82 * Expands to a call to a function that returns a quark for the "core" feature
83 * on a #TpConnectionManager.
84 *
85 * After this feature is prepared, basic information about the connection
86 * manager's protocols (tp_connection_manager_dup_protocols()), and their
87 * available parameters, will have been retrieved, either by activating the
88 * connection manager over D-Bus or by reading the .manager file in which
89 * that information is cached.
90 *
91 * Since 0.11.11, this feature also finds any extra interfaces that
92 * this connection manager has, and adds them to #TpProxy:interfaces (where
93 * they can be queried with tp_proxy_has_interface()).
94 *
95 * (These are the same guarantees offered by the older
96 * tp_connection_manager_call_when_ready() mechanism.)
97 *
98 * One can ask for a feature to be prepared using the
99 * tp_proxy_prepare_async() function, and waiting for it to callback.
100 *
101 * Since: 0.11.3
102 */
103
104 GQuark
tp_connection_manager_get_feature_quark_core(void)105 tp_connection_manager_get_feature_quark_core (void)
106 {
107 return g_quark_from_static_string ("tp-connection-manager-feature-core");
108 }
109
110 /**
111 * TpCMInfoSource:
112 * @TP_CM_INFO_SOURCE_NONE: no information available
113 * @TP_CM_INFO_SOURCE_FILE: information came from a .manager file
114 * @TP_CM_INFO_SOURCE_LIVE: information came from the connection manager
115 *
116 * Describes possible sources of information on connection managers'
117 * supported protocols.
118 *
119 * Since 0.11.5, there is a corresponding #GEnumClass type,
120 * %TP_TYPE_CM_INFO_SOURCE.
121 *
122 * Since: 0.7.1
123 */
124
125 /**
126 * TP_TYPE_CM_INFO_SOURCE:
127 *
128 * The #GEnumClass type of a #TpCMInfoSource.
129 *
130 * Since: 0.11.5
131 */
132
133 /**
134 * TpConnectionManagerClass:
135 *
136 * The class of a #TpConnectionManager.
137 *
138 * Since: 0.7.1
139 */
140
141 enum
142 {
143 SIGNAL_ACTIVATED,
144 SIGNAL_GOT_INFO,
145 SIGNAL_EXITED,
146 N_SIGNALS
147 };
148
149 static guint signals[N_SIGNALS] = {0};
150
151 enum
152 {
153 PROP_INFO_SOURCE = 1,
154 PROP_MANAGER_FILE,
155 PROP_ALWAYS_INTROSPECT,
156 PROP_CONNECTION_MANAGER,
157 PROP_CM_NAME,
158 N_PROPS
159 };
160
161 /**
162 * TpConnectionManager:
163 *
164 * A proxy object for a Telepathy connection manager.
165 *
166 * This might represent a connection manager which is currently running
167 * (in which case it can be introspected) or not (in which case its
168 * capabilities can be read from .manager files in the filesystem).
169 * Accordingly, this object never emits #TpProxy::invalidated unless all
170 * references to it are discarded.
171 *
172 * Various fields and methods on this object do not work until
173 * %TP_CONNECTION_MANAGER_FEATURE_CORE is prepared. Use
174 * tp_proxy_prepare_async() to wait for this to happen.
175 *
176 * Since 0.19.1, accessing the fields of this struct is deprecated,
177 * and they are no longer documented here.
178 * Use the accessors tp_connection_manager_get_name(),
179 * tp_connection_manager_is_running(),
180 * tp_connection_manager_dup_protocols(),
181 * tp_connection_manager_get_info_source()
182 * and the #TpConnectionManager:always-introspect property instead.
183 *
184 * Since: 0.7.1
185 */
186
187 /**
188 * TpConnectionManagerParam:
189 *
190 * Structure representing a connection manager parameter.
191 *
192 * Since 0.19.1, accessing the fields of this struct is deprecated,
193 * and they are no longer documented here.
194 * Use the accessors tp_connection_manager_param_get_name(),
195 * tp_connection_manager_param_get_dbus_signature(),
196 * tp_connection_manager_param_is_required(),
197 * tp_connection_manager_param_is_required_for_registration(),
198 * tp_connection_manager_param_is_secret(),
199 * tp_connection_manager_param_is_dbus_property(),
200 * tp_connection_manager_param_get_default(),
201 * tp_connection_manager_param_dup_default_variant() instead.
202 *
203 * Since: 0.7.1
204 */
205
206 /**
207 * TpConnectionManagerProtocol:
208 * @name: The name of this connection manager
209 * @params: Array of #TpConnectionManagerParam structures, terminated by
210 * a structure whose @name is %NULL
211 *
212 * Structure representing a protocol supported by a connection manager.
213 * Note that the size of this structure may change, so its size must not be
214 * relied on.
215 *
216 * Since: 0.7.1
217 *
218 * Deprecated: 0.19.1, use #TpProtocol objects instead
219 */
220
221 typedef enum {
222 INTROSPECT_IDLE,
223 INTROSPECT_GETTING_PROPERTIES,
224 INTROSPECT_LISTING_PROTOCOLS,
225 INTROSPECT_GETTING_PARAMETERS
226 } IntrospectionStep;
227
228 struct _TpConnectionManagerPrivate {
229 /* absolute path to .manager file */
230 gchar *manager_file;
231
232 /* source ID for reading the manager file later */
233 guint manager_file_read_idle_id;
234
235 /* source ID for introspecting later */
236 guint introspect_idle_id;
237
238 /* TRUE if dispose() has run already */
239 unsigned disposed:1;
240
241 /* dup'd name => referenced TpProtocol, corresponding exactly to
242 * @protocol_structs */
243 GHashTable *protocol_objects;
244
245 /* GPtrArray of TpConnectionManagerProtocol *. This is the implementation
246 * for self->protocols. Each item is borrowed from the corresponding
247 * object in protocol_objects.
248 *
249 * NULL if file_info and live_info are both FALSE
250 * Protocols from file, if file_info is TRUE but live_info is FALSE
251 * Protocols from last time introspecting the CM succeeded, if live_info
252 * is TRUE */
253 GPtrArray *protocol_structs;
254
255 /* If we're waiting for a GetParameters, then GPtrArray of g_strdup'd
256 * gchar * representing protocols we haven't yet introspected.
257 * Otherwise NULL */
258 GPtrArray *pending_protocols;
259
260 /* dup'd name => referenced TpProtocol
261 *
262 * If we're waiting for a GetParameters, protocols we found so far for
263 * the introspection that is in progress (will replace protocol_objects
264 * when finished). Otherwise NULL */
265 GHashTable *found_protocols;
266
267 /* list of WhenReadyContext */
268 GList *waiting_for_ready;
269
270 /* things we introspected so far */
271 IntrospectionStep introspection_step;
272
273 /* the method call currently pending, or NULL if none. */
274 TpProxyPendingCall *introspection_call;
275
276 /* FALSE if initial name-owner (if any) hasn't been found yet */
277 gboolean name_known;
278 /* TRUE if someone asked us to activate but we're putting it off until
279 * name_known */
280 gboolean want_activation;
281 /* TRUE if the CM exited (crashed?) during introspection.
282 * We'll retry, but only once. */
283 gboolean retried_introspection;
284 };
285
G_DEFINE_TYPE(TpConnectionManager,tp_connection_manager,TP_TYPE_PROXY)286 G_DEFINE_TYPE (TpConnectionManager,
287 tp_connection_manager,
288 TP_TYPE_PROXY)
289
290
291 static void
292 _tp_connection_manager_param_copy_contents (
293 const TpConnectionManagerParam *in,
294 TpConnectionManagerParam *out)
295 {
296 out->name = g_strdup (in->name);
297 out->dbus_signature = g_strdup (in->dbus_signature);
298 out->flags = in->flags;
299
300 if (G_IS_VALUE (&in->default_value))
301 {
302 g_value_init (&out->default_value, G_VALUE_TYPE (&in->default_value));
303 g_value_copy (&in->default_value, &out->default_value);
304 }
305 }
306
307
308 /**
309 * tp_connection_manager_param_copy:
310 * @in: the #TpConnectionManagerParam to copy
311 *
312 * <!-- Returns: says it all -->
313 *
314 * Returns: a newly (slice) allocated #TpConnectionManagerParam, free with
315 * tp_connection_manager_param_free()
316 *
317 * Since: 0.11.3
318 */
319 TpConnectionManagerParam *
tp_connection_manager_param_copy(const TpConnectionManagerParam * in)320 tp_connection_manager_param_copy (const TpConnectionManagerParam *in)
321 {
322 TpConnectionManagerParam *out = g_slice_new0 (TpConnectionManagerParam);
323
324 _tp_connection_manager_param_copy_contents (in, out);
325
326 return out;
327 }
328
329
330 /**
331 * tp_connection_manager_param_free:
332 * @param: the #TpConnectionManagerParam to free
333 *
334 * Frees @param, which was copied with tp_connection_manager_param_copy().
335 *
336 * Since: 0.11.3
337 */
338 void
tp_connection_manager_param_free(TpConnectionManagerParam * param)339 tp_connection_manager_param_free (TpConnectionManagerParam *param)
340 {
341 _tp_connection_manager_param_free_contents (param);
342
343 g_slice_free (TpConnectionManagerParam, param);
344 }
345
346
347 /**
348 * tp_connection_manager_protocol_copy:
349 * @in: the #TpConnectionManagerProtocol to copy
350 *
351 * <!-- Returns: says it all -->
352 *
353 * Returns: a newly (slice) allocated #TpConnectionManagerProtocol, free with
354 * tp_connection_manager_protocol_free()
355 *
356 * Since: 0.11.3
357 *
358 * Deprecated: 0.19.1, use #TpProtocol objects instead
359 */
360 TpConnectionManagerProtocol *
tp_connection_manager_protocol_copy(const TpConnectionManagerProtocol * in)361 tp_connection_manager_protocol_copy (const TpConnectionManagerProtocol *in)
362 {
363 TpConnectionManagerProtocol *out = g_slice_new0 (TpConnectionManagerProtocol);
364 TpConnectionManagerParam *param;
365 GArray *params = g_array_new (TRUE, TRUE,
366 sizeof (TpConnectionManagerParam));
367
368 out->name = g_strdup (in->name);
369
370 for (param = in->params; param->name != NULL; param++)
371 {
372 TpConnectionManagerParam copy = { 0, };
373
374 _tp_connection_manager_param_copy_contents (param, ©);
375 g_array_append_val (params, copy);
376 }
377
378 out->params = (TpConnectionManagerParam *) g_array_free (params, FALSE);
379
380 return out;
381 }
382
383
384 /**
385 * tp_connection_manager_protocol_free:
386 * @proto: the #TpConnectionManagerProtocol to free
387 *
388 * Frees @proto, which was copied with tp_connection_manager_protocol_copy().
389 *
390 * Since: 0.11.3
391 *
392 * Deprecated: 0.19.1, use #TpProtocol objects instead
393 */
394 void
tp_connection_manager_protocol_free(TpConnectionManagerProtocol * proto)395 tp_connection_manager_protocol_free (TpConnectionManagerProtocol *proto)
396 {
397 _tp_connection_manager_protocol_free_contents (proto);
398
399 g_slice_free (TpConnectionManagerProtocol, proto);
400 }
401
402
403 /**
404 * TP_TYPE_CONNECTION_MANAGER_PARAM:
405 *
406 * The boxed type of a #TpConnectionManagerParam.
407 *
408 * Since: 0.11.3
409 */
410
411 G_DEFINE_BOXED_TYPE (TpConnectionManagerParam, tp_connection_manager_param,
412 tp_connection_manager_param_copy, tp_connection_manager_param_free)
413
414 /**
415 * TP_TYPE_CONNECTION_MANAGER_PROTOCOL:
416 *
417 * The boxed type of a #TpConnectionManagerProtocol.
418 *
419 * Since: 0.11.3
420 *
421 * Deprecated: 0.19.1, use #TpProtocol objects instead
422 */
423
424 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
425 G_DEFINE_BOXED_TYPE (TpConnectionManagerProtocol,
426 tp_connection_manager_protocol,
427 tp_connection_manager_protocol_copy, tp_connection_manager_protocol_free)
428 G_GNUC_END_IGNORE_DEPRECATIONS
429
430 typedef struct {
431 TpConnectionManager *cm;
432 TpConnectionManagerWhenReadyCb callback;
433 gpointer user_data;
434 GDestroyNotify destroy;
435 TpWeakRef *weak_ref;
436 } WhenReadyContext;
437
438 static void
when_ready_context_free(gpointer d)439 when_ready_context_free (gpointer d)
440 {
441 WhenReadyContext *c = d;
442
443 if (c->weak_ref != NULL)
444 {
445 tp_weak_ref_destroy (c->weak_ref);
446 c->weak_ref = NULL;
447 }
448
449 if (c->cm != NULL)
450 {
451 g_object_unref (c->cm);
452 c->cm = NULL;
453 }
454
455 if (c->destroy != NULL)
456 c->destroy (c->user_data);
457
458 g_slice_free (WhenReadyContext, c);
459 }
460
461 static void
tp_connection_manager_ready_or_failed(TpConnectionManager * self,const GError * error)462 tp_connection_manager_ready_or_failed (TpConnectionManager *self,
463 const GError *error)
464 {
465 if (self->info_source > TP_CM_INFO_SOURCE_NONE)
466 {
467 /* we have info already, so suppress any error and return the old info */
468 error = NULL;
469 }
470 else
471 {
472 g_assert (error != NULL);
473 }
474
475 if (error == NULL)
476 {
477 _tp_proxy_set_feature_prepared ((TpProxy *) self,
478 TP_CONNECTION_MANAGER_FEATURE_CORE, TRUE);
479 }
480 else
481 {
482 _tp_proxy_set_features_failed ((TpProxy *) self, error);
483 }
484 }
485
486 static void
tp_connection_manager_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)487 tp_connection_manager_ready_cb (GObject *source_object,
488 GAsyncResult *res,
489 gpointer user_data)
490 {
491 WhenReadyContext *c = user_data;
492 GError *error = NULL;
493 GObject *weak_object = NULL;
494
495 g_return_if_fail (source_object == (GObject *) c->cm);
496
497 if (c->weak_ref != NULL)
498 {
499 weak_object = tp_weak_ref_dup_object (c->weak_ref);
500
501 if (weak_object == NULL)
502 goto finally;
503 }
504
505 if (tp_proxy_prepare_finish (source_object, res, &error))
506 {
507 c->callback (c->cm, NULL, c->user_data, weak_object);
508 }
509 else
510 {
511 g_assert (error != NULL);
512 c->callback (c->cm, error, c->user_data, weak_object);
513 g_error_free (error);
514 }
515
516 finally:
517 if (weak_object != NULL)
518 g_object_unref (weak_object);
519
520 when_ready_context_free (c);
521 }
522
523 /**
524 * TpConnectionManagerWhenReadyCb:
525 * @cm: a connection manager
526 * @error: %NULL on success, or the reason why tp_connection_manager_is_ready()
527 * would return %FALSE
528 * @user_data: the @user_data passed to tp_connection_manager_call_when_ready()
529 * @weak_object: the @weak_object passed to
530 * tp_connection_manager_call_when_ready()
531 *
532 * Called as the result of tp_connection_manager_call_when_ready(). If the
533 * connection manager's protocol and parameter information could be retrieved,
534 * @error is %NULL and @cm is considered to be ready. Otherwise, @error is
535 * non-%NULL and @cm is not ready.
536 *
537 * Deprecated: since 0.17.6, use tp_proxy_prepare_async() instead
538 */
539
540 /**
541 * tp_connection_manager_call_when_ready: (skip)
542 * @self: a connection manager
543 * @callback: callback to call when information has been retrieved or on
544 * error
545 * @user_data: arbitrary data to pass to the callback
546 * @destroy: called to destroy @user_data
547 * @weak_object: object to reference weakly; if it is destroyed, @callback
548 * will not be called, but @destroy will still be called
549 *
550 * Call the @callback from the main loop when information about @cm's
551 * supported protocols and parameters has been retrieved.
552 *
553 * Since: 0.7.26
554 * Deprecated: since 0.17.6, use tp_proxy_prepare_async() instead
555 */
556 void
tp_connection_manager_call_when_ready(TpConnectionManager * self,TpConnectionManagerWhenReadyCb callback,gpointer user_data,GDestroyNotify destroy,GObject * weak_object)557 tp_connection_manager_call_when_ready (TpConnectionManager *self,
558 TpConnectionManagerWhenReadyCb callback,
559 gpointer user_data,
560 GDestroyNotify destroy,
561 GObject *weak_object)
562 {
563 WhenReadyContext *c;
564
565 g_return_if_fail (TP_IS_CONNECTION_MANAGER (self));
566 g_return_if_fail (callback != NULL);
567
568 c = g_slice_new0 (WhenReadyContext);
569
570 c->cm = g_object_ref (self);
571 c->callback = callback;
572 c->user_data = user_data;
573 c->destroy = destroy;
574
575 if (weak_object != NULL)
576 {
577 c->weak_ref = tp_weak_ref_new (weak_object, NULL, NULL);
578 }
579
580 tp_proxy_prepare_async (self, NULL, tp_connection_manager_ready_cb, c);
581 }
582
583 static void tp_connection_manager_continue_introspection
584 (TpConnectionManager *self);
585
586 static void
tp_connection_manager_got_parameters(TpConnectionManager * self,const GPtrArray * parameters,const GError * error,gpointer user_data,GObject * user_object)587 tp_connection_manager_got_parameters (TpConnectionManager *self,
588 const GPtrArray *parameters,
589 const GError *error,
590 gpointer user_data,
591 GObject *user_object)
592 {
593 gchar *protocol = user_data;
594 TpProtocol *proto_object;
595 GHashTable *immutables;
596
597 g_assert (self->priv->introspection_step == INTROSPECT_GETTING_PARAMETERS);
598 g_assert (self->priv->introspection_call != NULL);
599 self->priv->introspection_call = NULL;
600
601 if (error != NULL)
602 {
603 DEBUG ("%s/%s: error from legacy GetParameters, skipping protocol: "
604 "%s #%d: %s",
605 self->name, protocol,
606 g_quark_to_string (error->domain), error->code, error->message);
607 goto out;
608 }
609
610 DEBUG ("%s/%s: legacy GetParameters() returned %d parameters",
611 self->name, protocol, parameters->len);
612
613 immutables = tp_asv_new (
614 TP_PROP_PROTOCOL_PARAMETERS, TP_ARRAY_TYPE_PARAM_SPEC_LIST, parameters,
615 NULL);
616 proto_object = tp_protocol_new (tp_proxy_get_dbus_daemon (self),
617 self->name, protocol, immutables, NULL);
618 g_hash_table_unref (immutables);
619
620 /* tp_protocol_new can currently only fail because of malformed names,
621 * and we already checked those */
622 g_assert (proto_object != NULL);
623
624 g_hash_table_insert (self->priv->found_protocols,
625 g_strdup (protocol), proto_object);
626
627 out:
628 tp_connection_manager_continue_introspection (self);
629 }
630
631 static void tp_connection_manager_ready_or_failed (TpConnectionManager *self,
632 const GError *error);
633
634 static void
tp_connection_manager_reset_introspection(TpConnectionManager * self)635 tp_connection_manager_reset_introspection (TpConnectionManager *self)
636 {
637 guint i;
638
639 self->priv->introspection_step = INTROSPECT_IDLE;
640
641 if (self->priv->introspection_call != NULL)
642 {
643 tp_proxy_pending_call_cancel (self->priv->introspection_call);
644 self->priv->introspection_call = NULL;
645 }
646
647 if (self->priv->found_protocols != NULL)
648 {
649 g_hash_table_unref (self->priv->found_protocols);
650 self->priv->found_protocols = NULL;
651 }
652
653 if (self->priv->pending_protocols != NULL)
654 {
655 for (i = 0; i < self->priv->pending_protocols->len; i++)
656 g_free (self->priv->pending_protocols->pdata[i]);
657
658 g_ptr_array_unref (self->priv->pending_protocols);
659 self->priv->pending_protocols = NULL;
660 }
661
662 }
663
664 static void
tp_connection_manager_end_introspection(TpConnectionManager * self,const GError * error)665 tp_connection_manager_end_introspection (TpConnectionManager *self,
666 const GError *error)
667 {
668 tp_connection_manager_reset_introspection (self);
669
670 DEBUG ("%s: end of introspection, info source %s (%d)",
671 self->name,
672 _tp_enum_to_nick_nonnull (TP_TYPE_CM_INFO_SOURCE, self->info_source),
673 self->info_source);
674 g_signal_emit (self, signals[SIGNAL_GOT_INFO], 0, self->info_source);
675 tp_connection_manager_ready_or_failed (self, error);
676 }
677
678 static void
tp_connection_manager_update_protocol_structs(TpConnectionManager * self)679 tp_connection_manager_update_protocol_structs (TpConnectionManager *self)
680 {
681 GHashTableIter iter;
682 gpointer protocol_object;
683
684 g_assert (self->priv->protocol_objects != NULL);
685
686 if (self->priv->protocol_structs != NULL)
687 g_ptr_array_unref (self->priv->protocol_structs);
688
689 self->priv->protocol_structs = g_ptr_array_sized_new (
690 g_hash_table_size (self->priv->protocol_objects) + 1);
691
692 g_hash_table_iter_init (&iter, self->priv->protocol_objects);
693
694 while (g_hash_table_iter_next (&iter, NULL, &protocol_object))
695 {
696 g_ptr_array_add (self->priv->protocol_structs,
697 _tp_protocol_get_struct (protocol_object));
698 }
699
700 g_ptr_array_add (self->priv->protocol_structs, NULL);
701 self->protocols = (const TpConnectionManagerProtocol * const *)
702 self->priv->protocol_structs->pdata;
703 }
704
705 static void
tp_connection_manager_get_all_cb(TpProxy * proxy,GHashTable * properties,const GError * error,gpointer nil G_GNUC_UNUSED,GObject * object G_GNUC_UNUSED)706 tp_connection_manager_get_all_cb (TpProxy *proxy,
707 GHashTable *properties,
708 const GError *error,
709 gpointer nil G_GNUC_UNUSED,
710 GObject *object G_GNUC_UNUSED)
711 {
712 TpConnectionManager *self = (TpConnectionManager *) proxy;
713
714 g_assert (TP_IS_CONNECTION_MANAGER (self));
715 g_assert (self->priv->introspection_step == INTROSPECT_GETTING_PROPERTIES);
716 g_assert (self->priv->introspection_call != NULL);
717 self->priv->introspection_call = NULL;
718
719 if (error == NULL)
720 {
721 GHashTable *protocols;
722
723 tp_proxy_add_interfaces (proxy,
724 tp_asv_get_strv (properties, "Interfaces"));
725
726 protocols = tp_asv_get_boxed (properties, "Protocols",
727 TP_HASH_TYPE_PROTOCOL_PROPERTIES_MAP);
728
729 if (protocols != NULL)
730 {
731 GHashTableIter iter;
732 gpointer k, v;
733
734 DEBUG ("%s: %u Protocols from GetAll()",
735 self->name, g_hash_table_size (protocols));
736
737 g_assert (self->priv->found_protocols == NULL);
738 self->priv->found_protocols = g_hash_table_new_full (g_str_hash,
739 g_str_equal, g_free, g_object_unref);
740
741 g_hash_table_iter_init (&iter, protocols);
742
743 while (g_hash_table_iter_next (&iter, &k, &v))
744 {
745 const gchar *name = k;
746 GHashTable *protocol_properties = v;
747
748 if (tp_connection_manager_check_valid_protocol_name (name, NULL))
749 {
750 TpProtocol *proto_object = tp_protocol_new (
751 tp_proxy_get_dbus_daemon (self), self->name, name,
752 protocol_properties, NULL);
753
754 /* tp_protocol_new can currently only fail because of
755 * malformed names, and we already checked for that */
756 g_assert (proto_object != NULL);
757
758 g_hash_table_insert (self->priv->found_protocols,
759 g_strdup (name), proto_object);
760 }
761 else
762 {
763 INFO ("ignoring invalid Protocol name %s from %s",
764 name, tp_proxy_get_object_path (self));
765 }
766 }
767 }
768 else
769 {
770 DEBUG ("%s: no Protocols property in GetAll() (old CM?)",
771 self->name);
772 }
773 }
774 else
775 {
776 DEBUG ("%s: ignoring error getting CM properties (old CM?): "
777 "%s %d: %s",
778 self->name,
779 g_quark_to_string (error->domain), error->code, error->message);
780 }
781
782 tp_connection_manager_continue_introspection (self);
783 }
784
785 static void tp_connection_manager_got_protocols (TpConnectionManager *self,
786 const gchar **protocols,
787 const GError *error,
788 gpointer user_data,
789 GObject *user_object);
790
791 static void
tp_connection_manager_continue_introspection(TpConnectionManager * self)792 tp_connection_manager_continue_introspection (TpConnectionManager *self)
793 {
794 gchar *next_protocol;
795
796 DEBUG ("%s", self->name);
797
798 if (self->priv->introspection_step == INTROSPECT_IDLE)
799 {
800 DEBUG ("%s: calling GetAll on CM", self->name);
801 self->priv->introspection_step = INTROSPECT_GETTING_PROPERTIES;
802 self->priv->introspection_call = tp_cli_dbus_properties_call_get_all (
803 self, -1, TP_IFACE_CONNECTION_MANAGER,
804 tp_connection_manager_get_all_cb, NULL, NULL, NULL);
805 return;
806 }
807
808 if (self->priv->introspection_step == INTROSPECT_GETTING_PROPERTIES)
809 {
810 g_assert (self->priv->pending_protocols == NULL);
811
812 if (self->priv->found_protocols == NULL)
813 {
814 DEBUG ("%s: calling legacy ListProtocols on CM", self->name);
815 self->priv->introspection_step = INTROSPECT_LISTING_PROTOCOLS;
816 self->priv->introspection_call =
817 tp_cli_connection_manager_call_list_protocols (self, -1,
818 tp_connection_manager_got_protocols, NULL, NULL, NULL);
819 return;
820 }
821 /* else we already found the protocols and their parameters, so behave
822 * as though we'd already called GetParameters n times */
823 }
824
825 if (self->priv->pending_protocols == NULL ||
826 self->priv->pending_protocols->len == 0)
827 {
828 GHashTable *tmp;
829 guint old;
830
831 /* swap found_protocols and protocol_objects, so we'll free the old
832 * protocol_objects as part of end_introspection */
833 tmp = self->priv->protocol_objects;
834 self->priv->protocol_objects = self->priv->found_protocols;
835 self->priv->found_protocols = tmp;
836
837 tp_connection_manager_update_protocol_structs (self);
838
839 old = self->info_source;
840 self->info_source = TP_CM_INFO_SOURCE_LIVE;
841
842 if (old != TP_CM_INFO_SOURCE_LIVE)
843 g_object_notify ((GObject *) self, "info-source");
844
845 tp_connection_manager_end_introspection (self, NULL);
846
847 g_assert (self->priv->introspection_step == INTROSPECT_IDLE);
848 }
849 else
850 {
851 next_protocol = g_ptr_array_remove_index_fast (
852 self->priv->pending_protocols, 0);
853 self->priv->introspection_step = INTROSPECT_GETTING_PARAMETERS;
854 DEBUG ("%s/%s: calling legacy ListProtocols",
855 self->name, next_protocol);
856 self->priv->introspection_call =
857 tp_cli_connection_manager_call_get_parameters (self, -1,
858 next_protocol, tp_connection_manager_got_parameters,
859 next_protocol, g_free, NULL);
860 }
861 }
862
863 static void
tp_connection_manager_got_protocols(TpConnectionManager * self,const gchar ** protocols,const GError * error,gpointer user_data,GObject * user_object)864 tp_connection_manager_got_protocols (TpConnectionManager *self,
865 const gchar **protocols,
866 const GError *error,
867 gpointer user_data,
868 GObject *user_object)
869 {
870 guint i = 0;
871 const gchar **iter;
872
873 g_assert (self->priv->introspection_call != NULL);
874 self->priv->introspection_call = NULL;
875
876 if (error != NULL)
877 {
878 DEBUG ("%s: legacy GetProtocols() failed: %s #%d: %s",
879 self->name,
880 g_quark_to_string (error->domain), error->code, error->message);
881
882 if (!self->running)
883 {
884 /* ListProtocols failed to start it - we assume this is because
885 * activation failed */
886 DEBUG ("%s: ListProtocols didn't start it: activation failure?",
887 self->name);
888 g_signal_emit (self, signals[SIGNAL_EXITED], 0);
889 }
890
891 tp_connection_manager_end_introspection (self, error);
892 return;
893 }
894
895 for (iter = protocols; *iter != NULL; iter++)
896 i++;
897
898 DEBUG ("%s: legacy GetProtocols() returned %u protocols", self->name, i);
899
900 g_assert (self->priv->found_protocols == NULL);
901 self->priv->found_protocols = g_hash_table_new_full (g_str_hash,
902 g_str_equal, g_free, g_object_unref);
903
904 g_assert (self->priv->pending_protocols == NULL);
905 self->priv->pending_protocols = g_ptr_array_sized_new (i);
906
907 for (iter = protocols; *iter != NULL; iter++)
908 {
909 if (!tp_connection_manager_check_valid_protocol_name (*iter, NULL))
910 {
911 DEBUG ("%s: protocol %s has an invalid name", self->name, *iter);
912 continue;
913 }
914
915 g_ptr_array_add (self->priv->pending_protocols, g_strdup (*iter));
916 }
917
918 tp_connection_manager_continue_introspection (self);
919 }
920
921 static gboolean
introspection_in_progress(TpConnectionManager * self)922 introspection_in_progress (TpConnectionManager *self)
923 {
924 return (self->priv->introspection_call != NULL ||
925 self->priv->found_protocols != NULL);
926 }
927
928 static gboolean
tp_connection_manager_idle_introspect(gpointer data)929 tp_connection_manager_idle_introspect (gpointer data)
930 {
931 TpConnectionManager *self = data;
932
933 /* Start introspecting if we want to and we're not already */
934 if (!introspection_in_progress (self) &&
935 (self->always_introspect ||
936 self->info_source == TP_CM_INFO_SOURCE_NONE))
937 {
938 tp_connection_manager_continue_introspection (self);
939 }
940
941 self->priv->introspect_idle_id = 0;
942
943 return FALSE;
944 }
945
946 static gboolean tp_connection_manager_idle_read_manager_file (gpointer data);
947
948 static void
tp_connection_manager_name_owner_changed_cb(TpDBusDaemon * bus,const gchar * name,const gchar * new_owner,gpointer user_data)949 tp_connection_manager_name_owner_changed_cb (TpDBusDaemon *bus,
950 const gchar *name,
951 const gchar *new_owner,
952 gpointer user_data)
953 {
954 TpConnectionManager *self = user_data;
955
956 /* make sure self exists for the duration of this callback */
957 g_object_ref (self);
958
959 if (new_owner[0] == '\0')
960 {
961 GError e = { TP_DBUS_ERRORS, TP_DBUS_ERROR_NAME_OWNER_LOST,
962 "Connection manager process exited during introspection" };
963
964 self->running = FALSE;
965
966 /* cancel pending introspection, if any */
967 if (introspection_in_progress (self))
968 {
969 if (self->priv->retried_introspection)
970 {
971 DEBUG ("%s: %s, twice: assuming fatal and not retrying",
972 self->name, e.message);
973 tp_connection_manager_end_introspection (self, &e);
974 }
975 else
976 {
977 self->priv->retried_introspection = TRUE;
978 DEBUG ("%s: %s: retrying", self->name, e.message);
979 tp_connection_manager_reset_introspection (self);
980 tp_connection_manager_continue_introspection (self);
981 }
982 }
983
984 /* If our name wasn't known already, a change to "" is just the initial
985 * state, so we didn't *exit* as such. */
986 if (self->priv->name_known)
987 {
988 DEBUG ("%s: exited", self->name);
989 g_signal_emit (self, signals[SIGNAL_EXITED], 0);
990 }
991 }
992 else
993 {
994 /* represent an atomic change of ownership as if it was an exit and
995 * restart */
996 if (self->running)
997 {
998 DEBUG ("%s: atomic name owner change, behaving as if it exited",
999 self->name);
1000 tp_connection_manager_name_owner_changed_cb (bus, name, "", self);
1001 DEBUG ("%s: back to normal handling", self->name);
1002 }
1003
1004 DEBUG ("%s: is now running", self->name);
1005 self->running = TRUE;
1006 g_signal_emit (self, signals[SIGNAL_ACTIVATED], 0);
1007
1008 if (self->priv->introspect_idle_id == 0)
1009 self->priv->introspect_idle_id = g_idle_add (
1010 tp_connection_manager_idle_introspect, self);
1011 }
1012
1013 /* if we haven't started introspecting yet, now would be a good time */
1014 if (!self->priv->name_known)
1015 {
1016 DEBUG ("%s: starting introspection now we know the name owner",
1017 self->name);
1018
1019 g_assert (self->priv->manager_file_read_idle_id == 0);
1020
1021 /* now we know whether we're running or not, we can try reading the
1022 * .manager file... */
1023 self->priv->manager_file_read_idle_id = g_idle_add (
1024 tp_connection_manager_idle_read_manager_file, self);
1025
1026 if (self->priv->want_activation && self->priv->introspect_idle_id == 0)
1027 {
1028 DEBUG ("%s: forcing introspection for its side-effect of "
1029 "activation",
1030 self->name);
1031 /* ... but if activation was requested, we should also do that */
1032 self->priv->introspect_idle_id = g_idle_add (
1033 tp_connection_manager_idle_introspect, self);
1034 }
1035
1036 /* Unfreeze automatic reading of .manager file if manager-file changes */
1037 self->priv->name_known = TRUE;
1038 }
1039
1040 g_object_unref (self);
1041 }
1042
1043 static gboolean
tp_connection_manager_read_file(TpDBusDaemon * dbus_daemon,const gchar * cm_name,const gchar * filename,GHashTable ** protocols_out,GStrv * interfaces_out,GError ** error)1044 tp_connection_manager_read_file (TpDBusDaemon *dbus_daemon,
1045 const gchar *cm_name,
1046 const gchar *filename,
1047 GHashTable **protocols_out,
1048 GStrv *interfaces_out,
1049 GError **error)
1050 {
1051 GKeyFile *file;
1052 gchar **groups = NULL;
1053 gchar **group;
1054 TpProtocol *proto_object;
1055 GHashTable *protocols = NULL;
1056 GStrv interfaces = NULL;
1057
1058 file = g_key_file_new ();
1059
1060 if (!g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, error))
1061 return FALSE;
1062
1063 /* if missing, it's not an error, so ignore @error */
1064 interfaces = g_key_file_get_string_list (file, "ConnectionManager",
1065 "Interfaces", NULL, NULL);
1066
1067 protocols = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1068 g_object_unref);
1069
1070 groups = g_key_file_get_groups (file, NULL);
1071
1072 if (groups == NULL)
1073 goto success;
1074
1075 for (group = groups; *group != NULL; group++)
1076 {
1077 gchar *name;
1078 GHashTable *immutables;
1079
1080 immutables = _tp_protocol_parse_manager_file (file, cm_name, *group,
1081 &name);
1082
1083 if (immutables == NULL)
1084 continue;
1085
1086 proto_object = tp_protocol_new (dbus_daemon, cm_name, name,
1087 immutables, NULL);
1088 g_assert (proto_object != NULL);
1089
1090 /* steals @name */
1091 g_hash_table_insert (protocols, name, proto_object);
1092
1093 g_hash_table_unref (immutables);
1094 }
1095
1096 success:
1097 g_strfreev (groups);
1098 g_key_file_free (file);
1099
1100 if (protocols_out != NULL)
1101 *protocols_out = protocols;
1102 else
1103 g_hash_table_unref (protocols);
1104
1105 if (interfaces_out != NULL)
1106 *interfaces_out = interfaces;
1107 else
1108 g_strfreev (interfaces);
1109
1110 return TRUE;
1111 }
1112
1113 static gboolean
tp_connection_manager_idle_read_manager_file(gpointer data)1114 tp_connection_manager_idle_read_manager_file (gpointer data)
1115 {
1116 TpConnectionManager *self = TP_CONNECTION_MANAGER (data);
1117
1118 self->priv->manager_file_read_idle_id = 0;
1119
1120 if (self->priv->protocol_objects == NULL)
1121 {
1122 if (self->priv->manager_file != NULL &&
1123 self->priv->manager_file[0] != '\0')
1124 {
1125 GError *error = NULL;
1126 GHashTable *protocols;
1127 GStrv interfaces = NULL;
1128
1129 DEBUG ("%s: reading %s", self->name, self->priv->manager_file);
1130
1131 if (!tp_connection_manager_read_file (
1132 tp_proxy_get_dbus_daemon (self),
1133 self->name, self->priv->manager_file, &protocols, &interfaces,
1134 &error))
1135 {
1136 DEBUG ("%s: failed to load %s: %s #%d: %s",
1137 self->name, self->priv->manager_file,
1138 g_quark_to_string (error->domain), error->code,
1139 error->message);
1140 g_error_free (error);
1141 error = NULL;
1142 }
1143 else
1144 {
1145 tp_proxy_add_interfaces ((TpProxy *) self,
1146 (const gchar * const *) interfaces);
1147 g_strfreev (interfaces);
1148
1149 self->priv->protocol_objects = protocols;
1150 tp_connection_manager_update_protocol_structs (self);
1151
1152 DEBUG ("%s: got info from file", self->name);
1153 /* previously it must have been NONE */
1154 self->info_source = TP_CM_INFO_SOURCE_FILE;
1155
1156 g_object_ref (self);
1157 g_object_notify ((GObject *) self, "info-source");
1158
1159 g_signal_emit (self, signals[SIGNAL_GOT_INFO], 0,
1160 self->info_source);
1161 tp_connection_manager_ready_or_failed (self, NULL);
1162 g_object_unref (self);
1163
1164 goto out;
1165 }
1166 }
1167
1168 if (self->priv->introspect_idle_id == 0)
1169 {
1170 DEBUG ("%s: no .manager file or failed to parse it, trying to "
1171 "activate CM instead",
1172 self->name);
1173 tp_connection_manager_idle_introspect (self);
1174 }
1175 else
1176 {
1177 DEBUG ("%s: no .manager file, but will activate CM soon anyway",
1178 self->name);
1179 }
1180 }
1181 else
1182 {
1183 DEBUG ("%s: not reading manager file, %u protocols already discovered",
1184 self->name, g_hash_table_size (self->priv->protocol_objects));
1185 }
1186
1187 out:
1188 return FALSE;
1189 }
1190
1191 static gchar *
tp_connection_manager_find_manager_file(const gchar * name)1192 tp_connection_manager_find_manager_file (const gchar *name)
1193 {
1194 gchar *filename;
1195 const gchar * const * data_dirs;
1196
1197 g_assert (name != NULL);
1198
1199 filename = g_strdup_printf ("%s/telepathy/managers/%s.manager",
1200 g_get_user_data_dir (), name);
1201
1202 DEBUG ("in XDG_DATA_HOME: trying %s", filename);
1203
1204 if (g_file_test (filename, G_FILE_TEST_EXISTS))
1205 return filename;
1206
1207 g_free (filename);
1208
1209 for (data_dirs = g_get_system_data_dirs ();
1210 *data_dirs != NULL;
1211 data_dirs++)
1212 {
1213 filename = g_strdup_printf ("%s/telepathy/managers/%s.manager",
1214 *data_dirs, name);
1215
1216 DEBUG ("in XDG_DATA_DIRS: trying %s", filename);
1217
1218 if (g_file_test (filename, G_FILE_TEST_EXISTS))
1219 return filename;
1220
1221 g_free (filename);
1222 }
1223
1224 return NULL;
1225 }
1226
1227 static GObject *
tp_connection_manager_constructor(GType type,guint n_params,GObjectConstructParam * params)1228 tp_connection_manager_constructor (GType type,
1229 guint n_params,
1230 GObjectConstructParam *params)
1231 {
1232 GObjectClass *object_class =
1233 (GObjectClass *) tp_connection_manager_parent_class;
1234 TpConnectionManager *self =
1235 TP_CONNECTION_MANAGER (object_class->constructor (type, n_params,
1236 params));
1237 TpProxy *as_proxy = (TpProxy *) self;
1238 const gchar *object_path = as_proxy->object_path;
1239 const gchar *bus_name = as_proxy->bus_name;
1240
1241 g_return_val_if_fail (object_path != NULL, NULL);
1242 g_return_val_if_fail (bus_name != NULL, NULL);
1243
1244 /* Watch my D-Bus name */
1245 tp_dbus_daemon_watch_name_owner (as_proxy->dbus_daemon,
1246 as_proxy->bus_name, tp_connection_manager_name_owner_changed_cb, self,
1247 NULL);
1248
1249 self->name = strrchr (object_path, '/') + 1;
1250 g_assert (self->name != NULL);
1251
1252 if (self->priv->manager_file == NULL)
1253 {
1254 self->priv->manager_file =
1255 tp_connection_manager_find_manager_file (self->name);
1256 }
1257
1258 return (GObject *) self;
1259 }
1260
1261 static void
tp_connection_manager_init(TpConnectionManager * self)1262 tp_connection_manager_init (TpConnectionManager *self)
1263 {
1264 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_CONNECTION_MANAGER,
1265 TpConnectionManagerPrivate);
1266 }
1267
1268 static void
tp_connection_manager_dispose(GObject * object)1269 tp_connection_manager_dispose (GObject *object)
1270 {
1271 TpConnectionManager *self = TP_CONNECTION_MANAGER (object);
1272 TpProxy *as_proxy = (TpProxy *) self;
1273
1274 if (self->priv->disposed)
1275 goto finally;
1276
1277 self->priv->disposed = TRUE;
1278
1279 tp_dbus_daemon_cancel_name_owner_watch (as_proxy->dbus_daemon,
1280 as_proxy->bus_name, tp_connection_manager_name_owner_changed_cb,
1281 object);
1282
1283 if (self->priv->protocol_structs != NULL)
1284 {
1285 g_ptr_array_unref (self->priv->protocol_structs);
1286 self->priv->protocol_structs = NULL;
1287 }
1288
1289 if (self->priv->protocol_objects != NULL)
1290 {
1291 g_hash_table_unref (self->priv->protocol_objects);
1292 self->priv->protocol_objects = NULL;
1293 }
1294
1295 if (self->priv->found_protocols != NULL)
1296 {
1297 g_hash_table_unref (self->priv->found_protocols);
1298 self->priv->found_protocols = NULL;
1299 }
1300
1301 finally:
1302 G_OBJECT_CLASS (tp_connection_manager_parent_class)->dispose (object);
1303 }
1304
1305 static void
tp_connection_manager_finalize(GObject * object)1306 tp_connection_manager_finalize (GObject *object)
1307 {
1308 TpConnectionManager *self = TP_CONNECTION_MANAGER (object);
1309 guint i;
1310
1311 g_free (self->priv->manager_file);
1312
1313 if (self->priv->manager_file_read_idle_id != 0)
1314 g_source_remove (self->priv->manager_file_read_idle_id);
1315
1316 if (self->priv->introspect_idle_id != 0)
1317 g_source_remove (self->priv->introspect_idle_id);
1318
1319 if (self->priv->pending_protocols != NULL)
1320 {
1321 for (i = 0; i < self->priv->pending_protocols->len; i++)
1322 g_free (self->priv->pending_protocols->pdata[i]);
1323
1324 g_ptr_array_unref (self->priv->pending_protocols);
1325 }
1326
1327 G_OBJECT_CLASS (tp_connection_manager_parent_class)->finalize (object);
1328 }
1329
1330 static void
tp_connection_manager_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1331 tp_connection_manager_get_property (GObject *object,
1332 guint property_id,
1333 GValue *value,
1334 GParamSpec *pspec)
1335 {
1336 TpConnectionManager *self = TP_CONNECTION_MANAGER (object);
1337
1338 switch (property_id)
1339 {
1340 case PROP_CONNECTION_MANAGER:
1341 g_value_set_string (value, self->name);
1342 break;
1343
1344 case PROP_CM_NAME:
1345 g_value_set_string (value, self->name);
1346 break;
1347
1348 case PROP_INFO_SOURCE:
1349 g_value_set_uint (value, self->info_source);
1350 break;
1351
1352 case PROP_MANAGER_FILE:
1353 g_value_set_string (value, self->priv->manager_file);
1354 break;
1355
1356 case PROP_ALWAYS_INTROSPECT:
1357 g_value_set_boolean (value, self->always_introspect);
1358 break;
1359
1360 default:
1361 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1362 break;
1363 }
1364 }
1365
1366 static void
tp_connection_manager_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1367 tp_connection_manager_set_property (GObject *object,
1368 guint property_id,
1369 const GValue *value,
1370 GParamSpec *pspec)
1371 {
1372 TpConnectionManager *self = TP_CONNECTION_MANAGER (object);
1373
1374 switch (property_id)
1375 {
1376 case PROP_MANAGER_FILE:
1377 g_free (self->priv->manager_file);
1378
1379 /* If initial code has already run, change the definition of where
1380 * we expect to find the .manager file and trigger re-introspection.
1381 * Otherwise, just take the value - when name_known becomes TRUE we
1382 * queue the first-time manager file lookup anyway.
1383 */
1384 if (self->priv->name_known)
1385 {
1386 const gchar *tmp = g_value_get_string (value);
1387
1388 if (tmp == NULL)
1389 {
1390 self->priv->manager_file =
1391 tp_connection_manager_find_manager_file (self->name);
1392 }
1393 else
1394 {
1395 self->priv->manager_file = g_strdup (tmp);
1396 }
1397
1398 if (self->priv->manager_file_read_idle_id == 0)
1399 self->priv->manager_file_read_idle_id = g_idle_add (
1400 tp_connection_manager_idle_read_manager_file, self);
1401 }
1402 else
1403 {
1404 self->priv->manager_file = g_value_dup_string (value);
1405 }
1406
1407 break;
1408
1409 case PROP_ALWAYS_INTROSPECT:
1410 {
1411 gboolean old = self->always_introspect;
1412
1413 self->always_introspect = g_value_get_boolean (value);
1414
1415 if (self->running && !old && self->always_introspect)
1416 {
1417 /* It's running, we weren't previously auto-introspecting,
1418 * but we are now. Try it when idle
1419 */
1420 if (self->priv->introspect_idle_id == 0)
1421 self->priv->introspect_idle_id = g_idle_add (
1422 tp_connection_manager_idle_introspect, self);
1423 }
1424 }
1425 break;
1426
1427 default:
1428 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1429 break;
1430 }
1431 }
1432
1433 /**
1434 * tp_connection_manager_init_known_interfaces:
1435 *
1436 * Ensure that the known interfaces for TpConnectionManager have been set up.
1437 * This is done automatically when necessary, but for correct
1438 * overriding of library interfaces by local extensions, you should
1439 * call this function before calling
1440 * tp_proxy_or_subclass_hook_on_interface_add() with first argument
1441 * %TP_TYPE_CONNECTION_MANAGER.
1442 *
1443 * Since: 0.7.32
1444 */
1445 void
tp_connection_manager_init_known_interfaces(void)1446 tp_connection_manager_init_known_interfaces (void)
1447 {
1448 static gsize once = 0;
1449
1450 if (g_once_init_enter (&once))
1451 {
1452 GType tp_type = TP_TYPE_CONNECTION_MANAGER;
1453
1454 tp_proxy_init_known_interfaces ();
1455 tp_proxy_or_subclass_hook_on_interface_add (tp_type,
1456 tp_cli_connection_manager_add_signals);
1457 tp_proxy_subclass_add_error_mapping (tp_type,
1458 TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR);
1459
1460 g_once_init_leave (&once, 1);
1461 }
1462 }
1463
1464 enum {
1465 FEAT_CORE,
1466 N_FEAT
1467 };
1468
1469 static const TpProxyFeature *
tp_connection_manager_list_features(TpProxyClass * cls G_GNUC_UNUSED)1470 tp_connection_manager_list_features (TpProxyClass *cls G_GNUC_UNUSED)
1471 {
1472 static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
1473
1474 if (G_LIKELY (features[0].name != 0))
1475 return features;
1476
1477 features[FEAT_CORE].name = TP_CONNECTION_MANAGER_FEATURE_CORE;
1478 features[FEAT_CORE].core = TRUE;
1479
1480 /* assert that the terminator at the end is there */
1481 g_assert (features[N_FEAT].name == 0);
1482
1483 return features;
1484 }
1485
1486 static void
tp_connection_manager_class_init(TpConnectionManagerClass * klass)1487 tp_connection_manager_class_init (TpConnectionManagerClass *klass)
1488 {
1489 TpProxyClass *proxy_class = (TpProxyClass *) klass;
1490 GObjectClass *object_class = (GObjectClass *) klass;
1491 GParamSpec *param_spec;
1492
1493 tp_connection_manager_init_known_interfaces ();
1494
1495 g_type_class_add_private (klass, sizeof (TpConnectionManagerPrivate));
1496
1497 object_class->constructor = tp_connection_manager_constructor;
1498 object_class->get_property = tp_connection_manager_get_property;
1499 object_class->set_property = tp_connection_manager_set_property;
1500 object_class->dispose = tp_connection_manager_dispose;
1501 object_class->finalize = tp_connection_manager_finalize;
1502
1503 proxy_class->interface = TP_IFACE_QUARK_CONNECTION_MANAGER;
1504 proxy_class->list_features = tp_connection_manager_list_features;
1505
1506 /**
1507 * TpConnectionManager:info-source:
1508 *
1509 * Where we got the current information on supported protocols
1510 * (a #TpCMInfoSource).
1511 *
1512 * Since 0.7.26, the #GObject::notify signal is emitted for this
1513 * property.
1514 *
1515 * (Note that this is of type %G_TYPE_UINT, not %TP_TYPE_CM_INFO_SOURCE,
1516 * for historical reasons.)
1517 */
1518 param_spec = g_param_spec_uint ("info-source", "CM info source",
1519 "Where we got the current information on supported protocols",
1520 TP_CM_INFO_SOURCE_NONE, TP_CM_INFO_SOURCE_LIVE, TP_CM_INFO_SOURCE_NONE,
1521 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1522 g_object_class_install_property (object_class, PROP_INFO_SOURCE,
1523 param_spec);
1524
1525 /**
1526 * TpConnectionManager:connection-manager:
1527 *
1528 * The name of the connection manager, e.g. "gabble" (read-only).
1529 *
1530 * Deprecated: Use #TpConnectionManager:cm-name instead.
1531 */
1532 param_spec = g_param_spec_string ("connection-manager", "CM name",
1533 "The name of the connection manager, e.g. \"gabble\" (read-only)",
1534 NULL,
1535 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1536 g_object_class_install_property (object_class, PROP_CONNECTION_MANAGER,
1537 param_spec);
1538
1539 /**
1540 * TpConnectionManager:cm-name:
1541 *
1542 * The name of the connection manager, e.g. "gabble" (read-only).
1543 *
1544 * Since: 0.19.3
1545 */
1546 param_spec = g_param_spec_string ("cm-name", "CM name",
1547 "The name of the connection manager, e.g. \"gabble\" (read-only)",
1548 NULL,
1549 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1550 g_object_class_install_property (object_class, PROP_CM_NAME,
1551 param_spec);
1552
1553 /**
1554 * TpConnectionManager:manager-file:
1555 *
1556 * The absolute path of the .manager file. If set to %NULL (the default),
1557 * the XDG data directories will be searched for a .manager file of the
1558 * correct name.
1559 *
1560 * If set to the empty string, no .manager file will be read.
1561 */
1562 param_spec = g_param_spec_string ("manager-file", ".manager filename",
1563 "The .manager filename",
1564 NULL,
1565 G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1566 g_object_class_install_property (object_class, PROP_MANAGER_FILE,
1567 param_spec);
1568
1569 /**
1570 * TpConnectionManager:always-introspect:
1571 *
1572 * If %TRUE, always introspect the connection manager as it comes online,
1573 * even if we already have its info from a .manager file. Default %FALSE.
1574 */
1575 param_spec = g_param_spec_boolean ("always-introspect", "Always introspect?",
1576 "Opportunistically introspect the CM when it's run", FALSE,
1577 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1578 g_object_class_install_property (object_class, PROP_ALWAYS_INTROSPECT,
1579 param_spec);
1580
1581 /**
1582 * TpConnectionManager::activated:
1583 * @self: the connection manager proxy
1584 *
1585 * Emitted when the connection manager's well-known name appears on the bus.
1586 */
1587 signals[SIGNAL_ACTIVATED] = g_signal_new ("activated",
1588 G_OBJECT_CLASS_TYPE (klass),
1589 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1590 0,
1591 NULL, NULL, NULL,
1592 G_TYPE_NONE, 0);
1593
1594 /**
1595 * TpConnectionManager::exited:
1596 * @self: the connection manager proxy
1597 *
1598 * Emitted when the connection manager's well-known name disappears from
1599 * the bus or when activation fails.
1600 */
1601 signals[SIGNAL_EXITED] = g_signal_new ("exited",
1602 G_OBJECT_CLASS_TYPE (klass),
1603 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1604 0,
1605 NULL, NULL, NULL,
1606 G_TYPE_NONE, 0);
1607
1608 /**
1609 * TpConnectionManager::got-info:
1610 * @self: the connection manager proxy
1611 * @source: a #TpCMInfoSource
1612 *
1613 * Emitted when the connection manager's capabilities have been discovered.
1614 *
1615 * This signal is not very helpful. Using
1616 * tp_proxy_prepare_async() instead is recommended.
1617 */
1618 signals[SIGNAL_GOT_INFO] = g_signal_new ("got-info",
1619 G_OBJECT_CLASS_TYPE (klass),
1620 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
1621 0,
1622 NULL, NULL, NULL,
1623 G_TYPE_NONE, 1, G_TYPE_UINT);
1624 }
1625
1626 /**
1627 * tp_connection_manager_new:
1628 * @dbus: Proxy for the D-Bus daemon
1629 * @name: The connection manager name (such as "gabble")
1630 * @manager_filename: (allow-none): The #TpConnectionManager:manager-file
1631 * property, which may (and generally should) be %NULL.
1632 * @error: used to return an error if %NULL is returned
1633 *
1634 * Convenience function to create a new connection manager proxy. If
1635 * its protocol and parameter information are required, you should call
1636 * tp_proxy_prepare_async() on the result.
1637 *
1638 * Returns: a new reference to a connection manager proxy, or %NULL if @error
1639 * is set.
1640 */
1641 TpConnectionManager *
tp_connection_manager_new(TpDBusDaemon * dbus,const gchar * name,const gchar * manager_filename,GError ** error)1642 tp_connection_manager_new (TpDBusDaemon *dbus,
1643 const gchar *name,
1644 const gchar *manager_filename,
1645 GError **error)
1646 {
1647 TpConnectionManager *cm;
1648 gchar *object_path, *bus_name;
1649
1650 g_return_val_if_fail (dbus != NULL, NULL);
1651 g_return_val_if_fail (name != NULL, NULL);
1652
1653 if (!tp_connection_manager_check_valid_name (name, error))
1654 return NULL;
1655
1656 object_path = g_strdup_printf ("%s%s", TP_CM_OBJECT_PATH_BASE, name);
1657 bus_name = g_strdup_printf ("%s%s", TP_CM_BUS_NAME_BASE, name);
1658
1659 cm = TP_CONNECTION_MANAGER (g_object_new (TP_TYPE_CONNECTION_MANAGER,
1660 "dbus-daemon", dbus,
1661 "dbus-connection", ((TpProxy *) dbus)->dbus_connection,
1662 "bus-name", bus_name,
1663 "object-path", object_path,
1664 "manager-file", manager_filename,
1665 NULL));
1666
1667 g_free (object_path);
1668 g_free (bus_name);
1669
1670 return cm;
1671 }
1672
1673 /**
1674 * tp_connection_manager_activate: (skip)
1675 * @self: a connection manager proxy
1676 *
1677 * Attempt to run and introspect the connection manager, asynchronously.
1678 * Since 0.7.26 this function is not generally very useful, since
1679 * the connection manager will now be activated automatically if necessary.
1680 *
1681 * If the CM was already running, do nothing and return %FALSE.
1682 *
1683 * On success, emit #TpConnectionManager::activated when the CM appears
1684 * on the bus, and #TpConnectionManager::got-info when its capabilities
1685 * have been (re-)discovered.
1686 *
1687 * On failure, emit #TpConnectionManager::exited without first emitting
1688 * activated.
1689 *
1690 * Returns: %TRUE if activation was needed and is now in progress, %FALSE
1691 * if the connection manager was already running and no additional signals
1692 * will be emitted.
1693 *
1694 * Since: 0.7.1
1695 */
1696 gboolean
tp_connection_manager_activate(TpConnectionManager * self)1697 tp_connection_manager_activate (TpConnectionManager *self)
1698 {
1699 if (self->priv->name_known)
1700 {
1701 if (self->running)
1702 {
1703 DEBUG ("%s: already running", self->name);
1704 return FALSE;
1705 }
1706
1707 if (self->priv->introspect_idle_id == 0)
1708 {
1709 DEBUG ("%s: adding idle introspection", self->name);
1710 self->priv->introspect_idle_id = g_idle_add (
1711 tp_connection_manager_idle_introspect, self);
1712 }
1713 else
1714 {
1715 DEBUG ("%s: idle introspection already added", self->name);
1716 }
1717 }
1718 else
1719 {
1720 /* we'll activate later, when we know properly whether we're running */
1721 DEBUG ("%s: queueing activation for when we know what's going on",
1722 self->name);
1723 self->priv->want_activation = TRUE;
1724 }
1725
1726 return TRUE;
1727 }
1728
1729 static gboolean
steal_into_ptr_array(gpointer key,gpointer value,gpointer user_data)1730 steal_into_ptr_array (gpointer key,
1731 gpointer value,
1732 gpointer user_data)
1733 {
1734 if (value != NULL)
1735 g_ptr_array_add (user_data, value);
1736
1737 g_free (key);
1738
1739 return TRUE;
1740 }
1741
1742 typedef struct
1743 {
1744 GHashTable *table;
1745 GPtrArray *arr;
1746 GSimpleAsyncResult *result;
1747 TpConnectionManagerListCb callback;
1748 gpointer user_data;
1749 GDestroyNotify destroy;
1750 gpointer weak_object;
1751 TpProxyPendingCall *pending_call;
1752 size_t base_len;
1753 gsize refcount;
1754 gsize cms_to_ready;
1755 unsigned getting_names:1;
1756 unsigned had_weak_object:1;
1757 } _ListContext;
1758
1759 static void
list_context_unref(_ListContext * list_context)1760 list_context_unref (_ListContext *list_context)
1761 {
1762 guint i;
1763
1764 if (--list_context->refcount > 0)
1765 return;
1766
1767 if (list_context->weak_object != NULL)
1768 g_object_remove_weak_pointer (list_context->weak_object,
1769 &list_context->weak_object);
1770
1771 if (list_context->destroy != NULL)
1772 list_context->destroy (list_context->user_data);
1773
1774 if (list_context->arr != NULL)
1775 {
1776 for (i = 0; i < list_context->arr->len; i++)
1777 {
1778 TpConnectionManager *cm = g_ptr_array_index (list_context->arr, i);
1779
1780 if (cm != NULL)
1781 g_object_unref (cm);
1782 }
1783
1784 g_ptr_array_unref (list_context->arr);
1785 }
1786
1787 g_hash_table_unref (list_context->table);
1788 g_slice_free (_ListContext, list_context);
1789 }
1790
1791 static void
all_cms_prepared(_ListContext * list_context)1792 all_cms_prepared (_ListContext *list_context)
1793 {
1794 TpConnectionManager **cms;
1795 guint n_cms = list_context->arr->len;
1796
1797 DEBUG ("We've prepared as many as possible of %u CMs", n_cms);
1798
1799 g_assert (list_context->callback != NULL);
1800
1801 g_ptr_array_add (list_context->arr, NULL);
1802 cms = (TpConnectionManager **) list_context->arr->pdata;
1803
1804 /* If we never had a weak object anyway, call the callback.
1805 * If we had a weak object when we started, only call the callback
1806 * if it hasn't died yet. */
1807 if (!list_context->had_weak_object || list_context->weak_object != NULL)
1808 {
1809 list_context->callback (cms, n_cms, NULL, list_context->user_data,
1810 list_context->weak_object);
1811 }
1812
1813 list_context->callback = NULL;
1814 }
1815
1816 static void
tp_list_connection_managers_cm_prepared(GObject * source,GAsyncResult * result,gpointer user_data)1817 tp_list_connection_managers_cm_prepared (GObject *source,
1818 GAsyncResult *result,
1819 gpointer user_data)
1820 {
1821 _ListContext *list_context = user_data;
1822 GError *error = NULL;
1823 TpConnectionManager *cm = TP_CONNECTION_MANAGER (source);
1824
1825 if (tp_proxy_prepare_finish (source, result, &error))
1826 {
1827 DEBUG ("%s: prepared", cm->name);
1828 }
1829 else
1830 {
1831 DEBUG ("%s: failed to prepare, continuing: %s #%d: %s", cm->name,
1832 g_quark_to_string (error->domain), error->code, error->message);
1833 g_clear_error (&error);
1834 /* other than that, ignore it - all we guarantee is that
1835 * the CM is ready *if possible* */
1836 }
1837
1838 list_context->cms_to_ready--;
1839
1840 if (list_context->cms_to_ready == 0)
1841 {
1842 all_cms_prepared (list_context);
1843 }
1844 else
1845 {
1846 DEBUG ("We still need to prepare %" G_GSIZE_FORMAT " CM(s)",
1847 list_context->cms_to_ready);
1848 }
1849
1850 list_context_unref (list_context);
1851 }
1852
1853 static void
tp_list_connection_managers_got_names(TpDBusDaemon * bus_daemon,const gchar * const * names,const GError * error,gpointer user_data,GObject * weak_object)1854 tp_list_connection_managers_got_names (TpDBusDaemon *bus_daemon,
1855 const gchar * const *names,
1856 const GError *error,
1857 gpointer user_data,
1858 GObject *weak_object)
1859 {
1860 _ListContext *list_context = user_data;
1861 const gchar * const *name_iter;
1862 const gchar *method;
1863
1864 if (list_context->getting_names)
1865 method = "ListNames";
1866 else
1867 method = "ListActivatableNames";
1868
1869 /* The TpProxy APIs we use guarantee this */
1870 g_assert (weak_object != NULL || !list_context->had_weak_object);
1871
1872 if (error != NULL)
1873 {
1874 DEBUG ("%s failed: %s #%d: %s", method,
1875 g_quark_to_string (error->domain), error->code, error->message);
1876 list_context->callback (NULL, 0, error, list_context->user_data,
1877 weak_object);
1878 return;
1879 }
1880
1881 DEBUG ("%s succeeded", method);
1882
1883 for (name_iter = names; name_iter != NULL && *name_iter != NULL; name_iter++)
1884 {
1885 const gchar *name;
1886 TpConnectionManager *cm;
1887
1888 if (strncmp (TP_CM_BUS_NAME_BASE, *name_iter, list_context->base_len)
1889 != 0)
1890 continue;
1891
1892 name = *name_iter + list_context->base_len;
1893 DEBUG (" found CM: %s", name);
1894
1895 if (g_hash_table_lookup (list_context->table, name) == NULL)
1896 {
1897 /* just ignore connection managers with bad names */
1898 cm = tp_connection_manager_new (bus_daemon, name, NULL, NULL);
1899 if (cm != NULL)
1900 g_hash_table_insert (list_context->table, g_strdup (name), cm);
1901 }
1902 }
1903
1904 if (list_context->getting_names)
1905 {
1906 /* now that we have all the CMs, wait for them all to be ready */
1907 guint i;
1908
1909 list_context->arr = g_ptr_array_sized_new (g_hash_table_size
1910 (list_context->table));
1911
1912 g_hash_table_foreach_steal (list_context->table, steal_into_ptr_array,
1913 list_context->arr);
1914
1915 list_context->cms_to_ready = list_context->arr->len;
1916 list_context->refcount += list_context->cms_to_ready;
1917
1918 DEBUG ("Total of %" G_GSIZE_FORMAT " CMs to be prepared",
1919 list_context->cms_to_ready);
1920
1921 if (list_context->cms_to_ready == 0)
1922 {
1923 all_cms_prepared (list_context);
1924 return;
1925 }
1926
1927 for (i = 0; i < list_context->cms_to_ready; i++)
1928 {
1929 TpConnectionManager *cm = g_ptr_array_index (list_context->arr, i);
1930
1931 DEBUG (" preparing %s", cm->name);
1932 tp_proxy_prepare_async (cm, NULL,
1933 tp_list_connection_managers_cm_prepared, list_context);
1934 }
1935 }
1936 else
1937 {
1938 DEBUG ("Calling ListNames");
1939 list_context->getting_names = TRUE;
1940 list_context->refcount++;
1941 tp_dbus_daemon_list_names (bus_daemon, 2000,
1942 tp_list_connection_managers_got_names, list_context,
1943 (GDestroyNotify) list_context_unref, weak_object);
1944 }
1945 }
1946
1947 /**
1948 * tp_list_connection_managers:
1949 * @bus_daemon: proxy for the D-Bus daemon
1950 * @callback: callback to be called when listing the CMs
1951 * succeeds or fails; not called if the @weak_object goes away
1952 * @user_data: user-supplied data for the callback
1953 * @destroy: callback to destroy the user-supplied data, called after
1954 * @callback, but also if the @weak_object goes away
1955 * @weak_object: (allow-none): if not %NULL, will be weakly
1956 * referenced; the callback will not be called, and the call will be
1957 * cancelled, if the object has vanished
1958 *
1959 * List the available (running or installed) connection managers. Call the
1960 * callback when done.
1961 *
1962 * Since 0.7.26, this function will wait for each #TpConnectionManager
1963 * to be ready, so all connection managers passed to @callback will have
1964 * their %TP_CONNECTION_MANAGER_FEATURE_CORE feature prepared, unless an error
1965 * occurred while launching that connection manager.
1966 *
1967 * Since: 0.7.1
1968 *
1969 * Deprecated: since 0.19.1, use tp_list_connection_managers_async()
1970 */
1971 void
tp_list_connection_managers(TpDBusDaemon * bus_daemon,TpConnectionManagerListCb callback,gpointer user_data,GDestroyNotify destroy,GObject * weak_object)1972 tp_list_connection_managers (TpDBusDaemon *bus_daemon,
1973 TpConnectionManagerListCb callback,
1974 gpointer user_data,
1975 GDestroyNotify destroy,
1976 GObject *weak_object)
1977 {
1978 _ListContext *list_context = g_slice_new0 (_ListContext);
1979
1980 list_context->base_len = strlen (TP_CM_BUS_NAME_BASE);
1981 list_context->callback = callback;
1982 list_context->user_data = user_data;
1983 list_context->destroy = destroy;
1984
1985 list_context->getting_names = FALSE;
1986 list_context->refcount = 1;
1987 list_context->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1988 g_object_unref);
1989 list_context->arr = NULL;
1990 list_context->cms_to_ready = 0;
1991
1992 if (weak_object != NULL)
1993 {
1994 list_context->weak_object = weak_object;
1995 list_context->had_weak_object = TRUE;
1996 g_object_add_weak_pointer (weak_object, &list_context->weak_object);
1997 }
1998
1999 DEBUG ("Calling ListActivatableNames");
2000 tp_dbus_daemon_list_activatable_names (bus_daemon, 2000,
2001 tp_list_connection_managers_got_names, list_context,
2002 (GDestroyNotify) list_context_unref, weak_object);
2003 }
2004
2005 static void
list_connection_managers_async_cb(TpConnectionManager * const * cms,gsize n_cms,const GError * error,gpointer user_data,GObject * weak_object)2006 list_connection_managers_async_cb (TpConnectionManager * const *cms,
2007 gsize n_cms,
2008 const GError *error,
2009 gpointer user_data,
2010 GObject *weak_object)
2011 {
2012 GSimpleAsyncResult *result = user_data;
2013
2014 if (error != NULL)
2015 {
2016 g_simple_async_result_set_from_error (result, error);
2017 }
2018 else
2019 {
2020 GList *l = NULL;
2021 gsize i;
2022
2023 for (i = 0; i < n_cms; i++)
2024 l = g_list_prepend (l, g_object_ref (cms[i]));
2025
2026 l = g_list_reverse (l);
2027
2028 g_simple_async_result_set_op_res_gpointer (result, l,
2029 (GDestroyNotify) _tp_object_list_free);
2030 }
2031
2032 g_simple_async_result_complete_in_idle (result);
2033
2034 /* result is unreffed by GDestroyNotify */
2035 }
2036
2037 /**
2038 * tp_list_connection_managers_async:
2039 * @dbus_daemon: (allow-none): a #TpDBusDaemon, or %NULL to use
2040 * tp_dbus_daemon_dup()
2041 * @callback: a callback to call with a list of CMs
2042 * @user_data: data to pass to @callback
2043 *
2044 * List the available (running or installed) connection managers,
2045 * asynchronously, and wait for their %TP_CONNECTION_MANAGER_FEATURE_CORE
2046 * feature to be ready.
2047 *
2048 * Since: 0.17.6
2049 */
2050 void
tp_list_connection_managers_async(TpDBusDaemon * dbus_daemon,GAsyncReadyCallback callback,gpointer user_data)2051 tp_list_connection_managers_async (TpDBusDaemon *dbus_daemon,
2052 GAsyncReadyCallback callback,
2053 gpointer user_data)
2054 {
2055 GSimpleAsyncResult *result;
2056 GError *error = NULL;
2057
2058 if (dbus_daemon == NULL)
2059 dbus_daemon = tp_dbus_daemon_dup (&error);
2060 else
2061 g_object_ref (dbus_daemon);
2062
2063 result = g_simple_async_result_new (NULL, callback, user_data,
2064 tp_list_connection_managers_async);
2065
2066 if (dbus_daemon == NULL)
2067 {
2068 g_simple_async_result_take_error (result, error);
2069 g_simple_async_result_complete_in_idle (result);
2070 g_object_unref (result);
2071 }
2072 else
2073 {
2074 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2075 tp_list_connection_managers (dbus_daemon,
2076 list_connection_managers_async_cb, result, g_object_unref, NULL);
2077 G_GNUC_END_IGNORE_DEPRECATIONS
2078 g_object_unref (dbus_daemon);
2079 }
2080 }
2081
2082 /**
2083 * tp_list_connection_managers_finish:
2084 * @result: the result of tp_list_connection_managers_async()
2085 * @error: used to raise an error if the operation failed
2086 *
2087 * Finish listing the available connection managers.
2088 *
2089 * Free the list after use, for instance with
2090 * <literal>g_list_free_full (list, g_object_unref)</literal>.
2091 *
2092 * Returns: (transfer full) (element-type TelepathyGLib.ConnectionManager): a
2093 * newly allocated list of references to #TpConnectionManager objects
2094 * Since: 0.17.6
2095 */
2096 GList *
tp_list_connection_managers_finish(GAsyncResult * result,GError ** error)2097 tp_list_connection_managers_finish (GAsyncResult *result,
2098 GError **error)
2099 {
2100 _tp_implement_finish_return_copy_pointer (NULL,
2101 tp_list_connection_managers_async,
2102 _tp_object_list_copy);
2103 }
2104
2105 /**
2106 * tp_connection_manager_check_valid_name:
2107 * @name: a possible connection manager name
2108 * @error: used to raise %TP_ERROR_INVALID_ARGUMENT if %FALSE is returned
2109 *
2110 * Check that the given string is a valid connection manager name, i.e. that
2111 * it consists entirely of ASCII letters, digits and underscores, and starts
2112 * with a letter.
2113 *
2114 * Returns: %TRUE if @name is valid
2115 *
2116 * Since: 0.7.1
2117 */
2118 gboolean
tp_connection_manager_check_valid_name(const gchar * name,GError ** error)2119 tp_connection_manager_check_valid_name (const gchar *name,
2120 GError **error)
2121 {
2122 const gchar *name_char;
2123
2124 if (tp_str_empty (name))
2125 {
2126 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2127 "The empty string is not a valid connection manager name");
2128 return FALSE;
2129 }
2130
2131 if (!g_ascii_isalpha (name[0]))
2132 {
2133 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2134 "Not a valid connection manager name because first character "
2135 "is not an ASCII letter: %s", name);
2136 return FALSE;
2137 }
2138
2139 for (name_char = name; *name_char != '\0'; name_char++)
2140 {
2141 if (!g_ascii_isalnum (*name_char) && *name_char != '_')
2142 {
2143 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2144 "Not a valid connection manager name because character '%c' "
2145 "is not an ASCII letter, digit or underscore: %s",
2146 *name_char, name);
2147 return FALSE;
2148 }
2149 }
2150
2151 return TRUE;
2152 }
2153
2154 /**
2155 * tp_connection_manager_check_valid_protocol_name:
2156 * @name: a possible protocol name
2157 * @error: used to raise %TP_ERROR_INVALID_ARGUMENT if %FALSE is returned
2158 *
2159 * Check that the given string is a valid protocol name, i.e. that
2160 * it consists entirely of ASCII letters, digits and hyphen/minus, and starts
2161 * with a letter.
2162 *
2163 * Returns: %TRUE if @name is valid
2164 *
2165 * Since: 0.7.1
2166 */
2167 gboolean
tp_connection_manager_check_valid_protocol_name(const gchar * name,GError ** error)2168 tp_connection_manager_check_valid_protocol_name (const gchar *name,
2169 GError **error)
2170 {
2171 const gchar *name_char;
2172
2173 if (name == NULL || name[0] == '\0')
2174 {
2175 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2176 "The empty string is not a valid protocol name");
2177 return FALSE;
2178 }
2179
2180 if (!g_ascii_isalpha (name[0]))
2181 {
2182 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2183 "Not a valid protocol name because first character "
2184 "is not an ASCII letter: %s", name);
2185 return FALSE;
2186 }
2187
2188 for (name_char = name; *name_char != '\0'; name_char++)
2189 {
2190 if (!g_ascii_isalnum (*name_char) && *name_char != '-')
2191 {
2192 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
2193 "Not a valid protocol name because character '%c' "
2194 "is not an ASCII letter, digit or hyphen/minus: %s",
2195 *name_char, name);
2196 return FALSE;
2197 }
2198 }
2199
2200 return TRUE;
2201 }
2202
2203 /**
2204 * tp_connection_manager_get_name:
2205 * @self: a connection manager
2206 *
2207 * Return the internal name of this connection manager in the Telepathy
2208 * D-Bus API, e.g. "gabble" or "haze". This is often the name of the binary
2209 * without the "telepathy-" prefix.
2210 *
2211 * The returned string is valid as long as @self is. Copy it with g_strdup()
2212 * if a longer lifetime is required.
2213 *
2214 * Returns: the #TpConnectionManager:cm-name property
2215 * Since: 0.7.26
2216 */
2217 const gchar *
tp_connection_manager_get_name(TpConnectionManager * self)2218 tp_connection_manager_get_name (TpConnectionManager *self)
2219 {
2220 g_return_val_if_fail (TP_IS_CONNECTION_MANAGER (self), NULL);
2221 return self->name;
2222 }
2223
2224 /**
2225 * tp_connection_manager_is_ready: (skip)
2226 * @self: a connection manager
2227 *
2228 * If protocol and parameter information has been obtained from the connection
2229 * manager or the cache in the .manager file, return %TRUE. Otherwise,
2230 * return %FALSE.
2231 *
2232 * This may change from %FALSE to %TRUE at any time that the main loop is
2233 * running; the #GObject::notify signal is emitted for the
2234 * #TpConnectionManager:info-source property.
2235 *
2236 * Returns: %TRUE, unless the #TpConnectionManager:info-source property is
2237 * %TP_CM_INFO_SOURCE_NONE
2238 * Since: 0.7.26
2239 * Deprecated: since 0.17.6, use tp_proxy_is_prepared()
2240 * with %TP_CONNECTION_MANAGER_FEATURE_CORE instead
2241 */
2242 gboolean
tp_connection_manager_is_ready(TpConnectionManager * self)2243 tp_connection_manager_is_ready (TpConnectionManager *self)
2244 {
2245 g_return_val_if_fail (TP_IS_CONNECTION_MANAGER (self), FALSE);
2246 return self->info_source != TP_CM_INFO_SOURCE_NONE;
2247 }
2248
2249 /**
2250 * tp_connection_manager_is_running:
2251 * @self: a connection manager
2252 *
2253 * Return %TRUE if this connection manager currently appears to be running.
2254 * This may change at any time that the main loop is running; the
2255 * #TpConnectionManager::activated and #TpConnectionManager::exited signals
2256 * are emitted.
2257 *
2258 * Returns: whether the connection manager is currently running
2259 * Since: 0.7.26
2260 */
2261 gboolean
tp_connection_manager_is_running(TpConnectionManager * self)2262 tp_connection_manager_is_running (TpConnectionManager *self)
2263 {
2264 g_return_val_if_fail (TP_IS_CONNECTION_MANAGER (self), FALSE);
2265 return self->running;
2266 }
2267
2268 /**
2269 * tp_connection_manager_get_info_source:
2270 * @self: a connection manager
2271 *
2272 * If protocol and parameter information has been obtained from the connection
2273 * manager, return %TP_CM_INFO_SOURCE_LIVE; if it has been obtained from the
2274 * cache in the .manager file, return %TP_CM_INFO_SOURCE_FILE. If this
2275 * information has not yet been obtained, or obtaining it failed, return
2276 * %TP_CM_INFO_SOURCE_NONE.
2277 *
2278 * This may increase at any time that the main loop is running; the
2279 * #GObject::notify signal is emitted.
2280 *
2281 * Returns: the value of the #TpConnectionManager:info-source property
2282 * Since: 0.7.26
2283 */
2284 TpCMInfoSource
tp_connection_manager_get_info_source(TpConnectionManager * self)2285 tp_connection_manager_get_info_source (TpConnectionManager *self)
2286 {
2287 g_return_val_if_fail (TP_IS_CONNECTION_MANAGER (self),
2288 TP_CM_INFO_SOURCE_NONE);
2289 return self->info_source;
2290 }
2291
2292 /**
2293 * tp_connection_manager_dup_protocol_names:
2294 * @self: a connection manager
2295 *
2296 * Returns a list of protocol names supported by this connection manager.
2297 * These are the internal protocol names used by the Telepathy specification
2298 * (e.g. "jabber" and "msn"), rather than user-visible names in any particular
2299 * locale.
2300 *
2301 * If this function is called before the connection manager information has
2302 * been obtained, the result is always %NULL. Use
2303 * tp_proxy_prepare_async() to wait for this.
2304 *
2305 * The result is copied and must be freed by the caller, but it is not
2306 * necessarily still true after the main loop is re-entered.
2307 *
2308 * Returns: (array zero-terminated=1) (transfer full): a #GStrv of protocol names
2309 * Since: 0.7.26
2310 */
2311 gchar **
tp_connection_manager_dup_protocol_names(TpConnectionManager * self)2312 tp_connection_manager_dup_protocol_names (TpConnectionManager *self)
2313 {
2314 GPtrArray *ret;
2315 guint i;
2316
2317 g_return_val_if_fail (TP_IS_CONNECTION_MANAGER (self), NULL);
2318
2319 if (self->info_source == TP_CM_INFO_SOURCE_NONE)
2320 return NULL;
2321
2322 g_assert (self->priv->protocol_structs != NULL);
2323
2324 ret = g_ptr_array_sized_new (self->priv->protocol_structs->len);
2325
2326 for (i = 0; i < self->priv->protocol_structs->len; i++)
2327 {
2328 TpConnectionManagerProtocol *proto = g_ptr_array_index (
2329 self->priv->protocol_structs, i);
2330
2331 if (proto != NULL)
2332 g_ptr_array_add (ret, g_strdup (proto->name));
2333 }
2334
2335 g_ptr_array_add (ret, NULL);
2336
2337 return (gchar **) g_ptr_array_free (ret, FALSE);
2338 }
2339
2340 /**
2341 * tp_connection_manager_get_protocol:
2342 * @self: a connection manager
2343 * @protocol: the name of a protocol as defined in the Telepathy D-Bus API,
2344 * e.g. "jabber" or "msn"
2345 *
2346 * Returns a structure representing a protocol, or %NULL if this connection
2347 * manager does not support the specified protocol.
2348 *
2349 * Since 0.11.11, you can get a #GObject version with more
2350 * functionality by calling tp_connection_manager_get_protocol_object().
2351 *
2352 * If this function is called before the connection manager information has
2353 * been obtained, the result is always %NULL. Use
2354 * tp_proxy_prepare_async() to wait for this.
2355 *
2356 * The result is not necessarily valid after the main loop is re-entered.
2357 * Since 0.11.3, it can be copied with tp_connection_manager_protocol_copy()
2358 * if a permanently-valid copy is needed.
2359 *
2360 * Returns: (transfer none): a structure representing the protocol
2361 * Since: 0.7.26
2362 *
2363 * Deprecated: 0.19.1, use tp_connection_manager_get_protocol_object()
2364 */
2365 const TpConnectionManagerProtocol *
tp_connection_manager_get_protocol(TpConnectionManager * self,const gchar * protocol)2366 tp_connection_manager_get_protocol (TpConnectionManager *self,
2367 const gchar *protocol)
2368 {
2369 TpProtocol *object;
2370
2371 object = tp_connection_manager_get_protocol_object (self, protocol);
2372
2373 if (object == NULL)
2374 return NULL;
2375
2376 return _tp_protocol_get_struct (object);
2377 }
2378
2379 /**
2380 * tp_connection_manager_get_protocol_object:
2381 * @self: a connection manager
2382 * @protocol: the name of a protocol as defined in the Telepathy D-Bus API,
2383 * e.g. "jabber" or "msn"
2384 *
2385 * Returns an object representing a protocol, or %NULL if this connection
2386 * manager does not support the specified protocol.
2387 *
2388 * If this function is called before the connection manager information has
2389 * been obtained, the result is always %NULL. Use tp_proxy_prepare_async()
2390 * to wait for this.
2391 *
2392 * The result should be referenced with g_object_ref() if it will be kept.
2393 *
2394 * Returns: (transfer none): an object representing the protocol, or %NULL
2395 *
2396 * Since: 0.11.11
2397 */
2398 TpProtocol *
tp_connection_manager_get_protocol_object(TpConnectionManager * self,const gchar * protocol)2399 tp_connection_manager_get_protocol_object (TpConnectionManager *self,
2400 const gchar *protocol)
2401 {
2402 g_return_val_if_fail (TP_IS_CONNECTION_MANAGER (self), NULL);
2403 g_return_val_if_fail (protocol != NULL, NULL);
2404
2405 if (self->priv->protocol_objects == NULL)
2406 return NULL;
2407
2408 return g_hash_table_lookup (self->priv->protocol_objects, protocol);
2409 }
2410
2411 /* FIXME: in Telepathy 1.0, rename to get_protocols */
2412 /**
2413 * tp_connection_manager_dup_protocols:
2414 * @self: a connection manager
2415 *
2416 * Return objects representing all protocols supported by this connection
2417 * manager.
2418 *
2419 * If this function is called before the connection manager information has
2420 * been obtained, the result is always %NULL. Use tp_proxy_prepare_async()
2421 * to wait for this.
2422 *
2423 * The caller must free the list, for instance with
2424 * <literal>g_list_free_full (l, g_object_unref)</literal>.
2425 *
2426 * Returns: (transfer full) (element-type TelepathyGLib.Protocol): a list
2427 * of #TpProtocol objects representing the protocols supported by @self,
2428 * owned by the caller
2429 *
2430 * Since: 0.17.6
2431 */
2432 GList *
tp_connection_manager_dup_protocols(TpConnectionManager * self)2433 tp_connection_manager_dup_protocols (TpConnectionManager *self)
2434 {
2435 GList *l;
2436
2437 g_return_val_if_fail (TP_IS_CONNECTION_MANAGER (self), NULL);
2438
2439 if (self->priv->protocol_objects == NULL)
2440 return NULL;
2441
2442 l = g_hash_table_get_values (self->priv->protocol_objects);
2443
2444 g_list_foreach (l, (GFunc) g_object_ref, NULL);
2445 return l;
2446 }
2447
2448 /**
2449 * tp_connection_manager_has_protocol:
2450 * @self: a connection manager
2451 * @protocol: the name of a protocol as defined in the Telepathy D-Bus API,
2452 * e.g. "jabber" or "msn"
2453 *
2454 * Return whether @protocol is supported by this connection manager.
2455 *
2456 * If this function is called before the connection manager information has
2457 * been obtained, the result is always %FALSE. Use tp_proxy_prepare_async()
2458 * to wait for this.
2459 *
2460 * Returns: %TRUE if this connection manager supports @protocol
2461 * Since: 0.7.26
2462 */
2463 gboolean
tp_connection_manager_has_protocol(TpConnectionManager * self,const gchar * protocol)2464 tp_connection_manager_has_protocol (TpConnectionManager *self,
2465 const gchar *protocol)
2466 {
2467 return (tp_connection_manager_get_protocol_object (self, protocol) != NULL);
2468 }
2469
2470 /**
2471 * tp_connection_manager_protocol_has_param:
2472 * @protocol: structure representing a supported protocol
2473 * @param: a parameter name
2474 *
2475 * <!-- no more to say -->
2476 *
2477 * Returns: %TRUE if @protocol supports the parameter @param.
2478 * Since: 0.7.26
2479 *
2480 * Deprecated: 0.19.1, use #TpProtocol objects instead
2481 */
2482 gboolean
tp_connection_manager_protocol_has_param(const TpConnectionManagerProtocol * protocol,const gchar * param)2483 tp_connection_manager_protocol_has_param (
2484 const TpConnectionManagerProtocol *protocol,
2485 const gchar *param)
2486 {
2487 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2488 return (tp_connection_manager_protocol_get_param (protocol, param) != NULL);
2489 G_GNUC_END_IGNORE_DEPRECATIONS
2490 }
2491
2492 /**
2493 * tp_connection_manager_protocol_get_param:
2494 * @protocol: structure representing a supported protocol
2495 * @param: a parameter name
2496 *
2497 * <!-- no more to say -->
2498 *
2499 * Returns: a structure representing the parameter @param, or %NULL if not
2500 * supported
2501 * Since: 0.7.26
2502 *
2503 * Deprecated: 0.19.1, use #TpProtocol objects instead
2504 */
2505 const TpConnectionManagerParam *
tp_connection_manager_protocol_get_param(const TpConnectionManagerProtocol * protocol,const gchar * param)2506 tp_connection_manager_protocol_get_param (
2507 const TpConnectionManagerProtocol *protocol,
2508 const gchar *param)
2509 {
2510 const TpConnectionManagerParam *ret = NULL;
2511 guint i;
2512
2513 g_return_val_if_fail (protocol != NULL, NULL);
2514
2515 for (i = 0; protocol->params[i].name != NULL; i++)
2516 {
2517 if (!tp_strdiff (param, protocol->params[i].name))
2518 {
2519 ret = &protocol->params[i];
2520 break;
2521 }
2522 }
2523
2524 return ret;
2525 }
2526
2527 /**
2528 * tp_connection_manager_protocol_can_register:
2529 * @protocol: structure representing a supported protocol
2530 *
2531 * Return whether a new account can be registered on this protocol, by setting
2532 * the special "register" parameter to %TRUE.
2533 *
2534 * Returns: %TRUE if @protocol supports the parameter "register"
2535 * Since: 0.7.26
2536 *
2537 * Deprecated: 0.19.1, use #TpProtocol objects instead
2538 */
2539 gboolean
tp_connection_manager_protocol_can_register(const TpConnectionManagerProtocol * protocol)2540 tp_connection_manager_protocol_can_register (
2541 const TpConnectionManagerProtocol *protocol)
2542 {
2543 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
2544 return tp_connection_manager_protocol_has_param (protocol, "register");
2545 G_GNUC_END_IGNORE_DEPRECATIONS
2546 }
2547
2548 /**
2549 * tp_connection_manager_protocol_dup_param_names:
2550 * @protocol: a protocol supported by a #TpConnectionManager
2551 *
2552 * Returns a list of parameter names supported by this connection manager
2553 * for this protocol.
2554 *
2555 * The result is copied and must be freed by the caller with g_strfreev().
2556 *
2557 * Returns: (array zero-terminated=1) (transfer full): a #GStrv of protocol names
2558 * Since: 0.7.26
2559 *
2560 * Deprecated: 0.19.1, use #TpProtocol objects instead
2561 */
2562 gchar **
tp_connection_manager_protocol_dup_param_names(const TpConnectionManagerProtocol * protocol)2563 tp_connection_manager_protocol_dup_param_names (
2564 const TpConnectionManagerProtocol *protocol)
2565 {
2566 GPtrArray *ret;
2567 guint i;
2568
2569 g_return_val_if_fail (protocol != NULL, NULL);
2570
2571 ret = g_ptr_array_new ();
2572
2573 for (i = 0; protocol->params[i].name != NULL; i++)
2574 g_ptr_array_add (ret, g_strdup (protocol->params[i].name));
2575
2576 g_ptr_array_add (ret, NULL);
2577 return (gchar **) g_ptr_array_free (ret, FALSE);
2578 }
2579
2580 /**
2581 * tp_connection_manager_param_get_name:
2582 * @param: a parameter supported by a #TpConnectionManager
2583 *
2584 * <!-- -->
2585 *
2586 * Returns: the name of the parameter
2587 * Since: 0.7.26
2588 */
2589 const gchar *
tp_connection_manager_param_get_name(const TpConnectionManagerParam * param)2590 tp_connection_manager_param_get_name (const TpConnectionManagerParam *param)
2591 {
2592 g_return_val_if_fail (param != NULL, NULL);
2593
2594 return param->name;
2595 }
2596
2597 /**
2598 * tp_connection_manager_param_get_dbus_signature:
2599 * @param: a parameter supported by a #TpConnectionManager
2600 *
2601 * <!-- -->
2602 *
2603 * Returns: the D-Bus signature of the parameter
2604 * Since: 0.7.26
2605 */
2606 const gchar *
tp_connection_manager_param_get_dbus_signature(const TpConnectionManagerParam * param)2607 tp_connection_manager_param_get_dbus_signature (
2608 const TpConnectionManagerParam *param)
2609 {
2610 g_return_val_if_fail (param != NULL, NULL);
2611
2612 return param->dbus_signature;
2613 }
2614
2615 /**
2616 * tp_connection_manager_param_dup_variant_type:
2617 * @param: a parameter supported by a #TpConnectionManager
2618 *
2619 * <!-- -->
2620 *
2621 * Returns: (transfer full): the #GVariantType of the parameter
2622 * Since: 0.23.1
2623 */
2624 GVariantType *
tp_connection_manager_param_dup_variant_type(const TpConnectionManagerParam * param)2625 tp_connection_manager_param_dup_variant_type (
2626 const TpConnectionManagerParam *param)
2627 {
2628 g_return_val_if_fail (param != NULL, NULL);
2629
2630 /* this should have been checked when we created it */
2631 g_return_val_if_fail (g_variant_type_string_is_valid (param->dbus_signature),
2632 NULL);
2633
2634 return g_variant_type_new (param->dbus_signature);
2635 }
2636
2637 /**
2638 * tp_connection_manager_param_is_required:
2639 * @param: a parameter supported by a #TpConnectionManager
2640 *
2641 * <!-- -->
2642 *
2643 * Returns: %TRUE if the parameter is normally required
2644 * Since: 0.7.26
2645 */
2646 gboolean
tp_connection_manager_param_is_required(const TpConnectionManagerParam * param)2647 tp_connection_manager_param_is_required (
2648 const TpConnectionManagerParam *param)
2649 {
2650 g_return_val_if_fail (param != NULL, FALSE);
2651
2652 return (param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED) != 0;
2653 }
2654
2655 /**
2656 * tp_connection_manager_param_is_required_for_registration:
2657 * @param: a parameter supported by a #TpConnectionManager
2658 *
2659 * <!-- -->
2660 *
2661 * Returns: %TRUE if the parameter is required when registering a new account
2662 * (by setting the special "register" parameter to %TRUE)
2663 * Since: 0.7.26
2664 */
2665 gboolean
tp_connection_manager_param_is_required_for_registration(const TpConnectionManagerParam * param)2666 tp_connection_manager_param_is_required_for_registration (
2667 const TpConnectionManagerParam *param)
2668 {
2669 g_return_val_if_fail (param != NULL, FALSE);
2670
2671 return (param->flags & TP_CONN_MGR_PARAM_FLAG_REGISTER) != 0;
2672 }
2673
2674 /**
2675 * tp_connection_manager_param_is_secret:
2676 * @param: a parameter supported by a #TpConnectionManager
2677 *
2678 * <!-- -->
2679 *
2680 * Returns: %TRUE if the parameter's value is a password or other secret
2681 * Since: 0.7.26
2682 */
2683 gboolean
tp_connection_manager_param_is_secret(const TpConnectionManagerParam * param)2684 tp_connection_manager_param_is_secret (
2685 const TpConnectionManagerParam *param)
2686 {
2687 g_return_val_if_fail (param != NULL, FALSE);
2688
2689 return (param->flags & TP_CONN_MGR_PARAM_FLAG_SECRET) != 0;
2690 }
2691
2692 /**
2693 * tp_connection_manager_param_is_dbus_property:
2694 * @param: a parameter supported by a #TpConnectionManager
2695 *
2696 * <!-- -->
2697 *
2698 * Returns: %TRUE if the parameter represents a D-Bus property of the same name
2699 * Since: 0.7.26
2700 */
2701 gboolean
tp_connection_manager_param_is_dbus_property(const TpConnectionManagerParam * param)2702 tp_connection_manager_param_is_dbus_property (
2703 const TpConnectionManagerParam *param)
2704 {
2705 g_return_val_if_fail (param != NULL, FALSE);
2706
2707 return (param->flags & TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY) != 0;
2708 }
2709
2710 /**
2711 * tp_connection_manager_param_get_default:
2712 * @param: a parameter supported by a #TpConnectionManager
2713 * @value: pointer to an unset (all zeroes) #GValue into which the default's
2714 * type and value are written
2715 *
2716 * Get the default value for this parameter, if there is one. If %FALSE is
2717 * returned, @value is left uninitialized.
2718 *
2719 * Returns: %TRUE if there is a default value
2720 * Since: 0.7.26
2721 */
2722 gboolean
tp_connection_manager_param_get_default(const TpConnectionManagerParam * param,GValue * value)2723 tp_connection_manager_param_get_default (
2724 const TpConnectionManagerParam *param,
2725 GValue *value)
2726 {
2727 g_return_val_if_fail (param != NULL, FALSE);
2728 g_return_val_if_fail (value != NULL, FALSE);
2729 g_return_val_if_fail (!G_IS_VALUE (value), FALSE);
2730
2731 if ((param->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) == 0
2732 || !G_IS_VALUE (¶m->default_value))
2733 return FALSE;
2734
2735 g_value_init (value, G_VALUE_TYPE (¶m->default_value));
2736 g_value_copy (¶m->default_value, value);
2737
2738 return TRUE;
2739 }
2740
2741 /**
2742 * tp_connection_manager_param_dup_default_variant:
2743 * @param: a parameter supported by a #TpConnectionManager
2744 *
2745 * Get the default value for this parameter.
2746 *
2747 * Use g_variant_get_type() to check that the type is what you expect.
2748 * For instance, a string parameter should have type
2749 * %G_VARIANT_TYPE_STRING.
2750 *
2751 * Returns: the default value, or %NULL if there is no default
2752 * Since: 0.19.0
2753 */
2754 GVariant *
tp_connection_manager_param_dup_default_variant(const TpConnectionManagerParam * param)2755 tp_connection_manager_param_dup_default_variant (
2756 const TpConnectionManagerParam *param)
2757 {
2758 g_return_val_if_fail (param != NULL, NULL);
2759
2760 if ((param->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) == 0
2761 || !G_IS_VALUE (¶m->default_value))
2762 return NULL;
2763
2764 return g_variant_ref_sink (dbus_g_value_build_g_variant (
2765 ¶m->default_value));
2766 }
2767