src/server/webdav/webdav.c

branch
webdav
changeset 212
d7e7ea9c6bc6
parent 211
2160585200ac
child 213
4a6be4f10d5f
--- a/src/server/webdav/webdav.c	Thu Oct 31 10:26:35 2019 +0100
+++ b/src/server/webdav/webdav.c	Sun Dec 29 15:09:58 2019 +0100
@@ -53,7 +53,7 @@
 
 static WebdavProperty dav_resourcetype_empty;
 static WebdavProperty dav_resourcetype_collection;
-static WSXmlNode dav_resourcetype_collection_value;
+static WSXmlNode dav_resourcetype_collection_value; // TODO: change type to WSXmlData
 
 static void init_default_backend(void) {
     memset(&default_backend, 0, sizeof(WebdavBackend));
@@ -100,7 +100,8 @@
     
     dav_resourcetype_collection.namespace = &dav_namespace;
     dav_resourcetype_collection.name = "resourcetype";
-    dav_resourcetype_collection.value = &dav_resourcetype_collection_value;
+    dav_resourcetype_collection.value.node = &dav_resourcetype_collection_value;
+    dav_resourcetype_collection.vtype = WS_VALUE_XML_NODE;
     dav_resourcetype_collection_value.content = (xmlChar*)"<D:collection/>";
     dav_resourcetype_collection_value.type = XML_TEXT_NODE;
     
@@ -127,6 +128,7 @@
 
 UcxBuffer* rqbody2buffer(Session *sn, Request *rq) {
     if(!sn->inbuf) {
+        //request body required, set http response code
         protocol_status(sn, rq, 400, NULL);
         return NULL;
     }
@@ -163,6 +165,8 @@
         return REQ_ABORTED;
     }
     
+    UcxAllocator *a = session_get_allocator(sn);
+    
     int error = 0;
     WebdavPropfindRequest *propfind = propfind_parse(
             sn,
@@ -178,23 +182,72 @@
         }
     }
     
-    
+    // The multistatus response object contains responses for all
+    // requested resources. At the end the Multistatus object will be
+    // serialized to xml
     Multistatus *ms = multistatus_response(sn, rq);
     if(!ms) {
         return REQ_ABORTED;
     }
+    // WebdavResponse is the public interface used by Backends
+    // for adding resources to the response
     WebdavResponse *response = (WebdavResponse*)ms;
     
     WebdavBackend *dav = 
             rq->davCollection ? rq->davCollection : &default_backend;
     
+    // requested uri path
     char *path = pblock_findkeyval(pb_key_path, rq->vars);
     
+    // Initialize WebDAV Backend Chain
+    //
+    // Call propfind_init of each Backend
+    // propfind_init can return a new property list, which
+    // will be passed to the next backend
+    //
+    // VFS settings are only taken from the first backend
     uint32_t settings = dav->settings;
-    if(dav->propfind_init(propfind, path)) {
-        return REQ_ABORTED;
+    
+    // list of individual WebdavPropfindRequest objects for each Backend
+    UcxList *backendPropfind = NULL;
+    
+    // new properties after init, start with clone of original plist
+    WebdavPList *newProp = webdav_plist_clone(sn->pool, propfind->properties);
+    size_t newPropCount = propfind->propcount;
+    
+    WebdavBackend *davList = dav;
+    while(davList) {
+        // create WebdavPropfindRequest copy
+        WebdavPropfindRequest *pReq = pool_malloc(
+                sn->pool,
+                sizeof(WebdavPropfindRequest));
+        memcpy(propfind, pReq, sizeof(WebdavPropfindRequest));
+        // use new plist after previous init (or orig. plist in the first run)
+        pReq->properties = newProp;
+        pReq->propcount = newPropCount;
+        
+        // add new WebdavPropfindRequest object to list for later use
+        backendPropfind = ucx_list_append_a(a, backendPropfind, pReq);
+        if(!backendPropfind) {
+            return REQ_ABORTED; // OOM
+        }
+        
+        // create plist copy as out-plist for init
+        newProp = webdav_plist_clone(sn->pool, newProp);
+        
+        // run init: this can generate a new properties list (newProp)
+        //           which will be passed to the next backend
+        if(dav->propfind_init(pReq, path, &newProp)) {
+            return REQ_ABORTED;
+        }
+        
+        newPropCount = webdav_plist_count(newProp);
+        
+        davList = davList->next;
     }
     
+    // some Backends can list all children by themselves, but some
+    // require the VFS for this
     WSBool usevfs = (settings & WS_PROPFIND_NO_VFS) != WS_PROPFIND_NO_VFS;
     struct stat s;
     struct stat *statptr = NULL;
@@ -204,10 +257,12 @@
         vfs = vfs_request_context(sn, rq);
         
         if(vfs_stat(vfs, path, &s)) {
+            protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL);
             return REQ_ABORTED;
         }
         statptr = &s;
         if(!S_ISDIR(s.st_mode)) {
+            // the file is not a directory, therefore we don't need the VFS
             usevfs = FALSE;
         }
     }
