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, &copy);
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 (&param->default_value))
2733     return FALSE;
2734 
2735   g_value_init (value, G_VALUE_TYPE (&param->default_value));
2736   g_value_copy (&param->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 (&param->default_value))
2762     return NULL;
2763 
2764   return g_variant_ref_sink (dbus_g_value_build_g_variant (
2765         &param->default_value));
2766 }
2767