2888a1b5fe586f713fb803e902b8cdf0e28ea49b
[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  *
5  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
6  *         Jorn Baayen <jorn@openedhand.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:gupnp-context-manager
26  * @short_description: Manages #GUPnPContext objects.
27  *
28  * A Utility class that takes care of creation and destruction of
29  * #GUPnPContext objects for all available network interfaces as they go up
30  * (connect) and down (disconnect), respectively.
31  *
32  */
33
34 #include <config.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <libsoup/soup-address.h>
40 #include <glib/gstdio.h>
41
42 #include "gupnp.h"
43 #include "gupnp-marshal.h"
44
45 #include "gupnp-unix-context-manager.h"
46
47 G_DEFINE_ABSTRACT_TYPE (GUPnPContextManager,
48                         gupnp_context_manager,
49                         G_TYPE_OBJECT);
50
51 struct _GUPnPContextManagerPrivate {
52         guint              port;
53
54         GUPnPContextManager *impl;
55
56         GList *objects; /* control points and root devices */
57 };
58
59 enum {
60         PROP_0,
61         PROP_MAIN_CONTEXT,
62         PROP_PORT,
63 };
64
65 enum {
66         CONTEXT_AVAILABLE,
67         CONTEXT_UNAVAILABLE,
68         SIGNAL_LAST
69 };
70
71 static guint signals[SIGNAL_LAST];
72
73 static void
74 on_context_unavailable (GUPnPContextManager *manager,
75                         GUPnPContext        *context,
76                         gpointer            *user_data)
77 {
78         GList *l;
79
80         /* Make sure we don't send anything on now unavailable network */
81         g_object_set (context, "active", FALSE, NULL);
82
83         /* Unref all associated objects */
84         l = manager->priv->objects;
85
86         while (l) {
87                 GUPnPContext *obj_context = NULL;
88
89                 if (GUPNP_IS_CONTROL_POINT (l->data)) {
90                         GUPnPControlPoint *cp;
91
92                         cp = GUPNP_CONTROL_POINT (l->data);
93                         obj_context = gupnp_control_point_get_context (cp);
94                 } else if (GUPNP_IS_ROOT_DEVICE (l->data)) {
95                         GUPnPDeviceInfo *info;
96
97                         info = GUPNP_DEVICE_INFO (l->data);
98                         obj_context = gupnp_device_info_get_context (info);
99                 } else {
100                         g_assert_not_reached ();
101                 }
102
103                 if (context == obj_context) {
104                         GList *next = l->next;
105
106                         g_object_unref (l->data);
107
108                         manager->priv->objects =
109                                 g_list_delete_link (manager->priv->objects, l);
110                         l = next;
111                 } else {
112                         l = l->next;
113                 }
114         }
115 }
116
117 static void
118 gupnp_context_manager_init (GUPnPContextManager *manager)
119 {
120         manager->priv =
121                 G_TYPE_INSTANCE_GET_PRIVATE (manager,
122                                              GUPNP_TYPE_CONTEXT_MANAGER,
123                                              GUPnPContextManagerPrivate);
124 }
125
126 static void
127 gupnp_context_manager_set_property (GObject      *object,
128                                     guint         property_id,
129                                     const GValue *value,
130                                     GParamSpec   *pspec)
131 {
132         GUPnPContextManager *manager;
133         GUPnPContextManagerPrivate *priv;
134
135         manager = GUPNP_CONTEXT_MANAGER (object);
136         priv = manager->priv;
137
138         switch (property_id) {
139         case PROP_PORT:
140                 priv->port = g_value_get_uint (value);
141                 break;
142         case PROP_MAIN_CONTEXT:
143                 if (g_value_get_pointer (value) != NULL)
144                         g_warning ("GUPnPContextManager:main-context is "
145                                    "deprecated. Use "
146                                    "g_main_context_push_thread_default()"
147                                    "instead.");
148                 break;
149         default:
150                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
151                 break;
152         }
153 }
154
155 static void
156 gupnp_context_manager_get_property (GObject    *object,
157                                     guint       property_id,
158                                     GValue     *value,
159                                     GParamSpec *pspec)
160 {
161         GUPnPContextManager *manager;
162
163         manager = GUPNP_CONTEXT_MANAGER (object);
164
165         switch (property_id) {
166         case PROP_PORT:
167                 g_value_set_uint (value, manager->priv->port);
168                 break;
169         case PROP_MAIN_CONTEXT:
170                 g_warning ("GUPnPContextManager:main-context is deprecated. "
171                            "Use g_main_context_push_thread_default()"
172                            "instead.");
173                 g_value_set_pointer (value,
174                                      g_main_context_get_thread_default ());
175                 break;
176         default:
177                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
178                 break;
179         }
180 }
181
182 static void
183 gupnp_context_manager_dispose (GObject *object)
184 {
185         GUPnPContextManager *manager;
186         GObjectClass *object_class;
187
188         manager = GUPNP_CONTEXT_MANAGER (object);
189
190         g_list_foreach (manager->priv->objects, (GFunc) g_object_unref, NULL);
191         g_list_free (manager->priv->objects);
192         manager->priv->objects = NULL;
193
194         /* Call super */
195         object_class = G_OBJECT_CLASS (gupnp_context_manager_parent_class);
196         object_class->dispose (object);
197 }
198
199 static void
200 gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
201 {
202         GObjectClass *object_class;
203
204         object_class = G_OBJECT_CLASS (klass);
205
206         object_class->set_property = gupnp_context_manager_set_property;
207         object_class->get_property = gupnp_context_manager_get_property;
208         object_class->dispose      = gupnp_context_manager_dispose;
209
210         g_type_class_add_private (klass, sizeof (GUPnPContextManagerPrivate));
211
212         /**
213          * GSSDPClient:main-context:
214          *
215          * The #GMainContext to pass to created #GUPnPContext objects. Set to
216          * NULL to use the default.
217          *
218          * Deprecated: 0.17.2: Use g_main_context_push_thread_default()
219          *             instead.
220          **/
221         g_object_class_install_property
222                 (object_class,
223                  PROP_MAIN_CONTEXT,
224                  g_param_spec_pointer
225                          ("main-context",
226                           "Main context",
227                           "GMainContext to pass to created GUPnPContext"
228                           " objects",
229                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
230                           G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
231                           G_PARAM_STATIC_BLURB));
232
233         /**
234          * GUPnPContextManager:port:
235          *
236          * Port the contexts listen on, or 0 if you don't care what
237          * port is used by #GUPnPContext objects created by this object.
238          **/
239         g_object_class_install_property
240                 (object_class,
241                  PROP_PORT,
242                  g_param_spec_uint ("port",
243                                     "Port",
244                                     "Port to create contexts for",
245                                     0, G_MAXUINT, SOUP_ADDRESS_ANY_PORT,
246                                     G_PARAM_READWRITE |
247                                     G_PARAM_CONSTRUCT_ONLY |
248                                     G_PARAM_STATIC_NAME |
249                                     G_PARAM_STATIC_NICK |
250                                     G_PARAM_STATIC_BLURB));
251
252         /**
253          * GUPnPContextManager::context-available:
254          * @context_manager: The #GUPnPContextManager that received the signal
255          * @context: The now available #GUPnPContext
256          *
257          * Signals the availability of new #GUPnPContext.
258          *
259          **/
260         signals[CONTEXT_AVAILABLE] =
261                 g_signal_new ("context-available",
262                               GUPNP_TYPE_CONTEXT_MANAGER,
263                               G_SIGNAL_RUN_LAST,
264                               0,
265                               NULL, NULL,
266                               g_cclosure_marshal_VOID__OBJECT,
267                               G_TYPE_NONE,
268                               1,
269                               GUPNP_TYPE_CONTEXT);
270
271         /**
272          * GUPnPContextManager::context-unavailable:
273          * @context_manager: The #GUPnPContextManager that received the signal
274          * @context: The now unavailable #GUPnPContext
275          *
276          * Signals the unavailability of a #GUPnPContext.
277          *
278          **/
279         signals[CONTEXT_UNAVAILABLE] =
280                 g_signal_new_class_handler
281                                         ("context-unavailable",
282                                          GUPNP_TYPE_CONTEXT_MANAGER,
283                                          G_SIGNAL_RUN_FIRST,
284                                          G_CALLBACK (on_context_unavailable),
285                                          NULL, NULL,
286                                          g_cclosure_marshal_VOID__OBJECT,
287                                          G_TYPE_NONE,
288                                          1,
289                                          GUPNP_TYPE_CONTEXT);
290 }
291
292 /**
293  * gupnp_context_manager_new:
294  * @main_context: (allow-none): Deprecated: 0.17.2: %NULL. If you want to use
295  *                a different main context use
296  *                g_main_context_push_thread_default() instead.
297  * @port: Port to create contexts for, or 0 if you don't care what port is used.
298  *
299  * Same as gupnp_context_manager_create().
300  *
301  * Returns: (transfer full): A new #GUPnPContextManager object.
302  * Deprecated: 0.17.2: Use gupnp_context_manager_create().
303  **/
304 GUPnPContextManager *
305 gupnp_context_manager_new (GMainContext *main_context,
306                            guint         port)
307 {
308     if (main_context)
309             g_warning ("gupnp_context_manager_new::main_context is"
310                        " deprecated. Use "
311                        " g_main_context_push_thread_default() instead");
312
313     return gupnp_context_manager_create (port);
314 }
315
316 #ifdef HAVE_LINUX_RTNETLINK_H
317 #include "gupnp-linux-context-manager.h"
318 #endif
319
320 /**
321  * gupnp_context_manager_create:
322  * @port: Port to create contexts for, or 0 if you don't care what port is used.
323  *
324  * Factory-method to create a new #GUPnPContextManager. The final type of the
325  * #GUPnPContextManager depends on the compile-time selection or - in case of
326  * NetworkManager - on its availability during runtime. If it is not available,
327  * the implementation falls back to the basic Unix context manager instead.
328  *
329  * Returns: (transfer full): A new #GUPnPContextManager object.
330  **/
331 GUPnPContextManager *
332 gupnp_context_manager_create (guint port)
333 {
334 #if defined(USE_NETWORK_MANAGER) || defined (USE_CONNMAN)
335         GDBusConnection *system_bus;
336 #endif
337         GUPnPContextManager *impl;
338         GType impl_type = G_TYPE_INVALID;
339
340 #ifdef USE_NETWORK_MANAGER
341 #include "gupnp-network-manager.h"
342         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
343
344         if (gupnp_network_manager_is_available ())
345                 impl_type = GUPNP_TYPE_NETWORK_MANAGER;
346 #elif USE_CONNMAN
347 #include "gupnp-connman-manager.h"
348         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
349
350        if (gupnp_connman_manager_is_available ())
351                 impl_type = GUPNP_TYPE_CONNMAN_MANAGER;
352 #endif
353
354         if (impl_type == G_TYPE_INVALID)
355             /* Either user requested us to use the Linux CM explicitly or we
356              * are using one of the DBus managers but it's not available, so we
357              * fall-back to it. */
358 #if defined (USE_NETLINK) || defined (HAVE_LINUX_RTNETLINK_H)
359                 impl_type = GUPNP_TYPE_LINUX_CONTEXT_MANAGER;
360 #else
361                 impl_type = GUPNP_TYPE_UNIX_CONTEXT_MANAGER;
362 #endif
363
364         impl = g_object_new (impl_type,
365                              "port", port,
366                              NULL);
367
368 #if defined(USE_NETWORK_MANAGER) || defined(USE_CONNMAN)
369         g_object_unref (system_bus);
370 #endif
371         return impl;
372 }
373
374 /**
375  * gupnp_context_manager_manage_control_point:
376  * @manager: A #GUPnPContextManager
377  * @control_point: The #GUPnPControlPoint to be taken care of
378  *
379  * By calling this function, you are asking @manager to keep a reference to
380  * @control_point until it's associated #GUPnPContext is no longer available.
381  * You usually want to call this function from
382  * #GUPnPContextManager::context-available handler after you create a
383  * #GUPnPControlPoint object for the newly available context.
384  **/
385 void
386 gupnp_context_manager_manage_control_point (GUPnPContextManager *manager,
387                                             GUPnPControlPoint   *control_point)
388 {
389         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
390         g_return_if_fail (GUPNP_IS_CONTROL_POINT (control_point));
391
392         manager->priv->objects = g_list_append (manager->priv->objects,
393                                                 g_object_ref (control_point));
394 }
395
396 /**
397  * gupnp_context_manager_manage_root_device:
398  * @manager: A #GUPnPContextManager
399  * @root_device: The #GUPnPRootDevice to be taken care of
400  *
401  * By calling this function, you are asking @manager to keep a reference to
402  * @root_device when it's associated #GUPnPContext is no longer available. You
403  * usually want to call this function from
404  * #GUPnPContextManager::context-available handler after you create a
405  * #GUPnPRootDevice object for the newly available context.
406  **/
407 void
408 gupnp_context_manager_manage_root_device (GUPnPContextManager *manager,
409                                           GUPnPRootDevice     *root_device)
410 {
411         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
412         g_return_if_fail (GUPNP_IS_ROOT_DEVICE (root_device));
413
414         manager->priv->objects = g_list_append (manager->priv->objects,
415                                                 g_object_ref (root_device));
416 }
417
418 /**
419  * gupnp_context_manager_get_port:
420  * @manager: A #GUPnPContextManager
421  *
422  * Get the network port associated with this context manager.
423  * Returns: The network port asssociated with this context manager.
424  */
425 guint
426 gupnp_context_manager_get_port (GUPnPContextManager *manager)
427 {
428         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
429
430         return manager->priv->port;
431 }