Thu, 16 Jan 2020 21:31:16 +0100
add function for getting all namespace definitions that are required for an element
--- 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; +}