src/server/webdav/operation.c

branch
webdav
changeset 217
8ed14d76db42
child 218
2ba512b284b9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/webdav/operation.c	Tue Dec 31 10:01:32 2019 +0100
@@ -0,0 +1,253 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2019 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ucx/list.h>
+
+#include "../daemon/session.h"
+
+#include "operation.h"
+
+#define WEBDAV_PATH_MAX 8192
+
+WebdavOperation* webdav_operation_create(
+        pool_handle_t *pool,
+        WebdavBackend *dav,
+        UcxList *requests,
+        WebdavResponse *response)
+{
+    WebdavOperation *op = pool_malloc(pool, sizeof(WebdavOperation));
+    ZERO(op, sizeof(WebdavOperation));
+    op->dav = dav;
+    op->requests = requests;
+    op->response = response;
+    
+    response->op = op;
+    
+    return op;
+}
+
+int webdav_op_propfind_begin(
+        WebdavOperation *op,
+        const char *path,
+        VFS_DIR parent,
+        struct stat *s)
+{
+    // create WebdavResource object for requested resource
+    WebdavResource *resource = op->response->addresource(op->response, path);
+    if(!resource) {
+        return REQ_ABORTED;
+    }
+    
+    // store data that we need when the resource will be closed
+    op->stat = s;
+    op->parent = parent;
+    
+    // get first propfind object
+    WebdavPropfindRequest *propfind = op->requests->data;
+    
+    // execute propfind_do of the first backend for the first resource
+    if(op->dav->propfind_do(propfind, op->response, NULL, resource, s)) {
+        return REQ_ABORTED;
+    }
+    
+    return REQ_PROCEED;
+}
+
+int webdav_op_propfind_children(
+        WebdavOperation *op,
+        VFSContext *vfs,
+        char *path)
+{
+    WebdavResponse *response = op->response;
+    WebdavPropfindRequest *request = op->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);
+    UcxList *stack_end = stack;
+    if(!stack) {
+        return 1;
+    }
+    
+    // reusable buffer for full child path
+    char *newpath = NULL;
+    size_t newpathlen = 0;
+    
+    int err = 0;
+    while(stack && !err) {
+        char *cur_path = stack->data;
+        size_t parent_len = strlen(cur_path);
+        if(parent_len > WEBDAV_PATH_MAX) {
+            log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded");
+            err = 1;
+            break;
+        }
+        if(cur_path[parent_len-1] == '/') {
+            parent_len--;
+        }
+        size_t max_child_len = WEBDAV_PATH_MAX - parent_len;
+        
+        // when newpath is initialized with the parent path
+        // set path_buf_init to TRUE
+        WSBool path_buf_init = FALSE;
+        
+        VFS_DIR dir = vfs_opendir(vfs, path);
+        if(!dir) {
+            log_ereport(
+                    LOG_FAILURE,
+                    "webdav: propfind: cannot open directory %d",
+                    vfs->vfs_errno);
+            err = 1;
+            break;
+        }
+        
+        VFS_ENTRY f;
+        while(vfs_readdir_stat(dir, &f)) {
+            if(f.stat_errno != 0) {
+                continue;
+            }
+            
+            size_t child_len = strlen(f.name);
+            if(child_len > max_child_len) {
+                log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded");
+                err = 1;
+                break;
+            }
+            size_t childpathlen = parent_len + child_len + 1; // +1 '/'
+            if(childpathlen > newpathlen) {
+                // we're gonna need a bigger boa^H^H^Hbuffer
+                if(newpath) {
+                    pool_free(pool, newpath);
+                }
+                newpath = pool_malloc(pool, childpathlen + 1);
+                if(!newpath) {
+                    err = 1;
+                    break;
+                }
+                newpathlen = childpathlen;
+                path_buf_init = FALSE;
+            }
+            // create full path string for this child
+            if(!path_buf_init) {
+                memcpy(newpath, cur_path, parent_len);
+                newpath[parent_len] = '/';
+            }
+            memcpy(newpath+parent_len+1, f.name, child_len);
+            newpath[childpathlen] = 0;
+            
+            // propfind for this child
+            if(webdav_op_propfind_begin(op, newpath, dir, &f.stat)) {
+                err = 1;
+                break;
+            }
+            
+            // depth of -1 means infinity
+            if(request->depth == -1 && S_ISDIR(f.stat.st_mode)) {
+                char *pathcp = pool_malloc(pool, childpathlen + 1);
+                memcpy(pathcp, newpath, childpathlen + 1);
+                
+                // add the newpath copy to the stack
+                // stack_end is always not NULL here, because we remove
+                // the first stack element at the end of the loop
+                UcxList *newlistelm = ucx_list_append_a(a, stack_end, pathcp);
+                if(!newlistelm) {
+                    err = 1;
+                    break;
+                }
+                stack_end = newlistelm;
+            }
+        }
+        
+        vfs_closedir(dir);
+        
+        if(cur_path != path) {
+            pool_free(pool, cur_path);
+        }
+        stack = ucx_list_remove_a(a, stack, stack);
+    }
+    
+    // in case of an error, we have to free all remaining stack elements
+    UCX_FOREACH(elm, stack) {
+        char *data = elm->data;
+        if(data != path) {
+            pool_free(pool, data);
+        }
+    }
+    
+    return err;
+}
+
+int webdav_op_propfiond_close_resource(
+        WebdavOperation *op,
+        WebdavResource *resource)
+{
+    // start with second backend and request, because
+    // the first one was already called by webdav_op_propfind_begin
+    WebdavBackend *dav = op->dav->next;
+    UcxList *request = op->requests->next;
+    
+    // call propfind_do of all remaining backends
+    int ret = REQ_PROCEED;
+    while(dav && request) {
+        if(dav->propfind_do(
+                request->data,
+                op->response,
+                op->parent,
+                resource,
+                op->stat))
+        {
+            ret = REQ_ABORTED;
+        }
+        
+        dav = dav->next;
+        request = request->next;
+    }
+    return ret;
+}
+
+/*
+ * Executes propfind_finish for each Backend
+ */
+int webdav_op_propfind_finish(WebdavOperation *op) {
+    WebdavBackend *dav = op->dav;
+    UcxList *requests = op->requests;
+    
+    int ret = REQ_PROCEED;
+    while(dav && requests) {
+        if(dav->propfind_finish(requests->data)) {
+            ret = REQ_ABORTED;
+        }
+        
+        dav = dav->next;
+        requests = requests->next;
+    }
+    return ret;
+}

mercurial