create namespace definition lists when properties are added to a multistatus response webdav

Tue, 14 Jan 2020 20:05:18 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 14 Jan 2020 20:05:18 +0100
branch
webdav
changeset 222
5f05e56cb8e2
parent 221
ff5826fc6a6c
child 223
bbaec8415c10

create namespace definition lists when properties are added to a multistatus response

src/server/public/webdav.h file | annotate | diff | comparison | revisions
src/server/test/webdav.c file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.c file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.h file | annotate | diff | comparison | revisions
src/server/webdav/operation.c file | annotate | diff | comparison | revisions
src/server/webdav/operation.h file | annotate | diff | comparison | revisions
src/server/webdav/webdav.c file | annotate | diff | comparison | revisions
--- a/src/server/public/webdav.h	Tue Dec 31 16:31:29 2019 +0100
+++ b/src/server/public/webdav.h	Tue Jan 14 20:05:18 2020 +0100
@@ -98,6 +98,8 @@
 struct WebdavProperty {
     WSNamespace *namespace;
     
+    WebdavNSList *nsdef;
+    
     const char *name;
     
     char *lang;
@@ -123,7 +125,7 @@
 };
 
 struct WebdavNSList {
-    WSNamespace *namespace;
+    WSNamespace  *namespace;
     WebdavNSList *prev;
     WebdavNSList *next;
 };
@@ -305,10 +307,16 @@
         const char *name);
 
 int webdav_property_set_value(
-        WebdavProperty *p,
+        WebdavProperty *property,
         pool_handle_t *pool,
         char *value);
 
+int webdav_property_add_nsdef(
+        WebdavProperty *p, 
+        pool_handle_t *pool,
+        const char *prefix,
+        const char *nsuri);
+
 WebdavVFSProperties webdav_vfs_properties(
         WebdavPropfindRequest *rq,
         WSBool removefromlist,
--- a/src/server/test/webdav.c	Tue Dec 31 16:31:29 2019 +0100
+++ b/src/server/test/webdav.c	Tue Jan 14 20:05:18 2020 +0100
@@ -200,7 +200,8 @@
     }
     
     return webdav_operation_create(
-            (*out_sn)->pool,
+            (*out_sn),
+            (*out_rq),
             &backend1,
             requests,
             response);
--- a/src/server/webdav/multistatus.c	Tue Dec 31 16:31:29 2019 +0100
+++ b/src/server/webdav/multistatus.c	Tue Jan 14 20:05:18 2020 +0100
@@ -56,206 +56,15 @@
     return ms;
 }
 
