2 * Copyright (C) 2007, 2008 OpenedHand Ltd.
4 * Author: Jorn Baayen <jorn@openedhand.com>
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.
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.
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., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * SECTION:gupnp-root-device
24 * @short_description: Class for root device implementations.
26 * #GUPnPRootDevice allows for implementing root devices.
31 #include <libgssdp/gssdp-resource-group.h>
33 #include "gupnp-root-device.h"
34 #include "gupnp-context-private.h"
35 #include "http-headers.h"
38 G_DEFINE_TYPE (GUPnPRootDevice,
42 struct _GUPnPRootDevicePrivate {
43 GUPnPXMLDoc *description_doc;
45 GSSDPResourceGroup *group;
47 char *description_path;
48 char *description_dir;
49 char *relative_location;
55 PROP_DESCRIPTION_PATH,
61 gupnp_root_device_finalize (GObject *object)
63 GUPnPRootDevice *device;
64 GObjectClass *object_class;
66 device = GUPNP_ROOT_DEVICE (object);
68 g_object_unref (device->priv->description_doc);
69 g_free (device->priv->description_path);
70 g_free (device->priv->description_dir);
71 g_free (device->priv->relative_location);
74 object_class = G_OBJECT_CLASS (gupnp_root_device_parent_class);
75 object_class->finalize (object);
79 gupnp_root_device_dispose (GObject *object)
81 GUPnPRootDevice *device;
82 GObjectClass *object_class;
84 device = GUPNP_ROOT_DEVICE (object);
86 if (device->priv->group) {
87 g_object_unref (device->priv->group);
88 device->priv->group = NULL;
92 object_class = G_OBJECT_CLASS (gupnp_root_device_parent_class);
93 object_class->dispose (object);
97 gupnp_root_device_init (GUPnPRootDevice *device)
99 device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
100 GUPNP_TYPE_ROOT_DEVICE,
101 GUPnPRootDevicePrivate);
105 gupnp_root_device_set_property (GObject *object,
110 GUPnPRootDevice *device;
112 device = GUPNP_ROOT_DEVICE (object);
114 switch (property_id) {
115 case PROP_DESCRIPTION_DOC:
116 device->priv->description_doc = g_value_dup_object (value);
118 case PROP_DESCRIPTION_PATH:
119 device->priv->description_path = g_value_dup_string (value);
121 case PROP_DESCRIPTION_DIR:
122 device->priv->description_dir = g_value_dup_string (value);
125 gupnp_root_device_set_available
126 (device, g_value_get_boolean (value));
129 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
135 gupnp_root_device_get_property (GObject *object,
140 GUPnPRootDevice *device;
142 device = GUPNP_ROOT_DEVICE (object);
144 switch (property_id) {
145 case PROP_DESCRIPTION_PATH:
146 g_value_set_string (value,
147 device->priv->description_path);
149 case PROP_DESCRIPTION_DIR:
150 g_value_set_string (value,
151 device->priv->description_dir);
154 g_value_set_boolean (value,
155 gupnp_root_device_get_available (device));
158 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
164 fill_resource_group (xmlNode *element,
165 const char *location,
166 GSSDPResourceGroup *group)
169 xmlChar *udn, *device_type;
173 udn = xml_util_get_child_element_content (element, "UDN");
175 g_warning ("No UDN specified.");
180 device_type = xml_util_get_child_element_content (element,
183 g_warning ("No deviceType specified.");
188 gssdp_resource_group_add_resource_simple (group,
193 usn = g_strdup_printf ("%s::%s", (char *) udn, (char *) device_type);
194 gssdp_resource_group_add_resource_simple (group,
195 (const char *) device_type,
200 xmlFree (device_type);
202 /* Add embedded services */
203 child = xml_util_get_element (element,
207 for (child = child->children; child; child = child->next) {
208 xmlChar *service_type;
210 if (strcmp ("service", (char *) child->name))
213 service_type = xml_util_get_child_element_content
214 (child, "serviceType");
218 usn = g_strdup_printf ("%s::%s",
220 (char *) service_type);
221 gssdp_resource_group_add_resource_simple
223 (const char *) service_type,
228 xmlFree (service_type);
235 /* Add embedded devices */
236 child = xml_util_get_element (element,
240 for (child = child->children; child; child = child->next)
241 if (!strcmp ("device", (char *) child->name))
242 fill_resource_group (child, location, group);
246 /* Load and parse @description_path as an XML document, synchronously. */
248 load_and_parse (const char *description_path)
250 GUPnPXMLDoc *description_doc;
251 GError *error = NULL;
253 /* We don't, so load and parse it */
254 description_doc = gupnp_xml_doc_new_from_path (description_path,
256 if (description_doc == NULL) {
257 g_critical ("Error loading description: %s\n", error->message);
259 g_error_free (error);
262 return description_doc;
266 gupnp_root_device_constructor (GType type,
267 guint n_construct_params,
268 GObjectConstructParam *construct_params)
270 GObjectClass *object_class;
272 GUPnPRootDevice *device;
273 GUPnPContext *context;
274 const char *description_path, *description_dir, *udn;
275 char *desc_path, *location, *usn, *relative_location;
277 GUPnPXMLDoc *description_doc;
278 xmlNode *root_element, *element;
284 /* Get 'description-doc', 'context', 'description-dir' and
285 * 'description-path' property values */
286 description_doc = NULL;
288 description_path = NULL;
289 description_dir = NULL;
291 for (i = 0; i < n_construct_params; i++) {
292 const char *par_name;
294 par_name = construct_params[i].pspec->name;
296 if (strcmp (par_name, "description-doc") == 0) {
298 g_value_get_object (construct_params[i].value);
303 if (strcmp (par_name, "context") == 0) {
305 g_value_get_object (construct_params[i].value);
310 if (strcmp (par_name, "description-path") == 0) {
312 g_value_get_string (construct_params[i].value);
317 if (strcmp (par_name, "description-dir") == 0) {
319 g_value_get_string (construct_params[i].value);
326 g_warning ("No context specified.");
331 if (!description_path) {
332 g_warning ("Path to description document not specified.");
337 if (!description_dir) {
338 g_warning ("Path to description directory not specified.");
343 if (g_path_is_absolute (description_path))
344 desc_path = g_strdup (description_path);
346 desc_path = g_build_filename (description_dir,
350 /* Check whether we have a parsed description document */
351 if (!description_doc) {
352 /* We don't, so load and parse it */
353 description_doc = load_and_parse (desc_path);
354 if (description_doc == NULL)
358 /* Find correct element */
359 root_element = xml_util_get_element ((xmlNode *) description_doc->doc,
363 g_warning ("\"/root\" element not found.");
368 element = xml_util_get_element (root_element,
372 g_warning ("\"/root/device\" element not found.");
377 /* Set 'element' and 'description-doc' properties */
378 for (i = 0; i < n_construct_params; i++) {
379 const char *par_name;
381 par_name = construct_params[i].pspec->name;
383 if (strcmp (par_name, "element") == 0) {
384 g_value_set_pointer (construct_params[i].value,
390 if (strcmp (par_name, "description-doc") == 0) {
391 g_value_set_object (construct_params[i].value,
399 object_class = G_OBJECT_CLASS (gupnp_root_device_parent_class);
401 object = object_class->constructor (type,
404 device = GUPNP_ROOT_DEVICE (object);
406 /* Generate location relative to HTTP root */
407 udn = gupnp_device_info_get_udn (GUPNP_DEVICE_INFO (device));
408 if (udn && strstr (udn, "uuid:") == udn)
409 device->priv->relative_location = g_strdup_printf ("%s.xml", udn + 5);
411 device->priv->relative_location = g_strdup_printf ("RootDevice%p.xml", device);
413 relative_location = g_strjoin (NULL,
415 device->priv->relative_location,
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, "");
422 /* Generate full location */
423 location = g_strjoin (NULL,
424 _gupnp_context_get_server_url (context),
427 g_free (relative_location);
429 /* Save the URL base, if any */
430 url_base = xml_util_get_child_element_content_uri (root_element,
434 url_base = soup_uri_new (location);
436 /* Set additional properties */
437 g_object_set (object,
438 "location", location,
439 "url-base", url_base,
442 soup_uri_free (url_base);
444 /* Create resource group */
445 device->priv->group = gssdp_resource_group_new (GSSDP_CLIENT (context));
447 /* Add services and devices to resource group */
448 usn = g_strdup_printf ("%s::upnp:rootdevice", (const char *) udn);
449 gssdp_resource_group_add_resource_simple (device->priv->group,
455 fill_resource_group (element, location, device->priv->group);
466 gupnp_root_device_class_init (GUPnPRootDeviceClass *klass)
468 GObjectClass *object_class;
470 object_class = G_OBJECT_CLASS (klass);
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;
478 g_type_class_add_private (klass, sizeof (GUPnPRootDevicePrivate));
481 * GUPnPRootDevice:description-doc:
483 * Device description document. Constructor property.
485 g_object_class_install_property
487 PROP_DESCRIPTION_DOC,
488 g_param_spec_object ("description-doc",
489 "Description document",
490 "Device description document",
493 G_PARAM_CONSTRUCT_ONLY |
494 G_PARAM_STATIC_NAME |
495 G_PARAM_STATIC_NICK |
496 G_PARAM_STATIC_BLURB));
499 * GUPnPRootDevice:description-path:
501 * The path to device description document. This could either be an
502 * absolute path or path relative to GUPnPRootDevice:description-dir.
504 g_object_class_install_property
506 PROP_DESCRIPTION_PATH,
507 g_param_spec_string ("description-path",
509 "The path to device descrition document",
512 G_PARAM_CONSTRUCT_ONLY |
513 G_PARAM_STATIC_NAME |
514 G_PARAM_STATIC_NICK |
515 G_PARAM_STATIC_BLURB));
518 * GUPnPRootDevice:description-dir:
520 * The path to directory where description documents are provided.
522 g_object_class_install_property
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",
531 G_PARAM_CONSTRUCT_ONLY |
532 G_PARAM_STATIC_NAME |
533 G_PARAM_STATIC_NICK |
534 G_PARAM_STATIC_BLURB));
537 * GUPnPRootDevice:available:
539 * TRUE if this device is available.
541 g_object_class_install_property
544 g_param_spec_boolean ("available",
546 "Whether this device is available",
549 G_PARAM_STATIC_NAME |
550 G_PARAM_STATIC_NICK |
551 G_PARAM_STATIC_BLURB));
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.
561 * Create a new #GUPnPRootDevice object, automatically loading and parsing
562 * device description document from @description_path.
564 * Return value: A new @GUPnPRootDevice object.
567 gupnp_root_device_new (GUPnPContext *context,
568 const char *description_path,
569 const char *description_dir)
571 GUPnPResourceFactory *factory;
573 factory = gupnp_resource_factory_get_default ();
575 return gupnp_root_device_new_full (context,
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.
591 * Create a new #GUPnPRootDevice, automatically loading and parsing
592 * device description document from @description_path if @description_doc is
595 * Return value: A new #GUPnPRootDevice object.
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)
604 g_return_val_if_fail (GUPNP_IS_CONTEXT (context), NULL);
605 g_return_val_if_fail (GUPNP_IS_RESOURCE_FACTORY (factory), NULL);
607 return g_object_new (GUPNP_TYPE_ROOT_DEVICE,
609 "resource-factory", factory,
611 "description-doc", description_doc,
612 "description-path", description_path,
613 "description-dir", description_dir,
618 * gupnp_root_device_set_available:
619 * @root_device: A #GUPnPRootDevice
620 * @available: %TRUE if @root_device should be available
622 * Controls whether or not @root_device is available (announcing
626 gupnp_root_device_set_available (GUPnPRootDevice *root_device,
629 g_return_if_fail (GUPNP_IS_ROOT_DEVICE (root_device));
631 gssdp_resource_group_set_available (root_device->priv->group,
634 g_object_notify (G_OBJECT (root_device), "available");
638 * gupnp_root_device_get_available:
639 * @root_device: A #GUPnPRootDevice
641 * Get whether or not @root_device is available (announcing its presence).
643 * Return value: %TRUE if @root_device is available, %FALSE otherwise.
646 gupnp_root_device_get_available (GUPnPRootDevice *root_device)
648 g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), FALSE);
650 return gssdp_resource_group_get_available (root_device->priv->group);
654 * gupnp_root_device_get_relative_location:
655 * @root_device: A #GUPnPRootDevice
657 * Get the relative location of @root_device.
659 * Return value: The relative location of @root_device.
662 gupnp_root_device_get_relative_location (GUPnPRootDevice *root_device)
664 g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL);
666 return root_device->priv->relative_location;
670 * gupnp_root_device_get_description_path:
671 * @root_device: A #GUPnPRootDevice
673 * Get the path to the device description document of @root_device.
675 * Return value: The path to device description document of @root_device.
678 gupnp_root_device_get_description_path (GUPnPRootDevice *root_device)
680 g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL);
682 return root_device->priv->description_path;
686 * gupnp_root_device_get_description_dir:
687 * @root_device: A #GUPnPRootDevice
689 * Get the path to the directory containing description documents related to
692 * Return value: The path to description document directory of @root_device.
695 gupnp_root_device_get_description_dir (GUPnPRootDevice *root_device)
697 g_return_val_if_fail (GUPNP_IS_ROOT_DEVICE (root_device), NULL);
699 return root_device->priv->description_dir;