2 * Copyright (C) 2009 Nokia Corporation.
3 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
4 * Copyright (C) 2013 Intel Corporation.
6 * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
7 * Jorn Baayen <jorn@openedhand.com>
8 * Ludovic Ferrandis <ludovic.ferrandis@intel.com>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 * SECTION:gupnp-context-manager
28 * @short_description: Manages #GUPnPContext objects.
30 * A Utility class that takes care of creation and destruction of
31 * #GUPnPContext objects for all available network interfaces as they go up
32 * (connect) and down (disconnect), respectively.
41 #include <libsoup/soup-address.h>
43 #include <glib/gstdio.h>
46 #include "gupnp-marshal.h"
48 #include "gupnp-unix-context-manager.h"
50 G_DEFINE_ABSTRACT_TYPE (GUPnPContextManager,
51 gupnp_context_manager,
54 struct _GUPnPContextManagerPrivate {
57 GUPnPContextManager *impl;
59 GList *objects; /* control points and root devices */
60 GList *blacklisted; /* Blacklisted Context */
62 GUPnPWhiteList *white_list;
78 static guint signals[SIGNAL_LAST];
81 on_context_available (GUPnPContextManager *manager,
82 GUPnPContext *context,
83 G_GNUC_UNUSED gpointer *user_data)
85 GUPnPWhiteList *white_list;
87 white_list = manager->priv->white_list;
89 /* Try to catch the notification, only if the white list
90 * is enabled, not empty and the context doesn't match */
91 if (!gupnp_white_list_is_empty (white_list) &&
92 gupnp_white_list_get_enabled (white_list) &&
93 !gupnp_white_list_check_context (white_list, context)) {
94 /* If the conext doesn't match, block the notification
95 * and disable the context */
96 g_signal_stop_emission_by_name (manager, "context-available");
98 /* Make sure we don't send anything on now blocked network */
99 g_object_set (context, "active", FALSE, NULL);
101 /* Save it in case we need to re-enable it */
102 manager->priv->blacklisted = g_list_prepend (
103 manager->priv->blacklisted,
104 g_object_ref (context));
109 on_context_unavailable (GUPnPContextManager *manager,
110 GUPnPContext *context,
111 G_GNUC_UNUSED gpointer *user_data)
116 /* Make sure we don't send anything on now unavailable network */
117 g_object_set (context, "active", FALSE, NULL);
119 /* Unref all associated objects */
120 l = manager->priv->objects;
123 GUPnPContext *obj_context = NULL;
125 if (GUPNP_IS_CONTROL_POINT (l->data)) {
126 GUPnPControlPoint *cp;
128 cp = GUPNP_CONTROL_POINT (l->data);
129 obj_context = gupnp_control_point_get_context (cp);
130 } else if (GUPNP_IS_ROOT_DEVICE (l->data)) {
131 GUPnPDeviceInfo *info;
133 info = GUPNP_DEVICE_INFO (l->data);
134 obj_context = gupnp_device_info_get_context (info);
136 g_assert_not_reached ();
139 if (context == obj_context) {
140 GList *next = l->next;
142 g_object_unref (l->data);
144 manager->priv->objects =
145 g_list_delete_link (manager->priv->objects, l);
152 black = g_list_find (manager->priv->blacklisted, context);
155 g_signal_stop_emission_by_name (manager, "context-unavailable");
157 g_object_unref (black->data);
158 manager->priv->blacklisted =
159 g_list_delete_link (manager->priv->blacklisted, black);
164 gupnp_context_manager_filter_context (GUPnPWhiteList *white_list,
165 GUPnPContextManager *manager,
172 GUPnPContext *context;
173 GSSDPResourceBrowser *browser;
175 obj = manager->priv->objects;
176 blk = manager->priv->blacklisted;
178 while (obj != NULL) {
179 if (!GUPNP_IS_CONTROL_POINT (obj->data))
182 /* If the white list is empty, treat it as disabled */
184 /* Filter out context */
185 context = gupnp_control_point_get_context (obj->data);
186 match = gupnp_white_list_check_context (white_list,
189 /* Re-activate all context, if needed */
193 browser = GSSDP_RESOURCE_BROWSER (obj->data);
194 gssdp_resource_browser_set_active (browser, match);
197 (void) gssdp_resource_browser_rescan (browser);
202 while (blk != NULL) {
203 /* If the white list is empty, treat it as disabled */
205 /* Filter out context */
206 match = gupnp_white_list_check_context (white_list,
209 /* Re-activate all context, if needed */
218 g_object_set (blk->data, "active", TRUE, NULL);
220 g_signal_emit_by_name (manager, "context-available", blk->data);
222 g_object_unref (blk->data);
223 manager->priv->blacklisted = g_list_delete_link (
224 manager->priv->blacklisted,
231 on_white_list_change_cb (GUPnPWhiteList *white_list,
235 GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
239 enabled = gupnp_white_list_get_enabled (white_list);
240 is_empty = gupnp_white_list_is_empty (white_list);
243 gupnp_context_manager_filter_context (white_list,
249 on_white_list_enabled_cb (GUPnPWhiteList *white_list,
253 GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
257 enabled = gupnp_white_list_get_enabled (white_list);
258 is_empty = gupnp_white_list_is_empty (white_list);
261 gupnp_context_manager_filter_context (white_list,
267 gupnp_context_manager_init (GUPnPContextManager *manager)
270 G_TYPE_INSTANCE_GET_PRIVATE (manager,
271 GUPNP_TYPE_CONTEXT_MANAGER,
272 GUPnPContextManagerPrivate);
274 manager->priv->white_list = gupnp_white_list_new ();
276 g_signal_connect_after (manager->priv->white_list, "notify::entries",
277 G_CALLBACK (on_white_list_change_cb), manager);
279 g_signal_connect_after (manager->priv->white_list, "notify::enabled",
280 G_CALLBACK (on_white_list_enabled_cb), manager);
284 gupnp_context_manager_set_property (GObject *object,
289 GUPnPContextManager *manager;
290 GUPnPContextManagerPrivate *priv;
292 manager = GUPNP_CONTEXT_MANAGER (object);
293 priv = manager->priv;
295 switch (property_id) {
297 priv->port = g_value_get_uint (value);
299 case PROP_MAIN_CONTEXT:
300 if (g_value_get_pointer (value) != NULL)
301 g_warning ("GUPnPContextManager:main-context is "
303 "g_main_context_push_thread_default()"
307 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
313 gupnp_context_manager_get_property (GObject *object,
318 GUPnPContextManager *manager;
320 manager = GUPNP_CONTEXT_MANAGER (object);
322 switch (property_id) {
324 g_value_set_uint (value, manager->priv->port);
326 case PROP_MAIN_CONTEXT:
327 g_warning ("GUPnPContextManager:main-context is deprecated. "
328 "Use g_main_context_push_thread_default()"
330 g_value_set_pointer (value,
331 g_main_context_get_thread_default ());
333 case PROP_WHITE_LIST:
334 g_value_set_object (value, manager->priv->white_list);
337 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
343 gupnp_context_manager_dispose (GObject *object)
345 GUPnPContextManager *manager;
347 GObjectClass *object_class;
349 manager = GUPNP_CONTEXT_MANAGER (object);
350 wl = manager->priv->white_list;
352 g_signal_handlers_disconnect_by_func (wl,
353 on_white_list_enabled_cb,
356 g_signal_handlers_disconnect_by_func (wl,
357 on_white_list_change_cb,
360 g_list_free_full (manager->priv->objects, g_object_unref);
361 manager->priv->objects = NULL;
362 g_list_free_full (manager->priv->blacklisted, g_object_unref);
363 manager->priv->blacklisted = NULL;
367 manager->priv->white_list = NULL;
371 object_class = G_OBJECT_CLASS (gupnp_context_manager_parent_class);
372 object_class->dispose (object);
376 gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
378 GObjectClass *object_class;
380 object_class = G_OBJECT_CLASS (klass);
382 object_class->set_property = gupnp_context_manager_set_property;
383 object_class->get_property = gupnp_context_manager_get_property;
384 object_class->dispose = gupnp_context_manager_dispose;
386 g_type_class_add_private (klass, sizeof (GUPnPContextManagerPrivate));
389 * GSSDPClient:main-context:
391 * The #GMainContext to pass to created #GUPnPContext objects. Set to
392 * NULL to use the default.
394 * Deprecated: 0.17.2: Use g_main_context_push_thread_default()
397 g_object_class_install_property
403 "GMainContext to pass to created GUPnPContext"
405 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
406 G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
407 G_PARAM_STATIC_BLURB));
410 * GUPnPContextManager:port:
412 * Port the contexts listen on, or 0 if you don't care what
413 * port is used by #GUPnPContext objects created by this object.
415 g_object_class_install_property
418 g_param_spec_uint ("port",
420 "Port to create contexts for",
421 0, G_MAXUINT, SOUP_ADDRESS_ANY_PORT,
423 G_PARAM_CONSTRUCT_ONLY |
424 G_PARAM_STATIC_NAME |
425 G_PARAM_STATIC_NICK |
426 G_PARAM_STATIC_BLURB));
429 * GUPnPContextManager:white-list:
431 * The white list to use.
433 g_object_class_install_property
436 g_param_spec_object ("white-list",
438 "The white list to use",
439 GUPNP_TYPE_WHITE_LIST,
441 G_PARAM_STATIC_NAME |
442 G_PARAM_STATIC_NICK |
443 G_PARAM_STATIC_BLURB));
446 * GUPnPContextManager::context-available:
447 * @context_manager: The #GUPnPContextManager that received the signal
448 * @context: The now available #GUPnPContext
450 * Signals the availability of new #GUPnPContext.
453 signals[CONTEXT_AVAILABLE] =
454 g_signal_new_class_handler ("context-available",
455 GUPNP_TYPE_CONTEXT_MANAGER,
457 G_CALLBACK (on_context_available),
459 g_cclosure_marshal_VOID__OBJECT,
465 * GUPnPContextManager::context-unavailable:
466 * @context_manager: The #GUPnPContextManager that received the signal
467 * @context: The now unavailable #GUPnPContext
469 * Signals the unavailability of a #GUPnPContext.
472 signals[CONTEXT_UNAVAILABLE] =
473 g_signal_new_class_handler
474 ("context-unavailable",
475 GUPNP_TYPE_CONTEXT_MANAGER,
477 G_CALLBACK (on_context_unavailable),
479 g_cclosure_marshal_VOID__OBJECT,
486 * gupnp_context_manager_new:
487 * @main_context: (allow-none): Deprecated: 0.17.2: %NULL. If you want to use
488 * a different main context use
489 * g_main_context_push_thread_default() instead.
490 * @port: Port to create contexts for, or 0 if you don't care what port is used.
492 * Same as gupnp_context_manager_create().
494 * Returns: (transfer full): A new #GUPnPContextManager object.
495 * Deprecated: 0.17.2: Use gupnp_context_manager_create().
497 GUPnPContextManager *
498 gupnp_context_manager_new (GMainContext *main_context,
502 g_warning ("gupnp_context_manager_new::main_context is"
504 " g_main_context_push_thread_default() instead");
506 return gupnp_context_manager_create (port);
509 #ifdef HAVE_LINUX_RTNETLINK_H
510 #include "gupnp-linux-context-manager.h"
514 * gupnp_context_manager_create:
515 * @port: Port to create contexts for, or 0 if you don't care what port is used.
517 * Factory-method to create a new #GUPnPContextManager. The final type of the
518 * #GUPnPContextManager depends on the compile-time selection or - in case of
519 * NetworkManager - on its availability during runtime. If it is not available,
520 * the implementation falls back to the basic Unix context manager instead.
522 * Returns: (transfer full): A new #GUPnPContextManager object.
524 GUPnPContextManager *
525 gupnp_context_manager_create (guint port)
527 #if defined(USE_NETWORK_MANAGER) || defined (USE_CONNMAN)
528 GDBusConnection *system_bus;
530 GUPnPContextManager *impl;
531 GType impl_type = G_TYPE_INVALID;
533 #include "gupnp-windows-context-manager.h"
535 impl_type = GUPNP_TYPE_WINDOWS_CONTEXT_MANAGER;
537 #ifdef USE_NETWORK_MANAGER
538 #include "gupnp-network-manager.h"
539 system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
541 if (gupnp_network_manager_is_available ())
542 impl_type = GUPNP_TYPE_NETWORK_MANAGER;
544 #include "gupnp-connman-manager.h"
545 system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
547 if (gupnp_connman_manager_is_available ())
548 impl_type = GUPNP_TYPE_CONNMAN_MANAGER;
551 if (impl_type == G_TYPE_INVALID) {
552 /* Either user requested us to use the Linux CM explicitly or we
553 * are using one of the DBus managers but it's not available, so we
554 * fall-back to it. */
555 #if defined (USE_NETLINK) || defined (HAVE_LINUX_RTNETLINK_H)
556 if (gupnp_linux_context_manager_is_available ())
557 impl_type = GUPNP_TYPE_LINUX_CONTEXT_MANAGER;
559 impl_type = GUPNP_TYPE_UNIX_CONTEXT_MANAGER;
561 impl_type = GUPNP_TYPE_UNIX_CONTEXT_MANAGER;
564 #endif /* G_OS_WIN32 */
565 impl = g_object_new (impl_type,
569 #if defined(USE_NETWORK_MANAGER) || defined(USE_CONNMAN)
570 g_object_unref (system_bus);
576 * gupnp_context_manager_rescan_control_points:
577 * @manager: A #GUPnPContextManager
579 * This function starts a rescan on every control point managed by @manager.
580 * Only the active control points send discovery messages.
581 * This function should be called when servers are suspected to have
585 gupnp_context_manager_rescan_control_points (GUPnPContextManager *manager)
589 g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
591 l = manager->priv->objects;
594 if (GUPNP_IS_CONTROL_POINT (l->data)) {
595 GSSDPResourceBrowser *browser =
596 GSSDP_RESOURCE_BROWSER (l->data);
597 gssdp_resource_browser_rescan (browser);
605 * gupnp_context_manager_manage_control_point:
606 * @manager: A #GUPnPContextManager
607 * @control_point: The #GUPnPControlPoint to be taken care of
609 * By calling this function, you are asking @manager to keep a reference to
610 * @control_point until it's associated #GUPnPContext is no longer available.
611 * You usually want to call this function from
612 * #GUPnPContextManager::context-available handler after you create a
613 * #GUPnPControlPoint object for the newly available context.
616 gupnp_context_manager_manage_control_point (GUPnPContextManager *manager,
617 GUPnPControlPoint *control_point)
619 g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
620 g_return_if_fail (GUPNP_IS_CONTROL_POINT (control_point));
622 manager->priv->objects = g_list_append (manager->priv->objects,
623 g_object_ref (control_point));
627 * gupnp_context_manager_manage_root_device:
628 * @manager: A #GUPnPContextManager
629 * @root_device: The #GUPnPRootDevice to be taken care of
631 * By calling this function, you are asking @manager to keep a reference to
632 * @root_device when it's associated #GUPnPContext is no longer available. You
633 * usually want to call this function from
634 * #GUPnPContextManager::context-available handler after you create a
635 * #GUPnPRootDevice object for the newly available context.
638 gupnp_context_manager_manage_root_device (GUPnPContextManager *manager,
639 GUPnPRootDevice *root_device)
641 g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
642 g_return_if_fail (GUPNP_IS_ROOT_DEVICE (root_device));
644 manager->priv->objects = g_list_append (manager->priv->objects,
645 g_object_ref (root_device));
649 * gupnp_context_manager_get_port:
650 * @manager: A #GUPnPContextManager
652 * Get the network port associated with this context manager.
653 * Returns: The network port asssociated with this context manager.
656 gupnp_context_manager_get_port (GUPnPContextManager *manager)
658 g_return_val_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager), 0);
660 return manager->priv->port;
664 * gupnp_context_manager_get_white_list:
665 * @manager: A #GUPnPContextManager
667 * Get the #GUPnPWhiteList associated with @manager.
669 * Returns: (transfer none): The #GUPnPWhiteList asssociated with this
673 gupnp_context_manager_get_white_list (GUPnPContextManager *manager)
675 g_return_val_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager), NULL);
677 return manager->priv->white_list;