src/server/webdav/xml.c

branch
webdav
changeset 225
e4f3e1433098
parent 224
0de1ec82628e
child 232
499711b2a970
--- 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