Blame libgupnp-av/gupnp-didl-lite-parser.c

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