[gupnp-av/wip/didl-lite-fragments: 16/20] Add fragment utils.
- From: Krzesimir Nowak <krnowak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gupnp-av/wip/didl-lite-fragments: 16/20] Add fragment utils.
- Date: Thu, 25 Oct 2012 09:49:46 +0000 (UTC)
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]