add function for getting all namespace definitions that are required for an element webdav

Thu, 16 Jan 2020 21:31:16 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 16 Jan 2020 21:31:16 +0100
branch
webdav
changeset 225
e4f3e1433098
parent 224
0de1ec82628e
child 226
49adcbd7d473

add function for getting all namespace definitions that are required for an element

src/server/public/webdav.h file | annotate | diff | comparison | revisions
src/server/test/main.c file | annotate | diff | comparison | revisions
src/server/test/xml.c file | annotate | diff | comparison | revisions
src/server/test/xml.h file | annotate | diff | comparison | revisions
src/server/webdav/xml.c file | annotate | diff | comparison | revisions
--- a/src/server/public/webdav.h	Thu Jan 16 19:14:53 2020 +0100
+++ b/src/server/public/webdav.h	Thu Jan 16 21:31:16 2020 +0100
@@ -337,6 +337,11 @@
         wsxml_func endcb,
         void *udata);
 
+WebdavNSList* wsxml_get_required_namespaces(
+        pool_handle_t *pool,
+        WSXmlNode *node,
+        int *error);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/server/test/main.c	Thu Jan 16 19:14:53 2020 +0100
+++ b/src/server/test/main.c	Thu Jan 16 21:31:16 2020 +0100
@@ -71,6 +71,7 @@
     
     // xml tests
     ucx_test_register(suite, test_wsxml_iterator);
+    ucx_test_register(suite, test_wsxml_get_required_namespaces);
     
     // webdav tests
     ucx_test_register(suite, test_propfind_parse);
--- a/src/server/test/xml.c	Thu Jan 16 19:14:53 2020 +0100
+++ b/src/server/test/xml.c	Thu Jan 16 21:31:16 2020 +0100
@@ -165,3 +165,97 @@
     xmlFreeDoc(doc2);
     UCX_TEST_END;
 }
+
+// checks if the namespace list contains the test namespaces x1, x2, x3 and x4
+static void check_ns_list(WebdavNSList *list, int *x1, int *x2, int *x3, int *x4) {
+    *x1 = 0;
+    *x2 = 0;
+    *x3 = 0;
+    *x4 = 0;
+    
+    WebdavNSList *elm = list;
+    while(elm) {
+        if(!strcmp((const char*)elm->namespace->prefix, "x1") &&
+           !strcmp((const char*)elm->namespace->href, "http://example.com/ns1/"))
+        {
+            *x1 = 1;
+        } else if(!strcmp((const char*)elm->namespace->prefix, "x2") &&
+                  !strcmp((const char*)elm->namespace->href, "http://example.com/ns2/"))
+        {
+            *x2 = 1;
+        } else if(!strcmp((const char*)elm->namespace->prefix, "x3") &&
+                  !strcmp((const char*)elm->namespace->href, "http://example.com/ns_0/"))
+        {
+            *x3 = 1;
+        } else if(!strcmp((const char*)elm->namespace->prefix, "x4") &&
+                  !strcmp((const char*)elm->namespace->href, "http://example.com/ns_0/"))
+        {
+            *x4 = 1;
+        }
+            
+        elm = elm->next;
+    }
+}
+
+UCX_TEST(test_wsxml_get_required_namespaces) {
+    Session *sn = testutil_session();
+    
+    UCX_TEST_BEGIN;
+    
+    xmlDoc *doc3 = xmlReadMemory(
+            XML_TESTDATA3, strlen(XML_TESTDATA3), NULL, NULL, 0);
+    xmlDoc *doc4 = xmlReadMemory(
+            XML_TESTDATA4, strlen(XML_TESTDATA4), NULL, NULL, 0);
+    xmlDoc *doc5 = xmlReadMemory(
+            XML_TESTDATA5, strlen(XML_TESTDATA5), NULL, NULL, 0);
+    
+    xmlNode *node0 = xmlDocGetRootElement(doc3);
+    xmlNode *node1 = xmlDocGetRootElement(doc3)->children;
+    xmlNode *node2 = xmlDocGetRootElement(doc4)->children;
+    xmlNode *node3 = xmlDocGetRootElement(doc5)->children;
+    
+    UCX_TEST_ASSERT(doc3, "doc3 is NULL");
+    UCX_TEST_ASSERT(doc4, "doc4 is NULL");
+    UCX_TEST_ASSERT(doc5, "doc5 is NULL");
+    
+    int err0, err1, err2, err3;
+    int x1 = 0;
+    int x2 = 0;
+    int x3 = 0;
+    int x4 = 0;
+    WebdavNSList *elm = NULL;
+    
+    // Test 0: 
+    WebdavNSList *ns0 = wsxml_get_required_namespaces(sn->pool, node0, &err0);
+    UCX_TEST_ASSERT(!err0, "ns0 failed");
+    UCX_TEST_ASSERT(!ns0, "ns0: nsdefs should be ignored");
+    
+    WebdavNSList *ns1 = wsxml_get_required_namespaces(sn->pool, node1, &err1);
+    check_ns_list(ns1, &x1, &x2, &x3, &x4);
+    UCX_TEST_ASSERT(!err1, "ns1 failed");
+    UCX_TEST_ASSERT(ns1, "ns1: no list");
+    UCX_TEST_ASSERT(x1, "ns1: x1 missing");
+    UCX_TEST_ASSERT(x2, "ns1: x2 missing");
+    UCX_TEST_ASSERT(x3, "ns1: x3 missing");
+    UCX_TEST_ASSERT(x4, "ns1: x4 missing");
+    
+    WebdavNSList *ns2 = wsxml_get_required_namespaces(sn->pool, node2, &err2);
+    check_ns_list(ns2, &x1, &x2, &x3, &x4);
+    UCX_TEST_ASSERT(!err2, "ns2 failed");
+    UCX_TEST_ASSERT(ns2, "ns2: no list");
+    UCX_TEST_ASSERT(x1, "ns2: x1 missing");
+    UCX_TEST_ASSERT(x2, "ns2: x2 missing");
+    UCX_TEST_ASSERT(!x3, "ns2: x3");
+    UCX_TEST_ASSERT(!x4, "ns2: x4");
+    
+    WebdavNSList *ns3 = wsxml_get_required_namespaces(sn->pool, node3, &err3);
+    check_ns_list(ns3, &x1, &x2, &x3, &x4);
+    UCX_TEST_ASSERT(!err3, "ns3 failed");
+    UCX_TEST_ASSERT(ns3, "ns3: no list");
+    UCX_TEST_ASSERT(x1, "ns3: x1 missing");
+    UCX_TEST_ASSERT(x2, "ns3: x2 missing");
+    UCX_TEST_ASSERT(!x3, "ns3: x3");
+    UCX_TEST_ASSERT(!x4, "ns3: x4");
+    
+    UCX_TEST_END;
+}
--- a/src/server/test/xml.h	Thu Jan 16 19:14:53 2020 +0100
+++ b/src/server/test/xml.h	Thu Jan 16 21:31:16 2020 +0100
@@ -37,6 +37,7 @@
 #endif
 
 UCX_TEST(test_wsxml_iterator);
