Blame libgupnp-av/fragment-util.c

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