[gupnp-av/wip/didl-lite-fragments: 16/20] Add fragment utils.



commit 545abba5aede457cbc42fbfe0ae0349fb1135b29
Author: Krzesimir Nowak <krnowak openismus com>
Date:   Thu Oct 25 11:38:25 2012 +0200

    Add fragment utils.
    
    This code does most of the fragments work.

 libgupnp-av/Makefile.am     |    4 +-
 libgupnp-av/fragment-util.c |  795 +++++++++++++++++++++++++++++++++++++++++++
 libgupnp-av/fragment-util.h |   54 +++
 3 files changed, 852 insertions(+), 1 deletions(-)
---
diff --git a/libgupnp-av/Makefile.am b/libgupnp-av/Makefile.am
index 5737f65..e4ccfc9 100644
--- a/libgupnp-av/Makefile.am
+++ b/libgupnp-av/Makefile.am
@@ -9,7 +9,7 @@
 # age to 0.
 LTVERSION = 2:0:0
 
-AM_CFLAGS = $(LIBGUPNP_CFLAGS) -I$(top_srcdir)
+AM_CFLAGS = $(LIBGUPNP_CFLAGS) -I$(top_srcdir) -DDATADIR="\"$(pkgdatadir)\""
 
 libgupnp_av_incdir = $(includedir)/gupnp-av-1.0/libgupnp-av
 
@@ -72,6 +72,8 @@ libgupnp_av_1_0_la_SOURCES = gupnp-didl-lite-object.c \
 			     xml-util.h \
 			     gvalue-util.c \
 			     gvalue-util.h \
+			     fragment-util.c \
+			     fragment-util.h \
 			     xsd-data.c \
 			     xsd-data.h \
 			     $(BUILT_SOURCES)