+UCX_TEST(test_wsxml_get_required_namespaces);
 
 
 #define XML_TESTDATA1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
@@ -65,6 +66,37 @@
             <x><z/></x> \
         </test><nextelm/></wrapper><ignore/></root>"
 
+#define XML_TESTDATA3 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
+        <x1:prop \
+            xmlns:x1=\"http://example.com/ns1/\" \
+            xmlns:x2=\"http://example.com/ns2/\" \
+            xmlns:x3=\"http://example.com/ns_0/\" \
+            xmlns:x4=\"http://example.com/ns_0/\" > \
+            <x1:elm1>str1</x1:elm1>\
+            <x2:elm2>str1</x2:elm2>\
+            <x3:elm3>str1</x3:elm3>\
+            <x4:elm4>str1</x4:elm4>\
+        </x1:prop>"
+
+#define XML_TESTDATA4 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
+        <x1:prop \
+            xmlns:x1=\"http://example.com/ns1/\" \
+            xmlns:x2=\"http://example.com/ns2/\" \
+            xmlns:x3=\"http://example.com/ns_0/\" \
+            xmlns:x4=\"http://example.com/ns_0/\" >\
+            <x1:elm1>str1</x1:elm1>\
+            <x2:elm2>str1</x2:elm2>\
+        </x1:prop>"
+
+#define XML_TESTDATA5 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
+        <x1:prop \
+            xmlns:x1=\"http://example.com/ns1/\" \
+            xmlns:x2=\"http://example.com/ns2/\" > \
+            <x1:elm1>str1</x1:elm1>\
+            <x2:elm2>str1</x2:elm2>\
+            <x3:elm3 xmlns:x3=\"http://example.com/ns_0/\" >str1</x3:elm3>\
+            <x4:elm4 xmlns:x4=\"http://example.com/ns_0/\" >str1</x4:elm4>\
+        </x1:prop>"
 
 #ifdef __cplusplus
 }
--- a/src/server/webdav/xml.c	Thu Jan 16 19:14:53 2020 +0100
+++ b/src/server/webdav/xml.c	Thu Jan 16 21:31:16 2020 +0100
@@ -30,8 +30,30 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <ucx/string.h>
+#include <ucx/map.h>
+
+#include "../util/util.h"
+
 #include "xml.h"
 
