1 /*
2  * Copyright (C) 2007 Zeeshan Ali.
3  * Copyright (C) 2007 OpenedHand Ltd.
4  *
5  * Author: Zeeshan Ali <zeenix@gstreamer.net>
6  * Author: Jorn Baayen <jorn@openedhand.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #include <libgupnp/gupnp-root-device.h>
24 #include <libgupnp/gupnp-service.h>
25 #include <stdio.h>
26 #include <locale.h>
27 #include <string.h>
28 #include <gmodule.h>
29 #include <glib/gstdio.h>
30 
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34 
35 #include "gui.h"
36 #include "upnp.h"
37 #include "main.h"
38 
39 #define DESCRIPTION_DOC "xml/network-light-desc.xml"
40 #define DIMMING_SERVICE "urn:schemas-upnp-org:service:Dimming:1"
41 #define SWITCH_SERVICE "urn:schemas-upnp-org:service:SwitchPower:1"
42 #define NETWORK_LIGHT "urn:schemas-upnp-org:device:DimmableLight:1"
43 
44 typedef struct
45 {
46         GUPnPRootDevice *dev;
47         GUPnPServiceInfo *switch_power;
48         GUPnPServiceInfo *dimming;
49 } NetworkLight;
50 
51 static GUPnPContextManager *context_manager;
52 static GHashTable *nl_hash;
53 
54 /* Other network light services on the network */
55 static GList *switch_proxies;
56 static GList *dimming_proxies;
57 
58 static GUPnPXMLDoc *doc;
59 static char *desc_location;
60 static char *uuid;
61 
62 void
63 on_get_status (GUPnPService       *service,
64                GUPnPServiceAction *action,
65                gpointer            user_data);
66 
67 void
68 on_get_target (GUPnPService       *service,
69                GUPnPServiceAction *action,
70                gpointer            user_data);
71 
72 void
73 on_set_target (GUPnPService       *service,
74                GUPnPServiceAction *action,
75                gpointer            user_data);
76 
77 void
78 on_query_status (GUPnPService *service,
79                  const char   *variable_name,
80                  GValue       *value,
81                  gpointer      user_data);
82 
83 void
84 on_query_target (GUPnPService *service,
85                  const char   *variable_name,
86                  GValue       *value,
87                  gpointer      user_data);
88 
89 void
90 on_get_load_level_status (GUPnPService       *service,
91                           GUPnPServiceAction *action,
92                           gpointer            user_data);
93 
94 void
95 on_get_load_level_target (GUPnPService       *service,
96                           GUPnPServiceAction *action,
97                           gpointer            user_data);
98 
99 void
100 on_set_load_level_target (GUPnPService       *service,
101                           GUPnPServiceAction *action,
102                           gpointer            user_data);
103 
104 void
105 on_query_load_level_status (GUPnPService *service,
106                             const char   *variable_name,
107                             GValue       *value,
108                             gpointer      user_data);
109 
110 void
111 on_query_load_level_target (GUPnPService *service,
112                             const char   *variable_name,
113                             GValue       *value,
114                             gpointer      user_data);
115 
116 static NetworkLight *
network_light_new(GUPnPRootDevice * dev,GUPnPServiceInfo * switch_power,GUPnPServiceInfo * dimming)117 network_light_new (GUPnPRootDevice  *dev,
118                    GUPnPServiceInfo *switch_power,
119                    GUPnPServiceInfo *dimming)
120 {
121         NetworkLight *network_light;
122 
123         network_light = g_slice_new (NetworkLight);
124 
125         network_light->dev = dev;
126         network_light->switch_power = switch_power;
127         network_light->dimming = dimming;
128 
129         return network_light;
130 }
131 
132 static void
network_light_free(NetworkLight * network_light)133 network_light_free (NetworkLight *network_light)
134 {
135         g_object_unref (network_light->dev);
136         g_object_unref (network_light->switch_power);
137         g_object_unref (network_light->dimming);
138 
139         g_slice_free (NetworkLight, network_light);
140 }
141 
142 void
notify_status_change(gboolean status)143 notify_status_change (gboolean status)
144 {
145         GList *network_lights;
146         GList *nl_node;
147 
148         network_lights = g_hash_table_get_values (nl_hash);
149 
150         for (nl_node = network_lights;
151              nl_node != NULL;
152              nl_node = nl_node->next) {
153                 NetworkLight *nl = (NetworkLight *) nl_node->data;
154 
155                 gupnp_service_notify (GUPNP_SERVICE (nl->switch_power),
156                                       "Status",
157                                       G_TYPE_BOOLEAN,
158                                       status,
159                                       NULL);
160         }
161 
162         g_list_free (network_lights);
163 }
164 
165 void
notify_load_level_change(gint load_level)166 notify_load_level_change (gint load_level)
167 {
168         GList *network_lights;
169         GList *nl_node;
170 
171         network_lights = g_hash_table_get_values (nl_hash);
172 
173         for (nl_node = network_lights;
174              nl_node != NULL;
175              nl_node = nl_node->next) {
176                 NetworkLight *nl = (NetworkLight *) nl_node->data;
177 
178                 gupnp_service_notify (GUPNP_SERVICE (nl->dimming),
179                                       "LoadLevelStatus",
180                                       G_TYPE_UINT,
181                                       load_level,
182                                       NULL);
183         }
184 
185         g_list_free (network_lights);
186 }
187 
188 G_MODULE_EXPORT
189 void
on_get_status(GUPnPService * service,GUPnPServiceAction * action,gpointer user_data)190 on_get_status (GUPnPService       *service,
191                GUPnPServiceAction *action,
192                gpointer            user_data)
193 {
194         gupnp_service_action_set (action,
195                                   "ResultStatus",
196                                   G_TYPE_BOOLEAN,
197                                   get_status (),
198                                   NULL);
199 
200         gupnp_service_action_return (action);
201 }
202 
203 G_MODULE_EXPORT
204 void
on_get_target(GUPnPService * service,GUPnPServiceAction * action,gpointer user_data)205 on_get_target (GUPnPService       *service,
206                GUPnPServiceAction *action,
207                gpointer            user_data)
208 {
209         gupnp_service_action_set (action,
210                                   "RetTargetValue",
211                                   G_TYPE_BOOLEAN,
212                                   get_status (),
213                                   NULL);
214 
215         gupnp_service_action_return (action);
216 }
217 
218 G_MODULE_EXPORT
219 void
on_set_target(GUPnPService * service,GUPnPServiceAction * action,gpointer user_data)220 on_set_target (GUPnPService       *service,
221                GUPnPServiceAction *action,
222                gpointer            user_data)
223 {
224         gboolean status;
225 
226         gupnp_service_action_get (action,
227                                   "newTargetValue",
228                                   G_TYPE_BOOLEAN,
229                                   &status,
230                                   NULL);
231         gupnp_service_action_return (action);
232 
233         set_status (status);
234 }
235 
236 G_MODULE_EXPORT
237 void
on_query_status(GUPnPService * service,const char * variable_name,GValue * value,gpointer user_data)238 on_query_status (GUPnPService *service,
239                  const char   *variable_name,
240                  GValue       *value,
241                  gpointer      user_data)
242 {
243         g_value_init (value, G_TYPE_BOOLEAN);
244         g_value_set_boolean (value, get_status ());
245 }
246 
247 G_MODULE_EXPORT
248 void
on_query_target(GUPnPService * service,const char * variable_name,GValue * value,gpointer user_data)249 on_query_target (GUPnPService *service,
250                  const char   *variable_name,
251                  GValue       *value,
252                  gpointer      user_data)
253 {
254         g_value_init (value, G_TYPE_BOOLEAN);
255         g_value_set_boolean (value, get_status ());
256 }
257 
258 G_MODULE_EXPORT
259 void
on_get_load_level_status(GUPnPService * service,GUPnPServiceAction * action,gpointer user_data)260 on_get_load_level_status (GUPnPService       *service,
261                           GUPnPServiceAction *action,
262                           gpointer            user_data)
263 {
264         gupnp_service_action_set (action,
265                                   "retLoadlevelStatus",
266                                   G_TYPE_UINT,
267                                   get_load_level (),
268                                   NULL);
269 
270         gupnp_service_action_return (action);
271 }
272 
273 G_MODULE_EXPORT
274 void
on_get_load_level_target(GUPnPService * service,GUPnPServiceAction * action,gpointer user_data)275 on_get_load_level_target (GUPnPService       *service,
276                           GUPnPServiceAction *action,
277                           gpointer            user_data)
278 {
279         gupnp_service_action_set (action,
280                                   "retLoadlevelTarget",
281                                   G_TYPE_UINT,
282                                   get_load_level (),
283                                   NULL);
284 
285         gupnp_service_action_return (action);
286 }
287 
288 G_MODULE_EXPORT
289 void
on_set_load_level_target(GUPnPService * service,GUPnPServiceAction * action,gpointer user_data)290 on_set_load_level_target (GUPnPService       *service,
291                           GUPnPServiceAction *action,
292                           gpointer            user_data)
293 {
294         guint load_level;
295 
296         gupnp_service_action_get (action,
297                                   "newLoadlevelTarget",
298                                   G_TYPE_UINT,
299                                   &load_level,
300                                   NULL);
301         gupnp_service_action_return (action);
302 
303         if (load_level > 100)
304                 load_level = 100;
305 
306         set_load_level (load_level);
307 }
308 
309 G_MODULE_EXPORT
310 void
on_query_load_level_status(GUPnPService * service,const char * variable_name,GValue * value,gpointer user_data)311 on_query_load_level_status (GUPnPService *service,
312                             const char   *variable_name,
313                             GValue       *value,
314                             gpointer      user_data)
315 {
316         g_value_init (value, G_TYPE_UINT);
317         g_value_set_uint (value, get_load_level ());
318 }
319 
320 G_MODULE_EXPORT
321 void
on_query_load_level_target(GUPnPService * service,const char * variable_name,GValue * value,gpointer user_data)322 on_query_load_level_target (GUPnPService *service,
323                             const char   *variable_name,
324                             GValue       *value,
325                             gpointer      user_data)
326 {
327         g_value_init (value, G_TYPE_UINT);
328         g_value_set_uint (value, get_load_level ());
329 }
330 
331 static void
on_notify_failed(GUPnPService * service,const GList * callback_urls,const GError * reason,gpointer user_data)332 on_notify_failed (GUPnPService *service,
333                   const GList  *callback_urls,
334                   const GError *reason,
335                   gpointer      user_data)
336 {
337         GList   *url_node;
338         GString *warning;
339 
340         warning = g_string_new (NULL);
341         g_string_printf (warning,
342                          "NOTIFY failed for the following client URLs:\n");
343         for (url_node = (GList *) callback_urls;
344              url_node;
345              url_node = url_node->next) {
346                 g_string_append_printf (warning,
347                                         "%s\n",
348                                         (char *) url_node->data);
349         }
350         g_string_append_printf (warning, "Reason: %s", reason->message);
351 
352         g_warning ("%s", warning->str);
353         g_string_free (warning, TRUE);
354 }
355 
356 /* Copied from gupnp/libgupnp/xml-utils.c */
357 static xmlNode *
xml_util_get_element(xmlNode * node,...)358 xml_util_get_element (xmlNode *node,
359                       ...)
360 {
361         va_list var_args;
362 
363         va_start (var_args, node);
364 
365         while (TRUE) {
366                 const char *arg;
367 
368                 arg = va_arg (var_args, const char *);
369                 if (!arg)
370                         break;
371 
372                 for (node = node->children; node; node = node->next) {
373                         if (node->name == NULL)
374                                 continue;
375 
376                         if (!strcmp (arg, (char *) node->name))
377                                 break;
378                 }
379 
380                 if (!node)
381                         break;
382         }
383 
384         va_end (var_args);
385 
386         return node;
387 }
388 
init_friendly_name(gchar * name)389 static void init_friendly_name (gchar *name)
390 {
391         xmlNode *fdn_node;
392 
393         fdn_node = xml_util_get_element ((xmlNode *)
394                 gupnp_xml_doc_get_doc (doc),
395                                           "root",
396                                           "device",
397                                           "friendlyName",
398                                           NULL);
399         if (fdn_node == NULL) {
400                 g_warning ("Failed to find friendly name element"
401                             "in device description, "
402                             "using default value");
403 
404                 return;
405         }
406 
407         xmlNodeSetContent (fdn_node, (unsigned char *) name);
408 }
409 
init_uuid(void)410 static void init_uuid (void)
411 {
412         xmlNode *uuid_node;
413         char *udn;
414 
415         uuid = gupnp_get_uuid ();
416         const xmlDoc *xml_doc = gupnp_xml_doc_get_doc (doc);
417 
418         uuid_node = xml_util_get_element ((xmlNode *) xml_doc,
419                                           "root",
420                                           "device",
421                                           "UDN",
422                                           NULL);
423         if (uuid_node == NULL) {
424                 g_critical ("Failed to find UDN element"
425                             "in device description");
426 
427                 return;
428         }
429 
430         udn = g_strdup_printf ("uuid:%s", uuid);
431 
432         xmlNodeSetContent (uuid_node, (unsigned char *) udn);
433 
434         g_free (udn);
435 }
436 
437 static void
on_service_proxy_action_ret(GObject * object,GAsyncResult * result,gpointer user_data)438 on_service_proxy_action_ret (GObject *object,
439                              GAsyncResult *result,
440                              gpointer user_data)
441 {
442         GError *error = NULL;
443 
444         gupnp_service_proxy_call_action_finish (GUPNP_SERVICE_PROXY (object),
445                                                 result,
446                                                 &error);
447 
448         if (error != NULL) {
449                 GUPnPServiceInfo *info = GUPNP_SERVICE_INFO (object);
450 
451                 g_warning ("Failed to call action \"%s\" on \"%s\": %s",
452                            (char *) user_data,
453                            gupnp_service_info_get_location (info),
454                            error->message);
455 
456                 g_error_free (error);
457         }
458 
459         g_free (user_data);
460 }
461 
462 void
set_all_status(gboolean status)463 set_all_status (gboolean status)
464 {
465         GList *proxy_node;
466 
467         for (proxy_node = switch_proxies;
468              proxy_node;
469              proxy_node = g_list_next (proxy_node)) {
470                 GUPnPServiceProxy *proxy;
471                 char *action_name;
472                 GUPnPServiceProxyAction *action;
473 
474                 proxy = GUPNP_SERVICE_PROXY (proxy_node->data);
475                 action_name = g_strdup ("SetTarget");
476 
477                 action = gupnp_service_proxy_action_new (action_name,
478                                                          "newTargetValue",
479                                                          G_TYPE_BOOLEAN,
480                                                          status,
481                                                          NULL);
482 
483                 gupnp_service_proxy_call_action_async (
484                         proxy,
485                         action,
486                         NULL,
487                         on_service_proxy_action_ret,
488                         action_name);
489                 gupnp_service_proxy_action_unref (action);
490         }
491 }
492 
493 void
set_all_load_level(gint load_level)494 set_all_load_level (gint load_level)
495 {
496         GList *proxy_node;
497 
498         for (proxy_node = dimming_proxies;
499              proxy_node;
500              proxy_node = g_list_next (proxy_node)) {
501                 GUPnPServiceProxy *proxy;
502                 char *action_name;
503                 GUPnPServiceProxyAction *action;
504 
505                 proxy = GUPNP_SERVICE_PROXY (proxy_node->data);
506                 action_name = g_strdup ("SetLoadLevelTarget");
507 
508                 action = gupnp_service_proxy_action_new (action_name,
509                                                          "newLoadlevelTarget",
510                                                          G_TYPE_UINT,
511                                                          load_level,
512                                                          NULL);
513 
514                 gupnp_service_proxy_call_action_async (
515                         proxy,
516                         action,
517                         NULL,
518                         on_service_proxy_action_ret,
519                         action_name);
520 
521                 gupnp_service_proxy_action_unref (action);
522         }
523 }
524 
525 static void
on_network_light_available(GUPnPControlPoint * cp,GUPnPDeviceProxy * light_proxy,gpointer user_data)526 on_network_light_available (GUPnPControlPoint *cp,
527                             GUPnPDeviceProxy  *light_proxy,
528                             gpointer           user_data)
529 {
530         GUPnPServiceProxy *switch_proxy;
531         GUPnPServiceProxy *dimming_proxy;
532         GUPnPServiceInfo  *info;
533 
534         info = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (light_proxy),
535                                               SWITCH_SERVICE);
536         switch_proxy = GUPNP_SERVICE_PROXY (info);
537 
538         if (switch_proxy) {
539                 switch_proxies = g_list_append (switch_proxies, switch_proxy);
540         }
541 
542         info = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (light_proxy),
543                                               DIMMING_SERVICE);
544         dimming_proxy = GUPNP_SERVICE_PROXY (info);
545 
546         if (dimming_proxy) {
547                 dimming_proxies = g_list_append (dimming_proxies,
548                                                  dimming_proxy);
549         }
550 }
551 
compare_service(GUPnPServiceInfo * info1,GUPnPServiceInfo * info2)552 static gint compare_service (GUPnPServiceInfo *info1,
553                              GUPnPServiceInfo *info2)
554 {
555         const char *udn1;
556         const char *udn2;
557 
558         udn1 = gupnp_service_info_get_udn (info1);
559         udn2 = gupnp_service_info_get_udn (info2);
560 
561         return strcmp (udn1, udn2);
562 }
563 
remove_service_from_list(GUPnPServiceInfo * info,GList ** list)564 static void remove_service_from_list (GUPnPServiceInfo *info,
565                                       GList           **list)
566 {
567         GList *proxy_node;
568 
569         proxy_node = g_list_find_custom (*list,
570                                          info,
571                                          (GCompareFunc) compare_service);
572         if (proxy_node) {
573                 g_object_unref (proxy_node->data);
574                 *list = g_list_remove (*list, proxy_node->data);
575         }
576 }
577 
578 static void
on_network_light_unavailable(GUPnPControlPoint * control_point,GUPnPDeviceProxy * light_proxy,gpointer user_data)579 on_network_light_unavailable (GUPnPControlPoint *control_point,
580                               GUPnPDeviceProxy  *light_proxy,
581                               gpointer           user_data)
582 {
583         GUPnPServiceInfo *info;
584 
585         info = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (light_proxy),
586                                               SWITCH_SERVICE);
587         if (info) {
588                 remove_service_from_list (info, &switch_proxies);
589         }
590 
591         g_clear_object (&info);
592 
593         info = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (light_proxy),
594                                               DIMMING_SERVICE);
595         if (info) {
596                 remove_service_from_list (info, &dimming_proxies);
597         }
598 
599         g_clear_object (&info);
600 }
601 
602 static gboolean
init_server(GUPnPContext * context)603 init_server (GUPnPContext *context)
604 {
605         NetworkLight *network_light;
606         GUPnPRootDevice *dev;
607         GUPnPServiceInfo *switch_power;
608         GUPnPServiceInfo *dimming;
609         GError *error = NULL;
610 
611         /* Create root device */
612         dev = gupnp_root_device_new_full (context,
613                                           gupnp_resource_factory_get_default (),
614                                           doc,
615                                           desc_location,
616                                           DATA_DIR,
617                                           &error);
618         if (error != NULL) {
619                 g_warning ("Failed to create root device: %s",
620                            error->message);
621                 g_error_free (error);
622 
623                 return FALSE;
624         }
625 
626         switch_power = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (dev),
627                                                       SWITCH_SERVICE);
628 
629         if (switch_power) {
630                 gupnp_service_signals_autoconnect
631                                 (GUPNP_SERVICE (switch_power),
632                                  NULL,
633                                  &error);
634 
635                 g_signal_connect (switch_power,
636                                   "notify-failed",
637                                   G_CALLBACK (on_notify_failed),
638                                   NULL);
639         }
640 
641         dimming = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (dev),
642                                                  DIMMING_SERVICE);
643 
644         if (dimming) {
645                 gupnp_service_signals_autoconnect (GUPNP_SERVICE (dimming),
646                                                    NULL,
647                                                    &error);
648 
649                 g_signal_connect (dimming,
650                                   "notify-failed",
651                                   G_CALLBACK (on_notify_failed),
652                                   NULL);
653         }
654 
655         network_light = network_light_new (dev, switch_power, dimming);
656         g_hash_table_insert (nl_hash, g_object_ref (context), network_light);
657 
658         /* Run */
659         gupnp_root_device_set_available (dev, TRUE);
660 
661         g_print ("Attaching to IP/Host %s on port %d\n",
662                  gssdp_client_get_host_ip (GSSDP_CLIENT (context)),
663                  gupnp_context_get_port (context));
664 
665         return TRUE;
666 }
667 
668 static gboolean
prepare_desc(gchar * name)669 prepare_desc (gchar *name)
670 {
671         GError *error = NULL;
672 
673         doc = gupnp_xml_doc_new_from_path (DATA_DIR "/" DESCRIPTION_DOC,
674                                            &error);
675         if (doc == NULL) {
676                 g_critical ("Unable to load the XML description file: %s",
677                             error->message);
678 
679                 g_error_free (error);
680 
681                 return FALSE;
682         }
683 
684         if (name && (strlen(name) > 0)) {
685                 /* set the friendlyName in the xmlDoc */
686                 init_friendly_name (name);
687         }
688 
689         /* create and set the UUID in the xmlDoc */
690         init_uuid ();
691 
692         /* saving the xml file to the temporal location with the uuid name */
693         desc_location = g_strdup_printf ("%s/gupnp-network-light-%s.xml",
694                                          g_get_tmp_dir (),
695                                          uuid);
696         g_assert (desc_location != NULL);
697 
698         if (xmlSaveFile (desc_location, (xmlDoc *) gupnp_xml_doc_get_doc (doc)) < 0) {
699                 g_print ("Error saving description file to %s.\n",
700                          desc_location);
701 
702                 g_free (desc_location);
703                 g_object_unref (doc);
704 
705                 return FALSE;
706         }
707 
708         return TRUE;
709 }
710 
711 static gboolean
init_client(GUPnPContext * context)712 init_client (GUPnPContext *context)
713 {
714         GUPnPControlPoint *cp;
715 
716         cp = gupnp_control_point_new (context, NETWORK_LIGHT);
717 
718         g_signal_connect (cp,
719                           "device-proxy-available",
720                           G_CALLBACK (on_network_light_available),
721                           NULL);
722         g_signal_connect (cp,
723                           "device-proxy-unavailable",
724                           G_CALLBACK (on_network_light_unavailable),
725                           NULL);
726 
727         gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
728 
729         /* Let context manager take care of the control point life cycle */
730         gupnp_context_manager_manage_control_point (context_manager, cp);
731 
732         /* We don't need to keep our own references to the control points */
733         g_object_unref (cp);
734 
735         return TRUE;
736 }
737 
738 static void
on_context_available(GUPnPContextManager * manager,GUPnPContext * context,gpointer user_data)739 on_context_available (GUPnPContextManager *manager,
740                       GUPnPContext        *context,
741                       gpointer             user_data)
742 {
743         /* Initialize client-side stuff */
744         init_client (context);
745 
746         /* Then the server-side stuff */
747         init_server (context);
748 }
749 
750 static void
on_context_unavailable(GUPnPContextManager * manager,GUPnPContext * context,gpointer user_data)751 on_context_unavailable (GUPnPContextManager *manager,
752                         GUPnPContext        *context,
753                         gpointer             user_data)
754 {
755         g_print ("Detaching from IP/Host %s and port %d\n",
756                  gssdp_client_get_host_ip (GSSDP_CLIENT (context)),
757                  gupnp_context_get_port (context));
758 
759         g_hash_table_remove (nl_hash, context);
760 }
761 
762 static gboolean
context_equal(GUPnPContext * context1,GUPnPContext * context2)763 context_equal (GUPnPContext *context1, GUPnPContext *context2)
764 {
765         return g_ascii_strcasecmp (gssdp_client_get_host_ip (GSSDP_CLIENT (context1)),
766                                    gssdp_client_get_host_ip (GSSDP_CLIENT (context2))) == 0;
767 }
768 
769 gboolean
init_upnp(gchar ** interfaces,guint port,gchar * name,gboolean ipv4,gboolean ipv6)770 init_upnp (gchar **interfaces, guint port, gchar *name, gboolean ipv4, gboolean ipv6)
771 {
772         GUPnPWhiteList *white_list;
773 
774         switch_proxies = NULL;
775         dimming_proxies = NULL;
776 
777         nl_hash = g_hash_table_new_full (g_direct_hash,
778                                          (GEqualFunc) context_equal,
779                                          g_object_unref,
780                                          (GDestroyNotify) network_light_free);
781 
782         if (!prepare_desc (name)) {
783                 return FALSE;
784         }
785 
786         // Default: Both
787         GSocketFamily family = G_SOCKET_FAMILY_INVALID;
788         if (!ipv4 && ipv6) {
789             g_debug ("Option a");
790             family = G_SOCKET_FAMILY_IPV6;
791         } else if (ipv4 && !ipv6) {
792             g_debug ("Option b");
793             family = G_SOCKET_FAMILY_IPV4;
794         } else {
795             g_debug ("Option c");
796             // Neither? Just do nothing and enable both
797         }
798 
799         context_manager = gupnp_context_manager_create_full (GSSDP_UDA_VERSION_1_0,
800                                                              family,
801                                                              port);
802         g_assert (context_manager != NULL);
803 
804         if (interfaces != NULL) {
805                 white_list = gupnp_context_manager_get_white_list
806                                             (context_manager);
807                 gupnp_white_list_add_entryv (white_list, interfaces);
808                 gupnp_white_list_set_enabled (white_list, TRUE);
809         }
810 
811         g_signal_connect (context_manager,
812                           "context-available",
813                           G_CALLBACK (on_context_available),
814                           NULL);
815         g_signal_connect (context_manager,
816                           "context-unavailable",
817                           G_CALLBACK (on_context_unavailable),
818                           NULL);
819 
820         return TRUE;
821 }
822 
823 void
deinit_upnp(void)824 deinit_upnp (void)
825 {
826         g_object_unref (context_manager);
827 
828         g_hash_table_unref (nl_hash);
829 
830         g_list_foreach (switch_proxies, (GFunc) g_object_unref, NULL);
831         g_list_foreach (dimming_proxies, (GFunc) g_object_unref, NULL);
832 
833         /* Unref the descriptiont doc */
834         g_object_unref (doc);
835 
836         if (g_remove (desc_location) != 0)
837                 g_warning ("error removing %s\n", desc_location);
838         g_free (desc_location);
839         g_free (uuid);
840         uuid = NULL;
841 }
842 
843