diff --git a/libgupnp-av/fragment-util.c b/libgupnp-av/fragment-util.c
new file mode 100644
index 0000000..32792ec
--- /dev/null
+++ b/libgupnp-av/fragment-util.c
@@ -0,0 +1,795 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Authors: Krzesimir Nowak <krnowak openismus com>
+ *
+ * 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.
+ */
+
+#include <stdarg.h>
+#include <libxml/parserInternals.h>
+
+#include "fragment-util.h"
+#include "xml-util.h"
+
+typedef struct {
+        gchar *node_name;
+        gchar *attribute_name;
+} NodeDiff;
+
+static NodeDiff *
+node_diff_new (const xmlChar *node_name,
+               const xmlChar *attribute_name)
+{
+        NodeDiff *diff = g_slice_new (NodeDiff);
+
+        diff->node_name = g_strdup ((gchar *) node_name);
+        diff->attribute_name = g_strdup ((gchar *) attribute_name);
+
+        return diff;
+}
+
+static void
+node_diff_free (NodeDiff *diff)
+{
+        if (diff != NULL) {
+                g_free (diff->node_name);
+                g_free (diff->attribute_name);
+                g_slice_free (NodeDiff, diff);
+        }
+}
+
+static GList *
+get_toplevel_changes (xmlNodePtr current_node,
+                      xmlNodePtr new_node)
+{
+        xmlAttrPtr attribute;
+        GHashTable *current_attributes = xml_util_get_attributes_map
+                                        (current_node);
+        GList *changes = NULL;
+        const xmlChar *name = new_node->name;
+
+        /* compare attributes */
+        for (attribute = new_node->properties;
+             attribute != NULL;
+             attribute = attribute->next) {
+                const xmlChar *value = NULL;
+                const xmlChar *key = attribute->name;
+                gboolean differs = FALSE;
+
+                if (g_hash_table_lookup_extended (current_attributes,
+                                                  key,
+                                                  NULL,
+                                                  (gpointer *) &value)) {
+                        if (xmlStrcmp (value, attribute->children->content))
+                                differs = TRUE;
+                        g_hash_table_remove (current_attributes, key);
+                } else
+                        differs = TRUE;
+                if (differs)
+                        changes = g_list_prepend (changes,
+                                                  node_diff_new (name,
+                                                                 key));
+        }
+
+        if (g_hash_table_size (current_attributes) > 0) {
+                GHashTableIter iter;
+                xmlChar *key = NULL;
+
+                g_hash_table_iter_init (&iter, current_attributes);
+                while (g_hash_table_iter_next (&iter,
+                                               (gpointer *) &key,
+                                               NULL))
+                        changes = g_list_prepend (changes, node_diff_new (name,
+                                                                          key));
+        }
+
+        g_hash_table_unref (current_attributes);
+
+        return changes;
+}
+
+static gboolean
+is_read_only (const gchar *changed_element,
+              const gchar *changed_attribute)
+{
+        static GHashTable *readonly_props = NULL;
+        static gsize readonly_props_loaded = 0;
+
+        if (g_once_init_enter (&readonly_props_loaded)) {
+                readonly_props = g_hash_table_new (g_str_hash,
+                                                   g_str_equal);
+
+                g_hash_table_add (readonly_props, "@id");
+                g_hash_table_add (readonly_props, "@parentID");
+                g_hash_table_add (readonly_props, "@refID");
+                g_hash_table_add (readonly_props, "@restricted");
+                g_hash_table_add (readonly_props, "@searchable");
+                g_hash_table_add (readonly_props, "@childCount");
+                g_hash_table_add (readonly_props, "searchClass");
+                g_hash_table_add (readonly_props, "searchClass name");
+                g_hash_table_add (readonly_props, "searchClass includeDerived");
+                g_hash_table_add (readonly_props, "createClass");
+                g_hash_table_add (readonly_props, "createClass name");
+                g_hash_table_add (readonly_props, "createClass includeDerived");
+                g_hash_table_add (readonly_props, "writeStatus");
+                g_hash_table_add (readonly_props, "res importUri");
+                g_hash_table_add (readonly_props, "storageTotal");
+                g_hash_table_add (readonly_props, "storageUsed");
+                g_hash_table_add (readonly_props, "storageFree");
+                g_hash_table_add (readonly_props, "storageMaxPartition");
+                g_hash_table_add (readonly_props, "storageMedium");
+                g_hash_table_add (readonly_props, "playbackCount");
+                g_hash_table_add (readonly_props, "srsRecordScheduleID");
+                g_hash_table_add (readonly_props, "srsRecordTaskID");
+                g_hash_table_add (readonly_props, "price");
+                g_hash_table_add (readonly_props, "price currency");
+                g_hash_table_add (readonly_props, "payPerView");
+                g_hash_table_add (readonly_props, "dateTimeRange");
+                g_hash_table_add (readonly_props,
+                                  "dateTimeRange daylightSaving");
+                g_hash_table_add (readonly_props, "signalStrength");
+                g_hash_table_add (readonly_props, "signalLocked");
+                g_hash_table_add (readonly_props, "tuned");
+                g_hash_table_add (readonly_props, "containerUpdateID");
+                g_hash_table_add (readonly_props, "objectUpdateID");
+                g_hash_table_add (readonly_props, "totalDeletedChildCount");
+                g_hash_table_add (readonly_props, "res updateCount");
+                g_once_init_leave (&readonly_props_loaded, 1);
+        }
+        if (changed_element != NULL) {
+                if (changed_attribute != NULL) {
+                        gchar *test_prop = g_strdup_printf ("%s %s",
+                                                            changed_element,
+                                                            changed_attribute);
+                        gboolean result = g_hash_table_contains (readonly_props,
+                                                                 test_prop);
+
+                        g_free (test_prop);
+                        if (result)
+                                return TRUE;
+                        test_prop = g_strdup_printf ("@%s", changed_attribute);
+                        result = g_hash_table_contains (readonly_props,
+                                                        test_prop);
+                        g_free (test_prop);
+                        if (result)
+                                return TRUE;
+                }
+
+                return g_hash_table_contains (readonly_props, changed_element);
+        }
+
+        return FALSE;
+}
+
+static gboolean
+is_any_change_read_only (xmlNodePtr current_node,
+                         xmlNodePtr new_node)
+{
+        GList *changes = get_toplevel_changes (current_node, new_node);
+        GList *iter;
+        gboolean read_only = FALSE;
+
+        for (iter = changes; iter != NULL; iter = iter->next) {
+                NodeDiff *diff = (NodeDiff *) iter->data;
+
+                if (is_read_only (diff->node_name,
+                                  diff->attribute_name)) {
+                        read_only = TRUE;
+
+                        break;
+                }
+        }
+
+        if (changes != NULL)
+                g_list_free_full (changes, (GDestroyNotify) node_diff_free);
+        return read_only;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_modification (DocNode    *modified,
+                              xmlNodePtr  current_node,
+                              xmlNodePtr  new_node,
+                              XSDData    *xsd_data)
+{
+        xmlNodePtr mod_cur_node = xml_util_find_node (modified->node,
+                                                      current_node);
+        xmlNodePtr new_node_copy = xml_util_copy_node (new_node);
+
+        if (mod_cur_node == NULL) {
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+        }
+
+        xmlUnlinkNode (new_node_copy);
+        mod_cur_node = xmlReplaceNode (mod_cur_node, new_node_copy);
+        xmlUnlinkNode (mod_cur_node);
+        xmlFreeNode (mod_cur_node);
+
+        if (!xsd_data_validate_doc (xsd_data, modified->doc))
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+
+        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_addition (DocNode    *modified,
+                          xmlNodePtr  sibling,
+                          xmlNodePtr  new_node,
+                          XSDData    *xsd_data)
+{
+        xmlNodePtr mod_sibling;
+        xmlNodePtr new_node_copy = xml_util_copy_node (new_node);
+
+        if (sibling->doc == modified->doc)
+                mod_sibling = sibling;
+        else
+                mod_sibling = xml_util_find_node (modified->node, sibling);
+
+        if (mod_sibling == NULL)
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+
+        xmlUnlinkNode (new_node_copy);
+
+        if (xmlAddNextSibling (mod_sibling, new_node_copy) == NULL) {
+                xmlFreeNode (new_node_copy);
+
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+        }
+
+        if (!xsd_data_validate_doc (xsd_data, modified->doc))
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+
+        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_removal (DocNode    *modified,
+                         xmlNodePtr  current_node,
+                         XSDData    *xsd_data)
+{
+        xmlNodePtr mod_cur_node = xml_util_find_node (modified->node,
+                                                      current_node);
+
+        if (mod_cur_node == NULL)
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+
+        xmlUnlinkNode (mod_cur_node);
+        xmlFreeNode (mod_cur_node);
+        if (!xsd_data_validate_doc (xsd_data, modified->doc))
+                /* not sure if this is correct */
+                return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
+
+        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+typedef struct {
+        gboolean required;
+        GHashTable* required_dep_props; /* string set */
+        GHashTable* required_indep_props; /* string to indep prop */
+} IndependentProperty;
+
+static void
+independent_property_free (IndependentProperty *indep)
+{
+        if (indep != NULL) {
+                g_hash_table_unref (indep->required_dep_props);
+                g_hash_table_unref (indep->required_indep_props);
+                g_slice_free (IndependentProperty, indep);
+        }
+}
+
+static IndependentProperty *
+independent_property_new (gboolean required)
+{
+        IndependentProperty *indep = g_slice_new (IndependentProperty);
+
+        indep->required = required;
+        indep->required_dep_props = g_hash_table_new_full (g_str_hash,
+                                                           g_str_equal,
+                                                           g_free,
+                                                           NULL);
+        indep->required_indep_props = g_hash_table_new_full
+                                   (g_str_hash,
+                                    g_str_equal,
+                                    g_free,
+                                    (GDestroyNotify) independent_property_free);
+
+        return indep;
+}
+
+static void
+insert_indep_prop (GHashTable          *props,
+                   gchar               *name,
+                   IndependentProperty *prop)
+{
+        g_hash_table_insert (props, g_strdup (name), prop);
+}
+
+static void
+insert_indep_prop_to_indep (IndependentProperty *prop,
+                            gchar               *name,
+                            IndependentProperty *req_prop)
+{
+        insert_indep_prop (prop->required_indep_props, name, req_prop);
+}
+
+static void
+add_dep_prop (IndependentProperty *indep,
+              gchar               *name)
+{
+        g_hash_table_add (indep->required_dep_props, g_strdup (name));
+}
+
+static IndependentProperty *
+create_prop_with_required_dep_props (gboolean  required,
+                                     gchar    *dep_prop,
+                                     ...)
+{
+        IndependentProperty *indep = independent_property_new (required);
+
+        if (dep_prop != NULL) {
+                va_list var_args;
+                gchar *name = dep_prop;
+
+                va_start (var_args, dep_prop);
+                do {
+                        add_dep_prop (indep, name);
+                        name = va_arg (var_args, gchar *);
+                } while (name != NULL);
+                va_end (var_args);
+        }
+
+        return indep;
+}
+
+static IndependentProperty *
+create_foreign_metadata_props (void)
+{
+        IndependentProperty *fm = independent_property_new (FALSE);
+        IndependentProperty *other;
+
+        add_dep_prop (fm, "type");
+
+        other = independent_property_new (TRUE);
+        insert_indep_prop_to_indep (fm, "fmId", other);
+
+        other = independent_property_new (TRUE);
+        insert_indep_prop_to_indep (fm, "fmClass", other);
+
+        other = independent_property_new (TRUE);
+        insert_indep_prop_to_indep (fm, "fmProvider", other);
+
+        other = independent_property_new (TRUE);
+        add_dep_prop (other, "xmlFlag");
+        insert_indep_prop_to_indep (fm, "fmBody", other);
+
+        return fm;
+}
+
+static GHashTable *
+get_required_properties (void)
+{
+        static GHashTable *required_props = NULL;
+        static gsize required_props_loaded = 0;
+
+        if (g_once_init_enter (&required_props_loaded)) {
+                required_props = g_hash_table_new_full
+                                   (g_str_hash,
+                                    g_str_equal,
+                                    g_free,
+                                    (GDestroyNotify) independent_property_free);
+
+                insert_indep_prop (required_props,
+                                   "",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "id",
+                                         "parentID",
+                                         "restricted",
+                                         NULL));
+
+                insert_indep_prop (required_props,
+                                   "title",
+                                   independent_property_new (TRUE));
+                insert_indep_prop (required_props,
+                                   "class",
+                                   independent_property_new (TRUE));
+
+                insert_indep_prop (required_props,
+                                   "res",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "protocolInfo",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "programID",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "type",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "seriesID",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "type",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "channelID",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "type",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "programCode",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "type",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "channelGroupName",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "id",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "price",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "currency",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "desc",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "nameSpace",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "deviceUDN",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "serviceType",
+                                         "serviceId",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "stateVariableCollection",
+                                   create_prop_with_required_dep_props
+                                        (FALSE,
+                                         "serviceName",
+                                         "rcsInstanceType",
+                                         NULL));
+                insert_indep_prop (required_props,
+                                   "foreignMetadata",
+                                   create_foreign_metadata_props ());
+                g_once_init_leave (&required_props_loaded, 1);
+        }
+
+        return required_props;
+}
+
+static gboolean
+is_required (const xmlChar *changed_element,
+             const xmlChar *changed_attribute)
+{
+        GHashTable *required_props = get_required_properties ();
+
+        if (changed_element != NULL) {
+                IndependentProperty *toplevel_prop = g_hash_table_lookup
+                                        (required_props,
+                                         "");
+                IndependentProperty *this_prop = g_hash_table_lookup
+                                        (required_props,
+                                         (gpointer) changed_element);
+
+                if (changed_attribute != NULL) {
+                        if (g_hash_table_contains
+                                        (toplevel_prop->required_dep_props,
+                                         changed_attribute))
+                                return TRUE;
+                        if (g_hash_table_contains
+                                        (this_prop->required_dep_props,
+                                         changed_attribute))
+                                return TRUE;
+                }
+                if (g_hash_table_contains (toplevel_prop->required_indep_props,
+                                           changed_element))
+                                return TRUE;
+                /* TODO: check if changed element is not a required
+                 * property of its parent element. That needs some
+                 * additions in IndepependentProperty.
+                 */
+        }
+
+        return FALSE;
+}
+
+static GUPnPDIDLLiteFragmentResult
+new_doc_is_valid_modification (DocNode   *modified,
+                               xmlDocPtr  current_doc,
+                               xmlDocPtr  new_doc,
+                               XSDData   *xsd_data)
+{
+        xmlNodePtr current_node = current_doc->children->children;
+        xmlNodePtr new_node = new_doc->children->children;
+        xmlNodePtr last_sibling = NULL;
+
+        while (current_node != NULL && new_node != NULL) {
+                GUPnPDIDLLiteFragmentResult result;
+                xmlNodePtr temp_current_node = current_node;
+                xmlNodePtr temp_new_node = new_node;
+
+                last_sibling = new_node;
+                /* We can't put this line into for instruction,
+                 * because new_node could be unlinked from its
+                 * document and put into another one in
+                 * apply_temporary_modification. We have to get its
+                 * sibling before that could happen.
+                 */
+                new_node = new_node->next;
+                current_node = current_node->next;
+                if (xml_util_node_deep_equal (temp_current_node, temp_new_node))
+                        /* This is just a context, skip the checks. */
+                        continue;
+                if (xmlStrcmp (temp_current_node->name, temp_new_node->name))
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+                if (is_any_change_read_only (temp_current_node, temp_new_node))
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+                result = apply_temporary_modification (modified,
+                                                       temp_current_node,
+                                                       temp_new_node,
+                                                       xsd_data);
+                if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+                        return result;
+        }
+        if (last_sibling == NULL) {
+                if (modified->node->children != NULL)
+                        last_sibling = modified->node->last;
+                else
+                        /* We expect that modified object has some
+                         * required tags like <upnp:class> or
+                         * <dc:title>.
+                         */
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+        }
+        /* If there are some more nodes in current fragment then it
+         * means they are going to be removed. Check against required
+         * or read-only tag removal.
+         */
+        while (current_node != NULL) {
+                GUPnPDIDLLiteFragmentResult result;
+                xmlNodePtr temp_node = current_node;
+
+                current_node = current_node->next;
+                /* TODO: should we check if there are some readonly
+                 * attributes when we remove whole element?
+                 */
+                if (is_read_only ((gchar *) temp_node->name, NULL))
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+                /* We don't check for required attributes or
+                 * subelements, because most of them are required only
+                 * when the element exists. And we are removing this
+                 * one.
+                 */
+                if (is_required (temp_node->name, NULL))
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
+                result = apply_temporary_removal (modified,
+                                                  temp_node,
+                                                  xsd_data);
+
+                if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+                        return result;
+        }
+        /* If there are some more nodes in new fragment then it means
+         * they are going to be added. Check against read-only tags
+         * addition and general sanity check.
+         */
+        while (new_node != NULL) {
+                GUPnPDIDLLiteFragmentResult result;
+                xmlNodePtr temp_node;
+
+                if (is_read_only ((gchar *) new_node->name, NULL))
+                        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+                /* TODO: We probably should check if newly added node
+                 * has all required properties. Maybe XSD check could
+                 * do that for us.
+                 */
+                temp_node = new_node;
+                new_node = new_node->next;
+                result = apply_temporary_addition (modified,
+                                                   last_sibling,
+                                                   temp_node,
+                                                   xsd_data);
+                if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+                        return result;
+        }
+
+        return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static gchar *
+fix_fragment (const gchar *fragment)
+{
+        return g_strdup_printf
+                    ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                     "<DIDLLiteFragment\n"
+                     "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n";
+                     "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\"\n"
+                     "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"\n"
+                     "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
+                     ">%s</DIDLLiteFragment>\n",
+                     fragment);
+}
+
+static gboolean
+is_current_doc_part_of_original_doc (DocNode   *original,
+                                     xmlDocPtr  current_doc)
+{
+        xmlNodePtr current_node = current_doc->children->children;
+        xmlNodePtr this_node;
+
+        /* No current node means that we want to add new elements to
+           the document. */
+        if (current_node == NULL)
+                return TRUE;
+
+        this_node = xml_util_find_node (original->node, current_node);
+
+        if (this_node == NULL)
+                return FALSE;
+
+        for (current_node = current_node->next, this_node = this_node->next;
+             current_node != NULL && this_node != NULL;
+             current_node = current_node->next, this_node = this_node->next)
+                if (!xml_util_node_deep_equal (current_node, this_node))
+                        return FALSE;
+
+        return TRUE;
+}
+
+GUPnPDIDLLiteFragmentResult
+fragment_util_check_fragments (DocNode     *original,
+                               DocNode     *modified,
+                               const gchar *current_fragment,
+                               const gchar *new_fragment,
+                               XSDData     *xsd_data)
+{
+        gchar *fixed_current_fragment = fix_fragment (current_fragment);
+        gchar *fixed_new_fragment = fix_fragment (new_fragment);
+        xmlDocPtr current_doc = xmlReadDoc (BAD_CAST (fixed_current_fragment),
+                                            NULL,
+                                            NULL,
+                                            XML_PARSE_NONET);
+        xmlDocPtr new_doc = xmlReadDoc (BAD_CAST (fixed_new_fragment),
+                                        NULL,
+                                        NULL,
+                                        XML_PARSE_NONET);
+        GUPnPDIDLLiteFragmentResult result;
+
+        if (current_doc == NULL) {
+                result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML;
+
+                goto out;
+        }
+        if (new_doc == NULL) {
+                result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_BAD_XML;
+
+                goto out;
+        }
+
+        if (!is_current_doc_part_of_original_doc (original, current_doc)) {
+                result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_INVALID;
+
+                goto out;
+        }
+
+        result = new_doc_is_valid_modification (modified,
+                                                current_doc,
+                                                new_doc,
+                                                xsd_data);
+
+ out:
+        if (new_doc != NULL)
+                xmlFreeDoc (new_doc);
+        if (current_doc != NULL)
+                xmlFreeDoc (current_doc);
+        g_free (fixed_new_fragment);
+        g_free (fixed_current_fragment);
+
+        return result;
+}
+
+static const gchar *
+get_data_dir (void)
+{
+        const gchar *datadir = g_getenv ("GUPNP_AV_DATADIR");
+
+        if (datadir == NULL)
+                /* that's a macro defined by -DDATADIR=foo */
+                datadir = DATADIR;
+
+        return datadir;
+}
+
+static gchar *
+get_xsd_path (const gchar *path)
+{
+        return g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s",
+                                get_data_dir (),
+                                path);
+}
+
+static xmlParserInputPtr
+our_own_loader (const char       *url,
+                const char       *id,
+                xmlParserCtxtPtr  context)
+{
+        gchar *basename;
+        gchar *path;
+        xmlParserInputPtr input;
+
+        g_debug ("URL: %s, ID: %s.", url, id);
+
+        basename = g_path_get_basename (url);
+        path = get_xsd_path (basename);
+        g_debug ("BASENAME: %s, PATH: %s", basename, path);
+        input = xmlNewInputFromFile (context, path);
+
+        g_free (basename);
+        g_free (path);
+
+        return input;
+}
+
+XSDData *
+fragment_util_get_didl_lite_xsd_data (void)
+{
+        gchar *path = get_xsd_path ("didl-lite-v2.xsd");
+        xmlExternalEntityLoader original_loader = xmlGetExternalEntityLoader ();
+        XSDData *xsd_data;
+
+        xmlSetExternalEntityLoader (our_own_loader);
+        xsd_data = xsd_data_new (path);
+        xmlSetExternalEntityLoader (original_loader);
+
+        g_free (path);
+
+        return xsd_data;
+}
+
+gboolean
+fragment_util_apply_modification (xmlNodePtr *node_ptr,
+                                  DocNode    *modified)
+{
+        xmlNodePtr node_copy;
+        xmlNodePtr old;
+
+        if (node_ptr == NULL || *node_ptr == NULL)
+                return FALSE;
+
+        node_copy = xml_util_copy_node (modified->node);
+
+        if (node_copy == NULL)
+                return FALSE;
+
+        old = xmlReplaceNode (*node_ptr, node_copy);
+
+        if (old == NULL)
+                return FALSE;
+
+        *node_ptr = node_copy;
+        xmlFreeNode (old);
+
+        return TRUE;
+}
diff --git a/libgupnp-av/fragment-util.h b/libgupnp-av/fragment-util.h
new file mode 100644
index 0000000..c24d7ec
--- /dev/null
+++ b/libgupnp-av/fragment-util.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * Authors: Krzesimir Nowak <krnowak openismus com>
+ *
+ * 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.
+ */
+
+#ifndef __FRAGMENT_UTIL_H__
+#define __FRAGMENT_UTIL_H__
+
+#include <glib.h>
+#include <libxml/tree.h>
+
+#include "xsd-data.h"
+#include "gupnp-av-enums.h"
+
+G_BEGIN_DECLS
+
+typedef struct {
+        xmlDocPtr doc;
+        xmlNodePtr node;
+} DocNode;
+
+G_GNUC_INTERNAL XSDData *
+fragment_util_get_didl_lite_xsd_data    (void);
+
+G_GNUC_INTERNAL GUPnPDIDLLiteFragmentResult
+fragment_util_check_fragments           (DocNode     *original,
+                                         DocNode     *modified,
+                                         const gchar *current_fragment,
+                                         const gchar *new_fragment,
+                                         XSDData     *xsd_data);
+
+G_GNUC_INTERNAL gboolean
+fragment_util_apply_modification        (xmlNodePtr *node_ptr,
+                                         DocNode    *modified);
+
+G_END_DECLS
+
+#endif /* __FRAGMENT_UTIL_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]