1 /*
2  * base-connection-manager.c - Source for TpBaseConnectionManager
3  *
4  * Copyright (C) 2007-2009 Collabora Ltd.
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 /**
23  * SECTION:base-connection-manager
24  * @title: TpBaseConnectionManager
25  * @short_description: base class for #TpSvcConnectionManager implementations
26  * @see_also: #TpBaseConnection, #TpSvcConnectionManager, #run
27  *
28  * This base class makes it easier to write #TpSvcConnectionManager
29  * implementations by managing the D-Bus object path and bus name,
30  * and maintaining a table of active connections. Subclasses should usually
31  * only need to override the members of the class data structure.
32  */
33 
34 #include "config.h"
35 
36 #include <telepathy-glib/base-connection-manager.h>
37 
38 #include <string.h>
39 
40 #include <dbus/dbus-protocol.h>
41 
42 #include <telepathy-glib/telepathy-glib.h>
43 
44 #define DEBUG_FLAG TP_DEBUG_PARAMS
45 #include "telepathy-glib/base-protocol-internal.h"
46 #include "telepathy-glib/debug-internal.h"
47 
48 /**
49  * TpCMProtocolSpec:
50  * @name: The name which should be passed to RequestConnection for this
51  *        protocol.
52  * @parameters: An array of #TpCMParamSpec representing the valid parameters
53  *              for this protocol, terminated by a #TpCMParamSpec whose name
54  *              entry is NULL.
55  * @params_new: A function which allocates an opaque data structure to store
56  *              the parsed parameters for this protocol. The offset fields
57  *              in the members of the @parameters array refer to offsets
58  *              within this opaque structure.
59  * @params_free: A function which deallocates the opaque data structure
60  *               provided by #params_new, including deallocating its
61  *               data members (currently, only strings) if necessary.
62  * @set_param: A function which sets a parameter within the opaque data
63  *             structure provided by #params_new. If %NULL,
64  *             tp_cm_param_setter_offset() will be used. (New in 0.7.0 -
65  *             previously, code equivalent to tp_cm_param_setter_offset() was
66  *             always used.)
67  *
68  * Structure representing a connection manager protocol.
69  *
70  * In addition to the fields documented here, there are three gpointer fields
71  * which must currently be %NULL. A meaning may be defined for these in a
72  * future version of telepathy-glib.
73  */
74 
75 /*
76  * _TpLegacyProtocol:
77  *
78  * A limited implementation of TpProtocol, in terms of the ConnectionManager
79  * API from telepathy-spec 0.18.
80  */
81 typedef struct {
82     TpBaseProtocol parent;
83     /* Really a TpBaseConnectionManager, but using that type with
84      * g_object_add_weak_pointer violates strict aliasing */
85     gpointer cm;
86     const TpCMProtocolSpec *protocol_spec;
87 } _TpLegacyProtocol;
88 
89 typedef struct {
90     TpBaseProtocolClass parent;
91 } _TpLegacyProtocolClass;
92 
93 #define _TP_TYPE_LEGACY_PROTOCOL (_tp_legacy_protocol_get_type ())
94 GType _tp_legacy_protocol_get_type (void) G_GNUC_CONST;
95 
G_DEFINE_TYPE(_TpLegacyProtocol,_tp_legacy_protocol,TP_TYPE_BASE_PROTOCOL)96 G_DEFINE_TYPE(_TpLegacyProtocol,
97     _tp_legacy_protocol,
98     TP_TYPE_BASE_PROTOCOL)
99 
100 static const TpCMParamSpec *
101 _tp_legacy_protocol_get_parameters (TpBaseProtocol *protocol)
102 {
103   _TpLegacyProtocol *self = (_TpLegacyProtocol *) protocol;
104 
105   return self->protocol_spec->parameters;
106 }
107 
108 static gboolean parse_parameters (const TpCMParamSpec *paramspec,
109     GHashTable *provided, TpIntset *params_present,
110     const TpCMParamSetter set_param, void *params, GError **error);
111 
112 static TpBaseConnection *
_tp_legacy_protocol_new_connection(TpBaseProtocol * protocol,GHashTable * asv,GError ** error)113 _tp_legacy_protocol_new_connection (TpBaseProtocol *protocol,
114     GHashTable *asv,
115     GError **error)
116 {
117   _TpLegacyProtocol *self = (_TpLegacyProtocol *) protocol;
118   const TpCMProtocolSpec *protospec = self->protocol_spec;
119   TpBaseConnectionManagerClass *cls;
120   TpBaseConnection *conn = NULL;
121   void *params = NULL;
122   TpIntset *params_present = NULL;
123   TpCMParamSetter set_param;
124 
125   if (self->cm == NULL)
126     {
127       g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE,
128           "Connection manager no longer available");
129       return NULL;
130     }
131 
132   g_object_ref (self->cm);
133 
134   g_assert (protospec->parameters != NULL);
135   g_assert (protospec->params_new != NULL);
136   g_assert (protospec->params_free != NULL);
137 
138   cls = TP_BASE_CONNECTION_MANAGER_GET_CLASS (self->cm);
139 
140   params_present = tp_intset_new ();
141   params = protospec->params_new ();
142 
143   set_param = protospec->set_param;
144 
145   if (set_param == NULL)
146     set_param = tp_cm_param_setter_offset;
147 
148   if (!parse_parameters (protospec->parameters, (GHashTable *) asv,
149         params_present, set_param, params, error))
150     {
151       goto finally;
152     }
153 
154   conn = (cls->new_connection) (self->cm, protospec->name, params_present,
155       params, error);
156 
157 finally:
158   if (params_present != NULL)
159     tp_intset_destroy (params_present);
160 
161   if (params != NULL)
162     protospec->params_free (params);
163 
164   g_object_unref (self->cm);
165 
166   return conn;
167 }
168 
169 static void
_tp_legacy_protocol_class_init(_TpLegacyProtocolClass * cls)170 _tp_legacy_protocol_class_init (_TpLegacyProtocolClass *cls)
171 {
172   TpBaseProtocolClass *base_class = (TpBaseProtocolClass *) cls;
173 
174   base_class->is_stub = TRUE;
175   base_class->get_parameters = _tp_legacy_protocol_get_parameters;
176   base_class->new_connection = _tp_legacy_protocol_new_connection;
177 }
178 
179 static void
_tp_legacy_protocol_init(_TpLegacyProtocol * self)180 _tp_legacy_protocol_init (_TpLegacyProtocol *self)
181 {
182 }
183 
184 static TpBaseProtocol *
_tp_legacy_protocol_new(TpBaseConnectionManager * cm,const TpCMProtocolSpec * protocol_spec)185 _tp_legacy_protocol_new (TpBaseConnectionManager *cm,
186     const TpCMProtocolSpec *protocol_spec)
187 {
188   _TpLegacyProtocol *self = g_object_new (_TP_TYPE_LEGACY_PROTOCOL,
189       "name", protocol_spec->name,
190       NULL);
191 
192   self->protocol_spec = protocol_spec;
193   self->cm = cm;
194   g_object_add_weak_pointer ((GObject *) cm, &(self->cm));
195   return (TpBaseProtocol *) self;
196 }
197 
198 /**
199  * TpBaseConnectionManager:
200  *
201  * A base class for connection managers. There are no interesting public
202  * fields in the instance structure.
203  */
204 
205 /**
206  * TpBaseConnectionManagerClass:
207  * @parent_class: The parent class
208  * @cm_dbus_name: The name of this connection manager, as used to construct
209  *  D-Bus object paths and bus names. Must contain only letters, digits
210  *  and underscores, and may not start with a digit. Must be filled in by
211  *  subclasses in their class_init function.
212  * @get_interfaces: Returns a #GPtrArray of static strings of extra
213  *  D-Bus interfaces implemented by instances of this class, which may be
214  *  filled in by subclasses. The default is to list no additional interfaces.
215  *  Implementations must first chainup on parent class implementation and then
216  *  add extra interfaces to the #GPtrArray. Replaces @interfaces. Since:
217  *  0.19.4
218  *
219  * The class structure for #TpBaseConnectionManager.
220  *
221  * In addition to the fields documented here, there are some gpointer fields
222  * which must currently be %NULL (a meaning may be defined for these in a
223  * future version of telepathy-glib).
224  *
225  * Changed in 0.7.1: it is a fatal error for @cm_dbus_name not to conform to
226  * the specification.
227  *
228  * Changed in 0.11.11: protocol_params and new_connection may both be
229  * %NULL. If so, this connection manager is assumed to use Protocol objects
230  * instead. Since 0.19.2 those fields are deprecated and should not be
231  * used anymore.
232  */
233 
234 /**
235  * TpBaseConnectionManagerNewConnFunc:
236  * @self: The connection manager implementation
237  * @proto: The protocol name from the D-Bus request
238  * @params_present: A set of integers representing the indexes into the
239  *                  array of #TpCMParamSpec of those parameters that were
240  *                  present in the request
241  * @parsed_params: An opaque data structure as returned by the protocol's
242  *                 params_new function, populated according to the
243  *                 parameter specifications
244  * @error: if not %NULL, used to indicate the error if %NULL is returned
245  *
246  * A function that will return a new connection according to the
247  * parsed parameters; used to implement RequestConnection.
248  *
249  * The connection manager base class will register the bus name for the
250  * new connection, and place a reference to it in its table of
251  * connections until the connection's shutdown process finishes.
252  *
253  * Returns: the new connection object, or %NULL on error.
254  */
255 
256 /**
257  * TpBaseConnectionManagerGetInterfacesFunc:
258  * @self: a #TpBaseConnectionManager
259  *
260  * Signature of an implementation of
261  * #TpBaseConnectionManagerClass.get_interfaces virtual function.
262  *
263  * Implementation must first chainup on parent class implementation and then
264  * add extra interfaces into the #GPtrArray.
265  *
266  * |[
267  * static GPtrArray *
268  * my_connection_manager_get_interfaces (TpBaseConnectionManager *self)
269  * {
270  *   GPtrArray *interfaces;
271  *
272  *   interfaces = TP_BASE_CONNECTION_MANAGER_CLASS (
273  *       my_connection_manager_parent_class)->get_interfaces (self);
274  *
275  *   g_ptr_array_add (interfaces, TP_IFACE_BADGERS);
276  *
277  *   return interfaces;
278  * }
279  * ]|
280  *
281  * Returns: (transfer container): a #GPtrArray of static strings for D-Bus
282  *   interfaces implemented by this client.
283  *
284  * Since: 0.19.4
285  */
286 
287 static void service_iface_init (gpointer, gpointer);
288 
289 G_DEFINE_ABSTRACT_TYPE_WITH_CODE(TpBaseConnectionManager,
290     tp_base_connection_manager,
291     G_TYPE_OBJECT,
292     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
293       tp_dbus_properties_mixin_iface_init);
294     G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_MANAGER,
295         service_iface_init))
296 
297 struct _TpBaseConnectionManagerPrivate
298 {
299   /* if TRUE, the object has gone away */
300   gboolean dispose_has_run;
301   /* used as a set: key is TpBaseConnection *, value is TRUE */
302   GHashTable *connections;
303   /* true after tp_base_connection_manager_register is called */
304   gboolean registered;
305    /* dup'd string => ref to TpBaseProtocol */
306   GHashTable *protocols;
307 
308   TpDBusDaemon *dbus_daemon;
309 };
310 
311 enum
312 {
313     PROP_DBUS_DAEMON = 1,
314     PROP_INTERFACES,
315     PROP_PROTOCOLS,
316     N_PROPS
317 };
318 
319 enum
320 {
321     NO_MORE_CONNECTIONS,
322     N_SIGNALS
323 };
324 
325 static guint signals[N_SIGNALS] = {0};
326 
327 static void
tp_base_connection_manager_dispose(GObject * object)328 tp_base_connection_manager_dispose (GObject *object)
329 {
330   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (object);
331   TpBaseConnectionManagerPrivate *priv = self->priv;
332   GObjectFinalizeFunc dispose =
333     G_OBJECT_CLASS (tp_base_connection_manager_parent_class)->dispose;
334 
335   if (priv->dispose_has_run)
336     return;
337 
338   priv->dispose_has_run = TRUE;
339 
340   if (priv->dbus_daemon != NULL)
341     {
342       g_object_unref (priv->dbus_daemon);
343       priv->dbus_daemon = NULL;
344     }
345 
346   if (priv->protocols != NULL)
347     {
348       g_hash_table_unref (priv->protocols);
349       priv->protocols = NULL;
350     }
351 
352   if (dispose != NULL)
353     dispose (object);
354 }
355 
356 static void
tp_base_connection_manager_finalize(GObject * object)357 tp_base_connection_manager_finalize (GObject *object)
358 {
359   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (object);
360   TpBaseConnectionManagerPrivate *priv = self->priv;
361 
362   g_hash_table_unref (priv->connections);
363 
364   G_OBJECT_CLASS (tp_base_connection_manager_parent_class)->finalize (object);
365 }
366 
367 static gboolean
tp_base_connection_manager_ensure_dbus(TpBaseConnectionManager * self,GError ** error)368 tp_base_connection_manager_ensure_dbus (TpBaseConnectionManager *self,
369     GError **error)
370 {
371   if (self->priv->dbus_daemon == NULL)
372     {
373       self->priv->dbus_daemon = tp_dbus_daemon_dup (error);
374 
375       if (self->priv->dbus_daemon == NULL)
376         return FALSE;
377     }
378 
379   return TRUE;
380 }
381 
382 static GObject *
tp_base_connection_manager_constructor(GType type,guint n_params,GObjectConstructParam * params)383 tp_base_connection_manager_constructor (GType type,
384                                         guint n_params,
385                                         GObjectConstructParam *params)
386 {
387   GObjectClass *object_class =
388       (GObjectClass *) tp_base_connection_manager_parent_class;
389   TpBaseConnectionManager *self =
390       TP_BASE_CONNECTION_MANAGER (object_class->constructor (type, n_params,
391             params));
392   TpBaseConnectionManagerClass *cls =
393       TP_BASE_CONNECTION_MANAGER_GET_CLASS (self);
394   GError *error = NULL;
395 
396   g_assert (tp_connection_manager_check_valid_name (cls->cm_dbus_name, NULL));
397 
398   /* if one of these is NULL, the other must be too */
399   g_assert (cls->new_connection == NULL || cls->protocol_params != NULL);
400   g_assert (cls->protocol_params == NULL || cls->new_connection != NULL);
401 
402   if (!tp_base_connection_manager_ensure_dbus (self, &error))
403     {
404       WARNING ("%s", error->message);
405       g_error_free (error);
406     }
407 
408   return (GObject *) self;
409 }
410 
411 static void
tp_base_connection_manager_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)412 tp_base_connection_manager_get_property (GObject *object,
413     guint property_id,
414     GValue *value,
415     GParamSpec *pspec)
416 {
417   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (object);
418   TpBaseConnectionManagerClass *cls = TP_BASE_CONNECTION_MANAGER_GET_CLASS (
419       object);
420 
421   switch (property_id)
422     {
423     case PROP_DBUS_DAEMON:
424       g_value_set_object (value, self->priv->dbus_daemon);
425       break;
426 
427     case PROP_INTERFACES:
428       {
429         GPtrArray *interfaces = cls->get_interfaces (self);
430 
431         /* make sure there's a terminating NULL */
432         g_ptr_array_add (interfaces, NULL);
433         g_value_set_boxed (value, interfaces->pdata);
434 
435         g_ptr_array_unref (interfaces);
436       }
437       break;
438 
439     case PROP_PROTOCOLS:
440         {
441           GHashTable *map = g_hash_table_new_full (g_str_hash, g_str_equal,
442               g_free, (GDestroyNotify) g_hash_table_unref);
443           GHashTableIter iter;
444           gpointer name, protocol;
445 
446           g_hash_table_iter_init (&iter, self->priv->protocols);
447 
448           while (g_hash_table_iter_next (&iter, &name, &protocol))
449             {
450               GHashTable *props;
451 
452               g_object_get (protocol,
453                   "immutable-properties", &props,
454                   NULL);
455 
456               g_hash_table_insert (map, g_strdup (name), props);
457             }
458 
459           g_value_take_boxed (value, map);
460         }
461       break;
462 
463     default:
464       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
465       break;
466     }
467 }
468 
469 static void
tp_base_connection_manager_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)470 tp_base_connection_manager_set_property (GObject *object,
471     guint property_id,
472     const GValue *value,
473     GParamSpec *pspec)
474 {
475   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (object);
476 
477   switch (property_id)
478     {
479     case PROP_DBUS_DAEMON:
480         {
481           TpDBusDaemon *dbus_daemon = g_value_get_object (value);
482 
483           g_assert (self->priv->dbus_daemon == NULL);     /* construct-only */
484 
485           if (dbus_daemon != NULL)
486             self->priv->dbus_daemon = g_object_ref (dbus_daemon);
487         }
488       break;
489 
490     default:
491       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
492       break;
493   }
494 }
495 
496 static GPtrArray *
tp_base_connection_manager_get_interfaces(TpBaseConnectionManager * self)497 tp_base_connection_manager_get_interfaces (TpBaseConnectionManager *self)
498 {
499   GPtrArray *interfaces = g_ptr_array_new ();
500   const char * const *ptr;
501 
502   /* copy the klass->interfaces property for backwards compatibility */
503   for (ptr = TP_BASE_CONNECTION_MANAGER_GET_CLASS (self)->interfaces;
504        ptr != NULL && *ptr != NULL;
505        ptr++)
506     {
507       g_ptr_array_add (interfaces, (char *) *ptr);
508     }
509 
510   return interfaces;
511 }
512 
513 static void
tp_base_connection_manager_class_init(TpBaseConnectionManagerClass * klass)514 tp_base_connection_manager_class_init (TpBaseConnectionManagerClass *klass)
515 {
516   static TpDBusPropertiesMixinPropImpl cm_properties[] = {
517       { "Protocols", "protocols", NULL },
518       { "Interfaces", "interfaces", NULL },
519       { NULL }
520   };
521   GObjectClass *object_class = G_OBJECT_CLASS (klass);
522 
523   g_type_class_add_private (klass, sizeof (TpBaseConnectionManagerPrivate));
524   object_class->constructor = tp_base_connection_manager_constructor;
525   object_class->get_property = tp_base_connection_manager_get_property;
526   object_class->set_property = tp_base_connection_manager_set_property;
527   object_class->dispose = tp_base_connection_manager_dispose;
528   object_class->finalize = tp_base_connection_manager_finalize;
529 
530   klass->get_interfaces = tp_base_connection_manager_get_interfaces;
531 
532   /**
533    * TpBaseConnectionManager:dbus-daemon:
534    *
535    * #TpDBusDaemon object encapsulating this object's connection to D-Bus.
536    * Read-only except during construction.
537    *
538    * If this property is %NULL or omitted during construction, the object will
539    * automatically attempt to connect to the starter or session bus with
540    * tp_dbus_daemon_dup() just after it is constructed; if this fails, a
541    * warning will be logged with g_warning(), and this property will remain
542    * %NULL.
543    *
544    * Since: 0.11.3
545    */
546   g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
547       g_param_spec_object ("dbus-daemon", "D-Bus daemon",
548         "The D-Bus daemon used by this object", TP_TYPE_DBUS_DAEMON,
549         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
550 
551   /**
552    * TpBaseConnectionManager:interfaces:
553    *
554    * The set of D-Bus interfaces available on this ConnectionManager, other
555    * than ConnectionManager itself.
556    *
557    * Since: 0.11.11
558    */
559   g_object_class_install_property (object_class, PROP_INTERFACES,
560       g_param_spec_boxed ("interfaces",
561         "ConnectionManager.Interfaces",
562         "The set of D-Bus interfaces available on this ConnectionManager, "
563         "other than ConnectionManager itself",
564         G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
565 
566   /**
567    * TpBaseConnectionManager:protocols:
568    *
569    * The Protocol objects available on this ConnectionManager.
570    *
571    * Since: 0.11.11
572    */
573   g_object_class_install_property (object_class, PROP_PROTOCOLS,
574       g_param_spec_boxed ("protocols",
575         "ConnectionManager.Protocols",
576         "The set of protocols available on this Connection, other than "
577         "ConnectionManager itself",
578         TP_HASH_TYPE_PROTOCOL_PROPERTIES_MAP,
579         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
580 
581   /**
582    * TpBaseConnectionManager::no-more-connections:
583    *
584    * Emitted when the table of active connections becomes empty.
585    * tp_run_connection_manager() uses this to detect when to shut down the
586    * connection manager.
587    */
588   signals[NO_MORE_CONNECTIONS] =
589     g_signal_new ("no-more-connections",
590                   G_OBJECT_CLASS_TYPE (klass),
591                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
592                   0,
593                   NULL, NULL, NULL,
594                   G_TYPE_NONE, 0);
595 
596   tp_dbus_properties_mixin_class_init (object_class, 0);
597   tp_dbus_properties_mixin_implement_interface (object_class,
598       TP_IFACE_QUARK_CONNECTION_MANAGER,
599       tp_dbus_properties_mixin_getter_gobject_properties, NULL,
600       cm_properties);
601 }
602 
603 static void
tp_base_connection_manager_init(TpBaseConnectionManager * self)604 tp_base_connection_manager_init (TpBaseConnectionManager *self)
605 {
606   TpBaseConnectionManagerPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
607       TP_TYPE_BASE_CONNECTION_MANAGER, TpBaseConnectionManagerPrivate);
608 
609   self->priv = priv;
610 
611   priv->connections = g_hash_table_new (g_direct_hash, g_direct_equal);
612   priv->protocols = g_hash_table_new_full (g_str_hash, g_str_equal,
613       g_free, g_object_unref);
614 }
615 
616 /**
617  * connection_shutdown_finished_cb:
618  * @conn: #TpBaseConnection
619  * @data: data passed in callback
620  *
621  * Signal handler called when a connection object disconnects.
622  * When they become disconnected, we can unref and discard
623  * them, and they will disappear from the bus.
624  */
625 static void
connection_shutdown_finished_cb(TpBaseConnection * conn,gpointer data)626 connection_shutdown_finished_cb (TpBaseConnection *conn,
627                                  gpointer data)
628 {
629   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (data);
630   TpBaseConnectionManagerPrivate *priv = self->priv;
631 
632   /* temporary ref, because disconnecting this signal handler might release
633    * the last ref */
634   g_object_ref (self);
635 
636   g_assert (g_hash_table_lookup (priv->connections, conn));
637   g_hash_table_remove (priv->connections, conn);
638 
639   DEBUG ("dereferenced connection");
640   if (g_hash_table_size (priv->connections) == 0)
641     {
642       g_signal_emit (self, signals[NO_MORE_CONNECTIONS], 0);
643     }
644 
645   g_signal_handlers_disconnect_by_func (conn,
646       connection_shutdown_finished_cb, data);
647 
648   g_object_unref (conn);
649   g_object_unref (self);
650 }
651 
652 /* Parameter parsing */
653 
654 static TpBaseProtocol *
tp_base_connection_manager_get_protocol(TpBaseConnectionManager * self,const gchar * protocol_name,GError ** error)655 tp_base_connection_manager_get_protocol (TpBaseConnectionManager *self,
656     const gchar *protocol_name,
657     GError **error)
658 {
659   TpBaseProtocol *protocol = g_hash_table_lookup (self->priv->protocols,
660       protocol_name);
661 
662   if (protocol != NULL)
663     return protocol;
664 
665   g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED,
666       "unknown protocol %s", protocol_name);
667 
668   return NULL;
669 }
670 
671 /**
672  * tp_cm_param_setter_offset:
673  * @paramspec: A parameter specification with offset set to some
674  *  meaningful value.
675  * @value: The value for that parameter, either provided by the user or
676  *  constructed from the parameter's default.
677  * @params: An opaque data structure such that the address at (@params +
678  *  @paramspec->offset) is a valid pointer to a variable of the
679  *  appropriate type.
680  *
681  * A #TpCMParamSetter which sets parameters by dereferencing an offset
682  * from @params.  If @paramspec->offset is G_MAXSIZE, the parameter is
683  * deemed obsolete, and is accepted but ignored.
684  *
685  * Since: 0.7.0
686  */
687 void
tp_cm_param_setter_offset(const TpCMParamSpec * paramspec,const GValue * value,gpointer params)688 tp_cm_param_setter_offset (const TpCMParamSpec *paramspec,
689                            const GValue *value,
690                            gpointer params)
691 {
692   char *params_mem = params;
693 
694   if (paramspec->offset == G_MAXSIZE)
695     {
696       /* quietly ignore any obsolete params provided */
697       return;
698     }
699 
700   switch (paramspec->dtype[0])
701     {
702       case DBUS_TYPE_STRING:
703         {
704           gchar **save_to = (gchar **) (params_mem + paramspec->offset);
705           const gchar *str;
706 
707           g_assert (paramspec->gtype == G_TYPE_STRING);
708           str = g_value_get_string (value);
709           g_free (*save_to);
710           if (str == NULL)
711             {
712               *save_to = g_strdup ("");
713             }
714           else
715             {
716               *save_to = g_value_dup_string (value);
717             }
718           if (DEBUGGING)
719             {
720               if (strstr (paramspec->name, "password") != NULL)
721                 DEBUG ("%s = <hidden>", paramspec->name);
722               else
723                 DEBUG ("%s = \"%s\"", paramspec->name, *save_to);
724             }
725         }
726         break;
727 
728       case DBUS_TYPE_INT16:
729       case DBUS_TYPE_INT32:
730         {
731           gint *save_to = (gint *) (params_mem + paramspec->offset);
732           gint i = g_value_get_int (value);
733 
734           g_assert (paramspec->gtype == G_TYPE_INT);
735           *save_to = i;
736           DEBUG ("%s = %d = 0x%x", paramspec->name, i, i);
737         }
738         break;
739 
740       case DBUS_TYPE_UINT16:
741       case DBUS_TYPE_UINT32:
742         {
743           guint *save_to = (guint *) (params_mem + paramspec->offset);
744           guint i = g_value_get_uint (value);
745 
746           g_assert (paramspec->gtype == G_TYPE_UINT);
747           *save_to = i;
748           DEBUG ("%s = %u = 0x%x", paramspec->name, i, i);
749         }
750         break;
751 
752       case DBUS_TYPE_INT64:
753         {
754           gint64 *save_to = (gint64 *) (params_mem + paramspec->offset);
755           gint64 i = g_value_get_int64 (value);
756 
757           g_assert (paramspec->gtype == G_TYPE_INT64);
758           *save_to = i;
759           DEBUG ("%s = %" G_GINT64_FORMAT, paramspec->name, i);
760         }
761         break;
762 
763       case DBUS_TYPE_UINT64:
764         {
765           guint64 *save_to = (guint64 *) (params_mem + paramspec->offset);
766           guint64 i = g_value_get_uint64 (value);
767 
768           g_assert (paramspec->gtype == G_TYPE_UINT64);
769           *save_to = i;
770           DEBUG ("%s = %" G_GUINT64_FORMAT, paramspec->name, i);
771         }
772         break;
773 
774       case DBUS_TYPE_DOUBLE:
775         {
776           gdouble *save_to = (gdouble *) (params_mem + paramspec->offset);
777           gdouble i = g_value_get_double (value);
778 
779           g_assert (paramspec->gtype == G_TYPE_DOUBLE);
780           *save_to = i;
781           DEBUG ("%s = %f", paramspec->name, i);
782         }
783         break;
784 
785       case DBUS_TYPE_OBJECT_PATH:
786         {
787           gchar **save_to = (gchar **) (params_mem + paramspec->offset);
788 
789           g_assert (paramspec->gtype == DBUS_TYPE_G_OBJECT_PATH);
790           g_free (*save_to);
791 
792           *save_to = g_value_dup_boxed (value);
793           DEBUG ("%s = \"%s\"", paramspec->name, *save_to);
794         }
795         break;
796 
797       case DBUS_TYPE_BOOLEAN:
798         {
799           gboolean *save_to = (gboolean *) (params_mem + paramspec->offset);
800           gboolean b = g_value_get_boolean (value);
801 
802           g_assert (paramspec->gtype == G_TYPE_BOOLEAN);
803           g_assert (b == TRUE || b == FALSE);
804           *save_to = b;
805           DEBUG ("%s = %s", paramspec->name, b ? "TRUE" : "FALSE");
806         }
807         break;
808 
809       case DBUS_TYPE_ARRAY:
810         switch (paramspec->dtype[1])
811           {
812             case DBUS_TYPE_STRING:
813               {
814                 GStrv *save_to = (GStrv *) (params_mem + paramspec->offset);
815 
816                 g_strfreev (*save_to);
817                 *save_to = g_value_dup_boxed (value);
818 
819                 if (DEBUGGING)
820                   {
821                     gchar *joined = g_strjoinv (", ", *save_to);
822 
823                     DEBUG ("%s = [%s]", paramspec->name, joined);
824                     g_free (joined);
825                   }
826               }
827               break;
828 
829             case DBUS_TYPE_BYTE:
830               {
831                 GArray **save_to = (GArray **) (params_mem + paramspec->offset);
832 
833                 if (*save_to != NULL)
834                   {
835                     g_array_unref (*save_to);
836                   }
837                 *save_to = g_value_dup_boxed (value);
838                 DEBUG ("%s = ...[%u]", paramspec->name, (*save_to)->len);
839               }
840               break;
841 
842             default:
843               ERROR ("encountered unhandled D-Bus array type %s on "
844                   "argument %s", paramspec->dtype, paramspec->name);
845               g_assert_not_reached ();
846           }
847         break;
848 
849       default:
850         ERROR ("encountered unhandled D-Bus type %s on argument %s",
851             paramspec->dtype, paramspec->name);
852         g_assert_not_reached ();
853     }
854 }
855 
856 static gboolean
set_param_from_value(const TpCMParamSpec * paramspec,GValue * value,const TpCMParamSetter set_param,void * params,GError ** error)857 set_param_from_value (const TpCMParamSpec *paramspec,
858                       GValue *value,
859                       const TpCMParamSetter set_param,
860                       void *params,
861                       GError **error)
862 {
863   if (G_VALUE_TYPE (value) != paramspec->gtype)
864     {
865       DEBUG ("expected type %s for parameter %s, got %s",
866                g_type_name (paramspec->gtype), paramspec->name,
867                G_VALUE_TYPE_NAME (value));
868       g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
869           "expected type %s for account parameter %s, got %s",
870           g_type_name (paramspec->gtype), paramspec->name,
871           G_VALUE_TYPE_NAME (value));
872       return FALSE;
873     }
874 
875   set_param (paramspec, value, params);
876 
877   return TRUE;
878 }
879 
880 static gboolean
parse_parameters(const TpCMParamSpec * paramspec,GHashTable * provided,TpIntset * params_present,const TpCMParamSetter set_param,void * params,GError ** error)881 parse_parameters (const TpCMParamSpec *paramspec,
882                   GHashTable *provided,
883                   TpIntset *params_present,
884                   const TpCMParamSetter set_param,
885                   void *params,
886                   GError **error)
887 {
888   int i;
889   GValue *value;
890 
891   for (i = 0; paramspec[i].name; i++)
892     {
893       value = g_hash_table_lookup (provided, paramspec[i].name);
894 
895       if (value != NULL)
896         {
897           if (!set_param_from_value (&paramspec[i], value, set_param, params,
898                 error))
899             {
900               return FALSE;
901             }
902 
903           tp_intset_add (params_present, i);
904 
905           g_hash_table_remove (provided, paramspec[i].name);
906         }
907     }
908 
909   return TRUE;
910 }
911 
912 
913 /*
914  * tp_base_connection_manager_get_parameters:
915  *
916  * Implements D-Bus method GetParameters
917  * on interface org.freedesktop.Telepathy.ConnectionManager
918  */
919 static void
tp_base_connection_manager_get_parameters(TpSvcConnectionManager * iface,const gchar * proto,DBusGMethodInvocation * context)920 tp_base_connection_manager_get_parameters (TpSvcConnectionManager *iface,
921                                            const gchar *proto,
922                                            DBusGMethodInvocation *context)
923 {
924   GPtrArray *ret;
925   GError *error = NULL;
926   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (iface);
927   guint i;
928   TpBaseProtocol *protocol;
929   const TpCMParamSpec *parameters;
930 
931   g_assert (TP_IS_BASE_CONNECTION_MANAGER (iface));
932   /* a D-Bus method shouldn't be happening til we're on D-Bus */
933   g_assert (self->priv->registered);
934 
935   protocol = tp_base_connection_manager_get_protocol (self, proto, &error);
936 
937   if (protocol == NULL)
938     {
939       dbus_g_method_return_error (context, error);
940       g_error_free (error);
941       return;
942     }
943 
944   parameters = tp_base_protocol_get_parameters (protocol);
945   g_assert (parameters != NULL);
946 
947   ret = g_ptr_array_new ();
948 
949   for (i = 0; parameters[i].name != NULL; i++)
950     {
951       g_ptr_array_add (ret,
952           _tp_cm_param_spec_to_dbus (parameters + i));
953     }
954 
955   tp_svc_connection_manager_return_from_get_parameters (context, ret);
956 
957   for (i = 0; i < ret->len; i++)
958     {
959       tp_value_array_free (g_ptr_array_index (ret, i));
960     }
961 
962   g_ptr_array_unref (ret);
963 }
964 
965 
966 /*
967  * tp_base_connection_manager_list_protocols:
968  *
969  * Implements D-Bus method ListProtocols
970  * on interface org.freedesktop.Telepathy.ConnectionManager
971  */
972 static void
tp_base_connection_manager_list_protocols(TpSvcConnectionManager * iface,DBusGMethodInvocation * context)973 tp_base_connection_manager_list_protocols (TpSvcConnectionManager *iface,
974                                            DBusGMethodInvocation *context)
975 {
976   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (iface);
977   GPtrArray *protocols;
978   GHashTableIter iter;
979   gpointer name;
980 
981   /* a D-Bus method shouldn't be happening til we're on D-Bus */
982   g_assert (self->priv->registered);
983 
984   protocols = g_ptr_array_sized_new (
985       g_hash_table_size (self->priv->protocols) + 1);
986 
987   g_hash_table_iter_init (&iter, self->priv->protocols);
988 
989   while (g_hash_table_iter_next (&iter, &name, NULL))
990     {
991       g_ptr_array_add (protocols, name);
992     }
993 
994   g_ptr_array_add (protocols, NULL);
995 
996   tp_svc_connection_manager_return_from_list_protocols (
997       context, (const gchar **) protocols->pdata);
998   g_ptr_array_unref (protocols);
999 }
1000 
1001 
1002 /*
1003  * tp_base_connection_manager_request_connection:
1004  *
1005  * Implements D-Bus method RequestConnection
1006  * on interface org.freedesktop.Telepathy.ConnectionManager
1007  *
1008  * @error: Used to return a pointer to a GError detailing any error
1009  *         that occurred, D-Bus will throw the error only if this
1010  *         function returns FALSE.
1011  *
1012  * Returns: TRUE if successful, FALSE if an error was thrown.
1013  */
1014 static void
tp_base_connection_manager_request_connection(TpSvcConnectionManager * iface,const gchar * proto,GHashTable * parameters,DBusGMethodInvocation * context)1015 tp_base_connection_manager_request_connection (TpSvcConnectionManager *iface,
1016                                                const gchar *proto,
1017                                                GHashTable *parameters,
1018                                                DBusGMethodInvocation *context)
1019 {
1020   TpBaseConnectionManager *self = TP_BASE_CONNECTION_MANAGER (iface);
1021   TpBaseConnectionManagerClass *cls =
1022     TP_BASE_CONNECTION_MANAGER_GET_CLASS (self);
1023   TpBaseConnectionManagerPrivate *priv = self->priv;
1024   TpBaseConnection *conn;
1025   gchar *bus_name;
1026   gchar *object_path;
1027   GError *error = NULL;
1028   TpBaseProtocol *protocol;
1029 
1030   g_assert (TP_IS_BASE_CONNECTION_MANAGER (iface));
1031 
1032   /* a D-Bus method shouldn't be happening til we're on D-Bus */
1033   g_assert (self->priv->registered);
1034 
1035   if (!tp_connection_manager_check_valid_protocol_name (proto, &error))
1036     goto ERROR;
1037 
1038   protocol = tp_base_connection_manager_get_protocol (self, proto, &error);
1039 
1040   if (protocol == NULL)
1041     goto ERROR;
1042 
1043   conn = tp_base_protocol_new_connection (protocol, parameters, &error);
1044 
1045   if (conn == NULL)
1046     goto ERROR;
1047 
1048   /* register on bus and save bus name and object path */
1049   if (!tp_base_connection_register (conn, cls->cm_dbus_name,
1050         &bus_name, &object_path, &error))
1051     {
1052       DEBUG ("failed: %s", error->message);
1053 
1054       g_object_unref (G_OBJECT (conn));
1055       goto ERROR;
1056     }
1057 
1058   /* bind to status change signals from the connection object */
1059   g_signal_connect_data (conn, "shutdown-finished",
1060       G_CALLBACK (connection_shutdown_finished_cb),
1061       g_object_ref (self), (GClosureNotify) g_object_unref, 0);
1062 
1063   /* store the connection, using a hash table as a set */
1064   g_hash_table_insert (priv->connections, conn, GINT_TO_POINTER(TRUE));
1065 
1066   /* emit the new connection signal */
1067   tp_svc_connection_manager_emit_new_connection (
1068       self, bus_name, object_path, proto);
1069 
1070   tp_svc_connection_manager_return_from_request_connection (
1071       context, bus_name, object_path);
1072 
1073   g_free (bus_name);
1074   g_free (object_path);
1075   return;
1076 
1077 ERROR:
1078   dbus_g_method_return_error (context, error);
1079   g_error_free (error);
1080 }
1081 
1082 /**
1083  * tp_base_connection_manager_register:
1084  * @self: The connection manager implementation
1085  *
1086  * Register the connection manager with an appropriate object path as
1087  * determined from its @cm_dbus_name, and register the appropriate well-known
1088  * bus name.
1089  *
1090  * Returns: %TRUE on success, %FALSE (having emitted a warning to stderr)
1091  *          on failure
1092  */
1093 
1094 gboolean
tp_base_connection_manager_register(TpBaseConnectionManager * self)1095 tp_base_connection_manager_register (TpBaseConnectionManager *self)
1096 {
1097   GError *error = NULL;
1098   TpBaseConnectionManagerClass *cls;
1099   GString *string = NULL;
1100   guint i;
1101   GHashTableIter iter;
1102   gpointer name, protocol;
1103 
1104   g_assert (TP_IS_BASE_CONNECTION_MANAGER (self));
1105   cls = TP_BASE_CONNECTION_MANAGER_GET_CLASS (self);
1106 
1107   if (!tp_base_connection_manager_ensure_dbus (self, &error))
1108     goto except;
1109 
1110   if (cls->protocol_params != NULL)
1111     {
1112       for (i = 0; cls->protocol_params[i].name != NULL; i++)
1113         {
1114           TpBaseProtocol *p = _tp_legacy_protocol_new (self,
1115               cls->protocol_params + i);
1116 
1117           tp_base_connection_manager_add_protocol (self, p);
1118           g_object_unref (p);
1119         }
1120     }
1121 
1122   g_assert (self->priv->dbus_daemon != NULL);
1123 
1124   string = g_string_new (TP_CM_BUS_NAME_BASE);
1125   g_string_append (string, cls->cm_dbus_name);
1126 
1127   if (!tp_dbus_daemon_request_name (self->priv->dbus_daemon, string->str,
1128         TRUE, &error))
1129     goto except;
1130 
1131   g_string_assign (string, TP_CM_OBJECT_PATH_BASE);
1132   g_string_append (string, cls->cm_dbus_name);
1133   tp_dbus_daemon_register_object (self->priv->dbus_daemon, string->str, self);
1134 
1135   g_hash_table_iter_init (&iter, self->priv->protocols);
1136 
1137   while (g_hash_table_iter_next (&iter, &name, &protocol))
1138     {
1139       TpBaseProtocolClass *protocol_class =
1140         TP_BASE_PROTOCOL_GET_CLASS (protocol);
1141 
1142       if (!tp_connection_manager_check_valid_protocol_name (name, &error))
1143         {
1144           g_critical ("%s", error->message);
1145           goto except;
1146         }
1147 
1148       /* don't export uninformative "stub" protocol objects on D-Bus */
1149       if (protocol_class->is_stub)
1150         continue;
1151 
1152       g_string_assign (string, TP_CM_OBJECT_PATH_BASE);
1153       g_string_append (string, cls->cm_dbus_name);
1154       g_string_append_c (string, '/');
1155       g_string_append (string, name);
1156 
1157       g_strdelimit (string->str, "-", '_');
1158 
1159       tp_dbus_daemon_register_object (self->priv->dbus_daemon, string->str,
1160           protocol);
1161     }
1162 
1163   g_string_free (string, TRUE);
1164 
1165   self->priv->registered = TRUE;
1166 
1167   return TRUE;
1168 
1169 except:
1170   WARNING ("Couldn't claim bus name. If you are trying to debug this "
1171       "connection manager, disable all accounts and kill any running "
1172       "copies of this CM, then try again. %s", error->message);
1173   g_error_free (error);
1174 
1175   if (string != NULL)
1176     g_string_free (string, TRUE);
1177 
1178   return FALSE;
1179 }
1180 
1181 static void
service_iface_init(gpointer g_iface,gpointer iface_data)1182 service_iface_init (gpointer g_iface, gpointer iface_data)
1183 {
1184   TpSvcConnectionManagerClass *klass = g_iface;
1185 
1186 #define IMPLEMENT(x) tp_svc_connection_manager_implement_##x (klass, \
1187     tp_base_connection_manager_##x)
1188   IMPLEMENT(get_parameters);
1189   IMPLEMENT(list_protocols);
1190   IMPLEMENT(request_connection);
1191 #undef IMPLEMENT
1192 }
1193 
1194 /**
1195  * tp_base_connection_manager_get_dbus_daemon:
1196  * @self: the connection manager
1197  *
1198  * <!-- -->
1199  *
1200  * Returns: (transfer none): the value of the
1201  *  #TpBaseConnectionManager:dbus-daemon property. The caller must reference
1202  *  the returned object with g_object_ref() if it will be kept.
1203  *
1204  * Since: 0.11.3
1205  */
1206 TpDBusDaemon *
tp_base_connection_manager_get_dbus_daemon(TpBaseConnectionManager * self)1207 tp_base_connection_manager_get_dbus_daemon (TpBaseConnectionManager *self)
1208 {
1209   g_return_val_if_fail (TP_IS_BASE_CONNECTION_MANAGER (self), NULL);
1210 
1211   return self->priv->dbus_daemon;
1212 }
1213 
1214 /**
1215  * tp_base_connection_manager_add_protocol:
1216  * @self: a connection manager object which has not yet registered on D-Bus
1217  *  (i.e. tp_base_connection_manager_register() must not have been called)
1218  * @protocol: a protocol object, which must not have the same protocol name as
1219  *  any that has already been added
1220  *
1221  * Add a protocol object to the set of supported protocols.
1222  */
1223 void
tp_base_connection_manager_add_protocol(TpBaseConnectionManager * self,TpBaseProtocol * protocol)1224 tp_base_connection_manager_add_protocol (TpBaseConnectionManager *self,
1225     TpBaseProtocol *protocol)
1226 {
1227   g_return_if_fail (TP_IS_BASE_CONNECTION_MANAGER (self));
1228   g_return_if_fail (!self->priv->registered);
1229   g_return_if_fail (TP_IS_BASE_PROTOCOL (protocol));
1230 
1231   g_hash_table_insert (self->priv->protocols,
1232       g_strdup (tp_base_protocol_get_name (protocol)),
1233       g_object_ref (protocol));
1234 }
1235