-static int send_xml_root(Multistatus *ms, Writer *out) {
-    writer_puts(out, S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
-                       "<D:multistatus"));
-    
-    // write the namespaces definitions
-    // key is the namespace prefix
-    // the map always contains the "DAV:" namespace with the prefix "D"
-    UcxMapIterator i = ucx_map_iterator(ms->namespaces);
-    char *href;
-    UCX_MAP_FOREACH(key, href, i) {
-        writer_puts(out, S(" xmlns:"));
-        writer_put(out, key.data, key.len);
-        writer_puts(out, S("=\""));
-        writer_puts(out, sstr(href));
-        writer_puts(out, S("\""));
-    }
-    
-    writer_puts(out, S(">\n"));
-    
-    return out->error;
-}
-
-static int send_prop_name(
-        Multistatus *ms,
-        WebdavProperty *p,
-        Writer *out,
-        char *prefixbuf,
-        int *prefixbuflen)
-{
-    int in_prefix_len = *prefixbuflen;
-    *prefixbuflen = 0;
-    
-    writer_putc(out, '<');
-    
-    const char *prefix = NULL;
-    const char *href = NULL;
-    
-    if(p->namespace && p->namespace->href) {
-        href = (const char*)p->namespace->href;
-
-        if(p->namespace->prefix) {
-            // check if there is a namespace with this prefix already defined
-            // and has the same namespace uri
-            prefix = (const char*)p->namespace->prefix;
-            char *nshref = ucx_map_cstr_get(
-                    ms->namespaces,
-                    (const char*)p->namespace->prefix);
-            if(!strcmp(nshref, href)) {
-                href = NULL; // we don't need a new xmlns def
-            }
-        } else {
-            // generate new prefix
-            for(int i=0;i<1024;i++) {
-                int len = snprintf(prefixbuf, in_prefix_len, "x%d\0", i);
-                char *test = ucx_map_cstr_get(ms->namespaces, prefixbuf);
-                if(!test) {
-                    prefix = prefixbuf;
-                    *prefixbuflen = len;
-                    break; // found an unused prefix
-                }
-                if(!prefix) {
-                    // What? Can't find a free prefix?
-                    return 1;
-                }
-            }
-        }
-    }
-    
-    if(prefix) {
-        writer_put(out, prefix, strlen(prefix));
-        writer_put(out, ":", 1);
-    }
-    
-    // write xml element name
-    writer_put(out, (const char*)p->name, strlen((const char*)p->name));
-    
-    if(href) {
-        writer_puts(out, S(" xmlns:"));
-        writer_put(out, prefix, strlen(prefix));
-        writer_puts(out, S("=\""));
-        writer_put(out, href, strlen(href));
-        writer_putc(out, '\"');
-    }
-    
-    if(p->lang) {
-        writer_puts(out, S(" lang=\""));
-        writer_puts(out, sstr(p->lang));
-        writer_putc(out, '\"');
-    }
-    
-    writer_putc(out, '>');
-    
-    return out->error;
-}
-
-
-
-#define MAX_XML_TREE_DEPTH 128
-static int send_xml(Multistatus *ms, Writer *out, WSXmlNode *node, WSNamespace *rootns, int depth) {
-    int ret = 0;
-    const char *s;
-    while(node) {
-        switch(node->type) {
-            case XML_ELEMENT_NODE: {
-                writer_putc(out, '<');
-                if(node->ns && node->ns->prefix) {
-                    s = (const char*)node->ns->prefix;
-                    writer_put(out, s, strlen(s));
-                    writer_putc(out, ':');
-                }
-                s = (const char*)node->name;
-                writer_put(out, s, strlen(s));
-                
-                
-            }
-            
-        }
-        node = node->next;
-    }
-    
-    return ret;
-}
-
-static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) {
-    writer_puts(out, S("  <D:response>\n"
-                       "    <D:href>"));
-    writer_puts(out, sstr(rp->resource.href));
-    writer_puts(out, S("</href>\n"));
-    
-    if(rp->plist_begin) {
-        writer_puts(out, S("    <D:propstat>"
-                           "      <D:prop>\n"));
-        WebdavPList *p = rp->plist_begin;
-        char prefix[16];
-        while(p) {
-            WebdavProperty *prop = p->property;
-            int prefixlen = 16;
-            if(send_prop_name(ms, prop, out, prefix, &prefixlen)) {
-                return 1;
-            }
-            
-            // send content
-            
-            
-            // send end tag
-            writer_put(out, "<", 1);
-            if(prop->namespace && prop->namespace->href) {
-                const char *pre = NULL;
-                if(prop->namespace->prefix) {
-                    pre = (const char*)prop->namespace->prefix;
-                } else if(prefixlen > 0) {
-                    pre = prefix;
-                }
-                
-                if(pre) {
-                    writer_put(out, pre, strlen(pre));
-                    writer_put(out, ":", 1);
-                }
-            }
-            writer_put(out, prop->name, strlen(prop->name));
-            writer_put(out, ">", 1);
-            
-            if(out->error) {
-                return 1;
-            }
-            
-            p = p->next;
-        }
-        writer_puts(out, S("      </D:prop>\n"
-                           "      <D:status>HTTP/1.1 200 OK</D:status>"
-                           "    </D:propstat>\n"));
-    }
-    
-    return out->error;
-}
-
 int multistatus_send(Multistatus *ms, SYS_NETFD net) {
     char buffer[MULTISTATUS_BUFFER_LENGTH];
     Writer writer;
     Writer *out = &writer;
     writer_init(out, net, buffer, MULTISTATUS_BUFFER_LENGTH);
     
-    // send the xml root element with namespace defs
-    if(send_xml_root(ms, out)) {
-        return 1;
-    }
     
-    // send response tags
-    MSResponse *response = ms->first;
-    while(response) {
-        if(send_response_tag(ms, response, out)) {
-            return 1;
-        }
-        response = response->next;
-    }
-    
-    return 0;
 }
 