+/* ------------------------     utils      ------------------------ */
+
+/*
+ * generates a string key for an xml namespace
+ * format: prefix '\0' href
+ */
+static sstr_t namespace_key(UcxAllocator *a, WSNamespace *ns) {
+    sstr_t key = sstrcat_a(a, 3,
+            ns->prefix ? sstr((char*)ns->prefix) : S("\0"),
+            S("\0"),
+            sstr((char*)ns->href));
+    return key;
+}
+
+
+/* ------------------------ wsxml_iterator ------------------------ */
+
 typedef struct StackElm {
     WSXmlNode *node; // list of nodes
     //WSXmlNode *parent; // if not NULL, call endcb after node->next is NULL
@@ -162,3 +184,124 @@
     return ret;
 }
 
+/* ------------------------ wsxml_get_namespaces ------------------------ */
+
+typedef struct WSNsCollector {
+    UcxAllocator *a;
+    UcxMap *nsmap;
+    WebdavNSList *def;
+    int error;
+} WSNsCollector;
+
+static int nslist_node_begin(xmlNode *node, void *userdata) {
+    WSNsCollector *col = userdata;
+    // namespace required for all elements
+    if(node->type == XML_ELEMENT_NODE && node->ns) {
+        // we create a list of unique prefix-href namespaces by putting
+        // all namespaces in a map
+        sstr_t nskey = namespace_key(col->a, node->ns);
+        if(!nskey.ptr) {
+            col->error = 1;
+            return 1;
+        }
+        if(ucx_map_sstr_put(col->nsmap, nskey, node->ns)) {
+            col->error = 1;
+            return 1;
+        }
+        
+        // collect all namespace definitions for removing these namespaces
+        // from col->nsmap later
+        WSNamespace *def = node->nsDef;
+        while(def) {
+            WebdavNSList *newdef = col->a->malloc(
+                    col->a->pool, sizeof(WebdavNSList));
+            if(!newdef) {
+                col->error = 1;
+                return 1;
+            }
+            newdef->namespace = def;
+            newdef->prev = NULL;
+            newdef->next = NULL;
+            // prepend newdef to the list
+            if(col->def) {
+                newdef->next = col->def;
+                col->def->prev = newdef;
+            }
+            col->def = newdef;
+            
+            // continue with next namespace definition
+            def = def->next;
+        }
+    }
+    return 0;
+}
+
+static int nslist_node_end(xmlNode *node, void *userdata) {
+    return 0;
+}
+
+WebdavNSList* wsxml_get_required_namespaces(
+        pool_handle_t *pool,
+        WSXmlNode *node,
+        int *error)
+{
+    if(error) *error = 0;
+    
+    UcxAllocator a = util_pool_allocator(pool);
+    UcxMap *nsmap = ucx_map_new_a(&a, 16);
+    if(!nsmap) {
+        if(error) *error = 1;
+        return NULL;
+    }
+    
+    WSNsCollector col;
+    col.a = &a;
+    col.nsmap = nsmap;
+    col.def = NULL;
+    
+    // iterate over all xml elements
+    // this will fill the hashmap with all namespaces
+    // all namespace definitions are added to col.def
+    WebdavNSList *list = NULL;
+    WebdavNSList *end = NULL;
+    if(wsxml_iterator(pool, node, nslist_node_begin, nslist_node_end, &col)) {
+        if(error) *error = 1;
+    } else {
+        // remove all namespace definitions from the map
+        // what we get is a map that contains all missing namespace definitions
+        WebdavNSList *def = col.def;
+        while(def) {
+            sstr_t nskey = namespace_key(&a, def->namespace);
+            if(!nskey.ptr) {
+                if(error) *error = 1;
+                break;
+            }
+            ucx_map_sstr_remove(nsmap, nskey);
+            def = def->next;
+        }
+        
+        // convert nsmap to a list
+        UcxMapIterator i = ucx_map_iterator(nsmap);
+        WSNamespace *ns;
+        UCX_MAP_FOREACH(key, ns, i) {
+            WebdavNSList *newelm = pool_malloc(pool, sizeof(WebdavNSList));
+            if(!newelm) {
+                if(error) *error = 1;
+                list = NULL;
+                break;
+            }
+            newelm->namespace = ns;
+            newelm->next = NULL;
+            newelm->prev = end; // NULL or the end of list
+            if(end) {
+                end->next = newelm; // append new element
+            } else {
+                list = newelm; // start new list
+            }
+            end = newelm;
+        }
+    }
+    
+    ucx_map_free(nsmap);
+    return list;
+}

mercurial