[gupnp-av/wip/didl-lite-fragments] wip: some more work on fragments.
- From: Krzesimir Nowak <krnowak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gupnp-av/wip/didl-lite-fragments] wip: some more work on fragments.
- Date: Wed, 17 Oct 2012 13:01:19 +0000 (UTC)
commit c66de08a2a6c1b868e6330e86b021b84cac2833a
Author: Krzesimir Nowak <krnowak openismus com>
Date: Wed Oct 17 14:39:38 2012 +0200
wip: some more work on fragments.
data/didl-lite-v2.xsd | 424 ++++++++++++++++++++++++++++++++++
libgupnp-av/Makefile.am | 2 +-
libgupnp-av/gupnp-didl-lite-object.c | 369 +++++++++++++++++++++++------
libgupnp-av/gupnp-didl-lite-object.h | 16 +-
4 files changed, 722 insertions(+), 89 deletions(-)
---
diff --git a/data/didl-lite-v2.xsd b/data/didl-lite-v2.xsd
new file mode 100644
index 0000000..efc0448
--- /dev/null
+++ b/data/didl-lite-v2.xsd
@@ -0,0 +1,424 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema targetNamespace="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:av="urn:schemas-upnp-org:av:av" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:didl-lite="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" version="2-20060531">
+
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ DIDL-Lite schema for UPnP A/V ContentDirectory services,
+ version 2.0.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:import namespace="urn:schemas-upnp-org:metadata-1-0/upnp/" schemaLocation="http://www.upnp.org/schemas/av/upnp.xsd"/>
+ <xsd:import namespace="urn:schemas-upnp-org:av:av" schemaLocation="http://www.upnp.org/schemas/av/av.xsd"/>
+ <xsd:import namespace="http://purl.org/dc/elements/1.1/" schemaLocation="http://dublincore.org/schemas/xmls/simpledc20021212.xsd"/>
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2005/08/xml.xsd"/>
+
+ <!--============================================================
+
+ This group defines the subset of Dublin Core elements that are
+ employed in DIDL-Lite.
+
+ Included Excluded
+ ___________ ___________
+ title subject
+ rights type
+ description format
+ date identifier
+ language source
+ creator coverage
+ publisher
+ contributor
+ relation
+ ============================================================-->
+ <xsd:group name="DublinCoreUsedExcluding-title">
+ <xsd:choice>
+ <xsd:element ref="dc:contributor"/>
+ <xsd:element ref="dc:creator"/>
+<!-- XXX - JGR
+ Would like to use an extension of the dc:date element rather than an extension its data type ("dc:elementType").
+ As it is now, changes to the dc:date element are not propagated to the 'qualifedDate' data type. Also, the
+ namespace of the <date> element is no longer "dc" but rather "didl-lite" which is not exactly what we want.
+ <dc:date didl-lite:daylightSaving="STANDARD">datevalue</dc:date>
+ <xsd:element ref="dc:date"/>
+ <xsd:element ref="didl-lite:qualifiedDCDate"/>
+ <xsd:element name="dcDate" type="didl-lite:qualifiedDCDate"/>
+-->
+ <xsd:element ref="dc:date"/>
+ <xsd:element ref="dc:description"/>
+ <xsd:element ref="dc:language"/>
+ <xsd:element ref="dc:publisher"/>
+ <xsd:element ref="dc:relation"/>
+ <xsd:element ref="dc:rights"/>
+ </xsd:choice>
+ </xsd:group>
+
+<!-- XXX - JGR
+ <xsd:complexType name="qualifiedDCDate">
+ <xsd:complexContent>
+ <xsd:element ref="dc:date">
+ <xsd:attributeGroup ref="upnp:dateTime.attr.group"/>
+ </xsd:element>
+ </xsd:complexContent>
+ </xsd:complexType>
+-->
+ <xsd:complexType name="qualifiedDCDate">
+ <xsd:simpleContent>
+ <xsd:extension base="dc:elementType">
+ <xsd:attributeGroup ref="upnp:dateTime.attr.group"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+
+ <!--============================================================
+ 'DIDL-Lite' is the root element of DIDL-Lite documents.
+
+ Attributes:
+ xml:lang: optional. The 'xml:lang' attribute may optionally be
+ used to specify the language of text in the DIDL-Lite document.
+ ============================================================-->
+ <xsd:group name="allowed-under-DIDL-Lite">
+ <xsd:annotation>
+ <xsd:documentation>
+ This group defines the elements allowed under the
+ DIDL-Lite root
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:choice>
+ <xsd:element name="item" type="didl-lite:item.type"/>
+ <xsd:element name="container" type="didl-lite:container.type"/>
+ <xsd:element name="desc" type="didl-lite:desc.type"/>
+ </xsd:choice>
+ </xsd:group>
+ <xsd:element name="DIDL-Lite" type="didl-lite:root.type"/>
+ <xsd:complexType name="root.type">
+ <xsd:annotation>
+ <xsd:documentation>
+ DIDL-Lite is the root element
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:group ref="didl-lite:allowed-under-DIDL-Lite" maxOccurs="unbounded"/>
+ <xsd:attribute ref="xml:lang"/>
+ </xsd:complexType>
+
+
+ <!--============================================================
+ Parts shared by 'container' and 'item' objects.
+ ============================================================-->
+
+ <xsd:attributeGroup name="commonAttrs-item-container.group">
+ <xsd:attribute name="id" type="av:didl-lite_COLON_at_id.vd.type" use="required"/>
+ <xsd:attribute name="parentID" type="av:didl-lite_COLON_at_id.vd.type" use="required"/>
+ <xsd:attribute name="restricted" type="xsd:boolean" use="required"/>
+ <xsd:attribute name="neverPlayable" type="xsd:boolean"/>
+ </xsd:attributeGroup>
+
+ <!--============================================================
+ A 'container' element may contain any number of
+ 1. Dublin Core,
+ 2. upnp,
+ 3. res,
+ 4. ref,
+ 5. item,
+ 6. container, and
+ 7. desc elements.
+
+ In all cases, the first element in container child element sequence
+ is required to be "dc:title".
+ The 'upnp:class' must also appear under container.
+ Each container is required to specify a value for the 'id' and
+ 'parentID' attributes.
+ Each container is also required to specify a value for the
+ 'restricted' attribute (true, false, 1, 0).
+ When restricted="true", the ability to change or delete the
+ Container is restricted.
+ Other optional container elements are:
+
+ 'parentID', 'childCount', and 'searchable'.
+ Other optional attributes are 'childCount' and 'searchable'.
+
+ id: type: string use: required
+ parentID: type: string use: required
+ restricted: type: string use: required
+ searchable: type: string use: optional
+ childCount: type: string use: optional
+
+ The equivalent MPEG21 DIDL element is 'CONTAINER'
+ ============================================================-->
+ <xsd:group name="allowed-under-container">
+ <xsd:annotation>
+ <xsd:documentation>
+ This group defines the elements allowed under the
+ 'container' element
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:choice>
+ <xsd:group ref="upnp:upnpForContainer"/>
+ <xsd:group ref="didl-lite:DublinCoreUsedExcluding-title"/>
+ <xsd:element name="desc" type="didl-lite:desc.type"/>
+ <xsd:element name="item" type="didl-lite:item.type"/>
+ <xsd:element name="container" type="didl-lite:container.type"/>
+ <xsd:element name="res" type="didl-lite:res.type"/>
+ </xsd:choice>
+ </xsd:group>
+
+ <xsd:complexType name="container.type">
+ <xsd:annotation>
+ <xsd:documentation>
+ 'container' element may contain any number of
+ 1. Dublin Core,
+ 2. upnp,
+ 3. res,
+ 4. ref,
+ 5 item,
+ 6. container and
+ 7. desc elements.
+ In all cases, the first element in each container child
+ element sequence is required to be "dc:title".
+ A 'upnp:class' element must also appear under container.
+ container is required to specify a value for the 'id' and
+ 'parentID' attributes.
+ container is also required to specify a value for the
+ 'restricted' attribute (true, false, 1, 0).
+ When restricted="true", the ability to change or delete the
+ container is restricted.
+ Optional container element attributes are 'childCount', and
+ 'searchable'.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="dc:title"/>
+ <xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="upnp:class.group"/>
+ <xsd:group ref="didl-lite:allowed-under-container" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attributeGroup ref="didl-lite:commonAttrs-item-container.group"/>
+ <xsd:attribute name="searchable" type="xsd:boolean"/>
+ <xsd:attribute name="childCount" type="xsd:unsignedInt"/>
+ </xsd:complexType>
+<!--============================================================
+ An 'item' element contains any number of
+ 1. Dublin Core,
+ 2. upnp,
+ 3. res, and
+ 4. desc elements.
+
+ In all cases, the first element in each item child element
+ sequence is required to be "dc:title".
+ The 'upnp:class' element must also appear under item.
+ Each item is additionally required to specify a value for the 'id'
+ attribute. If the item is actually a reference to another item, a
+ value for 'refID' is specified.
+ Each item is also required to specify a value for the 'parentID'
+ Attribute and the 'restricted' attribute (true, false, 1, 0).
+ When restricted="true", the ability to change or delete the item is
+ restricted.
+
+ Attributes:
+ Id: type: string use: required
+ parentID: type: string use: required
+ refID: type: string use: optional
+ restricted: type: boolean use: required
+
+ The equivalent MPEG21 DIDL element is 'ITEM'.
+ ============================================================-->
+ <xsd:group name="allowed-under-item">
+ <xsd:annotation>
+ <xsd:documentation>
+ This group defines the elements allowed under the 'item'
+ Element
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:choice>
+ <xsd:group ref="upnp:upnpForItem"/>
+ <xsd:group ref="didl-lite:DublinCoreUsedExcluding-title"/>
+ <xsd:element name="desc" type="didl-lite:desc.type"/>
+ <xsd:element name="res" type="didl-lite:res.type"/>
+ </xsd:choice>
+ </xsd:group>
+
+ <xsd:complexType name="item.type">
+ <xsd:annotation>
+ <xsd:documentation>
+ 'item' element contains any number of
+ 1. Dublin Core,
+ 2. upnp,
+ 3. res, and
+ 4. desc elements.
+ In all cases, the first element in each item child element
+ sequence is required to be "dc:title".
+ A 'upnp:class' element must also appear under item.
+ Item is additionally required to specify a value for the
+ 'id' attribute.
+ If the item is actually a reference to another item,
+ a value for 'refID' must be specified.
+ Item is also required to specify a value for the 'parentID'
+ attribute, and the 'restricted' attribute
+ (true, false, 1, 0).
+ When restricted="true", the ability to change or delete the
+ item is restricted.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="dc:title"/>
+ <xsd:group ref="didl-lite:allowed-under-item" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:group ref="upnp:class.group"/>
+ <xsd:group ref="didl-lite:allowed-under-item" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attributeGroup ref="didl-lite:commonAttrs-item-container.group"/>
+ <xsd:attribute name="refID" type="av:didl-lite_COLON_at_id.vd.type"/>
+ </xsd:complexType>
+<!--============================================================
+ A 'res' element indentifies a resource. A resource is typically
+ some type of asset, such as a photo, song, video, etc.
+ A 'res' element contains a URI that identifies the resource.
+
+ Attributes:
+
+ 'importUri' type: anyURI use: optional
+ The 'importUri' attribute is the optional uri locator for
+ resource updates.
+
+ 'protocolInfo' type: string use: required
+ The 'protocolInfo' attribute is a string that identifies the
+ streaming or transport protocol for transmitting the resource.
+ If not present then the content has not yet been fully imported by
+ the ContentDirectory service and is not yet accessible for playback.
+
+ 'size' type: unsignedLong use: optional
+ size, in bytes, of the resource.
+
+ 'duration' type: string use: optional
+ The 'duration' attribute identifies the duration of the playback of
+ the resource, at normal speed.
+
+ The format of the duration string is:
+ H+:MM:SS[.F+], or H+:MM:SS[.F0/F1]
+ Where:
+ +H one or more digits to indicate elapsed hours,
+ MM exactly 2 digits to indicate minutes (00 to 59),
+ SS exactly 2 digits to indicate seconds (00 to 59),
+ F+ any number of digits (including no digits) to indicate fractions of seconds,
+ F0/F1 a fraction, with F0 and F1 at least one digit long,
+ and F0 < F1.
+ The string may be preceded by an optional + or - sign, and the
+ decimal point itself may be omitted if there are no fractional seconds digits.
+
+ 'bitrate' type: unsignedInt use: optional
+ The bitrate in bytes/second of the resource.
+
+ 'sampleFrequency' type: unsignedInt use: optional
+ The sample frequency of the resource in Hz
+
+ 'bitsPerSample' type: unsignedInt use: optional
+ The bits per sample of the resource.
+
+ 'nrAudioChannels' type: unsignedInt use: optional
+ Number of audio channels of the resource, e.g. 1 for mono,
+ 2 for stereo, 6 for Dolby surround, etc.
+
+ 'resolution' type: string use: optional
+ X*Y resolution of the resource (image or video).
+ The string pattern is restricted to strings of the form:
+ [0-9]+x[0-9]+
+ (one or more digits,'x', followed by one or more digits).
+
+ 'colorDepth' type: unsignedInt use: optional
+ The color depth in bits of the resource (image or video).
+
+ 'protection' type: string use: optional
+ Some statement of the protection type of the resource
+ (not standardized).
+
+ The equivalent MPEG21 DIDL element is 'RESOURCE'.
+ ============================================================-->
+
+ <!--=============================-=============================-->
+ <!-- Resource Encoding Characteristics Properties -->
+ <!--=============================-=============================-->
+ <xsd:complexType name="res.type" mixed="true">
+ <xsd:annotation>
+ <xsd:documentation>
+ A 'res' element indentifies a resource.
+ A resource is typically some type of binary asset,
+ such as a photo, song, video, etc.
+ A 'res' element contains a URI that identifies the resource
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:anyURI">
+ <xsd:attribute name="importUri" type="xsd:anyURI"/>
+ <xsd:attribute name="protocolInfo" type="xsd:string" use="required"/>
+ <xsd:attribute name="size" type="xsd:unsignedLong"/>
+ <xsd:attribute name="duration" type="av:duration.cds1"/>
+ <xsd:attribute name="bitrate" type="xsd:unsignedInt"/>
+ <xsd:attribute name="sampleFrequency" type="xsd:unsignedInt"/>
+ <xsd:attribute name="bitsPerSample" type="xsd:unsignedInt"/>
+ <xsd:attribute name="nrAudioChannels" type="xsd:unsignedInt"/>
+ <xsd:attribute name="resolution">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="[0-9]+x[0-9]+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="colorDepth" type="xsd:unsignedInt"/>
+ <xsd:attribute name="tspec" type="av:string.len.0_256"/>
+ <xsd:attribute name="allowedUse" type="av:csv.1_.allowedUse"/>
+ <xsd:attribute name="validityStart" type="av:dateTime"/>
+ <xsd:attribute name="validityEnd" type="av:dateTime"/>
+ <xsd:attribute name="remainingTime" type="av:unsignedDuration"/>
+ <xsd:attribute name="updateCount" type="xsd:unsignedInt"/>
+ <xsd:attribute name="usageInfo" type="xsd:string"/>
+ <xsd:attribute name="rightsInfoURI" type="xsd:anyURI"/>
+ <xsd:attribute name="contentInfoURI" type="xsd:anyURI"/>
+ <xsd:attribute name="recordQuality" type="av:csv.1_.colonDelimPairs"/>
+ <xsd:attribute name="protection" type="xsd:string"/>
+ <xsd:attributeGroup ref="upnp:dateTime.attr.group"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+<!--============================================================
+ A 'desc' element identifies a descriptor.
+ A descriptor is intended to contain a block of metadata.
+ The bio of a music artist is an example use of a 'desc' element.
+ A 'desc' element may possess child elements from any namespace
+ except the DIDL-Lite namespace.
+ Values for 'id' and 'nameSpace' are required.
+ An optional 'type' attribute allows designation of the metadata
+ type, e.g. 'ratings', 'rights', etc.
+
+ Attributes:
+
+ 'id' type: string use: required
+ 'type' type: string use: optional
+ 'nameSpace' type: uri use: required
+
+ The equivalent MPEG21 DIDL element is 'DESCRIPTOR'.
+ ============================================================-->
+ <xsd:complexType name="desc.type">
+ <xsd:annotation>
+ <xsd:documentation>
+ A'desc' element identifies a descriptor.
+ A descriptor is intended to contain a block of metadata.
+ The bio of a music artist is an example use of 'desc'.
+ A 'desc' element may possess child elements from any
+ namespace except the DIDL-Lite namespace.
+ A value for 'id' is required.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:any namespace="##other"/>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="type" type="xsd:string"/>
+ <xsd:attribute name="nameSpace" type="xsd:anyURI" use="required"/>
+ </xsd:complexType>
+
+ <!--===================================================================-->
+ <!-- -->
+ <!-- Vendor-Defined Component Datatypes -->
+ <!-- -->
+ <!--===================================================================-->
+
+</xsd:schema>
\ No newline at end of file
diff --git a/libgupnp-av/Makefile.am b/libgupnp-av/Makefile.am
index 464fea5..f7799f1 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
diff --git a/libgupnp-av/gupnp-didl-lite-object.c b/libgupnp-av/gupnp-didl-lite-object.c
index 3d2341a..8575728 100644
--- a/libgupnp-av/gupnp-didl-lite-object.c
+++ b/libgupnp-av/gupnp-didl-lite-object.c
@@ -31,6 +31,8 @@
#include <string.h>
#include <libgupnp/gupnp.h>
+#include <libxml/parser.h>
+#include <libxml/xmlschemas.h>
#include "gupnp-didl-lite-object.h"
#include "gupnp-didl-lite-object-private.h"
@@ -2546,12 +2548,6 @@ is_required (const xmlChar *changed_element,
return FALSE;
}
-static gboolean
-is_valid (xmlNodePtr node G_GNUC_UNUSED)
-{
- return TRUE;
-}
-
static GList *
get_toplevel_changes (xmlNodePtr current_node,
xmlNodePtr new_node)
@@ -2612,54 +2608,204 @@ get_toplevel_changes (xmlNodePtr current_node,
}
static gboolean
-new_doc_is_valid_modification (xmlDocPtr current_doc,
- xmlDocPtr new_doc,
- GUPnPDIDLLiteFragmentResult *result) {
+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; iter = iter->next) {
+ NodeDiff *diff = (NodeDiff *) iter->data;
+
+ if (is_read_only (diff->node_name,
+ diff->attribute_name)) {
+ read_only = TRUE;
+ break;
+ }
+ }
+
+ if (changes)
+ g_list_free_full (changes, (GDestroyNotify) node_diff_free);
+ return read_only;
+}
+
+typedef struct {
+ xmlDocPtr schema_doc;
+ xmlSchemaParserCtxtPtr parser_context;
+ xmlSchemaPtr schema;
+ xmlSchemaValidCtxtPtr valid_context;
+} XSDValidateData;
+
+void
+xsd_validate_data_free (XSDValidateData *data)
+{
+ if (!data)
+ return;
+ if (data->valid_context)
+ xmlSchemaFreeValidCtxt (data->valid_context);
+ if (data->schema)
+ xmlSchemaFree (data->schema);
+ if (data->parser_context)
+ xmlSchemaFreeParserCtxt (data->parser_context);
+ if (data->schema_doc)
+ xmlFreeDoc (data->schema_doc);
+ g_slice_free (XSDValidateData, data);
+}
+
+XSDValidateData *
+xsd_validate_data_new (const gchar *xsd_file)
+{
+ XSDValidateData *data = g_slice_new0 (XSDValidateData);
+ gboolean failed = TRUE;
+
+ data->schema_doc = xmlReadFile (xsd_file, NULL, 0);
+ if (!data->schema_doc) {
+ /* the schema cannot be loaded or is not well-formed */
+ goto out;
+ }
+ data->parser_context = xmlSchemaNewDocParserCtxt (data->schema_doc);
+ if (!data->parser_context) {
+ /* unable to create a parser context for the schema */
+ goto out;
+ }
+ data->schema = xmlSchemaParse (data->parser_context);
+ if (!data->schema) {
+ /* the schema itself is not valid */
+ goto out;
+ }
+ data->valid_context = xmlSchemaNewValidCtxt (data->schema);
+ if (!data->valid_context) {
+ /* unable to create a validation context for the schema */
+ goto out;
+ }
+ failed = FALSE;
+ out:
+ if (failed) {
+ xsd_validate_data_free (data);
+ data = NULL;
+ }
+
+ return data;
+}
+
+static gboolean
+validate_temporary_modification (xmlDocPtr modified_doc,
+ XSDValidateData *vdata)
+{
+ return (xmlSchemaValidateDoc (vdata->valid_context, modified_doc) == 0);
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_modification (xmlDocPtr modified_doc,
+ xmlNodePtr current_node,
+ xmlNodePtr new_node,
+ XSDValidateData *vdata)
+{
+ xmlNodePtr mod_cur_node = find_node (modified_doc->children,
+ current_node);
+
+ if (!mod_cur_node) {
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+ }
+
+ mod_cur_node = xmlReplaceNode (mod_cur_node, new_node);
+ xmlFreeNode (mod_cur_node);
+
+ if (!validate_temporary_modification (modified_doc, vdata)) {
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+ }
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_addition (xmlDocPtr modified_doc,
+ xmlNodePtr sibling,
+ xmlNodePtr new_node,
+ XSDValidateData *vdata)
+{
+ xmlNodePtr mod_sibling = find_node (modified_doc->children,
+ sibling);
+
+ if (!mod_sibling || (xmlAddSibling (mod_sibling, new_node) != NULL)) {
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+ }
+
+ if (validate_temporary_modification (modified_doc, vdata)) {
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
+ }
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static GUPnPDIDLLiteFragmentResult
+apply_temporary_removal (xmlDocPtr modified_doc,
+ xmlNodePtr current_node,
+ XSDValidateData *vdata)
+{
+ xmlNodePtr mod_cur_node = find_node (modified_doc->children,
+ current_node);
+
+ if (!mod_cur_node)
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+
+ xmlUnlinkNode (mod_cur_node);
+ xmlFreeNode (mod_cur_node);
+ if (validate_temporary_modification (modified_doc, vdata)) {
+ /* not sure if this is correct */
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
+ }
+
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+}
+
+static GUPnPDIDLLiteFragmentResult
+new_doc_is_valid_modification (xmlDocPtr modified_doc,
+ xmlDocPtr current_doc,
+ xmlDocPtr new_doc,
+ XSDValidateData *vdata) {
xmlNodePtr current_node;
xmlNodePtr new_node;
+ xmlNodePtr last_sibling;
for (current_node = current_doc->children->children,
new_node = new_doc->children->children;
current_node && new_node;
- current_node = current_node->next,
- new_node = new_node->next) {
- GList *changes;
+ current_node = current_node->next) {
+ GUPnPDIDLLiteFragmentResult result;
if (node_deep_equal (current_node, new_node)) {
/* this is just a context, skip the checks. */
+ last_sibling = current_node;
continue;
}
if (xmlStrcmp (current_node->name, new_node->name)) {
- *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
- return FALSE;
- }
- changes = get_toplevel_changes (current_node, new_node);
- if (changes) {
- GList *iter;
-
- for (iter = changes; iter; iter = iter->next) {
- NodeDiff *diff = (NodeDiff *) iter->data;
-
- if (is_read_only (diff->node_name,
- diff->attribute_name)) {
- *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
- g_list_free_full (changes, (GDestroyNotify) node_diff_free);
- return FALSE;
- }
- }
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
}
+ if (is_any_change_read_only (current_node, new_node))
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
+ last_sibling = new_node;
+ new_node = new_node->next;
+ result = apply_temporary_modification (modified_doc,
+ current_node,
+ last_sibling,
+ vdata);
+ if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+ return result;
}
/* 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.
*/
for (; current_node; current_node = current_node->next) {
+ GUPnPDIDLLiteFragmentResult result;
+
/* TODO: should we check if there are some readonly
* attributes when we remove whole element?
*/
if (is_read_only ((gchar *) current_node->name, NULL)) {
- *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
- return FALSE;
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
}
/* We don't check for required attributes or
* subelements, because most of them are required only
@@ -2667,28 +2813,38 @@ new_doc_is_valid_modification (xmlDocPtr current_doc,
* one.
*/
if (is_required (current_node->name, NULL)) {
- *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
- return FALSE;
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG;
}
+ result = apply_temporary_removal (modified_doc,
+ current_node,
+ vdata);
+
+ 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.
*/
for (; new_node; new_node = new_node->next) {
+ GUPnPDIDLLiteFragmentResult result;
+
if (is_read_only ((gchar *) new_node->name, NULL)) {
- *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
- return FALSE;
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG;
}
/* TODO: We probably should check if newly added node
- * has all required properties.
+ * has all required properties. Maybe XSD check could
+ * do that for us.
*/
- if (!is_valid (new_node)) {
- *result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID;
- }
+ result = apply_temporary_addition (modified_doc,
+ last_sibling,
+ new_node,
+ vdata);
+ if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK)
+ return result;
}
- return TRUE;
+ return GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
}
static gchar *
@@ -2699,33 +2855,20 @@ fix_fragment (const gchar *fragment)
}
GUPnPDIDLLiteFragmentResult
-gupnp_didl_lite_object_is_fragment_pair_valid
- (GUPnPDIDLLiteObject *object,
- const gchar *current_fragment,
- const gchar *new_fragment)
-{
- xmlDocPtr this_doc;
- xmlDocPtr current_doc;
- xmlDocPtr new_doc;
+check_fragments (xmlDocPtr this_doc,
+ xmlDocPtr modified_doc,
+ const gchar *current_fragment,
+ const gchar *new_fragment,
+ XSDValidateData *vdata)
+{
+ gchar *fixed_current_fragment = fix_fragment (current_fragment);
+ gchar *fixed_new_fragment = fix_fragment (new_fragment);
+ xmlDocPtr current_doc = xmlRecoverMemory
+ (fixed_current_fragment,
+ strlen (fixed_current_fragment));
+ xmlDocPtr new_doc = xmlRecoverMemory (fixed_new_fragment,
+ strlen (fixed_new_fragment));
GUPnPDIDLLiteFragmentResult result;
- gchar *fixed_current_fragment;
- gchar *fixed_new_fragment;
-
- g_return_val_if_fail (GUPNP_IS_DIDL_LITE_OBJECT (object),
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
- g_return_val_if_fail (current_fragment != NULL,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
- g_return_val_if_fail (new_fragment != NULL,
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
-
- fixed_current_fragment = fix_fragment (current_fragment);
- fixed_new_fragment = fix_fragment (new_fragment);
- result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_APPLIABLE;
- this_doc = object->priv->xml_doc->doc;
- current_doc = xmlRecoverMemory (fixed_current_fragment,
- strlen (fixed_current_fragment));
- new_doc = xmlRecoverMemory (fixed_new_fragment,
- strlen (fixed_new_fragment));
if (!current_doc) {
result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML;
@@ -2741,26 +2884,98 @@ gupnp_didl_lite_object_is_fragment_pair_valid
goto out;
}
- if (!new_doc_is_valid_modification (current_doc, new_doc, &result)) {
- goto out;
- }
+ result = new_doc_is_valid_modification (modified_doc,
+ current_doc,
+ new_doc,
+ vdata);
out:
- g_free (fixed_new_fragment);
- g_free (fixed_current_fragment);
if (new_doc)
xmlFreeDoc (new_doc);
if (current_doc)
xmlFreeDoc (current_doc);
+ g_free (fixed_new_fragment);
+ g_free (fixed_current_fragment);
return result;
}
-gboolean
-gupnp_didl_lite_object_apply_fragment_pair
- (GUPnPDIDLLiteObject *object G_GNUC_UNUSED,
- const gchar *current_fragment G_GNUC_UNUSED,
- const gchar *new_fragment G_GNUC_UNUSED)
+static void
+apply_modification (GUPnPXMLDoc *doc,
+ xmlDocPtr modified_doc)
{
- return FALSE;
+ xmlDocPtr original_doc = doc->doc;
+
+ doc->doc = modified_doc;
+ xmlFreeDoc (original_doc);
+}
+
+GUPnPDIDLLiteFragmentResult
+gupnp_didl_lite_object_apply_fragments (GUPnPDIDLLiteObject *object,
+ GList *current_fragments,
+ GList *new_fragments)
+{
+ xmlDocPtr this_doc;
+ xmlDocPtr modified_doc;
+ GUPnPDIDLLiteFragmentResult result;
+ GList *current_iter;
+ GList *new_iter;
+ XSDValidateData *vdata = xsd_validate_data_new (DATADIR
+ G_DIR_SEPARATOR_S
+ "didl-lite-v2.xsd");
+
+ g_return_val_if_fail (GUPNP_IS_DIDL_LITE_OBJECT (object),
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+ g_return_val_if_fail (current_fragments != NULL,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+ g_return_val_if_fail (new_fragments != NULL,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+ g_return_val_if_fail (vdata != NULL,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR);
+
+ result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK;
+ this_doc = object->priv->xml_doc->doc;
+ modified_doc = xmlCopyDoc (this_doc, 1);
+
+ if (!modified_doc) {
+ result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR;
+ goto out;
+ }
+
+ for (current_iter = current_fragments, new_iter = new_fragments;
+ current_iter && new_iter;
+ current_iter = current_iter->next, new_iter = new_iter->next) {
+ const gchar *current_fragment = (gchar *) current_iter->data;
+ const gchar *new_fragment = (gchar *) new_iter->data;
+
+ result = check_fragments (this_doc,
+ modified_doc,
+ current_fragment,
+ new_fragment,
+ vdata);
+
+ if (result != GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK) {
+ goto out;
+ }
+ }
+
+ if (current_iter || new_iter) {
+ result = GUPNP_DIDL_LITE_FRAGMENT_RESULT_MISMATCH;
+ goto out;
+ }
+
+ if (!modified_doc) {
+ goto out;
+ }
+
+ /* modified doc will be freed by GUPnPXMLDoc */
+ apply_modification (object->priv->xml_doc, modified_doc);
+ modified_doc = NULL;
+ out:
+ if (modified_doc) {
+ xmlFreeDoc (modified_doc);
+ }
+ xsd_validate_data_free (vdata);
+
+ return result;
}
diff --git a/libgupnp-av/gupnp-didl-lite-object.h b/libgupnp-av/gupnp-didl-lite-object.h
index 747b9ae..daca888 100644
--- a/libgupnp-av/gupnp-didl-lite-object.h
+++ b/libgupnp-av/gupnp-didl-lite-object.h
@@ -38,13 +38,14 @@
G_BEGIN_DECLS
typedef enum {
- GUPNP_DIDL_LITE_FRAGMENT_RESULT_APPLIABLE,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_OK,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_BAD_XML,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_BAD_XML,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_CURRENT_INVALID,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_NEW_INVALID,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_REQUIRED_TAG,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_READONLY_TAG,
+ GUPNP_DIDL_LITE_FRAGMENT_RESULT_MISMATCH,
GUPNP_DIDL_LITE_FRAGMENT_RESULT_UNKNOWN_ERROR
} GUPnPDIDLLiteFragmentResult;
@@ -277,16 +278,9 @@ void
gupnp_didl_lite_object_unset_update_id (GUPnPDIDLLiteObject *object);
GUPnPDIDLLiteFragmentResult
-gupnp_didl_lite_object_is_fragment_pair_valid
- (GUPnPDIDLLiteObject *object,
- const gchar *current_fragment,
- const gchar *new_fragment);
-
-gboolean
-gupnp_didl_lite_object_apply_fragment_pair
- (GUPnPDIDLLiteObject *object,
- const gchar *current_fragment,
- const gchar *new_fragment);
+gupnp_didl_lite_object_apply_fragments (GUPnPDIDLLiteObject *object,
+ GList *current_fragments,
+ GList *new_fragments);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]