-
 WebdavResource * multistatus_addresource(
         WebdavResponse *response,
         const char *path)
@@ -267,6 +76,10 @@
     }
     ZERO(res, sizeof(MSResponse));
     
+    // set href
+    res->resource.href = pool_strdup(response->op->sn->pool, path);
+    
+    // add resource funcs
     res->resource.addproperty = msresponse_addproperty;
     res->resource.close = msresponse_close;
     
@@ -275,7 +88,9 @@
     res->resource.isclosed = 0;
     res->closing = 0;
     
+    // add new resource to the resource list
     if(ms->current) {
+        // before adding a new resource, the current resource must be closed
         if(!ms->current->resource.isclosed) {
             msresponse_close((WebdavResource*)ms->current);
         }
@@ -302,26 +117,91 @@
         return 0;
     }
     
+    // some WebdavProperty checks to make sure nothing explodes  
+    if(!property->namespace || !property->namespace->href) {
+        // error: namespace is required
+        log_ereport(
+                LOG_FAILURE,
+                "%s",
+                "webdav: property '%s' has no namespace",
+                property->name);
+        return 1;
+    }
+    if(property->nsdef) {
+        // error: nsdef MUST be NULL, only fill nsdef in this func
+        log_ereport(
+                LOG_FAILURE,
+                "%s",
+                "webdav: property '%s': nsdef must be null",
+                property->name);
+        return 1;
+    }
+    
     // add namespace of this property to the namespace map
-    if(property->namespace && property->namespace->prefix) {
+    // the namespace map will be used for global namespace definitions
+    if(property->namespace->prefix) {
         char *ns = ucx_map_cstr_get(
                 response->multistatus->namespaces,
                 (const char*)property->namespace->prefix);
         if(!ns) {
+            // prefix is not in use -> we can add the namespace to the ns map
             int err = ucx_map_cstr_put(
                     response->multistatus->namespaces,
                     (const char*)property->namespace->prefix,
                     property->namespace->href);
             if(err) {
-                return 1;
+                return 1; // OOM
             }
+        } else if(strcmp((char*)property->namespace->href, ns)) {
+            // global namespace != local namespace
+            // therefore we need a namespace definition in this element
+            
+            if(webdav_property_add_nsdef(
+                    property,
+                    response->multistatus->sn->pool,
+                    (const char*)property->namespace->prefix,
+                    (const char*)property->namespace->href))
+            {
+                return 1; // OOM
+            }          
         }
     }
     
-    if(status != 200) {
+    // error properties will be added to a separate list
+    if(status != 200) { 
         return msresponse_addproperror(response, property, status);
     }
     
+    // add all namespaces used by this property to the nsdef list
+    if(property->vtype == WS_VALUE_XML_NODE) {
+        // iterate over xml tree and collect all namespaces
+        
+        // TODO: implement
+    } else if(property->vtype == WS_VALUE_XML_DATA) {
+        // xml data contains a list of all used namespaces
+        WebdavNSList *nslist = property->value.data->namespaces;
+        while(nslist) {
+            // only add the namespace to the definitions list, if it isn't
+            // property namespace, because the prop ns is already added
+            // to the element's def list or global definitions list
+            if(strcmp(
+                    (const char*)nslist->namespace->prefix,
+                    (const char*)property->namespace->prefix))
+            {
+                // ns-prefix != property-prefix -> add ns to nsdef
+                if(webdav_property_add_nsdef(
+                        property,
+                        response->multistatus->sn->pool,
+                        (const char*)nslist->namespace->prefix,
+                        (const char*)nslist->namespace->href))
+                {
+                    return 1; // OOM
+                }    
+            }
+            nslist = nslist->next;
+        }
+    } // other value types don't contain xml namespaces
+    
     // add property to the list
     WebdavPList *listelm = pool_malloc(
             response->multistatus->sn->pool,
--- a/src/server/webdav/multistatus.h	Tue Dec 31 16:31:29 2019 +0100
+++ b/src/server/webdav/multistatus.h	Tue Jan 14 20:05:18 2020 +0100
@@ -75,6 +75,8 @@
     WebdavPList *plist_begin;
     WebdavPList *plist_end;
     
+    UcxMap *nsdef;
+    
     MSResponse *next;
     WSBool closing;
 };
--- a/src/server/webdav/operation.c	Tue Dec 31 16:31:29 2019 +0100
+++ b/src/server/webdav/operation.c	Tue Jan 14 20:05:18 2020 +0100
@@ -38,14 +38,17 @@
 #define WEBDAV_PATH_MAX 8192
 
 WebdavOperation* webdav_operation_create(
-        pool_handle_t *pool,
+        Session *sn,
+        Request *rq,
         WebdavBackend *dav,
         UcxList *requests,
         WebdavResponse *response)
 {
-    WebdavOperation *op = pool_malloc(pool, sizeof(WebdavOperation));
+    WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation));
     ZERO(op, sizeof(WebdavOperation));
     op->dav = dav;
+    op->sn = sn;
+    op->rq = rq;
     op->requests = requests;
     op->response = response;
     
--- a/src/server/webdav/operation.h	Tue Dec 31 16:31:29 2019 +0100
+++ b/src/server/webdav/operation.h	Tue Jan 14 20:05:18 2020 +0100
@@ -37,6 +37,8 @@
 
 struct WebdavOperation {
     WebdavBackend    *dav;
+    Request          *rq;
+    Session          *sn;
     UcxList          *requests; /* backend specific request objects */
     
     WebdavResponse   *response;
@@ -46,7 +48,8 @@
 };
 
 WebdavOperation* webdav_operation_create(
-        pool_handle_t *pool,
+        Session *sn,
+        Request *rq,
         WebdavBackend *dav,
         UcxList *requests,
         WebdavResponse *response);
--- a/src/server/webdav/webdav.c	Tue Dec 31 16:31:29 2019 +0100
+++ b/src/server/webdav/webdav.c	Tue Jan 14 20:05:18 2020 +0100
@@ -214,7 +214,8 @@
     WebdavResponse *response = (WebdavResponse*)ms;
     
     WebdavOperation *op = webdav_operation_create(
-            sn->pool,
+            sn,
+            rq,
             dav,
             requestObjects,
             response);
@@ -268,6 +269,15 @@
         ret = REQ_ABORTED;
     }
     
