tizen 2.3.1 release
[external/gupnp.git] / libgupnp / gupnp-root-device.c
1 /*
2  * Copyright (C) 2007, 2008 OpenedHand Ltd.
3  *
4  * Author: Jorn Baayen <jorn@openedhand.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:gupnp-root-device
24  * @short_description: Class for root device implementations.
25  *
26  * #GUPnPRootDevice allows for implementing root devices.
27  */
28
29 #include <string.h>
30
31 #include <libgssdp/gssdp-resource-group.h>
32
33 #include "gupnp-root-device.h"
34 #include "gupnp-context-private.h"
35 #include "http-headers.h"
36 #include "xml-util.h"
37
38 G_DEFINE_TYPE (GUPnPRootDevice,
39                gupnp_root_device,
40                GUPNP_TYPE_DEVICE);
41
42 struct _GUPnPRootDevicePrivate {
43         GUPnPXMLDoc *description_doc;
44
45         GSSDPResourceGroup *group;
46
47         char  *description_path;
48         char  *description_dir;
49         char  *relative_location;
50 };
51
52 enum {
53         PROP_0,
54         PROP_DESCRIPTION_DOC,
55         PROP_DESCRIPTION_PATH,
56         PROP_DESCRIPTION_DIR,
57         PROP_AVAILABLE
58 };
59
60 static void
61 gupnp_root_device_finalize (GObject *object)
62 {
63         GUPnPRootDevice *device;
64         GObjectClass *object_class;
65
66         device = GUPNP_ROOT_DEVICE (object);
67
68         g_free (device->priv->description_path);
69         g_free (device->priv->description_dir);
70         g_free (device->priv->relative_location);
71
72         /* Call super */
73         object_class = G_OBJECT_CLASS (gupnp_root_device_parent_class);
74         object_class->finalize (object);
75 }
76
77 static void
78 gupnp_root_device_dispose (GObject *object)
79 {
80         GUPnPRootDevice *device;
81         GObjectClass *object_class;
82
83         device = GUPNP_ROOT_DEVICE (object);
84
85         if (device->priv->group) {
86                 g_object_unref (device->priv->group);
87                 device->priv->group = NULL;
88         }
89
90         if (device->priv->description_doc) {
91                 g_object_unref (device->priv->description_doc);
92                 device->priv->description_doc = NULL;
93         }
94
95         /* Call super */
96         object_class = G_OBJECT_CLASS (gupnp_root_device_parent_class);
97         object_class->dispose (object);
98 }
99
100 static void
101 gupnp_root_device_init (GUPnPRootDevice *device)
102 {
103         device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
104                                                     GUPNP_TYPE_ROOT_DEVICE,
105                                                     GUPnPRootDevicePrivate);
106 }
107
108 static void
109 gupnp_root_device_set_property (GObject      *object,
110                                 guint         property_id,
111                                 const GValue *value,
112                                 GParamSpec   *pspec)
113 {
114         GUPnPRootDevice *device;
115
116         device = GUPNP_ROOT_DEVICE (object);
117
118         switch (property_id) {
119         case PROP_DESCRIPTION_DOC:
120                 device->priv->description_doc = g_value_dup_object (value);
121                 break;
122         case PROP_DESCRIPTION_PATH:
123                 device->priv->description_path = g_value_dup_string (value);
124                 break;
125         case PROP_DESCRIPTION_DIR:
126                 device->priv->description_dir = g_value_dup_string (value);
127                 break;
128         case PROP_AVAILABLE:
129                 gupnp_root_device_set_available
130                                         (device, g_value_get_boolean (value));
131                 break;
132         default:
133                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
134                 break;
135         }
136 }
137
138 static void
139 gupnp_root_device_get_property (GObject    *object,
140                                 guint       property_id,
141                                 GValue     *value,
142                                 GParamSpec *pspec)
143 {
144         GUPnPRootDevice *device;
145
146         device = GUPNP_ROOT_DEVICE (object);
147
148         switch (property_id) {
149         case PROP_DESCRIPTION_PATH:
150                 g_value_set_string (value,
151                                     device->priv->description_path);
152                 break;
153         case PROP_DESCRIPTION_DIR:
154                 g_value_set_string (value,
155                                     device->priv->description_dir);
156                 break;
157         case PROP_AVAILABLE:
158                 g_value_set_boolean (value,
159                                      gupnp_root_device_get_available (device));
160                 break;
161         default:
162                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
163                 break;
164         }
165 }
166
167 static void
168 fill_resource_group (xmlNode            *element,
169                      const char         *location,
170                      GSSDPResourceGroup *group)
171 {
172         xmlNode *child;
173         xmlChar *udn, *device_type;
174         char *usn;
175
176         /* Add device */
177         udn = xml_util_get_child_element_content (element, "UDN");
178         if (!udn) {
179                 g_warning ("No UDN specified.");
180
181                 return;
182         }
183
184         device_type = xml_util_get_child_element_content (element,
185                                                           "deviceType");
186         if (!device_type) {
187                 g_warning ("No deviceType specified.");
188
189                 return;
190         }
191
192         gssdp_resource_group_add_resource_simple (group,
193                                                   (const char *) udn,
194                                                   (const char *) udn,
195                                                   location);
196
197         usn = g_strdup_printf ("%s::%s", (char *) udn, (char *) device_type);
198         gssdp_resource_group_add_resource_simple (group,
199                                                   (const char *) device_type,
200                                                   (const char *) usn,
201                                                   location);
202         g_free (usn);
203
204         xmlFree (device_type);
205
206         /* Add embedded services */
207         child = xml_util_get_element (element,
208                                       "serviceList",
209                                       NULL);
210         if (child) {
211                 for (child = child->children; child; child = child->next) {
212                         xmlChar *service_type;
213
214                         if (strcmp ("service", (char *) child->name))
215                                 continue;
216
217                         service_type = xml_util_get_child_element_content
218                                                 (child, "serviceType");
219                         if (!service_type)
220                                 continue;
221
222                         usn = g_strdup_printf ("%s::%s",
223                                                (char *) udn,
224                                                (char *) service_type);
225                         gssdp_resource_group_add_resource_simple
226                                                 (group,
227                                                  (const char *) service_type,
228                                                  (const char *) usn,
229                                                  location);
230                         g_free (usn);
231
232                         xmlFree (service_type);
233                 }
234         }
235
236         /* Cleanup */
237         xmlFree (udn);
238
239         /* Add embedded devices */
240         child = xml_util_get_element (element,
241                                       "deviceList",
242                                       NULL);
243         if (child) {
244                 for (child = child->children; child; child = child->next)
245                         if (!strcmp ("device", (char *) child->name))
246                                 fill_resource_group (child, location, group);
247         }
248 }
249
250 /* Load and parse @description_path as an XML document, synchronously. */
251 static GUPnPXMLDoc *
252 load_and_parse (const char *description_path)
253 {
254         GUPnPXMLDoc *description_doc;
255         GError *error = NULL;
256
257         /* We don't, so load and parse it */
258         description_doc = gupnp_xml_doc_new_from_path (description_path,
259                                                        &error);
260         if (description_doc == NULL) {
261                 g_critical ("Error loading description: %s\n", error->message);
262
263                 g_error_free (error);
264         }
265
266         return description_doc;
267 }
268
269 static GObject *
270 gupnp_root_device_constructor (GType                  type,
271                                guint                  n_construct_params,
272                                GObjectConstructParam *construct_params)
273 {
274         GObjectClass *object_class;
275         GObject *object;
276         GUPnPRootDevice *device;
277         GUPnPContext *context;
278         const char *description_path, *description_dir, *udn;
279         char *desc_path, *location, *usn, *relative_location;
280         unsigned int i;
281         GUPnPXMLDoc *description_doc;
282         xmlNode *root_element, *element;
283         SoupURI *url_base;
284
285         object = NULL;
286         location = NULL;
287
288         /* Get 'description-doc', 'context', 'description-dir' and
289          * 'description-path' property values */
290         description_doc   = NULL;
291         context           = NULL;
292         description_path  = NULL;
293         description_dir   = NULL;
294
295         for (i = 0; i < n_construct_params; i++) {
296                 const char *par_name;
297
298                 par_name = construct_params[i].pspec->name;
299
300                 if (strcmp (par_name, "description-doc") == 0) {
301                         description_doc =
302                                 g_value_get_object (construct_params[i].value);
303
304                         continue;
305                 } 
306
307                 if (strcmp (par_name, "context") == 0) {
308                         context =
309                                 g_value_get_object (construct_params[i].value);
310
311                         continue;
312                 }
313
314                 if (strcmp (par_name, "description-path") == 0) {
315                         description_path =
316                                 g_value_get_string (construct_params[i].value);
317
318                         continue;
319                 }
320
321                 if (strcmp (par_name, "description-dir") == 0) {
322                         description_dir =
323                                 g_value_get_string (construct_params[i].value);
324
325                         continue;
326                 }
327         }
328
329         if (!context) {
330                 g_warning ("No context specified.");
331
332                 return NULL;
333         }
334
335         if (!description_path) {
336                 g_warning ("Path to description document not specified.");
337
338                 return NULL;
339         }
340
341         if (!description_dir) {
342                 g_warning ("Path to description directory not specified.");
343
344                 return NULL;
345         }
346
347         if (g_path_is_absolute (description_path))
348                 desc_path = g_strdup (description_path);
349         else
350                 desc_path = g_build_filename (description_dir,
351                                               description_path,
352                                               NULL);
353
354         /* Check whether we have a parsed description document */
355         if (!description_doc) {
356                 /* We don't, so load and parse it */
357                 description_doc = load_and_parse (desc_path);
358                 if (description_doc == NULL)
359                         goto DONE;
360         }
361
362         /* Find correct element */
363         root_element = xml_util_get_element ((xmlNode *) description_doc->doc,
364                                              "root",
365                                              NULL);
366         if (!root_element) {
367                 g_warning ("\"/root\" element not found.");
368
369                 goto DONE;
370         }
371
372         element = xml_util_get_element (root_element,
373                                         "device",
374                                         NULL);
375         if (!element) {
376                 g_warning ("\"/root/device\" element not found.");
377
378                 goto DONE;
379         }
380
381         /* Set 'element' and 'description-doc' properties */
382         for (i = 0; i < n_construct_params; i++) {
383                 const char *par_name;
384
385                 par_name = construct_params[i].pspec->name;
386
387                 if (strcmp (par_name, "element") == 0) {
388                         g_value_set_pointer (construct_params[i].value,
389                                              element);
390
391                         continue;
392                 }
393
394                 if (strcmp (par_name, "description-doc") == 0) {
395                         g_value_set_object (construct_params[i].value,
396                                             description_doc);
397
398                         continue;
399                 }
400         }
401
402         /* Create object */
403         object_class = G_OBJECT_CLASS (gupnp_root_device_parent_class);
404
405         object = object_class->constructor (type,
406                                             n_construct_params,
407                                             construct_params);
408         device = GUPNP_ROOT_DEVICE (object);
409
410         /* Generate location relative to HTTP root */
411         device->priv->relative_location = g_strdup_printf ("RootDevice%p.xml",                                                             object);
412
413         relative_location = g_strjoin (NULL,
414                                        "/",
415                                        device->priv->relative_location,
416                                        NULL);
417
418         /* Host the description file and dir */
419         gupnp_context_host_path (context, desc_path, relative_location);
420         gupnp_context_host_path (context, device->priv->description_dir, "");
421
422         /* Generate full location */
423         location = g_strjoin (NULL,
424                               _gupnp_context_get_server_url (context),
425                               relative_location,
426                               NULL);
427         g_free (relative_location);
428
429         /* Save the URL base, if any */
430         url_base = xml_util_get_child_element_content_uri (root_element,
431                                                            "URLBase",
432                                                            NULL);
433         if (!url_base)
434                 url_base = soup_uri_new (location);
435
436         /* Set additional properties */
437         g_object_set (object,
438                       "location", location,
439                       "url-base", url_base,
440                       NULL);
441
442         soup_uri_free (url_base);
443
444         /* Create resource group */
445         device->priv->group = gssdp_resource_group_new (GSSDP_CLIENT (context));
446
447         /* Add services and devices to resource group */
448         udn = gupnp_device_info_get_udn (GUPNP_DEVICE_INFO (device));
449         usn = g_strdup_printf ("%s::upnp:rootdevice", (const char *) udn);
450         gssdp_resource_group_add_resource_simple (device->priv->group,
451                                                   "upnp:rootdevice",
452                                                   usn,
453                                                   location);
454         g_free (usn);
455
456         fill_resource_group (element, location, device->priv->group);
457
458  DONE:
459         /* Cleanup */
460         g_free (location);
461
462         return object;
463 }
464
465 static void
466 gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
467 {
468         GObjectClass *object_class;
469
470         object_class = G_OBJECT_CLASS (klass);
471
472         object_class->set_property = gupnp_root_device_set_property;
473         object_class->get_property = gupnp_root_device_get_property;
474         object_class->constructor  = gupnp_root_device_constructor;
475         object_class->dispose      = gupnp_root_device_dispose;
476         object_class->finalize     = gupnp_root_device_finalize;
477
478         g_type_class_add_private (klass, sizeof (GUPnPRootDevicePrivate));
479
480         /**
481          * GUPnPRootDevice:description-doc:
482          *
483          * Device description document. Constructor property.
484          **/
485         g_object_class_install_property
486                 (object_class,
487                  PROP_DESCRIPTION_DOC,
488                  g_param_spec_object ("description-doc",
489                                       "Description document",
490                                       "Device description document",
491                                       GUPNP_TYPE_XML_DOC,
492                                       G_PARAM_WRITABLE |
493                                       G_PARAM_CONSTRUCT_ONLY |
494                                       G_PARAM_STATIC_NAME |
495                                       G_PARAM_STATIC_NICK |
496                                       G_PARAM_STATIC_BLURB));
497
498         /**
499          * GUPnPRootDevice:description-path:
500          *
501          * The path to device description document. This could either be an
502          * absolute path or path relative to GUPnPRootDevice:description-dir.
503          **/
504         g_object_class_install_property
505                 (object_class,
506                  PROP_DESCRIPTION_PATH,
507                  g_param_spec_string ("description-path",
508                                       "Description Path",
509                                       "The path to device descrition document",
510                                       NULL,
511                                       G_PARAM_READWRITE |
512                                       G_PARAM_CONSTRUCT_ONLY |
513                                       G_PARAM_STATIC_NAME |
514                                       G_PARAM_STATIC_NICK |
515                                       G_PARAM_STATIC_BLURB));
516
517         /**
518          * GUPnPRootDevice:description-dir:
519          *
520          * The path to directory where description documents are provided.
521          **/
522         g_object_class_install_property
523                 (object_class,
524                  PROP_DESCRIPTION_DIR,
525                  g_param_spec_string ("description-dir",
526                                       "Description Directory",
527                                       "The path to directory where "
528                                       "description documents are provided",
529                                       NULL,
530                                       G_PARAM_READWRITE |
531                                       G_PARAM_CONSTRUCT_ONLY |
532                                       G_PARAM_STATIC_NAME |
533                                       G_PARAM_STATIC_NICK |
534                                       G_PARAM_STATIC_BLURB));
535
536         /**
537          * GUPnPRootDevice:available:
538          *
539          * TRUE if this device is available.
540          **/
541         g_object_class_install_property
542                 (object_class,
543                  PROP_AVAILABLE,
544                  g_param_spec_boolean ("available",
545                                        "Available",
546                                        "Whether this device is available",
547                                        FALSE,
548                                        G_PARAM_READWRITE |
549                                        G_PARAM_STATIC_NAME |
550                                        G_PARAM_STATIC_NICK |
551                                        G_PARAM_STATIC_BLURB));
552 }
553
554 /**
555  * gupnp_root_device_new
556  * @context: The #GUPnPContext
557  * @description_path: Path to device description document. This could either
558  * be an absolute path or path relative to @description_dir.
559  * @description_dir: Path to directory where description documents are provided.
560  *
561  * Create a new #GUPnPRootDevice object, automatically loading and parsing
562  * device description document from @description_path.
563  *
564  * Return value: A new @GUPnPRootDevice object.
565  **/
566 GUPnPRootDevice *
567 gupnp_root_device_new (GUPnPContext *context,
568                        const char   *description_path,
569                        const char   *description_dir)
570 {
571         GUPnPResourceFactory *factory;
572
573         factory = gupnp_resource_factory_get_default ();
574
575         return gupnp_root_device_new_full (context,
576                                            factory,
577                                            NULL,
578                                            description_path,
579                                            description_dir);
580 }
581
582 /**
583  * gupnp_root_device_new_full:
584  * @context: A #GUPnPContext
585  * @factory: A #GUPnPResourceFactory
586  * @description_doc: Device description document, or %NULL
587  * @description_path: Path to device description document. This could either
588  * be an absolute path or path relative to @description_dir.
589  * @description_dir: Path to directory where description documents are provided.
590  *
591  * Create a new #GUPnPRootDevice, automatically loading and parsing
592  * device description document from @description_path if @description_doc is
593  * %NULL.
594  *
595  * Return value: A new #GUPnPRootDevice object.
596  **/
597 GUPnPRootDevice *
598 gupnp_root_device_new_full (GUPnPContext         *context,
599                             GUPnPResourceFactory *factory,
600                             GUPnPXMLDoc          *description_doc,
601                             const char           *description_path,
602                             const char           *description_dir)
603 {
604         g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL);
605         g_return_val_if_fail (GUPNP_IS_RESOURCE_FACTORY (factory), NULL);
606
607         return g_object_new (GUPNP_TYPE_ROOT_DEVICE,
608                              "context", context,
609                              "resource-factory", factory,
610                              "root-device", NULL,
611                              "description-doc", description_doc,
612                              "description-path", description_path,
613                              "description-dir", description_dir,
614                              NULL);
615 }
616
617 /**
618  * gupnp_root_device_set_available:
619  * @root_device: A #GUPnPRootDevice
620  * @available: %TRUE if @root_device should be available
621  *
622  * Controls whether or not @root_device is available (announcing
623  * its presence).
624  **/
625 void
626 gupnp_root_device_set_available (GUPnPRootDevice *root_device,
627                                  gboolean         available)
628 {
629         g_return_if_fail (GUPNP_IS_ROOT_DEVICE (root_device));
630
631         gssdp_resource_group_set_available (root_device->priv->group,
632                                             available);
633
634         g_object_notify (G_OBJECT (root_device), "available");
635 }
636
637 /**
638  * gupnp_root_device_get_available:
639  * @root_device: A #GUPnPRootDevice
640  *
641  * Get whether or not @root_device is available (announcing its presence).
642  *
643  * Return value: %TRUE if @root_device is available, %FALSE otherwise.
644  **/
645 gboolean
646 gupnp_root_device_get_available (GUPnPRootDevice *root_device)
647 {
648         g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), FALSE);
649
650         return gssdp_resource_group_get_available (root_device->priv->group);
651 }
652
653 /**
654  * gupnp_root_device_get_relative_location:
655  * @root_device: A #GUPnPRootDevice
656  *
657  * Get the relative location of @root_device.
658  *
659  * Return value: The relative location of @root_device.
660  **/
661 const char *
662 gupnp_root_device_get_relative_location (GUPnPRootDevice *root_device)
663 {
664         g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL);
665
666         return root_device->priv->relative_location;
667 }
668
669 /**
670  * gupnp_root_device_get_description_path:
671  * @root_device: A #GUPnPRootDevice
672  *
673  * Get the path to the device description document of @root_device.
674  *
675  * Return value: The path to device description document of @root_device.
676  **/
677 const char *
678 gupnp_root_device_get_description_path (GUPnPRootDevice *root_device)
679 {
680         g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL);
681
682         return root_device->priv->description_path;
683 }
684
685 /**
686  * gupnp_root_device_get_description_dir:
687  * @root_device: A #GUPnPRootDevice
688  *
689  * Get the path to the directory containing description documents related to
690  * @root_device.
691  *
692  * Return value: The path to description document directory of @root_device.
693  **/
694 const char *
695 gupnp_root_device_get_description_dir (GUPnPRootDevice *root_device)
696 {
697         g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL);
698
699         return root_device->priv->description_dir;
700 }