2 * Copyright (C) 2007, 2008 OpenedHand Ltd.
4 * Authors: 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-didl-lite-writer
24 * @short_description: DIDL-Lite fragment writer
26 * #GUPnPDIDLLiteWriter is a helper class for writing DIDL-Lite fragments.
31 #include "gupnp-didl-lite-writer.h"
32 #include "gupnp-didl-lite-object.h"
33 #include "gupnp-didl-lite-object-private.h"
34 #include "gupnp-didl-lite-descriptor-private.h"
38 G_DEFINE_TYPE (GUPnPDIDLLiteWriter,
39 gupnp_didl_lite_writer,
42 struct _GUPnPDIDLLiteWriterPrivate {
60 compare_prop (const char *a, xmlAttr *attr)
68 attr_name = g_strjoin (":", attr->ns->prefix, attr->name, NULL);
70 attr_name = g_strdup ((const char *) attr->name);
72 if (attr->parent->ns != NULL)
73 parent_name = g_strjoin (":",
74 attr->parent->ns->prefix,
78 parent_name = g_strdup ((const char *) attr->parent->name);
83 /* Top-level property */
84 ret = strcmp (a + 1, attr_name);
86 ret = strncmp (a, parent_name, p - a) ||
87 strcmp (p + 1, attr_name);
89 ret = strcmp (a, attr_name);
98 is_attribute_forbidden (xmlAttr *attr,
101 return g_list_find_custom (allowed,
103 (GCompareFunc) compare_prop) == NULL;
107 compare_node_name (const char *a, const char *b)
113 /* Filer is for top-level property */
118 /* Compare only the string before '@' */
123 return strncmp (a, b, len);
127 is_node_forbidden (xmlNode *node,
135 name = g_strjoin (":", ns, node->name, NULL);
137 name = g_strdup ((const char *) node->name);
139 ret = g_list_find_custom (allowed,
141 (GCompareFunc) compare_node_name) == NULL;
149 is_container_standard_prop (const char *name,
150 const char *namespace,
151 const char *upnp_class)
153 return g_strcmp0 (upnp_class, "object.container.storageFolder") == 0 &&
154 g_strcmp0 (namespace, "upnp") == 0 &&
155 strcmp (name, "storageUsed") == 0;
159 is_standard_prop (const char *name,
160 const char *namespace,
161 const char *parent_name)
163 return strcmp (name, "id") == 0 ||
164 strcmp (name, "parentID") == 0 ||
165 strcmp (name, "restricted") == 0 ||
166 (g_strcmp0 (namespace, "dc") == 0 &&
167 strcmp (name, "title") == 0) ||
168 (g_strcmp0 (namespace, "upnp") == 0 &&
169 strcmp (name, "class") == 0) ||
170 (g_strcmp0 (parent_name, "res") == 0 &&
171 strcmp (name, "protocolInfo") == 0);
175 filter_attributes (xmlNode *node,
177 GUPnPDIDLLiteWriter *writer)
180 GList *forbidden = NULL;
183 /* Find forbidden properties */
184 for (attr = node->properties; attr != NULL; attr = attr->next)
185 if (!is_standard_prop ((const char *) attr->name,
187 (const char *) attr->parent->name) &&
188 is_attribute_forbidden (attr, allowed))
189 forbidden = g_list_append (forbidden, attr);
191 /* Now unset forbidden properties */
192 for (l = forbidden; l != NULL; l = l->next)
193 xmlRemoveProp ((xmlAttr *) l->data);
195 g_list_free (forbidden);
199 filter_node (xmlNode *node,
201 GUPnPDIDLLiteWriter *writer)
204 GList *forbidden = NULL;
206 gboolean is_container;
207 const char *container_class = NULL;
209 filter_attributes (node, allowed, writer);
211 if (strcmp ((const char *) node->name, "container") == 0) {
213 container_class = xml_util_get_child_element_content (node,
218 for (child = node->children; child != NULL; child = child->next) {
219 const char *ns = NULL;
221 if (xmlNodeIsText (child))
224 if (child->ns != NULL)
225 ns = (const char *) child->ns->prefix;
227 if (!(is_container && is_container_standard_prop
228 ((const char *) child->name,
231 !is_standard_prop ((const char *) child->name,
233 (const char *) node->name) &&
234 is_node_forbidden (child, allowed, ns))
235 forbidden = g_list_append (forbidden, child);
238 /* Now remove the forbidden nodes */
239 for (l = forbidden; l != NULL; l = l->next) {
242 n = (xmlNode *) l->data;
248 g_list_free (forbidden);
251 for (child = node->children; child != NULL; child = child->next)
252 if (!xmlNodeIsText (child))
253 filter_node (child, allowed, writer);
257 gupnp_didl_lite_writer_init (GUPnPDIDLLiteWriter *writer)
259 writer->priv = G_TYPE_INSTANCE_GET_PRIVATE (writer,
260 GUPNP_TYPE_DIDL_LITE_WRITER,
261 GUPnPDIDLLiteWriterPrivate);
265 gupnp_didl_lite_writer_set_property (GObject *object,
271 GUPnPDIDLLiteWriter *writer;
273 writer = GUPNP_DIDL_LITE_WRITER (object);
275 switch (property_id) {
277 writer->priv->language = g_value_dup_string (value);
280 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
286 gupnp_didl_lite_writer_get_property (GObject *object,
291 GUPnPDIDLLiteWriter *writer;
293 writer = GUPNP_DIDL_LITE_WRITER (object);
295 switch (property_id) {
298 (value, gupnp_didl_lite_writer_get_xml_node (writer));
302 (value, gupnp_didl_lite_writer_get_language (writer));
305 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
311 gupnp_didl_lite_writer_constructed (GObject *object)
313 GObjectClass *object_class;
314 GUPnPDIDLLiteWriterPrivate *priv;
317 priv = GUPNP_DIDL_LITE_WRITER (object)->priv;
319 doc = xmlNewDoc ((unsigned char *) "1.0");
320 priv->xml_doc = gupnp_xml_doc_new (doc);
322 priv->xml_node = xmlNewDocNode (priv->xml_doc->doc,
324 (unsigned char *) "DIDL-Lite",
326 xmlDocSetRootElement (priv->xml_doc->doc, priv->xml_node);
327 priv->dc_ns = xmlNewNs (priv->xml_node,
329 "http://purl.org/dc/elements/1.1/",
331 GUPNP_DIDL_LITE_WRITER_NAMESPACE_DC);
332 priv->upnp_ns = xmlNewNs (priv->xml_node,
334 "urn:schemas-upnp-org:metadata-1-0/upnp/",
336 GUPNP_DIDL_LITE_WRITER_NAMESPACE_UPNP);
337 priv->dlna_ns = xmlNewNs (priv->xml_node,
339 "urn:schemas-dlna-org:metadata-1-0/",
341 GUPNP_DIDL_LITE_WRITER_NAMESPACE_DLNA);
342 xmlNewNs (priv->xml_node,
344 "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/",
348 xmlSetProp (priv->xml_node,
349 (unsigned char *) "lang",
350 (unsigned char *) priv->language);
352 object_class = G_OBJECT_CLASS (gupnp_didl_lite_writer_parent_class);
353 if (object_class->constructed != NULL)
354 object_class->constructed (object);
358 gupnp_didl_lite_writer_dispose (GObject *object)
360 GObjectClass *object_class;
361 GUPnPDIDLLiteWriterPrivate *priv;
363 priv = GUPNP_DIDL_LITE_WRITER (object)->priv;
366 g_object_unref (priv->xml_doc);
367 priv->xml_doc = NULL;
370 object_class = G_OBJECT_CLASS (gupnp_didl_lite_writer_parent_class);
371 object_class->dispose (object);
375 gupnp_didl_lite_writer_finalize (GObject *object)
377 GObjectClass *object_class;
378 GUPnPDIDLLiteWriterPrivate *priv;
380 priv = GUPNP_DIDL_LITE_WRITER (object)->priv;
383 g_free (priv->language);
385 object_class = G_OBJECT_CLASS (gupnp_didl_lite_writer_parent_class);
386 object_class->finalize (object);
390 gupnp_didl_lite_writer_class_init (GUPnPDIDLLiteWriterClass *klass)
392 GObjectClass *object_class;
394 object_class = G_OBJECT_CLASS (klass);
396 object_class->set_property = gupnp_didl_lite_writer_set_property;
397 object_class->get_property = gupnp_didl_lite_writer_get_property;
398 object_class->constructed = gupnp_didl_lite_writer_constructed;
399 object_class->dispose = gupnp_didl_lite_writer_dispose;
400 object_class->finalize = gupnp_didl_lite_writer_finalize;
402 g_type_class_add_private (klass, sizeof (GUPnPDIDLLiteWriterPrivate));
405 * GUPnPDIDLLiteWriter:xml-node:
407 * The pointer to root node in XML document.
409 g_object_class_install_property
412 g_param_spec_pointer ("xml-node",
414 "The pointer to root node in XML"
417 G_PARAM_STATIC_NAME |
418 G_PARAM_STATIC_NICK |
419 G_PARAM_STATIC_BLURB));
422 * GUPnPDIDLLiteWriter:language:
424 * The language the DIDL-Lite fragment is in.
427 g_object_class_install_property
430 g_param_spec_string ("language",
432 "The language the DIDL-Lite fragment"
435 G_PARAM_CONSTRUCT_ONLY |
437 G_PARAM_STATIC_NAME |
438 G_PARAM_STATIC_NICK |
439 G_PARAM_STATIC_BLURB));
443 * gupnp_didl_lite_writer_new:
444 * @language: The language the DIDL-Lite fragment is in, or NULL
446 * Return value: A new #GUPnPDIDLLiteWriter object.
448 GUPnPDIDLLiteWriter *
449 gupnp_didl_lite_writer_new (const char *language)
451 return g_object_new (GUPNP_TYPE_DIDL_LITE_WRITER,
452 "language", language,
457 * gupnp_didl_lite_writer_add_item:
458 * @writer: A #GUPnPDIDLLiteWriter
460 * Creates a new item, attaches it to @writer and returns it.
462 * Returns: (transfer full): A new #GUPnPDIDLLiteItem object. Unref after usage.
465 gupnp_didl_lite_writer_add_item (GUPnPDIDLLiteWriter *writer)
468 GUPnPDIDLLiteObject *object;
470 g_return_val_if_fail (GUPNP_IS_DIDL_LITE_WRITER (writer), NULL);
472 item_node = xmlNewChild (writer->priv->xml_node,
474 (unsigned char *) "item",
477 object = gupnp_didl_lite_object_new_from_xml (item_node,
478 writer->priv->xml_doc,
479 writer->priv->upnp_ns,
481 writer->priv->dlna_ns);
482 return GUPNP_DIDL_LITE_ITEM (object);
486 * gupnp_didl_lite_writer_add_container:
487 * @writer: A #GUPnPDIDLLiteWriter
489 * Creates a new container, attaches it to @writer and returns it.
491 * Returns: (transfer full): A new #GUPnPDIDLLiteContainer object. Unref after usage.
493 GUPnPDIDLLiteContainer *
494 gupnp_didl_lite_writer_add_container (GUPnPDIDLLiteWriter *writer)
496 xmlNode *container_node;
497 GUPnPDIDLLiteObject *object;
499 g_return_val_if_fail (GUPNP_IS_DIDL_LITE_WRITER (writer), NULL);
501 container_node = xmlNewChild (writer->priv->xml_node,
503 (unsigned char *) "container",
506 object = gupnp_didl_lite_object_new_from_xml (container_node,
507 writer->priv->xml_doc,
508 writer->priv->upnp_ns,
510 writer->priv->dlna_ns);
511 return GUPNP_DIDL_LITE_CONTAINER (object);
515 * gupnp_didl_lite_writer_add_descriptor:
516 * @writer: A #GUPnPDIDLLiteWriter
518 * Creates a new descriptor, attaches it to @object and returns it.
520 * Returns: (transfer full): A new #GUPnPDIDLLiteDescriptor object. Unref after usage.
522 GUPnPDIDLLiteDescriptor *
523 gupnp_didl_lite_writer_add_descriptor (GUPnPDIDLLiteWriter *writer)
527 g_return_val_if_fail (GUPNP_IS_DIDL_LITE_WRITER (writer), NULL);
529 desc_node = xmlNewChild (writer->priv->xml_node,
531 (unsigned char *) "desc",
534 return gupnp_didl_lite_descriptor_new_from_xml (desc_node,
535 writer->priv->xml_doc);
539 * gupnp_didl_lite_writer_get_string:
540 * @writer: A #GUPnPDIDLLiteWriter
542 * Creates a string representation of the DIDL-Lite XML document.
544 * Return value: The DIDL-Lite XML string, or %NULL. #g_free after usage.
547 gupnp_didl_lite_writer_get_string (GUPnPDIDLLiteWriter *writer)
552 g_return_val_if_fail (GUPNP_IS_DIDL_LITE_WRITER (writer), NULL);
554 buffer = xmlBufferCreate ();
556 writer->priv->xml_doc->doc,
557 writer->priv->xml_node,
560 ret = g_strndup ((char *) xmlBufferContent (buffer),
561 xmlBufferLength (buffer));
562 xmlBufferFree (buffer);
568 * gupnp_didl_lite_writer_get_xml_node:
569 * @writer: The #GUPnPDIDLLiteWriter
571 * Get the pointer to root node in XML document.
573 * Returns: (transfer none): The pointer to root node in XML document.
576 gupnp_didl_lite_writer_get_xml_node (GUPnPDIDLLiteWriter *writer)
578 g_return_val_if_fail (GUPNP_IS_DIDL_LITE_WRITER (writer), NULL);
580 return writer->priv->xml_node;
584 * gupnp_didl_lite_writer_get_language:
585 * @writer: #GUPnPDIDLLiteWriter
587 * Get the language the DIDL-Lite fragment is in.
589 * Returns: (transfer none): The language of the @writer, or %NULL.
592 gupnp_didl_lite_writer_get_language (GUPnPDIDLLiteWriter *writer)
594 g_return_val_if_fail (GUPNP_IS_DIDL_LITE_WRITER (writer), NULL);
596 return writer->priv->language;
600 * gupnp_didl_lite_writer_filter:
601 * @writer: A #GUPnPDIDLLiteWriter
602 * @filter: A filter string
604 * Clears the DIDL-Lite XML document of the properties not specified in the
605 * @filter. The passed filter string would typically come from the 'Filter'
606 * argument of Browse or Search actions from a ContentDirectory control point.
607 * Please refer to Section 2.3.15 of UPnP AV ContentDirectory version 3
608 * specification for details on this string.
610 * Return value: None.
613 gupnp_didl_lite_writer_filter (GUPnPDIDLLiteWriter *writer,
617 GList *allowed = NULL;
621 g_return_if_fail (GUPNP_IS_DIDL_LITE_WRITER (writer));
622 g_return_if_fail (filter != NULL);
624 if (filter[0] == '*')
625 return; /* Wildcard */
627 tokens = g_strsplit (filter, ",", -1);
628 g_return_if_fail (tokens != NULL);
630 for (i = 0; tokens[i] != NULL; i++)
631 allowed = g_list_append (allowed, tokens[i]);
633 for (node = writer->priv->xml_node->children;
636 filter_node (node, allowed, writer);
638 g_list_free (allowed);