21ce53b112551b52bf998e209ef045d1f51e0ff1
[profile/ivi/GUPnP.git] / libgupnp / gupnp-context-manager.c
1 /*
2  * Copyright (C) 2009 Nokia Corporation.
3  * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd.
4  * Copyright (C) 2013 Intel Corporation.
5  *
6  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
7  *         Jorn Baayen <jorn@openedhand.com>
8  *         Ludovic Ferrandis <ludovic.ferrandis@intel.com>
9  *
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.
14  *
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.
19  *
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.
24  */
25
26 /**
27  * SECTION:gupnp-context-manager
28  * @short_description: Manages #GUPnPContext objects.
29  *
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.
33  *
34  */
35
36 #include <config.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <libsoup/soup-address.h>
42 #include <glib.h>
43 #include <glib/gstdio.h>
44
45 #include "gupnp.h"
46 #include "gupnp-marshal.h"
47
48 #include "gupnp-unix-context-manager.h"
49
50 G_DEFINE_ABSTRACT_TYPE (GUPnPContextManager,
51                         gupnp_context_manager,
52                         G_TYPE_OBJECT);
53
54 struct _GUPnPContextManagerPrivate {
55         guint              port;
56
57         GUPnPContextManager *impl;
58
59         GList *objects; /* control points and root devices */
60         GList *blacklisted; /* Blacklisted Context */
61
62         GUPnPWhiteList *white_list;
63 };
64
65 enum {
66         PROP_0,
67         PROP_MAIN_CONTEXT,
68         PROP_PORT,
69         PROP_WHITE_LIST
70 };
71
72 enum {
73         CONTEXT_AVAILABLE,
74         CONTEXT_UNAVAILABLE,
75         SIGNAL_LAST
76 };
77
78 static guint signals[SIGNAL_LAST];
79
80 static void
81 on_context_available (GUPnPContextManager    *manager,
82                       GUPnPContext           *context,
83                       G_GNUC_UNUSED gpointer *user_data)
84 {
85         GUPnPWhiteList *white_list;
86
87         white_list = manager->priv->white_list;
88
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");
97
98                 /* Make sure we don't send anything on now blocked network */
99                 g_object_set (context, "active", FALSE, NULL);
100
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));
105         }
106 }
107
108 static void
109 on_context_unavailable (GUPnPContextManager    *manager,
110                         GUPnPContext           *context,
111                         G_GNUC_UNUSED gpointer *user_data)
112 {
113         GList *l;
114         GList *black;
115
116         /* Make sure we don't send anything on now unavailable network */
117         g_object_set (context, "active", FALSE, NULL);
118
119         /* Unref all associated objects */
120         l = manager->priv->objects;
121
122         while (l) {
123                 GUPnPContext *obj_context = NULL;
124
125                 if (GUPNP_IS_CONTROL_POINT (l->data)) {
126                         GUPnPControlPoint *cp;
127
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;
132
133                         info = GUPNP_DEVICE_INFO (l->data);
134                         obj_context = gupnp_device_info_get_context (info);
135                 } else {
136                         g_assert_not_reached ();
137                 }
138
139                 if (context == obj_context) {
140                         GList *next = l->next;
141
142                         g_object_unref (l->data);
143
144                         manager->priv->objects =
145                                 g_list_delete_link (manager->priv->objects, l);
146                         l = next;
147                 } else {
148                         l = l->next;
149                 }
150         }
151
152         black = g_list_find (manager->priv->blacklisted, context);
153
154         if (black != NULL) {
155                 g_signal_stop_emission_by_name (manager, "context-unavailable");
156
157                 g_object_unref (black->data);
158                 manager->priv->blacklisted =
159                         g_list_delete_link (manager->priv->blacklisted, black);
160         }
161 }
162
163 static void
164 gupnp_context_manager_filter_context (GUPnPWhiteList *white_list,
165                                       GUPnPContextManager *manager,
166                                       gboolean check)
167 {
168         GList *next;
169         GList *obj;
170         GList *blk;
171         gboolean match;
172         GUPnPContext *context;
173         GSSDPResourceBrowser *browser;
174
175         obj = manager->priv->objects;
176         blk = manager->priv->blacklisted;
177
178         while (obj != NULL) {
179                 if (!GUPNP_IS_CONTROL_POINT (obj->data))
180                         continue;
181
182                 /* If the white list is empty, treat it as disabled */
183                 if (check) {
184                         /* Filter out context */
185                         context = gupnp_control_point_get_context (obj->data);
186                         match = gupnp_white_list_check_context (white_list,
187                                                                 context);
188                 } else {
189                         /* Re-activate all context, if needed */
190                         match = TRUE;
191                 }
192
193                 browser = GSSDP_RESOURCE_BROWSER (obj->data);
194                 gssdp_resource_browser_set_active (browser, match);
195
196                 if (match)
197                         (void) gssdp_resource_browser_rescan (browser);
198
199                 obj = obj->next;
200         }
201
202         while (blk != NULL) {
203                 /* If the white list is empty, treat it as disabled */
204                 if (check)
205                         /* Filter out context */
206                         match = gupnp_white_list_check_context (white_list,
207                                                                 blk->data);
208                 else
209                         /* Re-activate all context, if needed */
210                         match = TRUE;
211
212                 if (!match) {
213                         blk = blk->next;
214                         continue;
215                 }
216
217                 next = blk->next;
218                 g_object_set (blk->data, "active", TRUE, NULL);
219
220                 g_signal_emit_by_name (manager, "context-available", blk->data);
221
222                 g_object_unref (blk->data);
223                 manager->priv->blacklisted = g_list_delete_link (
224                                                 manager->priv->blacklisted,
225                                                 blk);
226                 blk = next;
227         }
228 }
229
230 static void
231 on_white_list_change_cb (GUPnPWhiteList *white_list,
232                          GParamSpec *pspec,
233                          gpointer user_data)
234 {
235         GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
236         gboolean enabled;
237         gboolean is_empty;
238
239         enabled = gupnp_white_list_get_enabled (white_list);
240         is_empty = gupnp_white_list_is_empty (white_list);
241
242         if (enabled)
243                 gupnp_context_manager_filter_context (white_list,
244                                                       manager,
245                                                       !is_empty);
246 }
247
248 static void
249 on_white_list_enabled_cb (GUPnPWhiteList *white_list,
250                           GParamSpec *pspec,
251                           gpointer user_data)
252 {
253         GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
254         gboolean enabled;
255         gboolean is_empty;
256
257         enabled = gupnp_white_list_get_enabled (white_list);
258         is_empty = gupnp_white_list_is_empty (white_list);
259
260         if (!is_empty)
261                 gupnp_context_manager_filter_context (white_list,
262                                                       manager,
263                                                       enabled);
264 }
265
266 static void
267 gupnp_context_manager_init (GUPnPContextManager *manager)
268 {
269         manager->priv =
270                 G_TYPE_INSTANCE_GET_PRIVATE (manager,
271                                              GUPNP_TYPE_CONTEXT_MANAGER,
272                                              GUPnPContextManagerPrivate);
273
274         manager->priv->white_list = gupnp_white_list_new ();
275
276         g_signal_connect_after (manager->priv->white_list, "notify::entries",
277                                 G_CALLBACK (on_white_list_change_cb), manager);
278
279         g_signal_connect_after (manager->priv->white_list, "notify::enabled",
280                                 G_CALLBACK (on_white_list_enabled_cb), manager);
281 }
282
283 static void
284 gupnp_context_manager_set_property (GObject      *object,
285                                     guint         property_id,
286                                     const GValue *value,
287                                     GParamSpec   *pspec)
288 {
289         GUPnPContextManager *manager;
290         GUPnPContextManagerPrivate *priv;
291
292         manager = GUPNP_CONTEXT_MANAGER (object);
293         priv = manager->priv;
294
295         switch (property_id) {
296         case PROP_PORT:
297                 priv->port = g_value_get_uint (value);
298                 break;
299         case PROP_MAIN_CONTEXT:
300                 if (g_value_get_pointer (value) != NULL)
301                         g_warning ("GUPnPContextManager:main-context is "
302                                    "deprecated. Use "
303                                    "g_main_context_push_thread_default()"
304                                    "instead.");
305                 break;
306         default:
307                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
308                 break;
309         }
310 }
311
312 static void
313 gupnp_context_manager_get_property (GObject    *object,
314                                     guint       property_id,
315                                     GValue     *value,
316                                     GParamSpec *pspec)
317 {
318         GUPnPContextManager *manager;
319
320         manager = GUPNP_CONTEXT_MANAGER (object);
321
322         switch (property_id) {
323         case PROP_PORT:
324                 g_value_set_uint (value, manager->priv->port);
325                 break;
326         case PROP_MAIN_CONTEXT:
327                 g_warning ("GUPnPContextManager:main-context is deprecated. "
328                            "Use g_main_context_push_thread_default()"
329                            "instead.");
330                 g_value_set_pointer (value,
331                                      g_main_context_get_thread_default ());
332                 break;
333         case PROP_WHITE_LIST:
334                 g_value_set_object (value, manager->priv->white_list);
335                 break;
336         default:
337                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
338                 break;
339         }
340 }
341
342 static void
343 gupnp_context_manager_dispose (GObject *object)
344 {
345         GUPnPContextManager *manager;
346         GUPnPWhiteList *wl;
347         GObjectClass *object_class;
348
349         manager = GUPNP_CONTEXT_MANAGER (object);
350         wl = manager->priv->white_list;
351
352         g_signal_handlers_disconnect_by_func (wl,
353                                               on_white_list_enabled_cb,
354                                               manager);
355
356         g_signal_handlers_disconnect_by_func (wl,
357                                               on_white_list_change_cb,
358                                               NULL);
359
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;
364
365         if (wl) {
366                 g_object_unref (wl);
367                 manager->priv->white_list = NULL;
368         }
369
370         /* Call super */
371         object_class = G_OBJECT_CLASS (gupnp_context_manager_parent_class);
372         object_class->dispose (object);
373 }
374
375 static void
376 gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
377 {
378         GObjectClass *object_class;
379
380         object_class = G_OBJECT_CLASS (klass);
381
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;
385
386         g_type_class_add_private (klass, sizeof (GUPnPContextManagerPrivate));
387
388         /**
389          * GSSDPClient:main-context:
390          *
391          * The #GMainContext to pass to created #GUPnPContext objects. Set to
392          * NULL to use the default.
393          *
394          * Deprecated: 0.17.2: Use g_main_context_push_thread_default()
395          *             instead.
396          **/
397         g_object_class_install_property
398                 (object_class,
399                  PROP_MAIN_CONTEXT,
400                  g_param_spec_pointer
401                          ("main-context",
402                           "Main context",
403                           "GMainContext to pass to created GUPnPContext"
404                           " objects",
405                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
406                           G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
407                           G_PARAM_STATIC_BLURB));
408
409         /**
410          * GUPnPContextManager:port:
411          *
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.
414          **/
415         g_object_class_install_property
416                 (object_class,
417                  PROP_PORT,
418                  g_param_spec_uint ("port",
419                                     "Port",
420                                     "Port to create contexts for",
421                                     0, G_MAXUINT, SOUP_ADDRESS_ANY_PORT,
422                                     G_PARAM_READWRITE |
423                                     G_PARAM_CONSTRUCT_ONLY |
424                                     G_PARAM_STATIC_NAME |
425                                     G_PARAM_STATIC_NICK |
426                                     G_PARAM_STATIC_BLURB));
427
428          /**
429          * GUPnPContextManager:white-list:
430          *
431          * The white list to use.
432          **/
433         g_object_class_install_property
434                 (object_class,
435                  PROP_WHITE_LIST,
436                  g_param_spec_object ("white-list",
437                                       "White List",
438                                       "The white list to use",
439                                       GUPNP_TYPE_WHITE_LIST,
440                                       G_PARAM_READABLE |
441                                       G_PARAM_STATIC_NAME |
442                                       G_PARAM_STATIC_NICK |
443                                       G_PARAM_STATIC_BLURB));
444
445        /**
446          * GUPnPContextManager::context-available:
447          * @context_manager: The #GUPnPContextManager that received the signal
448          * @context: The now available #GUPnPContext
449          *
450          * Signals the availability of new #GUPnPContext.
451          *
452          **/
453         signals[CONTEXT_AVAILABLE] =
454                 g_signal_new_class_handler ("context-available",
455                                             GUPNP_TYPE_CONTEXT_MANAGER,
456                                             G_SIGNAL_RUN_FIRST,
457                                             G_CALLBACK (on_context_available),
458                                             NULL, NULL,
459                                             g_cclosure_marshal_VOID__OBJECT,
460                                             G_TYPE_NONE,
461                                             1,
462                                             GUPNP_TYPE_CONTEXT);
463
464         /**
465          * GUPnPContextManager::context-unavailable:
466          * @context_manager: The #GUPnPContextManager that received the signal
467          * @context: The now unavailable #GUPnPContext
468          *
469          * Signals the unavailability of a #GUPnPContext.
470          *
471          **/
472         signals[CONTEXT_UNAVAILABLE] =
473                 g_signal_new_class_handler
474                                         ("context-unavailable",
475                                          GUPNP_TYPE_CONTEXT_MANAGER,
476                                          G_SIGNAL_RUN_FIRST,
477                                          G_CALLBACK (on_context_unavailable),
478                                          NULL, NULL,
479                                          g_cclosure_marshal_VOID__OBJECT,
480                                          G_TYPE_NONE,
481                                          1,
482                                          GUPNP_TYPE_CONTEXT);
483 }
484
485 /**
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.
491  *
492  * Same as gupnp_context_manager_create().
493  *
494  * Returns: (transfer full): A new #GUPnPContextManager object.
495  * Deprecated: 0.17.2: Use gupnp_context_manager_create().
496  **/
497 GUPnPContextManager *
498 gupnp_context_manager_new (GMainContext *main_context,
499                            guint         port)
500 {
501     if (main_context)
502             g_warning ("gupnp_context_manager_new::main_context is"
503                        " deprecated. Use "
504                        " g_main_context_push_thread_default() instead");
505
506     return gupnp_context_manager_create (port);
507 }
508
509 #ifdef HAVE_LINUX_RTNETLINK_H
510 #include "gupnp-linux-context-manager.h"
511 #endif
512
513 /**
514  * gupnp_context_manager_create:
515  * @port: Port to create contexts for, or 0 if you don't care what port is used.
516  *
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.
521  *
522  * Returns: (transfer full): A new #GUPnPContextManager object.
523  **/
524 GUPnPContextManager *
525 gupnp_context_manager_create (guint port)
526 {
527 #if defined(USE_NETWORK_MANAGER) || defined (USE_CONNMAN)
528         GDBusConnection *system_bus;
529 #endif
530         GUPnPContextManager *impl;
531         GType impl_type = G_TYPE_INVALID;
532 #ifdef G_OS_WIN32
533 #include "gupnp-windows-context-manager.h"
534
535         impl_type = GUPNP_TYPE_WINDOWS_CONTEXT_MANAGER;
536 #else
537 #ifdef USE_NETWORK_MANAGER
538 #include "gupnp-network-manager.h"
539         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
540
541         if (gupnp_network_manager_is_available ())
542                 impl_type = GUPNP_TYPE_NETWORK_MANAGER;
543 #elif USE_CONNMAN
544 #include "gupnp-connman-manager.h"
545         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
546
547        if (gupnp_connman_manager_is_available ())
548                 impl_type = GUPNP_TYPE_CONNMAN_MANAGER;
549 #endif
550
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;
558                 else
559                     impl_type = GUPNP_TYPE_UNIX_CONTEXT_MANAGER;
560 #else
561                 impl_type = GUPNP_TYPE_UNIX_CONTEXT_MANAGER;
562 #endif
563         }
564 #endif /* G_OS_WIN32 */
565         impl = g_object_new (impl_type,
566                              "port", port,
567                              NULL);
568
569 #if defined(USE_NETWORK_MANAGER) || defined(USE_CONNMAN)
570         g_object_unref (system_bus);
571 #endif
572         return impl;
573 }
574
575 /**
576  * gupnp_context_manager_rescan_control_points:
577  * @manager: A #GUPnPContextManager
578  *
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
582  * disappeared.
583  **/
584 void
585 gupnp_context_manager_rescan_control_points (GUPnPContextManager *manager)
586 {
587         GList *l;
588
589         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
590
591         l = manager->priv->objects;
592
593         while (l) {
594                 if (GUPNP_IS_CONTROL_POINT (l->data)) {
595                         GSSDPResourceBrowser *browser =
596                                 GSSDP_RESOURCE_BROWSER (l->data);
597                         gssdp_resource_browser_rescan (browser);
598                 }
599
600                 l = l->next;
601         }
602 }
603
604 /**
605  * gupnp_context_manager_manage_control_point:
606  * @manager: A #GUPnPContextManager
607  * @control_point: The #GUPnPControlPoint to be taken care of
608  *
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.
614  **/
615 void
616 gupnp_context_manager_manage_control_point (GUPnPContextManager *manager,
617                                             GUPnPControlPoint   *control_point)
618 {
619         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
620         g_return_if_fail (GUPNP_IS_CONTROL_POINT (control_point));
621
622         manager->priv->objects = g_list_append (manager->priv->objects,
623                                                 g_object_ref (control_point));
624 }
625
626 /**
627  * gupnp_context_manager_manage_root_device:
628  * @manager: A #GUPnPContextManager
629  * @root_device: The #GUPnPRootDevice to be taken care of
630  *
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.
636  **/
637 void
638 gupnp_context_manager_manage_root_device (GUPnPContextManager *manager,
639                                           GUPnPRootDevice     *root_device)
640 {
641         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
642         g_return_if_fail (GUPNP_IS_ROOT_DEVICE (root_device));
643
644         manager->priv->objects = g_list_append (manager->priv->objects,
645                                                 g_object_ref (root_device));
646 }
647
648 /**
649  * gupnp_context_manager_get_port:
650  * @manager: A #GUPnPContextManager
651  *
652  * Get the network port associated with this context manager.
653  * Returns: The network port asssociated with this context manager.
654  */
655 guint
656 gupnp_context_manager_get_port (GUPnPContextManager *manager)
657 {
658         g_return_val_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager), 0);
659
660         return manager->priv->port;
661 }
662
663 /**
664  * gupnp_context_manager_get_white_list:
665  * @manager: A #GUPnPContextManager
666  *
667  * Get the #GUPnPWhiteList associated with @manager.
668  *
669  * Returns: (transfer none):  The #GUPnPWhiteList asssociated with this
670  * context manager.
671  */
672 GUPnPWhiteList *
673 gupnp_context_manager_get_white_list (GUPnPContextManager *manager)
674 {
675         g_return_val_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager), NULL);
676
677         return manager->priv->white_list;
678 }