+    // if propfind was successful, send the result to the client
+    if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) {
+        ret = REQ_ABORTED;
+        // TODO: log error
+    } else {
+        // TODO: log error
+        // TODO: error response
+    }
+    
     return ret;
 }
 
@@ -563,6 +573,44 @@
     return 0;
 }
 
+int webdav_property_add_nsdef(
+        WebdavProperty *property, 
+        pool_handle_t *pool,
+        const char *prefix,
+        const char *nsuri)
+{
+    // because we're using a memory pool, we don't free in case stuff in
+    // case of an error (OOM) - stuff will be freed by destroyinig the pool
+    
+    WebdavNSList *new_def = pool_malloc(pool, sizeof(WebdavNSList));
+    if(!new_def) {
+        return 1;
+    }
+    WSNamespace *new_ns = pool_malloc(pool, sizeof(WSNamespace));
+    if(!new_ns) {
+        return 1;
+    }
+    ZERO(new_ns, sizeof(WSNamespace));
+    
+    new_ns->prefix = (xmlChar*)pool_strdup(pool, prefix);
+    new_ns->href = (xmlChar*)pool_strdup(pool, nsuri);
+    if(!new_ns->prefix || !new_ns->href) {
+        return 1;
+    }
+    
+    new_def->namespace = new_ns;
+    new_def->prev = NULL;
+    new_def->next = NULL;
+    
+    if(property->nsdef) {
+        property->nsdef->prev = new_def;
+        new_def->next = property->nsdef;
+    }
+    property->nsdef = new_def;
+    
+    return 0;
+}
+
 WebdavVFSProperties webdav_vfs_properties(
         WebdavPropfindRequest *rq,
         WSBool removefromlist,

mercurial