@@ -216,7 +271,7 @@
     }
     
     int ret = REQ_ABORTED;
-    if(!dav->propfind_do(propfind, response, NULL, path, statptr)) {
+    if(!webdav_propfind_do(dav, backendPropfind, response, NULL, path, statptr)) {
         // propfind for the requested resource was successful
         
         // usevfsdir is TRUE if
@@ -224,7 +279,7 @@
         //   the file is a directory
         //   depth is not 0
         // in this case we need to execute propfind_do for all children
-        if(usevfs && !propfind_children(dav, propfind, response, vfs, path)) {
+        if(usevfs && !propfind_children(dav, backendPropfind, response, vfs, path)) {
             ret = REQ_PROCEED;
         }
     }
@@ -239,13 +294,60 @@
     return ret;
 }
 
+/*
+ * Executes propfind_do for each Backend
+ * The list requests must contain all WebdavPropfindRequest objects
+ * of all backends
+ */
+int webdav_propfind_do(
+        WebdavBackend *webdav,
+        UcxList *requests,
+        WebdavResponse *response,
+        VFS_DIR parent,
+        const char *path,
+        struct stat *s)
+{
+    while(webdav && requests) {
+        if(webdav->propfind_do(requests->data, response, parent, path, s)) {
+            return REQ_ABORTED;
+        }
+        
+        webdav = webdav->next;
+        requests = requests->next;
+    }
+    return REQ_PROCEED;
+}
+
+/*
+ * Executes propfind_finish for each Backend
+ */
+int webdav_propfind_finish(WebdavBackend *webdav, UcxList *requests) {
+    int ret = REQ_PROCEED;
+    while(webdav && requests) {
+        if(webdav->propfind_finish(requests->data)) {
+            ret = REQ_ABORTED;
+        }
+        
+        webdav = webdav->next;
+        requests = requests->next;
+    }
+    return ret;
+}
+
+
+/*
+ * Uses the VFS to iterate over all children of the requsted resource
+ * and executes propfind for each child
+ */
 int propfind_children(
         WebdavBackend *dav,
-        WebdavPropfindRequest *request,
+        UcxList *requests,
         WebdavResponse *response,
         VFSContext *vfs,
         char *path)
 {
+    WebdavPropfindRequest *request = requests->data;
+    
     UcxAllocator *a = session_get_allocator(request->sn);
     pool_handle_t *pool = request->sn->pool;
     UcxList *stack = ucx_list_prepend_a(a, NULL, path);
@@ -321,7 +423,7 @@
             newpath[childpathlen] = 0;
             
             // propfind for this child
-            if(dav->propfind_do(request, response, dir, newpath, &f.stat)) {
+            if(webdav_propfind_do(dav, requests, response, dir, newpath, &f.stat)) {
                 err = 1;
                 break;
             }
@@ -411,7 +513,8 @@
 
 int default_propfind_init(
         WebdavPropfindRequest *rq,
-        const char* path)
+        const char* path,
+        WebdavPList **outplist)
 {
     DefaultWebdavData *data = pool_malloc(
             rq->sn->pool,
@@ -484,6 +587,47 @@
     return depth;
 }
 
+WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list) {
+    WebdavPList *new_list = NULL;     // start of the new list
+    WebdavPList *new_list_end = NULL; // end of the new list
+    
+    WebdavPList *elm = list;
+    while(elm) {
+        // copy list item
+        WebdavPList *new_elm = pool_malloc(pool, sizeof(WebdavPList));
+        if(!new_elm) {
+            return NULL;
+        }
+        new_elm->property = elm->property; // new list contains original ptr
+        new_elm->prev = NULL;
+        new_elm->next = NULL;
+        
+        if(new_list_end) {
+            new_list_end->next = elm;
+            elm->prev = new_list_end;
+            
+            new_list_end = elm;
+        } else {
+            new_list = new_elm;
+            new_list_end = new_elm;
+        }
+        
+        elm = elm->next;
+    }
+    
+    return new_list;
+}
+
+size_t webdav_plist_count(WebdavPList *list) {
+    size_t count = 0;
+    WebdavPList *elm = list;
+    while(elm) {
+        count++;
+        elm = elm->next;
+    }
+    return count;
+}
+
 WSNamespace* webdav_dav_namespace(void) {
     return &dav_namespace;
 }
@@ -496,11 +640,10 @@
     if(!property) {
         return NULL;
     }
+    memset(property, 0, sizeof(WebdavProperty));
     
     property->namespace = &dav_namespace;
-    property->lang = NULL;
     property->name = name;
-    property->value = NULL;
     return property;
 }
 
@@ -518,7 +661,8 @@
     node->content = (xmlChar*)value;
     node->type = XML_TEXT_NODE;
     
-    p->value = node;
+    p->value.node = node;
+    p->vtype = WS_VALUE_XML_NODE;
     return 0;
 }
 
@@ -556,6 +700,7 @@
             }
             
             if(removefromlist && removethis) {
+                
                 if(prev) {
                     prev->next = next;
                 } else {

mercurial