--- 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; +}