1 /* Map containing registered Telepathy clients
2 *
3 * Copyright © 2007-2011 Nokia Corporation.
4 * Copyright © 2009-2011 Collabora Ltd.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * version 2.1 as published by the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 *
20 */
21
22 #include "config.h"
23
24 #include "client-registry.h"
25
26 #include <telepathy-glib/telepathy-glib.h>
27
28 #include "mcd-debug.h"
29
30 #include <dbus/dbus.h>
31 #include <dbus/dbus-glib.h>
32 #include <dbus/dbus-glib-lowlevel.h>
33
34 G_DEFINE_TYPE (McdClientRegistry, _mcd_client_registry, G_TYPE_OBJECT)
35
36 enum
37 {
38 PROP_0,
39 PROP_DBUS_DAEMON
40 };
41
42 enum
43 {
44 S_CLIENT_ADDED,
45 S_READY,
46 N_SIGNALS
47 };
48
49 static guint signals[N_SIGNALS] = { 0 };
50
51 struct _McdClientRegistryPrivate
52 {
53 /* hash table containing clients
54 * owned gchar * well_known_name -> owned McdClientProxy */
55 GHashTable *clients;
56
57 TpDBusDaemon *dbus_daemon;
58
59 /* We don't want to start dispatching until startup has finished. This
60 * is defined as:
61 * - activatable clients have been enumerated (ListActivatableNames)
62 * (1 lock)
63 * - running clients have been enumerated (ListNames) (1 lock)
64 * - each client found that way is ready (1 lock per client)
65 * When nothing more is stopping us from dispatching channels, we signal
66 * ready.
67 * */
68 gsize startup_lock;
69 gboolean startup_completed;
70 };
71
72 static void
_mcd_client_registry_inc_startup_lock(McdClientRegistry * self)73 _mcd_client_registry_inc_startup_lock (McdClientRegistry *self)
74 {
75 if (!self->priv->startup_completed)
76 {
77 DEBUG ("%" G_GSIZE_FORMAT " -> %" G_GSIZE_FORMAT,
78 self->priv->startup_lock, self->priv->startup_lock + 1);
79 g_return_if_fail (self->priv->startup_lock > 0);
80 self->priv->startup_lock++;
81 }
82 }
83
84 static void
_mcd_client_registry_dec_startup_lock(McdClientRegistry * self)85 _mcd_client_registry_dec_startup_lock (McdClientRegistry *self)
86 {
87 if (self->priv->startup_completed)
88 return;
89
90 DEBUG ("%" G_GSIZE_FORMAT " -> %" G_GSIZE_FORMAT,
91 self->priv->startup_lock, self->priv->startup_lock - 1);
92
93 g_return_if_fail (self->priv->startup_lock > 0);
94
95 self->priv->startup_lock--;
96
97 if (self->priv->startup_lock == 0)
98 {
99 self->priv->startup_completed = TRUE;
100 g_signal_emit (self, signals[S_READY], 0);
101 }
102 }
103
104 static void mcd_client_registry_ready_cb (McdClientProxy *client,
105 McdClientRegistry *self);
106 static void mcd_client_registry_gone_cb (McdClientProxy *client,
107 McdClientRegistry *self);
108
109 static void
_mcd_client_registry_found_name(McdClientRegistry * self,const gchar * well_known_name,const gchar * unique_name_if_known,gboolean activatable)110 _mcd_client_registry_found_name (McdClientRegistry *self,
111 const gchar *well_known_name,
112 const gchar *unique_name_if_known,
113 gboolean activatable)
114 {
115 McdClientProxy *client;
116
117 if (!g_str_has_prefix (well_known_name, TP_CLIENT_BUS_NAME_BASE))
118 {
119 /* This is not a Telepathy Client */
120 return;
121 }
122
123 if (!_mcd_client_check_valid_name (
124 well_known_name + MC_CLIENT_BUS_NAME_BASE_LEN, NULL))
125 {
126 /* This is probably meant to be a Telepathy Client, but it's not */
127 DEBUG ("Ignoring invalid Client name: %s",
128 well_known_name + MC_CLIENT_BUS_NAME_BASE_LEN);
129 return;
130 }
131
132 client = g_hash_table_lookup (self->priv->clients, well_known_name);
133
134 if (client != NULL)
135 {
136 if (activatable)
137 {
138 /* We already knew that it was active, but now we also know that
139 * it is activatable */
140 _mcd_client_proxy_set_activatable (client);
141 }
142 else if (unique_name_if_known != NULL)
143 {
144 /* We already knew that it was activatable, but now we also know
145 * that it is active */
146 _mcd_client_proxy_set_active (client, unique_name_if_known);
147 }
148
149 return;
150 }
151
152 DEBUG ("Registering client %s", well_known_name);
153
154 client = _mcd_client_proxy_new (self->priv->dbus_daemon,
155 well_known_name, unique_name_if_known, activatable);
156 g_hash_table_insert (self->priv->clients, g_strdup (well_known_name),
157 client);
158
159 /* paired with one in mcd_client_registry_ready_cb, when the
160 * McdClientProxy is ready */
161 _mcd_client_registry_inc_startup_lock (self);
162
163 g_signal_connect (client, "ready",
164 G_CALLBACK (mcd_client_registry_ready_cb),
165 self);
166
167 g_signal_connect (client, "gone",
168 G_CALLBACK (mcd_client_registry_gone_cb),
169 self);
170
171 g_signal_emit (self, signals[S_CLIENT_ADDED], 0, client);
172 }
173
174 McdClientProxy *
_mcd_client_registry_lookup(McdClientRegistry * self,const gchar * well_known_name)175 _mcd_client_registry_lookup (McdClientRegistry *self,
176 const gchar *well_known_name)
177 {
178 g_return_val_if_fail (MCD_IS_CLIENT_REGISTRY (self), NULL);
179 return g_hash_table_lookup (self->priv->clients, well_known_name);
180 }
181
182 static void
mcd_client_registry_disconnect_client_signals(gpointer k G_GNUC_UNUSED,gpointer v,gpointer data)183 mcd_client_registry_disconnect_client_signals (gpointer k G_GNUC_UNUSED,
184 gpointer v,
185 gpointer data)
186 {
187 g_signal_handlers_disconnect_by_func (v, mcd_client_registry_ready_cb, data);
188 g_signal_handlers_disconnect_by_func (v, mcd_client_registry_gone_cb, data);
189
190 if (!_mcd_client_proxy_is_ready (v))
191 {
192 /* we'll never receive the ready signal now, so release the lock that
193 * it would otherwise have released */
194 DEBUG ("client %s disappeared before it became ready - treating it "
195 "as ready for our purposes", tp_proxy_get_bus_name (v));
196 mcd_client_registry_ready_cb (v, data);
197 }
198 }
199
200 static void
_mcd_client_registry_remove(McdClientRegistry * self,const gchar * well_known_name)201 _mcd_client_registry_remove (McdClientRegistry *self,
202 const gchar *well_known_name)
203 {
204 McdClientProxy *client;
205
206 client = g_hash_table_lookup (self->priv->clients, well_known_name);
207
208 if (client != NULL)
209 {
210 mcd_client_registry_disconnect_client_signals (NULL,
211 client, self);
212 }
213
214 g_hash_table_remove (self->priv->clients, well_known_name);
215 }
216
_mcd_client_registry_init_hash_iter(McdClientRegistry * self,GHashTableIter * iter)217 void _mcd_client_registry_init_hash_iter (McdClientRegistry *self,
218 GHashTableIter *iter)
219 {
220 g_return_if_fail (MCD_IS_CLIENT_REGISTRY (self));
221 g_hash_table_iter_init (iter, self->priv->clients);
222 }
223
224 static void
_mcd_client_registry_init(McdClientRegistry * self)225 _mcd_client_registry_init (McdClientRegistry *self)
226 {
227 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MCD_TYPE_CLIENT_REGISTRY,
228 McdClientRegistryPrivate);
229
230 self->priv->startup_completed = FALSE;
231 /* the ListNames call we'll make in _constructed is the initial lock */
232 self->priv->startup_lock = 1;
233 self->priv->clients = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
234 g_object_unref);
235 }
236
237 static void
mcd_client_registry_list_activatable_names_cb(TpDBusDaemon * proxy,const gchar ** names,const GError * error,gpointer user_data,GObject * weak_object)238 mcd_client_registry_list_activatable_names_cb (TpDBusDaemon *proxy,
239 const gchar **names,
240 const GError *error,
241 gpointer user_data,
242 GObject *weak_object)
243 {
244 McdClientRegistry *self = MCD_CLIENT_REGISTRY (weak_object);
245
246 if (error != NULL)
247 {
248 DEBUG ("ListActivatableNames returned error, assuming none: %s %d: %s",
249 g_quark_to_string (error->domain), error->code, error->message);
250 }
251 else if (names != NULL)
252 {
253 const gchar **iter = names;
254
255 DEBUG ("ListActivatableNames returned");
256
257 while (*iter != NULL)
258 {
259 _mcd_client_registry_found_name (self, *iter, NULL, TRUE);
260 iter++;
261 }
262 }
263
264 /* paired with the lock taken when the McdClientRegistry was constructed */
265 _mcd_client_registry_dec_startup_lock (self);
266 }
267
268 static void
mcd_client_registry_list_names_cb(TpDBusDaemon * proxy,const gchar ** names,const GError * error,gpointer user_data,GObject * weak_object)269 mcd_client_registry_list_names_cb (TpDBusDaemon *proxy,
270 const gchar **names,
271 const GError *error,
272 gpointer user_data,
273 GObject *weak_object)
274 {
275 McdClientRegistry *self = MCD_CLIENT_REGISTRY (weak_object);
276
277 if (error != NULL)
278 {
279 DEBUG ("ListNames returned error, assuming none: %s %d: %s",
280 g_quark_to_string (error->domain), error->code, error->message);
281 }
282 else if (names != NULL)
283 {
284 const gchar **iter = names;
285
286 DEBUG ("ListNames returned");
287
288 while (*iter != NULL)
289 {
290 _mcd_client_registry_found_name (self, *iter, NULL, FALSE);
291 iter++;
292 }
293 }
294
295 tp_cli_dbus_daemon_call_list_activatable_names (proxy, -1,
296 mcd_client_registry_list_activatable_names_cb,
297 NULL, NULL, weak_object);
298 /* deliberately not calling _mcd_client_registry_dec_startup_lock here -
299 * this function is "lock-neutral", similarly to list_names_cb (we would
300 * take a lock for ListActivatableNames then release the one used for
301 * ReloadConfig), so simplify by doing nothing */
302 }
303
304 static DBusHandlerResult
mcd_client_registry_name_owner_filter(DBusConnection * conn,DBusMessage * msg,gpointer data)305 mcd_client_registry_name_owner_filter (DBusConnection *conn,
306 DBusMessage *msg,
307 gpointer data)
308 {
309 McdClientRegistry *self = MCD_CLIENT_REGISTRY (data);
310
311 /* make sure this is the right kind of signal: */
312 if (dbus_message_is_signal (msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
313 {
314 const gchar *dbus_name = NULL;
315 const gchar *old_owner = NULL;
316 const gchar *new_owner = NULL;
317 gboolean ok = dbus_message_get_args (msg, NULL,
318 DBUS_TYPE_STRING, &dbus_name,
319 DBUS_TYPE_STRING, &old_owner,
320 DBUS_TYPE_STRING, &new_owner,
321 DBUS_TYPE_INVALID);
322
323 /* could not unpack args -> invalid -> stop processing right here */
324 if (!ok)
325 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
326
327 if (tp_str_empty (old_owner) && !tp_str_empty (new_owner))
328 _mcd_client_registry_found_name (self, dbus_name, new_owner, FALSE);
329 }
330
331 /* in case somebody else is also interested */
332 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
333 }
334
335 static void
watch_clients(McdClientRegistry * self)336 watch_clients (McdClientRegistry *self)
337 {
338 TpDBusDaemon *dbus_daemon = self->priv->dbus_daemon;
339 DBusGConnection *gconn = tp_proxy_get_dbus_connection (dbus_daemon);
340 DBusConnection *dconn = dbus_g_connection_get_connection (gconn);
341 DBusError error = { 0 };
342
343 #define MATCH_ITEM(t,x) #t "='" x "'"
344
345 #define NAME_OWNER_RULE \
346 MATCH_ITEM (type, "signal") "," \
347 MATCH_ITEM (sender, DBUS_SERVICE_DBUS) "," \
348 MATCH_ITEM (interface, DBUS_INTERFACE_DBUS) "," \
349 MATCH_ITEM (member, "NameOwnerChanged")
350
351 #define CLIENT_MATCH_RULE \
352 NAME_OWNER_RULE "," \
353 MATCH_ITEM (arg0namespace, "org.freedesktop.Telepathy.Client")
354
355 if (!dbus_connection_add_filter (dconn, mcd_client_registry_name_owner_filter,
356 self, NULL))
357 g_critical ("Could not add filter for NameOwnerChanged (out of memory?)");
358
359 dbus_error_init (&error);
360
361 dbus_bus_add_match (dconn, CLIENT_MATCH_RULE, &error);
362 if (dbus_error_is_set (&error))
363 {
364 DEBUG ("Could not add client names match rule (D-Bus 1.6 required): %s",
365 error.message);
366
367 dbus_error_free (&error);
368
369 dbus_bus_add_match (dconn, NAME_OWNER_RULE, &error);
370 if (dbus_error_is_set (&error))
371 {
372 g_critical ("Could not add all dbus names match rule: %s",
373 error.message);
374 dbus_error_free (&error);
375 }
376 }
377 }
378
379 static void
mcd_client_registry_constructed(GObject * object)380 mcd_client_registry_constructed (GObject *object)
381 {
382 McdClientRegistry *self = MCD_CLIENT_REGISTRY (object);
383 void (*chain_up) (GObject *) =
384 G_OBJECT_CLASS (_mcd_client_registry_parent_class)->constructed;
385
386 if (chain_up != NULL)
387 chain_up (object);
388
389 g_return_if_fail (self->priv->dbus_daemon != NULL);
390
391 DEBUG ("Starting to look for clients");
392
393 watch_clients (self);
394
395 tp_cli_dbus_daemon_call_list_names (self->priv->dbus_daemon, -1,
396 mcd_client_registry_list_names_cb, NULL, NULL, object);
397 }
398
399 static void
mcd_client_registry_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)400 mcd_client_registry_set_property (GObject *object,
401 guint prop_id,
402 const GValue *value,
403 GParamSpec *pspec)
404 {
405 McdClientRegistry *self = MCD_CLIENT_REGISTRY (object);
406
407 switch (prop_id)
408 {
409 case PROP_DBUS_DAEMON:
410 g_assert (self->priv->dbus_daemon == NULL); /* it's construct-only */
411 self->priv->dbus_daemon = TP_DBUS_DAEMON (g_value_dup_object (value));
412 break;
413
414 default:
415 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
416 break;
417 }
418 }
419
420 static void
mcd_client_registry_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)421 mcd_client_registry_get_property (GObject *object,
422 guint prop_id,
423 GValue *value,
424 GParamSpec *pspec)
425 {
426 McdClientRegistry *self = MCD_CLIENT_REGISTRY (object);
427
428 switch (prop_id)
429 {
430 case PROP_DBUS_DAEMON:
431 g_value_set_object (value, self->priv->dbus_daemon);
432 break;
433
434 default:
435 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
436 break;
437 }
438 }
439
440 static void
mcd_client_registry_dispose(GObject * object)441 mcd_client_registry_dispose (GObject *object)
442 {
443 McdClientRegistry *self = MCD_CLIENT_REGISTRY (object);
444 void (*chain_up) (GObject *) =
445 G_OBJECT_CLASS (_mcd_client_registry_parent_class)->dispose;
446
447 if (self->priv->dbus_daemon != NULL)
448 {
449 DBusGConnection *gconn =
450 tp_proxy_get_dbus_connection (self->priv->dbus_daemon);
451 DBusConnection *dconn = dbus_g_connection_get_connection (gconn);
452
453 dbus_connection_remove_filter (dconn,
454 mcd_client_registry_name_owner_filter,
455 self);
456 }
457
458 tp_clear_object (&self->priv->dbus_daemon);
459
460 if (self->priv->clients != NULL)
461 {
462 g_hash_table_foreach (self->priv->clients,
463 mcd_client_registry_disconnect_client_signals, self);
464
465 }
466
467 tp_clear_pointer (&self->priv->clients, g_hash_table_unref);
468
469 if (chain_up != NULL)
470 chain_up (object);
471 }
472
473 static void
_mcd_client_registry_class_init(McdClientRegistryClass * cls)474 _mcd_client_registry_class_init (McdClientRegistryClass *cls)
475 {
476 GObjectClass *object_class = G_OBJECT_CLASS (cls);
477
478 g_type_class_add_private (cls, sizeof (McdClientRegistryPrivate));
479
480 object_class->constructed = mcd_client_registry_constructed;
481 object_class->get_property = mcd_client_registry_get_property;
482 object_class->set_property = mcd_client_registry_set_property;
483 object_class->dispose = mcd_client_registry_dispose;
484
485 g_object_class_install_property (object_class, PROP_DBUS_DAEMON,
486 g_param_spec_object ("dbus-daemon", "D-Bus daemon", "D-Bus daemon",
487 TP_TYPE_DBUS_DAEMON,
488 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
489
490 signals[S_CLIENT_ADDED] = g_signal_new ("client-added",
491 G_OBJECT_CLASS_TYPE (cls),
492 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
493 0, NULL, NULL,
494 g_cclosure_marshal_VOID__OBJECT,
495 G_TYPE_NONE, 1, MCD_TYPE_CLIENT_PROXY);
496
497 signals[S_READY] = g_signal_new ("ready",
498 G_OBJECT_CLASS_TYPE (cls),
499 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
500 0, NULL, NULL,
501 g_cclosure_marshal_VOID__VOID,
502 G_TYPE_NONE, 0);
503 }
504
505 McdClientRegistry *
_mcd_client_registry_new(TpDBusDaemon * dbus_daemon)506 _mcd_client_registry_new (TpDBusDaemon *dbus_daemon)
507 {
508 return g_object_new (MCD_TYPE_CLIENT_REGISTRY,
509 "dbus-daemon", dbus_daemon,
510 NULL);
511 }
512
513 static void
mcd_client_registry_ready_cb(McdClientProxy * client,McdClientRegistry * self)514 mcd_client_registry_ready_cb (McdClientProxy *client,
515 McdClientRegistry *self)
516 {
517 DEBUG ("%s", tp_proxy_get_bus_name (client));
518
519 g_signal_handlers_disconnect_by_func (client,
520 mcd_client_registry_ready_cb, self);
521
522 /* paired with the one in _mcd_client_registry_found_name */
523 _mcd_client_registry_dec_startup_lock (self);
524 }
525
526 static void
mcd_client_registry_gone_cb(McdClientProxy * client,McdClientRegistry * self)527 mcd_client_registry_gone_cb (McdClientProxy *client,
528 McdClientRegistry *self)
529 {
530 _mcd_client_registry_remove (self, tp_proxy_get_bus_name (client));
531 }
532
533 GPtrArray *
_mcd_client_registry_dup_client_caps(McdClientRegistry * self)534 _mcd_client_registry_dup_client_caps (McdClientRegistry *self)
535 {
536 GPtrArray *vas;
537 GHashTableIter iter;
538 gpointer p;
539
540 g_return_val_if_fail (MCD_IS_CLIENT_REGISTRY (self), NULL);
541
542 vas = g_ptr_array_sized_new (g_hash_table_size (self->priv->clients));
543
544 g_hash_table_iter_init (&iter, self->priv->clients);
545
546 while (g_hash_table_iter_next (&iter, NULL, &p))
547 {
548 g_ptr_array_add (vas,
549 _mcd_client_proxy_dup_handler_capabilities (p));
550 }
551
552 return vas;
553 }
554
555 gboolean
_mcd_client_registry_is_ready(McdClientRegistry * self)556 _mcd_client_registry_is_ready (McdClientRegistry *self)
557 {
558 g_return_val_if_fail (MCD_IS_CLIENT_REGISTRY (self), FALSE);
559 return self->priv->startup_completed;
560 }
561
562 typedef struct
563 {
564 McdClientProxy *client;
565 gboolean bypass;
566 gsize quality;
567 } PossibleHandler;
568
569 static gint
possible_handler_cmp(gconstpointer a_,gconstpointer b_)570 possible_handler_cmp (gconstpointer a_,
571 gconstpointer b_)
572 {
573 const PossibleHandler *a = a_;
574 const PossibleHandler *b = b_;
575
576 if (a->bypass)
577 {
578 if (!b->bypass)
579 {
580 /* BypassApproval wins, so a is better than b */
581 return 1;
582 }
583 }
584 else if (b->bypass)
585 {
586 /* BypassApproval wins, so b is better than a */
587 return -1;
588 }
589
590 if (a->quality < b->quality)
591 {
592 return -1;
593 }
594
595 if (b->quality < a->quality)
596 {
597 return 1;
598 }
599
600 return 0;
601 }
602
603 GList *
_mcd_client_registry_list_possible_handlers(McdClientRegistry * self,const gchar * preferred_handler,GVariant * request_props,TpChannel * channel,const gchar * must_have_unique_name)604 _mcd_client_registry_list_possible_handlers (McdClientRegistry *self,
605 const gchar *preferred_handler,
606 GVariant *request_props,
607 TpChannel *channel,
608 const gchar *must_have_unique_name)
609 {
610 GList *handlers = NULL;
611 GList *handlers_iter;
612 GHashTableIter client_iter;
613 gpointer client_p;
614
615 _mcd_client_registry_init_hash_iter (self, &client_iter);
616
617 while (g_hash_table_iter_next (&client_iter, NULL, &client_p))
618 {
619 McdClientProxy *client = MCD_CLIENT_PROXY (client_p);
620 gsize quality;
621
622 if (must_have_unique_name != NULL &&
623 tp_strdiff (must_have_unique_name,
624 _mcd_client_proxy_get_unique_name (client)))
625 {
626 /* we're trying to redispatch to an existing handler, and this is
627 * not it */
628 continue;
629 }
630
631 if (!tp_proxy_has_interface_by_id (client,
632 TP_IFACE_QUARK_CLIENT_HANDLER))
633 {
634 /* not a handler at all */
635 continue;
636 }
637
638 if (channel == NULL)
639 {
640 /* We don't know the channel's properties (the next part will not
641 * execute), so we must work out the quality of match from the
642 * channel request. We can assume that the request will return one
643 * channel, with the requested properties, plus Requested == TRUE.
644 */
645 g_assert (request_props != NULL);
646 quality = _mcd_client_match_filters (request_props,
647 _mcd_client_proxy_get_handler_filters (client), TRUE);
648 }
649 else
650 {
651 GVariant *properties;
652
653 g_assert (TP_IS_CHANNEL (channel));
654 properties = tp_channel_dup_immutable_properties (channel);
655 quality = _mcd_client_match_filters (properties,
656 _mcd_client_proxy_get_handler_filters (client), FALSE);
657 g_variant_unref (properties);
658 }
659
660 if (quality > 0)
661 {
662 PossibleHandler *ph = g_slice_new0 (PossibleHandler);
663
664 ph->client = client;
665 ph->bypass = _mcd_client_proxy_get_bypass_approval (client);
666 ph->quality = quality;
667
668 handlers = g_list_prepend (handlers, ph);
669 }
670 }
671
672 /* if no handlers can take them all, fail - unless we're operating on
673 * a request that specified a preferred handler, in which case assume
674 * it's suitable */
675 if (handlers == NULL)
676 {
677 McdClientProxy *client;
678
679 if (preferred_handler == NULL || preferred_handler[0] == '\0')
680 {
681 return NULL;
682 }
683
684 client = _mcd_client_registry_lookup (self, preferred_handler);
685
686 if (client == NULL)
687 {
688 return NULL;
689 }
690
691 return g_list_append (NULL, client);
692 }
693
694 /* We have at least one handler that can take the whole batch. Sort
695 * the possible handlers, most preferred first (i.e. sort by ascending
696 * quality then reverse) */
697 handlers = g_list_sort (handlers, possible_handler_cmp);
698 handlers = g_list_reverse (handlers);
699
700 /* convert in-place from a list of PossibleHandler to a list of
701 * McdClientProxy */
702 for (handlers_iter = handlers;
703 handlers_iter != NULL;
704 handlers_iter = handlers_iter->next)
705 {
706 PossibleHandler *ph = handlers_iter->data;
707
708 handlers_iter->data = ph->client;
709 g_slice_free (PossibleHandler, ph);
710 }
711
712 return handlers;
713 }
714
715 TpDBusDaemon *
_mcd_client_registry_get_dbus_daemon(McdClientRegistry * self)716 _mcd_client_registry_get_dbus_daemon (McdClientRegistry *self)
717 {
718 g_return_val_if_fail (MCD_IS_CLIENT_REGISTRY (self), NULL);
719 return self->priv->dbus_daemon;
720 }
721