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 (¶mspec[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