tizen 2.3.1 release
[external/gupnp.git] / libgupnp / gupnp-context-manager.c
1 /*
2  * Copyright (C) 2009 Nokia Corporation, all rights reserved.
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., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, 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_TYPE (GUPnPContextManager,
48                gupnp_context_manager,
49                G_TYPE_OBJECT);
50
51 struct _GUPnPContextManagerPrivate {
52         GMainContext      *main_context;
53
54         guint              port;
55
56         GUPnPContextManager *impl;
57
58         GList *objects; /* control points and root devices */
59 };
60
61 enum {
62         PROP_0,
63         PROP_MAIN_CONTEXT,
64         PROP_PORT,
65         PROP_CONTEXT_MANAGER
66 };
67
68 enum {
69         CONTEXT_AVAILABLE,
70         CONTEXT_UNAVAILABLE,
71         SIGNAL_LAST
72 };
73
74 static guint signals[SIGNAL_LAST];
75
76 static void
77 on_context_available (GUPnPContextManager *impl,
78                       GUPnPContext        *context,
79                       gpointer            *user_data)
80 {
81         GUPnPContextManager *manager = GUPNP_CONTEXT_MANAGER (user_data);
82
83         /* Just proxy the signal */
84         g_signal_emit (manager,
85                        signals[CONTEXT_AVAILABLE],
86                        0,
87                        context);
88 }
89
90 static void
91 on_context_unavailable (GUPnPContextManager *impl,
92                         GUPnPContext        *context,
93                         gpointer            *user_data)
94 {
95         GUPnPContextManager *manager;
96         GList *l;
97
98         manager = GUPNP_CONTEXT_MANAGER (user_data);
99
100         /* Make sure we don't send anything on now unavailable network */
101         g_object_set (context, "active", FALSE, NULL);
102
103         /* Unref all associated objects */
104         l = manager->priv->objects;
105
106         while (l) {
107                 GUPnPContext *obj_context = NULL;
108
109                 if (GUPNP_IS_CONTROL_POINT (l->data)) {
110                         GUPnPControlPoint *cp;
111
112                         cp = GUPNP_CONTROL_POINT (l->data);
113                         obj_context = gupnp_control_point_get_context (cp);
114                 } else if (GUPNP_IS_ROOT_DEVICE (l->data)) {
115                         GUPnPDeviceInfo *info;
116
117                         info = GUPNP_DEVICE_INFO (l->data);
118                         obj_context = gupnp_device_info_get_context (info);
119                 } else {
120                         g_assert_not_reached ();
121                 }
122
123                 if (context == obj_context) {
124                         GList *next = l->next;
125
126                         g_object_unref (l->data);
127
128                         manager->priv->objects =
129                                 g_list_delete_link (manager->priv->objects, l);
130                         l = next;
131                 } else {
132                         l = l->next;
133                 }
134         }
135
136         /* Just proxy the signal */
137         g_signal_emit (manager,
138                        signals[CONTEXT_UNAVAILABLE],
139                        0,
140                        context);
141 }
142
143 static void
144 gupnp_context_manager_init (GUPnPContextManager *manager)
145 {
146         manager->priv =
147                 G_TYPE_INSTANCE_GET_PRIVATE (manager,
148                                              GUPNP_TYPE_CONTEXT_MANAGER,
149                                              GUPnPContextManagerPrivate);
150 }
151
152 static void
153 gupnp_context_manager_set_property (GObject      *object,
154                                     guint         property_id,
155                                     const GValue *value,
156                                     GParamSpec   *pspec)
157 {
158         GUPnPContextManager *manager;
159         GUPnPContextManagerPrivate *priv;
160
161         manager = GUPNP_CONTEXT_MANAGER (object);
162         priv = manager->priv;
163
164         switch (property_id) {
165         case PROP_PORT:
166                 priv->port = g_value_get_uint (value);
167                 break;
168         case PROP_MAIN_CONTEXT:
169                 priv->main_context = g_value_get_pointer (value);
170                 break;
171         case PROP_CONTEXT_MANAGER:
172                 priv->impl = g_value_get_object (value);
173                 if (priv->impl != NULL) {
174                         priv->impl = g_object_ref (priv->impl);
175
176                         g_signal_connect (priv->impl,
177                                           "context-available",
178                                           G_CALLBACK (on_context_available),
179                                           manager);
180                         g_signal_connect (priv->impl,
181                                           "context-unavailable",
182                                           G_CALLBACK (on_context_unavailable),
183                                           manager);
184                 }
185                 break;
186         default:
187                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
188                 break;
189         }
190 }
191
192 static void
193 gupnp_context_manager_get_property (GObject    *object,
194                                     guint       property_id,
195                                     GValue     *value,
196                                     GParamSpec *pspec)
197 {
198         GUPnPContextManager *manager;
199
200         manager = GUPNP_CONTEXT_MANAGER (object);
201
202         switch (property_id) {
203         case PROP_PORT:
204                 g_value_set_uint (value, manager->priv->port);
205                 break;
206         case PROP_MAIN_CONTEXT:
207                 g_value_set_pointer (value, manager->priv->main_context);
208                 break;
209         case PROP_CONTEXT_MANAGER:
210                 g_value_set_object (value, manager->priv->impl);
211                 break;
212         default:
213                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
214                 break;
215         }
216 }
217
218 static void
219 gupnp_context_manager_dispose (GObject *object)
220 {
221         GUPnPContextManager *manager;
222         GObjectClass *object_class;
223
224         manager = GUPNP_CONTEXT_MANAGER (object);
225
226         if (manager->priv->impl != NULL) {
227                 g_signal_handlers_disconnect_by_func (manager->priv->impl,
228                     on_context_available, manager);
229                 g_signal_handlers_disconnect_by_func (manager->priv->impl,
230                     on_context_unavailable, manager);
231                 g_object_unref (manager->priv->impl);
232                 manager->priv->impl = NULL;
233         }
234
235         g_list_foreach (manager->priv->objects, (GFunc) g_object_unref, NULL);
236         g_list_free (manager->priv->objects);
237         manager->priv->objects = NULL;
238
239         /* Call super */
240         object_class = G_OBJECT_CLASS (gupnp_context_manager_parent_class);
241         object_class->dispose (object);
242 }
243
244 static void
245 gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
246 {
247         GObjectClass *object_class;
248
249         object_class = G_OBJECT_CLASS (klass);
250
251         object_class->set_property = gupnp_context_manager_set_property;
252         object_class->get_property = gupnp_context_manager_get_property;
253         object_class->dispose      = gupnp_context_manager_dispose;
254
255         g_type_class_add_private (klass, sizeof (GUPnPContextManagerPrivate));
256
257         /**
258          * GSSDPClient:main-context:
259          *
260          * The #GMainContext to pass to created #GUPnPContext objects. Set to
261          * NULL to use the default.
262          **/
263         g_object_class_install_property
264                 (object_class,
265                  PROP_MAIN_CONTEXT,
266                  g_param_spec_pointer
267                          ("main-context",
268                           "Main context",
269                           "GMainContext to pass to created GUPnPContext"
270                           " objects",
271                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
272                           G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
273                           G_PARAM_STATIC_BLURB));
274
275         /**
276          * GUPnPContextManager:port:
277          *
278          * @port: Port to create contexts for, or 0 if you don't care what
279          * port is used by #GUPnPContext objects created by this object.
280          **/
281         g_object_class_install_property
282                 (object_class,
283                  PROP_PORT,
284                  g_param_spec_uint ("port",
285                                     "Port",
286                                     "Port to create contexts for",
287                                     0, G_MAXUINT, SOUP_ADDRESS_ANY_PORT,
288                                     G_PARAM_READWRITE |
289                                     G_PARAM_CONSTRUCT_ONLY |
290                                     G_PARAM_STATIC_NAME |
291                                     G_PARAM_STATIC_NICK |
292                                     G_PARAM_STATIC_BLURB));
293
294         /**
295          * GUPnPContextManager:context-manager:
296          *
297          * The actual GUPnPContextManager implementation used. This is an
298          * internal property and therefore Application developer should just
299          * ignore it.
300          *
301          **/
302         g_object_class_install_property
303                 (object_class,
304                  PROP_CONTEXT_MANAGER,
305                  g_param_spec_object ("context-manager",
306                                       "ContextManager",
307                                       "ContextManager implemention",
308                                       GUPNP_TYPE_CONTEXT_MANAGER,
309                                       G_PARAM_WRITABLE |
310                                       G_PARAM_CONSTRUCT_ONLY |
311                                       G_PARAM_STATIC_NAME |
312                                       G_PARAM_STATIC_NICK |
313                                       G_PARAM_STATIC_BLURB));
314
315         /**
316          * GUPnPContextManager::context-available:
317          * @context_manager: The #GUPnPContextManager that received the signal
318          * @context: The now available #GUPnPContext
319          *
320          * Signals the availability of new #GUPnPContext.
321          *
322          **/
323         signals[CONTEXT_AVAILABLE] =
324                 g_signal_new ("context-available",
325                               GUPNP_TYPE_CONTEXT_MANAGER,
326                               G_SIGNAL_RUN_LAST,
327                               0,
328                               NULL, NULL,
329                               g_cclosure_marshal_VOID__OBJECT,
330                               G_TYPE_NONE,
331                               1,
332                               GUPNP_TYPE_CONTEXT);
333
334         /**
335          * GUPnPContextManager::context-unavailable:
336          * @context_manager: The #GUPnPContextManager that received the signal
337          * @context: The now unavailable #GUPnPContext
338          *
339          * Signals the unavailability of a #GUPnPContext.
340          *
341          **/
342         signals[CONTEXT_UNAVAILABLE] =
343                 g_signal_new ("context-unavailable",
344                               GUPNP_TYPE_CONTEXT_MANAGER,
345                               G_SIGNAL_RUN_LAST,
346                               0,
347                               NULL, NULL,
348                               g_cclosure_marshal_VOID__OBJECT,
349                               G_TYPE_NONE,
350                               1,
351                               GUPNP_TYPE_CONTEXT);
352 }
353
354 /**
355  * gupnp_context_manager_new:
356  * @port: Port to create contexts for, or 0 if you don't care what port is used.
357  * @main_context: GMainContext to pass to created GUPnPContext objects.
358  *
359  * Create a new #GUPnPContextManager.
360  *
361  * Return value: A new #GUPnPContextManager object.
362  **/
363 GUPnPContextManager *
364 gupnp_context_manager_new (GMainContext *main_context,
365                            guint         port)
366 {
367         GUPnPContextManager *manager;
368         GUPnPContextManager *impl;
369         GType impl_type = G_TYPE_INVALID;
370
371 #ifdef USE_NETWORK_MANAGER
372 #include "gupnp-network-manager.h"
373
374         if (gupnp_network_manager_is_available (main_context))
375                 impl_type = GUPNP_TYPE_NETWORK_MANAGER;
376 #endif
377
378         if (impl_type == G_TYPE_INVALID)
379                 impl_type = GUPNP_TYPE_UNIX_CONTEXT_MANAGER;
380
381         impl = g_object_new (impl_type,
382                              "main-context", main_context,
383                              "port", port,
384                              NULL);
385
386         manager = g_object_new (GUPNP_TYPE_CONTEXT_MANAGER,
387                                 "main-context", main_context,
388                                 "port", port,
389                                 "context-manager", impl,
390                                 NULL);
391         g_object_unref (impl);
392
393         return manager;
394 }
395
396 /**
397  * gupnp_context_manager_manage_control_point:
398  * @manager: A #GUPnPContextManager
399  * @control_point: The #GUPnPControlPoint to be taken care of
400  *
401  * By calling this function, you are asking @manager to keep a reference to
402  * @control_point until it's associated #GUPnPContext is no longer available.
403  * You usually want to call this function from
404  * #GUPnPContextManager::context-available handler after you create a
405  * #GUPnPControlPoint object for the newly available context.
406  **/
407 void
408 gupnp_context_manager_manage_control_point (GUPnPContextManager *manager,
409                                             GUPnPControlPoint   *control_point)
410 {
411         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
412         g_return_if_fail (GUPNP_IS_CONTROL_POINT (control_point));
413
414         manager->priv->objects = g_list_append (manager->priv->objects,
415                                                 g_object_ref (control_point));
416 }
417
418 /**
419  * gupnp_context_manager_manage_root_device:
420  * @manager: A #GUPnPContextManager
421  * @root_device: The #GUPnPRootDevice to be taken care of
422  *
423  * By calling this function, you are asking @manager to keep a reference to
424  * @root_device when it's associated #GUPnPContext is no longer available. You
425  * usually want to call this function from
426  * #GUPnPContextManager::context-available handler after you create a
427  * #GUPnPRootDevice object for the newly available context.
428  **/
429 void
430 gupnp_context_manager_manage_root_device (GUPnPContextManager *manager,
431                                           GUPnPRootDevice     *root_device)
432 {
433         g_return_if_fail (GUPNP_IS_CONTEXT_MANAGER (manager));
434         g_return_if_fail (GUPNP_IS_ROOT_DEVICE (root_device));
435
436         manager->priv->objects = g_list_append (manager->priv->objects,
437                                                 g_object_ref (root_device));
438 }
439