/* * Copyright (C) 2007 Zeeshan Ali (Khattak) * * Authors: Zeeshan Ali (Khattak) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:gupnp-didl-lite-parser * @short_description: A/V DIDL-Lite XML parser * * #GUPnPDIDLLiteParser parses DIDL-Lite XML strings. * */ #include #include #include "gupnp-av.h" #include "gupnp-didl-lite-object-private.h" #include "xml-util.h" #include "gupnp-didl-lite-parser-private.h" G_DEFINE_TYPE (GUPnPDIDLLiteParser, gupnp_didl_lite_parser, G_TYPE_OBJECT); enum { OBJECT_AVAILABLE, ITEM_AVAILABLE, CONTAINER_AVAILABLE, SIGNAL_LAST }; static guint signals[SIGNAL_LAST]; static gboolean verify_didl_attributes (xmlNode *node) { const char *content; content = xml_util_get_child_element_content (node, "date"); if (content) { /* try to roughly verify the passed date with ^\d{4}-\d{2}-\d{2} */ char *ptr = (char *) content; int state = 0; while (*ptr) { if (state == 4 || state == 7) { if (*ptr != '-') return FALSE; } else { if (!isdigit (*ptr)) return FALSE; } ptr++; state++; if (state == 10) break; } } if (xml_util_get_attribute_content (node, "restricted") != NULL) { return xml_util_verify_attribute_is_boolean (node, "restricted"); } return TRUE; } static gboolean parse_elements (GUPnPDIDLLiteParser *parser, xmlNode *node, GUPnPAVXMLDoc *xml_doc, xmlNs *upnp_ns, xmlNs *dc_ns, xmlNs *dlna_ns, xmlNs *pv_ns, gboolean recursive, GError **error); static void gupnp_didl_lite_parser_init (G_GNUC_UNUSED GUPnPDIDLLiteParser *parser) { } static void gupnp_didl_lite_parser_dispose (GObject *object) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (gupnp_didl_lite_parser_parent_class); gobject_class->dispose (object); } static void gupnp_didl_lite_parser_class_init (GUPnPDIDLLiteParserClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); object_class->dispose = gupnp_didl_lite_parser_dispose; /** * GUPnPDIDLLiteParser::object-available: * @parser: The #GUPnPDIDLLiteParser that received the signal * @object: The now available #GUPnPDIDLLiteObject * * The ::object-available signal is emitted each time an object is * found in the DIDL-Lite XML being parsed. **/ signals[OBJECT_AVAILABLE] = g_signal_new ("object-available", GUPNP_TYPE_DIDL_LITE_PARSER, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GUPnPDIDLLiteParserClass, object_available), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GUPNP_TYPE_DIDL_LITE_OBJECT); /** * GUPnPDIDLLiteParser::item-available: * @parser: The #GUPnPDIDLLiteParser that received the signal * @item: The now available #GUPnPDIDLLiteItem * * The ::item-available signal is emitted each time an item is found in * the DIDL-Lite XML being parsed. **/ signals[ITEM_AVAILABLE] = g_signal_new ("item-available", GUPNP_TYPE_DIDL_LITE_PARSER, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GUPnPDIDLLiteParserClass, item_available), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GUPNP_TYPE_DIDL_LITE_ITEM); /** * GUPnPDIDLLiteParser::container-available: * @parser: The #GUPnPDIDLLiteParser that received the signal * @container: The now available #GUPnPDIDLLiteContainer * * The ::container-available signal is emitted each time a container is * found in the DIDL-Lite XML being parsed. **/ signals[CONTAINER_AVAILABLE] = g_signal_new ("container-available", GUPNP_TYPE_DIDL_LITE_PARSER, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GUPnPDIDLLiteParserClass, container_available), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GUPNP_TYPE_DIDL_LITE_CONTAINER); } /** * gupnp_didl_lite_parser_new: * * Return value: A new #GUPnPDIDLLiteParser object. **/ GUPnPDIDLLiteParser * gupnp_didl_lite_parser_new (void) { return g_object_new (GUPNP_TYPE_DIDL_LITE_PARSER, NULL); } /** * gupnp_didl_lite_parser_parse_didl: * @parser: A #GUPnPDIDLLiteParser * @didl: The DIDL-Lite XML string to be parsed * @error: The location where to store any error, or NULL * * Parses DIDL-Lite XML string @didl, emitting the ::object-available, * ::item-available and ::container-available signals appropriately during the * process. * * Return value: TRUE on success. **/ gboolean gupnp_didl_lite_parser_parse_didl (GUPnPDIDLLiteParser *parser, const char *didl, GError **error) { return gupnp_didl_lite_parser_parse_didl_recursive (parser, didl, FALSE, error); } /** * gupnp_didl_lite_parser_parse_didl_recursive: * @parser: A #GUPnPDIDLLiteParser * @didl: The DIDL-Lite XML string to be parsed * @error: The location where to store any error, or %NULL * * Parses DIDL-Lite XML string @didl, emitting the ::object-available, * ::item-available and ::container-available signals appropriately during the * process. * * Return value: TRUE on success. **/ gboolean gupnp_didl_lite_parser_parse_didl_recursive (GUPnPDIDLLiteParser *parser, const char *didl, gboolean recursive, GError **error) { xmlDoc *doc; xmlNode *element; xmlNs *upnp_ns = NULL; xmlNs *dc_ns = NULL; xmlNs *dlna_ns = NULL; xmlNs *pv_ns = NULL; GUPnPAVXMLDoc *xml_doc = NULL; gboolean result; doc = xmlRecoverMemory (didl, strlen (didl)); if (doc == NULL) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Could not parse DIDL-Lite XML:\n%s", didl); return FALSE; } /* Get a pointer to root element */ element = xml_util_get_element ((xmlNode *) doc, "DIDL-Lite", NULL); if (element == NULL) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "No 'DIDL-Lite' node in the DIDL-Lite XML:\n%s", didl); xmlFreeDoc (doc); return FALSE; } if (element->children == NULL) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_EMPTY, "Empty 'DIDL-Lite' node in the DIDL-Lite XML:\n%s", didl); xmlFreeDoc (doc); return FALSE; } /* Create namespaces if they don't exist */ upnp_ns = xml_util_lookup_namespace (doc, GUPNP_XML_NAMESPACE_UPNP); if (! upnp_ns) upnp_ns = xml_util_create_namespace (xmlDocGetRootElement (doc), GUPNP_XML_NAMESPACE_UPNP); dc_ns = xml_util_lookup_namespace (doc, GUPNP_XML_NAMESPACE_DC); if (! dc_ns) dc_ns = xml_util_create_namespace (xmlDocGetRootElement (doc), GUPNP_XML_NAMESPACE_DC); dlna_ns = xml_util_lookup_namespace (doc, GUPNP_XML_NAMESPACE_DLNA); if (! dlna_ns) dlna_ns = xml_util_create_namespace (xmlDocGetRootElement (doc), GUPNP_XML_NAMESPACE_DLNA); pv_ns = xml_util_lookup_namespace (doc, GUPNP_XML_NAMESPACE_PV); if (! pv_ns) pv_ns = xml_util_create_namespace (xmlDocGetRootElement (doc), GUPNP_XML_NAMESPACE_PV); xml_doc = xml_doc_new (doc); result = parse_elements (parser, element, xml_doc, upnp_ns, dc_ns, dlna_ns, pv_ns, recursive, error); xml_doc_unref (xml_doc); return result; } static gboolean parse_elements (GUPnPDIDLLiteParser *parser, xmlNode *node, GUPnPAVXMLDoc *xml_doc, xmlNs *upnp_ns, xmlNs *dc_ns, xmlNs *dlna_ns, xmlNs *pv_ns, gboolean recursive, GError **error) { xmlNode *element; for (element = node->children; element; element = element->next) { GUPnPDIDLLiteObject *object; object = gupnp_didl_lite_object_new_from_xml (element, xml_doc, upnp_ns, dc_ns, dlna_ns, pv_ns); if (object == NULL) continue; if (GUPNP_IS_DIDL_LITE_CONTAINER (object)) { g_signal_emit (parser, signals[CONTAINER_AVAILABLE], 0, object); if (recursive && !parse_elements (parser, element, xml_doc, upnp_ns, dc_ns, dlna_ns, pv_ns, recursive, error)) { g_object_unref (object); return FALSE; } } else if (GUPNP_IS_DIDL_LITE_ITEM (object)) { node = gupnp_didl_lite_object_get_xml_node (object); if (!verify_didl_attributes (node)) { g_object_unref (object); g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "Could not parse DIDL-Lite XML"); return FALSE; } g_signal_emit (parser, signals[ITEM_AVAILABLE], 0, object); } g_signal_emit (parser, signals[OBJECT_AVAILABLE], 0, object); g_object_unref (object); } return TRUE; }