tizen 2.3.1 release
[external/gupnp.git] / libgupnp / gupnp-network-manager.c
1 /*
2  * Copyright (C) 2009 Nokia Corporation, all rights reserved.
3  *
4  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
5  *                               <zeeshan.ali@nokia.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:gupnp-network-manager
25  * @short_description: NetworkManager-based implementation of
26  * #GUPnPContextManager.
27  *
28  */
29
30 #include <config.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <dbus/dbus-glib.h>
36 #include <dbus/dbus-glib-lowlevel.h>
37 #include <dbus/dbus.h>
38
39 #include "gupnp-network-manager.h"
40 #include "gupnp-context.h"
41 #include "gupnp-marshal.h"
42
43 #define DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH \
44         (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
45
46 #define DBUS_SERVICE_NM "org.freedesktop.NetworkManager"
47 #define MANAGER_PATH "/org/freedesktop/NetworkManager"
48 #define MANAGER_INTERFACE "org.freedesktop.NetworkManager"
49 #define DEVICE_INTERFACE "org.freedesktop.NetworkManager.Device"
50
51 G_DEFINE_TYPE (GUPnPNetworkManager,
52                gupnp_network_manager,
53                GUPNP_TYPE_CONTEXT_MANAGER);
54
55 typedef enum
56 {
57         NM_DEVICE_STATE_UNKNOWN,
58         NM_DEVICE_STATE_UNMANAGED,
59         NM_DEVICE_STATE_UNAVAILABLE,
60         NM_DEVICE_STATE_DISCONNECTED,
61         NM_DEVICE_STATE_PREPARE,
62         NM_DEVICE_STATE_CONFIG,
63         NM_DEVICE_STATE_NEED_AUTH,
64         NM_DEVICE_STATE_IP_CONFIG,
65         NM_DEVICE_STATE_ACTIVATED,
66         NM_DEVICE_STATE_FAILED
67 } NMDeviceState;
68
69 typedef struct
70 {
71         GUPnPNetworkManager *manager;
72
73         GUPnPContext *context;
74
75         DBusGProxy *device_proxy;
76         DBusGProxy *prop_proxy;
77 } NMDevice;
78
79 struct _GUPnPNetworkManagerPrivate {
80         DBusConnection *dbus_connection;
81         DBusGConnection *connection;
82         DBusGProxy *manager_proxy;
83
84         GSource *idle_context_creation_src;
85
86         GList *nm_devices;
87 };
88
89 static NMDevice *
90 nm_device_new (GUPnPNetworkManager *manager,
91                const char          *device_path)
92 {
93         NMDevice *nm_device;
94
95         nm_device = g_slice_new0 (NMDevice);
96
97         nm_device->manager = g_object_ref (manager);
98
99         nm_device->prop_proxy = dbus_g_proxy_new_for_name (
100                                         manager->priv->connection,
101                                         DBUS_SERVICE_NM,
102                                         device_path,
103                                         DBUS_INTERFACE_PROPERTIES);
104
105         nm_device->device_proxy = dbus_g_proxy_new_for_name (
106                                                   manager->priv->connection,
107                                                   DBUS_SERVICE_NM,
108                                                   device_path,
109                                                   DEVICE_INTERFACE);
110
111         return nm_device;
112 }
113
114 static void
115 nm_device_free (NMDevice *nm_device)
116 {
117         g_object_unref (nm_device->prop_proxy);
118         g_object_unref (nm_device->device_proxy);
119
120         if (nm_device->context != NULL) {
121                 g_signal_emit_by_name (nm_device->manager,
122                                        "context-unavailable",
123                                        nm_device->context);
124
125                 g_object_unref (nm_device->context);
126         }
127
128         g_object_unref (nm_device->manager);
129
130         g_slice_free (NMDevice, nm_device);
131 }
132
133 #define LOOPBACK_IFACE "lo"
134
135 static gboolean
136 create_loopback_context (gpointer data)
137 {
138         GUPnPNetworkManager *manager = (GUPnPNetworkManager *) data;
139         GUPnPContext *context;
140         GMainContext *main_context;
141         guint port;
142         GError *error = NULL;
143
144         manager->priv->idle_context_creation_src = NULL;
145
146         g_object_get (manager,
147                       "main-context", &main_context,
148                       "port", &port,
149                       NULL);
150
151         context = g_object_new (GUPNP_TYPE_CONTEXT,
152                                 "main-context", main_context,
153                                 "interface", LOOPBACK_IFACE,
154                                 "port", port,
155                                 "error", &error,
156                                 NULL);
157         if (error) {
158                 g_warning ("Error creating GUPnP context: %s\n",
159                            error->message);
160
161                 g_error_free (error);
162                 return FALSE;
163         }
164
165         g_signal_emit_by_name (manager, "context-available", context);
166
167         g_object_unref (context);
168
169         return FALSE;
170 }
171
172 static void
173 create_context_for_device (NMDevice *nm_device, const char *iface)
174 {
175         GError *error = NULL;
176         GMainContext *main_context;
177         guint port;
178
179         g_object_get (nm_device->manager,
180                       "main-context", &main_context,
181                       "port", &port,
182                       NULL);
183
184         nm_device->context = g_object_new (GUPNP_TYPE_CONTEXT,
185                                            "main-context", main_context,
186                                            "interface", iface,
187                                            "port", port,
188                                            "error", &error,
189                                            NULL);
190         if (error) {
191                 g_warning ("Error creating GUPnP context: %s\n",
192                            error->message);
193
194                 g_error_free (error);
195
196                 return;
197         }
198
199         g_signal_emit_by_name (nm_device->manager,
200                                "context-available",
201                                nm_device->context);
202 }
203
204 static void
205 get_device_interface_cb (DBusGProxy     *proxy,
206                          DBusGProxyCall *call,
207                          void           *user_data)
208 {
209         GValue value = {0,};
210         GError *error = NULL;
211         NMDevice *nm_device = (NMDevice *) user_data;
212
213         if (!dbus_g_proxy_end_call (nm_device->prop_proxy,
214                                     call,
215                                     &error,
216                                     G_TYPE_VALUE, &value,
217                                     G_TYPE_INVALID)) {
218                 g_warning ("Error reading property from object: %s\n",
219                            error->message);
220
221                 g_error_free (error);
222                 return;
223         }
224
225         create_context_for_device (nm_device, g_value_get_string (&value));
226         g_value_unset (&value);
227 }
228
229 static void
230 get_device_state_cb (DBusGProxy     *proxy,
231                      DBusGProxyCall *call,
232                      void           *user_data)
233 {
234         GValue value = {0,};
235         GError *error = NULL;
236         NMDevice *nm_device = (NMDevice *) user_data;
237
238         if (!dbus_g_proxy_end_call (proxy,
239                                     call,
240                                     &error,
241                                     G_TYPE_VALUE, &value,
242                                     G_TYPE_INVALID)) {
243                 g_warning ("Error reading property: %s\n", error->message);
244
245                 g_error_free (error);
246                 return;
247         }
248
249         NMDeviceState state = g_value_get_uint (&value);
250
251         if (state == NM_DEVICE_STATE_ACTIVATED) {
252                 dbus_g_proxy_begin_call (nm_device->prop_proxy,
253                                          "Get",
254                                          get_device_interface_cb,
255                                          nm_device,
256                                          NULL,
257                                          G_TYPE_STRING, DEVICE_INTERFACE,
258                                          G_TYPE_STRING, "Interface",
259                                          G_TYPE_INVALID);
260         }
261
262         g_value_unset (&value);
263 }
264
265 static void
266 on_device_state_changed (DBusGProxy *proxy,
267                          guint       new_state,
268                          guint       old_state,
269                          guint       reason,
270                          void       *user_data)
271 {
272         NMDevice *nm_device;
273
274         nm_device = (NMDevice *) user_data;
275
276         if (new_state == NM_DEVICE_STATE_ACTIVATED) {
277                 dbus_g_proxy_begin_call (nm_device->prop_proxy,
278                                          "Get",
279                                          get_device_interface_cb,
280                                          nm_device,
281                                          NULL,
282                                          G_TYPE_STRING, DEVICE_INTERFACE,
283                                          G_TYPE_STRING, "Interface",
284                                          G_TYPE_INVALID);
285         } else if (nm_device->context != NULL) {
286                 /* For all other states we just destroy the context */
287                 g_signal_emit_by_name (nm_device->manager,
288                                        "context-unavailable",
289                                        nm_device->context);
290
291                 g_object_unref (nm_device->context);
292                 nm_device->context = NULL;
293         }
294 }
295
296 static void
297 add_device_from_path (char                *device_path,
298                       GUPnPNetworkManager *manager)
299 {
300         NMDevice *nm_device;
301
302         nm_device = nm_device_new (manager, device_path);
303
304         manager->priv->nm_devices = g_list_append (manager->priv->nm_devices,
305                                                    nm_device);
306
307         dbus_g_proxy_add_signal (nm_device->device_proxy,
308                                  "StateChanged",
309                                  G_TYPE_UINT,
310                                  G_TYPE_UINT,
311                                  G_TYPE_UINT,
312                                  G_TYPE_INVALID);
313
314         dbus_g_proxy_connect_signal (nm_device->device_proxy,
315                                      "StateChanged",
316                                      G_CALLBACK (on_device_state_changed),
317                                      nm_device,
318                                      NULL);
319
320         dbus_g_proxy_begin_call (nm_device->prop_proxy,
321                                  "Get",
322                                  get_device_state_cb,
323                                  nm_device,
324                                  NULL,
325                                  G_TYPE_STRING, DEVICE_INTERFACE,
326                                  G_TYPE_STRING, "State",
327                                  G_TYPE_INVALID);
328 }
329
330 static void
331 on_device_added (DBusGProxy *proxy,
332                  char       *device_path,
333                  gpointer user_data)
334 {
335         add_device_from_path (device_path, GUPNP_NETWORK_MANAGER (user_data));
336 }
337
338 static int
339 compare_device_path (NMDevice *nm_device, char *device_path)
340 {
341      const char *path;
342
343      path = dbus_g_proxy_get_path (nm_device->device_proxy);
344      if (G_UNLIKELY (path == NULL))
345              return -1;
346
347      return strcmp (path, device_path);
348 }
349
350 static void
351 on_device_removed (DBusGProxy *proxy,
352                    char       *device_path,
353                    gpointer    user_data)
354 {
355         GList *device_node;
356         NMDevice *nm_device;
357         GUPnPNetworkManagerPrivate *priv;
358
359         priv = GUPNP_NETWORK_MANAGER (user_data)->priv;
360
361         device_node = g_list_find_custom (priv->nm_devices,
362                                           device_path,
363                                           (GCompareFunc) compare_device_path);
364         if (G_UNLIKELY (device_node == NULL))
365                 return;
366
367         nm_device = (NMDevice *) device_node->data;
368
369         priv->nm_devices = g_list_remove (priv->nm_devices, nm_device);
370         nm_device_free (nm_device);
371 }
372
373 static void
374 get_devices_cb (DBusGProxy     *proxy,
375                 DBusGProxyCall *call,
376                 void           *user_data)
377 {
378         GPtrArray *device_paths;
379         GError *error = NULL;
380
381         if (!dbus_g_proxy_end_call (proxy,
382                                     call,
383                                     &error,
384                                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
385                                     &device_paths,
386                                     G_TYPE_INVALID)) {
387                 g_warning ("Error fetching list of devices: %s\n",
388                            error->message);
389
390                 g_error_free (error);
391                 return;
392         }
393
394         g_ptr_array_foreach (device_paths,
395                              (GFunc) add_device_from_path,
396                              user_data);
397
398         g_ptr_array_foreach (device_paths, (GFunc) g_free, NULL);
399         g_ptr_array_free (device_paths, TRUE);
400 }
401
402 static void
403 schedule_loopback_context_creation (GUPnPNetworkManager *manager)
404 {
405         GMainContext *main_context;
406
407         g_object_get (manager,
408                       "main-context", &main_context,
409                       NULL);
410
411         /* Create contexts in mainloop so that is happens after user has hooked
412          * to the "context-available" signal.
413          */
414         manager->priv->idle_context_creation_src = g_idle_source_new ();
415         g_source_attach (manager->priv->idle_context_creation_src,
416             main_context);
417         g_source_set_callback (manager->priv->idle_context_creation_src,
418                                create_loopback_context,
419                                manager,
420                                NULL);
421         g_source_unref (manager->priv->idle_context_creation_src);
422 }
423
424 static DBusGConnection *
425 connect_to_system_bus (GMainContext    *main_context,
426                        DBusConnection **connection)
427 {
428         DBusGConnection *gconnection = NULL;
429         DBusError derror;
430
431         /* Do fake open to initialize types */
432         dbus_g_connection_open ("", NULL);
433         dbus_error_init (&derror);
434         *connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &derror);
435         if (*connection == NULL) {
436                 g_message ("Failed to connect to System Bus: %s",
437                            derror.message);
438                 dbus_error_free (&derror);
439
440                 return NULL;
441         }
442
443         dbus_connection_setup_with_g_main (*connection, main_context);
444         gconnection = dbus_connection_get_g_connection (*connection);
445         if (G_UNLIKELY (gconnection == NULL)) {
446                 g_message ("Failed to connect to System Bus.");
447
448                 return NULL;
449         }
450
451         return gconnection;
452 }
453
454 static void
455 init_network_manager (GUPnPNetworkManager *manager)
456 {
457         GUPnPNetworkManagerPrivate *priv;
458         GMainContext *main_context;
459
460         priv = manager->priv;
461
462         g_object_get (manager, "main-context", &main_context, NULL);
463
464         priv->connection = connect_to_system_bus (main_context,
465                                                   &priv->dbus_connection);
466         if (G_UNLIKELY (priv->connection == NULL)) {
467                 return;
468         }
469
470         priv->manager_proxy = dbus_g_proxy_new_for_name (priv->connection,
471                                                          DBUS_SERVICE_NM,
472                                                          MANAGER_PATH,
473                                                          MANAGER_INTERFACE);
474
475         dbus_g_proxy_add_signal (priv->manager_proxy,
476                                  "DeviceAdded",
477                                  DBUS_TYPE_G_OBJECT_PATH,
478                                  G_TYPE_INVALID);
479         dbus_g_proxy_connect_signal (priv->manager_proxy,
480                                      "DeviceAdded",
481                                      G_CALLBACK (on_device_added),
482                                      manager,
483                                      NULL);
484
485         dbus_g_proxy_add_signal (priv->manager_proxy,
486                                  "DeviceRemoved",
487                                  DBUS_TYPE_G_OBJECT_PATH,
488                                  G_TYPE_INVALID);
489         dbus_g_proxy_connect_signal (priv->manager_proxy,
490                                      "DeviceRemoved",
491                                      G_CALLBACK (on_device_removed),
492                                      manager,
493                                      NULL);
494
495         dbus_g_proxy_begin_call (priv->manager_proxy,
496                                  "GetDevices",
497                                  get_devices_cb,
498                                  manager,
499                                  NULL,
500                                  G_TYPE_INVALID);
501 }
502
503 static void
504 gupnp_network_manager_init (GUPnPNetworkManager *manager)
505 {
506         manager->priv =
507                 G_TYPE_INSTANCE_GET_PRIVATE (manager,
508                                              GUPNP_TYPE_NETWORK_MANAGER,
509                                              GUPnPNetworkManagerPrivate);
510 }
511
512 static void
513 gupnp_network_manager_constructed (GObject *object)
514 {
515         GUPnPNetworkManager *manager;
516         GUPnPNetworkManagerPrivate *priv;
517         GObjectClass *object_class;
518
519         manager = GUPNP_NETWORK_MANAGER (object);
520         priv = manager->priv;
521
522         priv->nm_devices = NULL;
523
524         init_network_manager (manager);
525
526         schedule_loopback_context_creation (manager);
527
528         /* Call super */
529         object_class = G_OBJECT_CLASS (gupnp_network_manager_parent_class);
530         if (object_class->constructed != NULL) {
531                 object_class->constructed (object);
532         }
533 }
534
535 static void
536 gupnp_network_manager_dispose (GObject *object)
537 {
538         GUPnPNetworkManager *manager;
539         GUPnPNetworkManagerPrivate *priv;
540         GObjectClass *object_class;
541
542         manager = GUPNP_NETWORK_MANAGER (object);
543         priv = manager->priv;
544
545         if (manager->priv->idle_context_creation_src) {
546                 g_source_destroy (manager->priv->idle_context_creation_src);
547                 manager->priv->idle_context_creation_src = NULL;
548         }
549
550         if (priv->manager_proxy != NULL) {
551                 g_object_unref (priv->manager_proxy);
552                 priv->manager_proxy = NULL;
553         }
554
555         if (priv->nm_devices != NULL) {
556                 g_list_foreach (priv->nm_devices, (GFunc) nm_device_free, NULL);
557                 g_list_free (priv->nm_devices);
558                 priv->nm_devices = NULL;
559         }
560
561         if (priv->connection)  {
562           dbus_connection_close (priv->dbus_connection);
563           dbus_g_connection_unref (priv->connection);
564           priv->connection = NULL;
565         }
566
567         /* Call super */
568         object_class = G_OBJECT_CLASS (gupnp_network_manager_parent_class);
569         object_class->dispose (object);
570 }
571
572 static void
573 gupnp_network_manager_class_init (GUPnPNetworkManagerClass *klass)
574 {
575         GObjectClass *object_class;
576
577         object_class = G_OBJECT_CLASS (klass);
578
579         object_class->constructed  = gupnp_network_manager_constructed;
580         object_class->dispose      = gupnp_network_manager_dispose;
581
582         dbus_g_object_register_marshaller (gupnp_marshal_VOID__UINT_UINT_UINT,
583                                            G_TYPE_NONE,
584                                            G_TYPE_UINT,
585                                            G_TYPE_UINT,
586                                            G_TYPE_UINT,
587                                            G_TYPE_INVALID);
588
589         g_type_class_add_private (klass, sizeof (GUPnPNetworkManagerPrivate));
590 }
591
592 gboolean
593 gupnp_network_manager_is_available (GMainContext *main_context)
594 {
595         DBusConnection *connection;
596         DBusGConnection *gconnection;
597         DBusGProxy *dbus_proxy;
598         GError *error = NULL;
599         gboolean ret = FALSE;
600
601         gconnection = connect_to_system_bus (main_context, &connection);
602         if (gconnection == NULL)
603                 return ret;
604
605         dbus_proxy = dbus_g_proxy_new_for_name (gconnection,
606                                                 DBUS_SERVICE_DBUS,
607                                                 DBUS_PATH_DBUS,
608                                                 DBUS_INTERFACE_DBUS);
609
610         if (!dbus_g_proxy_call (dbus_proxy,
611                                 "NameHasOwner",
612                                 &error,
613                                 G_TYPE_STRING, DBUS_SERVICE_NM,
614                                 G_TYPE_INVALID,
615                                 G_TYPE_BOOLEAN, &ret,
616                                 G_TYPE_INVALID)) {
617                 g_warning ("%s.NameHasOwner() failed: %s",
618                            DBUS_INTERFACE_DBUS,
619                            error->message);
620                 g_error_free (error);
621         }
622
623         g_object_unref (dbus_proxy);
624         dbus_connection_close (connection);
625         dbus_g_connection_unref (gconnection);
626
627         return ret;
628 }