add proppatch op webdav

Sat, 25 Jan 2020 21:37:38 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 25 Jan 2020 21:37:38 +0100
branch
webdav
changeset 241
4adad7faf452
parent 240
cd74667f6c85
child 242
c337a7ac82a8

add proppatch op

src/server/daemon/acl.c file | annotate | diff | comparison | revisions
src/server/daemon/acl.h file | annotate | diff | comparison | revisions
src/server/daemon/vfs.c file | annotate | diff | comparison | revisions
src/server/daemon/vfs.h file | annotate | diff | comparison | revisions
src/server/public/vfs.h file | annotate | diff | comparison | revisions
src/server/public/webdav.h file | annotate | diff | comparison | revisions
src/server/test/main.c file | annotate | diff | comparison | revisions
src/server/test/vfs.c file | annotate | diff | comparison | revisions
src/server/test/webdav.c file | annotate | diff | comparison | revisions
src/server/test/webdav.h file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.c 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/daemon/acl.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/daemon/acl.c	Sat Jan 25 21:37:38 2020 +0100
@@ -317,7 +317,7 @@
         uid_t owner,
         gid_t owninggroup);
 
-int fs_acl_check(SysACL *acl, User *user, char *path, uint32_t access_mask) {
+int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask) {
     sstr_t p;
     if(path[0] != '/') {
         size_t n = 128;
@@ -332,11 +332,11 @@
             }
         }
         sstr_t wd = sstr(cwd);
-        sstr_t pp = sstr(path);
+        sstr_t pp = sstr((char*)path);
 
         p = sstrcat(3, wd, sstrn("/", 1), pp);
     } else {
-        p = sstrdup(sstr(path));
+        p = sstrdup(sstr((char*)path));
     }
     if(p.ptr[p.length-1] == '/') {
         p.ptr[p.length-1] = 0;
--- a/src/server/daemon/acl.h	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/daemon/acl.h	Sat Jan 25 21:37:38 2020 +0100
@@ -48,7 +48,7 @@
 
 // file system acl functions
 
-int fs_acl_check(SysACL *acl, User *user, char *path, uint32_t access_mask);
+int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask);
 int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask);
 void fs_acl_finish();
 
--- a/src/server/daemon/vfs.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/daemon/vfs.c	Sat Jan 25 21:37:38 2020 +0100
@@ -99,6 +99,9 @@
     WS_ASSERT(rq);
     
     VFSContext *ctx = pool_malloc(sn->pool, sizeof(VFSContext));
+    if(!ctx) {
+        return NULL;
+    }
     ctx->sn = sn;
     ctx->rq = rq;
     ctx->vfs = rq->vfs ? rq->vfs : &sys_vfs;
@@ -110,7 +113,7 @@
     return ctx;
 }
 
-SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags) {
+SYS_FILE vfs_open(VFSContext *ctx, const char *path, int oflags) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
@@ -131,19 +134,19 @@
     return file;
 }
 
-SYS_FILE vfs_openRO(VFSContext *ctx, char *path) {
+SYS_FILE vfs_openRO(VFSContext *ctx, const char *path) {
     return vfs_open(ctx, path, O_RDONLY);
 }
 
-SYS_FILE vfs_openWO(VFSContext *ctx, char *path) {
+SYS_FILE vfs_openWO(VFSContext *ctx, const char *path) {
     return vfs_open(ctx, path, O_WRONLY | O_CREAT);
 }
 
-SYS_FILE vfs_openRW(VFSContext *ctx, char *path) {
+SYS_FILE vfs_openRW(VFSContext *ctx, const char *path) {
     return vfs_open(ctx, path, O_RDONLY | O_WRONLY | O_CREAT);
 }
 
-int vfs_stat(VFSContext *ctx, char *path, struct stat *buf) {
+int vfs_stat(VFSContext *ctx, const char *path, struct stat *buf) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
@@ -183,7 +186,7 @@
     }
 }
 
-VFS_DIR vfs_opendir(VFSContext *ctx, char *path) {
+VFS_DIR vfs_opendir(VFSContext *ctx, const char *path) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
@@ -250,14 +253,14 @@
     }
 }
 
-int vfs_mkdir(VFSContext *ctx, char *path) {
+int vfs_mkdir(VFSContext *ctx, const char *path) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
     return vfs_path_op(ctx, path, ctx->vfs->mkdir, ACL_ADD_FILE);
 }
 
-int vfs_unlink(VFSContext *ctx, char *path) {
+int vfs_unlink(VFSContext *ctx, const char *path) {
     WS_ASSERT(ctx);
     WS_ASSERT(path);
     
@@ -266,7 +269,7 @@
 
 
 // private
-int vfs_path_op(VFSContext *ctx, char *path, vfs_op_f op, uint32_t access) {  
+int vfs_path_op(VFSContext *ctx, const char *path, vfs_op_f op, uint32_t access) {  
     uint32_t access_mask = ctx->aclreqaccess;
     access_mask |= access;
     
@@ -287,7 +290,7 @@
 
 /* system vfs implementation */
 
-SYS_FILE sys_vfs_open(VFSContext *ctx, char *path, int oflags) {
+SYS_FILE sys_vfs_open(VFSContext *ctx, const char *path, int oflags) {
     uint32_t access_mask = ctx->aclreqaccess;
     pool_handle_t *pool = ctx->pool;
     
@@ -336,7 +339,7 @@
     return file;
 }
 
-int sys_vfs_stat(VFSContext *ctx, char *path, struct stat *buf) {
+int sys_vfs_stat(VFSContext *ctx, const char *path, struct stat *buf) {
     uint32_t access_mask = ctx->aclreqaccess;
     
     // check ACLs
@@ -376,7 +379,7 @@
     return 0;
 }
 
-VFS_DIR sys_vfs_opendir(VFSContext *ctx, char *path) {
+VFS_DIR sys_vfs_opendir(VFSContext *ctx, const char *path) {
     uint32_t access_mask = ctx->aclreqaccess;
     pool_handle_t *pool = ctx->pool;
     
@@ -501,16 +504,16 @@
     return dir;
 }
 
-int sys_vfs_mkdir(VFSContext *ctx, char *path) {
+int sys_vfs_mkdir(VFSContext *ctx, const char *path) {
     return sys_path_op(ctx, path, sys_mkdir);
 }
 
-int sys_vfs_unlink(VFSContext *ctx, char *path) {
+int sys_vfs_unlink(VFSContext *ctx, const char *path) {
     return sys_path_op(ctx, path, sys_unlink);
 }
 
 
-int sys_path_op(VFSContext *ctx, char *path, sys_op_f op) {
+int sys_path_op(VFSContext *ctx, const char *path, sys_op_f op) {
     uint32_t access_mask = ctx->aclreqaccess;
     
     // check ACLs
@@ -647,7 +650,7 @@
     }
 }
 
-int sys_mkdir(VFSContext *ctx, char *path, SysACL *sysacl) {
+int sys_mkdir(VFSContext *ctx, const char *path, SysACL *sysacl) {
     mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
     int ret = mkdir(path, mode);
     if(ret == 0) {
@@ -660,7 +663,7 @@
     return ret;
 }
 
-int sys_unlink(VFSContext *ctx, char *path, SysACL *sysacl) {
+int sys_unlink(VFSContext *ctx, const char *path, SysACL *sysacl) {
     return unlink(path);
 }
 
--- a/src/server/daemon/vfs.h	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/daemon/vfs.h	Sat Jan 25 21:37:38 2020 +0100
@@ -49,19 +49,19 @@
     
 int vfs_init();
 
-typedef int(*vfs_op_f)(VFSContext *, char *);
-typedef int(*sys_op_f)(VFSContext *, char *, SysACL *);
-int vfs_path_op(VFSContext *ctx, char *path, vfs_op_f op, uint32_t access);
+typedef int(*vfs_op_f)(VFSContext *, const char *);
+typedef int(*sys_op_f)(VFSContext *, const char *, SysACL *);
+int vfs_path_op(VFSContext *ctx, const char *path, vfs_op_f op, uint32_t access);
 
-SYS_FILE sys_vfs_open(VFSContext *ctx, char *path, int oflags);
-int sys_vfs_stat(VFSContext *ctx, char *path, struct stat *buf);
+SYS_FILE sys_vfs_open(VFSContext *ctx, const char *path, int oflags);
+int sys_vfs_stat(VFSContext *ctx, const char *path, struct stat *buf);
 int sys_vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf);
-VFS_DIR sys_vfs_opendir(VFSContext *ctx, char *path);
+VFS_DIR sys_vfs_opendir(VFSContext *ctx, const char *path);
 VFS_DIR sys_vfs_fdopendir(VFSContext *ctx, SYS_FILE fd);
-int sys_vfs_mkdir(VFSContext *ctx, char *path);
-int sys_vfs_unlink(VFSContext *ctx, char *path);
+int sys_vfs_mkdir(VFSContext *ctx, const char *path);
+int sys_vfs_unlink(VFSContext *ctx, const char *path);
 
-int sys_path_op(VFSContext *ctx, char *path, sys_op_f op);
+int sys_path_op(VFSContext *ctx, const char *path, sys_op_f op);
 int sys_acl_check(VFSContext *ctx, uint32_t access_mask, SysACL *externacl);
 void sys_set_error_status(VFSContext *ctx);
 
@@ -77,8 +77,8 @@
 int sys_dir_read(VFS_DIR dir, VFS_ENTRY *entry, int getstat);
 void sys_dir_close(VFS_DIR dir);
 
-int sys_mkdir(VFSContext *ctx, char *path, SysACL *sysacl);
-int sys_unlink(VFSContext *ctx, char *path, SysACL *sysacl);
+int sys_mkdir(VFSContext *ctx, const char *path, SysACL *sysacl);
+int sys_unlink(VFSContext *ctx, const char *path, SysACL *sysacl);
 
 void vfs_queue_aio(aiocb_s *aiocb, VFSAioOp op);
 
--- a/src/server/public/vfs.h	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/public/vfs.h	Sat Jan 25 21:37:38 2020 +0100
@@ -48,13 +48,13 @@
 #define VFS_ENTRY         VFSEntry
 
 struct VFS {
-    SYS_FILE (*open)(VFSContext *ctx, char *path, int oflags);
-    int (*stat)(VFSContext *ctx, char *path, struct stat *buf);
+    SYS_FILE (*open)(VFSContext *ctx, const char *path, int oflags);
+    int (*stat)(VFSContext *ctx, const char *path, struct stat *buf);
     int (*fstat)(VFSContext *ctx, SYS_FILE fd, struct stat *buf);
-    VFS_DIR (*opendir)(VFSContext *ctx, char *path);
+    VFS_DIR (*opendir)(VFSContext *ctx, const char *path);
     VFS_DIR (*fdopendir)(VFSContext *ctx, SYS_FILE fd);
-    int (*mkdir)(VFSContext *ctx, char *path);
-    int (*unlink)(VFSContext *ctx, char *path);
+    int (*mkdir)(VFSContext *ctx, const char *path);
+    int (*unlink)(VFSContext *ctx, const char *path);
     uint32_t flags;
     void *instance;
 };
@@ -118,20 +118,20 @@
  */
 VFSContext* vfs_request_context(Session *sn, Request *rq);
 
-SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags);
-SYS_FILE vfs_openRO(VFSContext *ctx, char *path);
-SYS_FILE vfs_openWO(VFSContext *ctx, char *path);
-SYS_FILE vfs_openRW(VFSContext *ctx, char *path);
-int vfs_stat(VFSContext *ctx, char *path, struct stat *buf);
+SYS_FILE vfs_open(VFSContext *ctx, const char *path, int oflags);
+SYS_FILE vfs_openRO(VFSContext *ctx, const char *path);
+SYS_FILE vfs_openWO(VFSContext *ctx, const char *path);
+SYS_FILE vfs_openRW(VFSContext *ctx, const char *path);
+int vfs_stat(VFSContext *ctx, const char *path, struct stat *buf);
 int vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf);
 void vfs_close(SYS_FILE fd);
-VFS_DIR vfs_opendir(VFSContext *ctx, char *path);
+VFS_DIR vfs_opendir(VFSContext *ctx, const char *path);
 VFS_DIR vfs_fdopendir(VFSContext *ctx, SYS_FILE fd);
 int vfs_readdir(VFS_DIR dir, VFS_ENTRY *entry);
 int vfs_readdir_stat(VFS_DIR dir, VFS_ENTRY *entry);
 void vfs_closedir(VFS_DIR dir);
-int vfs_mkdir(VFSContext *ctx, char *path);
-int vfs_unlink(VFSContext *ctx, char *path);
+int vfs_mkdir(VFSContext *ctx, const char *path);
+int vfs_unlink(VFSContext *ctx, const char *path);
 
 #ifdef	__cplusplus
 }
--- a/src/server/public/webdav.h	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/public/webdav.h	Sat Jan 25 21:37:38 2020 +0100
@@ -191,6 +191,11 @@
     
     WebdavPList *remove;
     size_t removecount;
+    
+    /*
+     * custom userdata for the backend
+     */
+    void *userdata;
 };
 
 struct WebdavLockRequest {
@@ -224,6 +229,8 @@
     
     WSBool isclosed;
     
+    int err;
+    
     /*
      * int addprop(WebdavResource *res, WebdavProperty *property, int status);
      * 
@@ -281,26 +288,39 @@
     /*
      * int proppatch_do(
      *     WebdavProppatchRequest *request,
-     *     WebdavResponse *response,
-     *     VFSFile *file);
+     *     WebdavResource *response,
+     *     VFSFile *file,
+     *     WebdavPList **out_set,
+     *     WebdavPList **out_remove);
      * 
-     * Modifies properties of the requsted resource:
+     * Modifies properties of the requsted resource.
      */
-    int (*proppatch_do)(WebdavProppatchRequest *, WebdavResponse *, VFSFile *);
+    int (*proppatch_do)(
+            WebdavProppatchRequest *,
+            WebdavResource *,
+            VFSFile *,
+            WebdavPList **,
+            WebdavPList **);
     
     /*
      * int proppatch_finish(
      *     WebdavProppatchRequest *request,
+     *     WebdavResource *response,
+     *     VFSFile *file,
      *     WSBool commit);
      * 
      * Called after all proppatch_do functions of all backends are executed
      * and should either permanently store the properties (commit == true) or
      * revert all changed (commit == false).
      */
-    int (*proppatch_finish)(WebdavProppatchRequest *, WSBool);
+    int (*proppatch_finish)(
+            WebdavProppatchRequest *,
+            WebdavResource *,
+            VFSFile *,
+            WSBool);
     
     /*
-     * See the WS_PROPFIND_ macros for informations about the settings
+     * See the WS_WEBDAV_* macros for informations about the settings
      */
     uint32_t settings;
     
@@ -326,6 +346,10 @@
         WebdavProperty *prop);
 
 WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list);
+WebdavPList* webdav_plist_clone_s(
+        pool_handle_t *pool,
+        WebdavPList *list,
+        size_t *newlen);
 
 size_t webdav_plist_size(WebdavPList *list);
 
--- a/src/server/test/main.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/test/main.c	Sat Jan 25 21:37:38 2020 +0100
@@ -95,6 +95,7 @@
     ucx_test_register(suite, test_webdav_op_propfind_children);
     ucx_test_register(suite, test_proppatch_msresponse);
     ucx_test_register(suite, test_msresponse_addproperty_with_errors);
+    ucx_test_register(suite, test_webdav_op_proppatch);
        
     // webdav methods
     ucx_test_register(suite, test_webdav_propfind);
--- a/src/server/test/vfs.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/test/vfs.c	Sat Jan 25 21:37:38 2020 +0100
@@ -113,11 +113,11 @@
 
 /* vfs funcs */
 
-SYS_FILE testvfs_open(VFSContext *ctx, char *path, int oflags) {
+SYS_FILE testvfs_open(VFSContext *ctx, const char *path, int oflags) {
     TestVFS *vfs = ctx->vfs->instance;  
     TestVFSFile *file = NULL;
     
-    sstr_t s_path = sstr(path);
+    sstr_t s_path = sstr((char*)path);
     if(sstrsuffix(s_path, S("/"))) {
         s_path.length--;
     }
@@ -136,11 +136,11 @@
     return (SYS_FILE)file;
 }
 
-int testvfs_stat(VFSContext *ctx, char *path, struct stat *buf) {
+int testvfs_stat(VFSContext *ctx, const char *path, struct stat *buf) {
     TestVFS *vfs = ctx->vfs->instance;  
     TestVFSFile *file = NULL;
     
-    sstr_t s_path = sstr(path);
+    sstr_t s_path = sstr((char*)path);
     if(sstrsuffix(s_path, S("/"))) {
         s_path.length--;
     }
@@ -156,11 +156,11 @@
     return 0;
 }
 
-VFS_DIR testvfs_opendir(VFSContext *ctx, char *path) {
+VFS_DIR testvfs_opendir(VFSContext *ctx, const char *path) {
     TestVFS *vfs = ctx->vfs->instance;  
     TestVFSFile *file = NULL;
     
-    sstr_t s_path = sstr(path);
+    sstr_t s_path = sstr((char*)path);
     if(sstrsuffix(s_path, S("/"))) {
         s_path.length--;
     }
@@ -203,7 +203,7 @@
     return (VFS_DIR)dir;
 }
 
-int testvfs_mkdir(VFSContext *ctx, char *path) {
+int testvfs_mkdir(VFSContext *ctx, const char *path) {
     SYS_FILE fd = testvfs_open(ctx, path, O_CREAT);
     if(!fd) {
         return 1;
@@ -215,7 +215,7 @@
     return 0;
 }
 
-int testvfs_unlink(VFSContext *ctx, char *path) {
+int testvfs_unlink(VFSContext *ctx, const char *path) {
     return 0;
 }
 
--- a/src/server/test/webdav.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/test/webdav.c	Sat Jan 25 21:37:38 2020 +0100
@@ -47,6 +47,9 @@
 static int backend2_init_called = 0;
 static int backend2_propfind_do_count = 0;
 static int backend2_propfind_finish_called = 0;
+static int backend2_proppatch_commit = 0;
+static int backend2_proppatch_do_count = 0;
+static int backend2_proppatch_finish_count = 0;
 
 // backend2
 static int backend2_propfind_init(
@@ -74,10 +77,52 @@
     return 0;
 }
 
+static int backend2_proppatch_do(
+        WebdavProppatchRequest *request,
+        WebdavResource *response,
+        VFSFile *file,
+        WebdavPList **out_set,
+        WebdavPList **out_remove)
+{
+    backend2_proppatch_do_count++;
+    
+    if(*out_remove) {
+        return 1; // backend1 should remove all remove-props
+    }
+    
+    WebdavPListIterator i = webdav_plist_iterator(out_set);
+    WebdavPList *cur;
+    while(webdav_plist_iterator_next(&i, &cur)) {
+        if(!strcmp(cur->property->name, "a")) {
+            // property 'a' should already be removed by backend1
+            return 1;
+        } else if(!strcmp(cur->property->name, "abort")) {
+            return 1; // test abort
+        }
+        response->addproperty(response, cur->property, 200);
+        webdav_plist_iterator_remove_current(&i);
+    }
+    
+    return 0;
+}
+
+static int backend2_proppatch_finish(
+        WebdavProppatchRequest *request,
+        WebdavResource *response,
+        VFSFile *file,
+        WSBool commit)
+{
+    backend2_proppatch_finish_count++;
+    backend2_proppatch_commit = commit;
+    return 0;
+}
+
 static WebdavBackend backend2 = {
     backend2_propfind_init,
     backend2_propfind_do,
     backend2_propfind_finish,
+    backend2_proppatch_do,
+    backend2_proppatch_finish,
     0,
     NULL
 };
@@ -87,6 +132,9 @@
 static int backend1_init_called = 0;
 static int backend1_propfind_do_count = 0;
 static int backend1_propfind_finish_called = 0;
+static int backend1_proppatch_commit = 0;
+static int backend1_proppatch_do_count = 0;
+static int backend1_proppatch_finish_count = 0;
 
 
 static int backend1_propfind_init(
@@ -124,10 +172,55 @@
     return 0;
 }
 
+static int backend1_proppatch_do(
+        WebdavProppatchRequest *request,
+        WebdavResource *response,
+        VFSFile *file,
+        WebdavPList **out_set,
+        WebdavPList **out_remove)
+{
+    backend1_proppatch_do_count++;
+    
+    // remove everything from out_remove
+    WebdavPListIterator i = webdav_plist_iterator(out_remove);
+    WebdavPList *cur;
+    while(webdav_plist_iterator_next(&i, &cur)) {
+        response->addproperty(response, cur->property, 200);
+        webdav_plist_iterator_remove_current(&i);
+    }
+    
+    // remove property 'a' and fail at property 'fail'
+    i = webdav_plist_iterator(out_set);
+    while(webdav_plist_iterator_next(&i, &cur)) {
+        if(!strcmp(cur->property->name, "fail")) {
+            response->addproperty(response, cur->property, 403);
+            webdav_plist_iterator_remove_current(&i);
+        } else if(!strcmp(cur->property->name, "a")) {
+            response->addproperty(response, cur->property, 200);
+            webdav_plist_iterator_remove_current(&i);
+        }
+    }
+        
+    return 0;
+}
+
+static int backend1_proppatch_finish(
+        WebdavProppatchRequest *request,
+        WebdavResource *response,
+        VFSFile *file,
+        WSBool commit)
+{
+    backend1_proppatch_finish_count++;
+    backend1_proppatch_commit = commit;
+    return 0;
+}
+
 WebdavBackend backend1 = {
     backend1_propfind_init,
     backend1_propfind_do,
     backend1_propfind_finish,
+    backend1_proppatch_do,
+    backend1_proppatch_finish,
     0,
     &backend2
 };
@@ -136,9 +229,15 @@
     backend1_init_called = 0;
     backend1_propfind_do_count = 0;
     backend1_propfind_finish_called = 0;
+    backend1_proppatch_commit = 0;
+    backend1_proppatch_do_count = 0;
+    backend1_proppatch_finish_count = 0;
     backend2_init_called = 0;
     backend2_propfind_do_count = 0;
     backend2_propfind_finish_called = 0;
+    backend2_proppatch_commit = 0;
+    backend2_proppatch_do_count = 0;
+    backend2_proppatch_finish_count = 0;
 }
 
 /* ----------------------------------------------------------------------*/
@@ -1235,3 +1334,63 @@
     UCX_TEST_END;
     testutil_destroy_session(sn);
 }
+
+UCX_TEST(test_webdav_op_proppatch) {
+    Session *sn;
+    Request *rq;
+    WebdavOperation *op;
+    
+    Multistatus *ms;
+    WebdavResource *res;
+    
+    WebdavProperty p[16];
+    const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"};
+    for(int i=0;i<8;i++) {
+        p[i].namespace = webdav_dav_namespace();
+        p[i].name = names[i];
+        p[i].lang = NULL;
+        p[i].value.node = NULL;
+        p[1].vtype = 0;
+    }
+    
+    UCX_TEST_BEGIN;
+    
+    // TEST_PROPPATCH2 should succeed
+    reset_backends();
+    op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH2);
+    UCX_TEST_ASSERT(op, "failed to create proppatch operation");
+    
+    int ret = webdav_op_proppatch(op, "/", "/");
+    UCX_TEST_ASSERT(ret == 0, "webdav_op_proppatch failed");
+    UCX_TEST_ASSERT(backend1_proppatch_commit, "backend1 no commit");
+    UCX_TEST_ASSERT(backend2_proppatch_commit, "backend2 no commit");
+    UCX_TEST_ASSERT(backend1_proppatch_do_count == 1, "backend1 wrong count (1)");
+    UCX_TEST_ASSERT(backend2_proppatch_do_count == 1, "backend1 wrong count (1)");
+    UCX_TEST_ASSERT(backend1_proppatch_finish_count == 1, "backend1 wrong finish count (1)");
+    UCX_TEST_ASSERT(backend2_proppatch_finish_count == 1, "backend1 wrong finish count (1)");
+    
+    // TEST_PROPPATCH3 should fail (commit == FALSE)
+    reset_backends();
+    op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH3);
+    UCX_TEST_ASSERT(op, "failed to create proppatch operation 2");
+    
+    ret = webdav_op_proppatch(op, "/", "/");
+    UCX_TEST_ASSERT(ret == 0, "webdav_op_proppatch failed (2)");
+    UCX_TEST_ASSERT(!backend1_proppatch_commit, "backend1 commit");
+    UCX_TEST_ASSERT(!backend2_proppatch_commit, "backend2 commit");
+    
+    // TEST_PROPPATCH4 should abort
+    reset_backends();
+    op = test_proppatch_op1(&sn, &rq, TEST_PROPPATCH4);
+    UCX_TEST_ASSERT(op, "failed to create proppatch operation 3");
+    
+    ret = webdav_op_proppatch(op, "/", "/");
+    UCX_TEST_ASSERT(ret != 0, "webdav_op_proppatch should fail");
+    UCX_TEST_ASSERT(backend1_proppatch_do_count == 1, "backend1 wrong count (2)");
+    UCX_TEST_ASSERT(backend2_proppatch_do_count == 1, "backend1 wrong count (2)");
+    UCX_TEST_ASSERT(backend1_proppatch_finish_count == 1, "backend1 wrong finish count (2)");
+    UCX_TEST_ASSERT(backend2_proppatch_finish_count == 0, "backend1 wrong finish count (2)");
+    
+    UCX_TEST_END;
+    testutil_destroy_session(sn);
+}
--- a/src/server/test/webdav.h	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/test/webdav.h	Sat Jan 25 21:37:38 2020 +0100
@@ -60,6 +60,7 @@
 UCX_TEST(test_webdav_propfind);
 
 UCX_TEST(test_proppatch_msresponse);
+UCX_TEST(test_webdav_op_proppatch);
 
 /* --------------------------- PROPFIND --------------------------- */
 
@@ -145,6 +146,28 @@
             </D:remove> \
         </D:propertyupdate>"
 
+#define TEST_PROPPATCH3 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
+        <D:propertyupdate xmlns:D=\"DAV:\" xmlns:X=\"http://example.com/\"> \
+            <D:set> \
+                <D:prop> \
+                    <X:a>test</X:a> \
+                    <X:fail>15</X:fail> \
+                </D:prop> \
+            </D:set> \
+            <D:remove> \
+                <D:prop> \
+                    <X:e/> \
+                </D:prop> \
+            </D:remove> \
+        </D:propertyupdate>"
+
+#define TEST_PROPPATCH4 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
+        <D:propertyupdate xmlns:D=\"DAV:\" xmlns:X=\"http://example.com/\"> \
+            <D:set> \
+                <D:prop><X:abort>error</X:abort></D:prop> \
+            </D:set> \
+        </D:propertyupdate>"
+
 /* --------------------------- LOCK --------------------------- */
 
 #define TEST_LOCK1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
--- a/src/server/webdav/multistatus.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/webdav/multistatus.c	Sat Jan 25 21:37:38 2020 +0100
@@ -303,6 +303,8 @@
         return NULL;
     }
     
+    res->resource.err = 0;
+    
     // add resource funcs
     res->resource.addproperty = msresponse_addproperty;
     res->resource.close = msresponse_close;
@@ -507,6 +509,8 @@
     pool_handle_t *pool = response->multistatus->sn->pool;
     UcxAllocator *a = session_get_allocator(response->multistatus->sn);
     
+    response->resource.err++;
+      
     // MSResponse contains a list of properties for each status code
     // at first find the list for this status code
     PropertyErrorList *errlist = NULL;
--- a/src/server/webdav/operation.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/webdav/operation.c	Sat Jan 25 21:37:38 2020 +0100
@@ -37,6 +37,16 @@
 
 #define WEBDAV_PATH_MAX 8192
 
+
+size_t webdav_num_backends(WebdavBackend *dav) {
+    size_t count = 0;
+    while(dav) {
+        count++;
+        dav = dav->next;
+    }
+    return count;
+}
+
 /****************************************************************************
  * 
  *                         PROPFIND OPERATION
@@ -367,16 +377,149 @@
     op->rq = rq;
     op->reqprops = NULL;
     op->response = response;
+    op->proppatch = proppatch;
     op->response_close = webdav_op_proppatch_close_resource;
     response->op = op;
     
     return op;
 }
 
+
+
+int webdav_op_proppatch(
+        WebdavOperation *op,
+        const char *href,
+        const char *path)
+{
+    WebdavProppatchRequest *orig_request = op->proppatch;
+    UcxAllocator *a = session_get_allocator(op->sn);
+    
+    // create WebdavResource object for the requested resource
+    WebdavResource *resource = op->response->addresource(op->response, href);
+    if(!resource) {
+        return REQ_ABORTED;
+    }
+    
+    VFSContext *ctx = NULL;
+    VFSFile *file = NULL;
+    
+    // requests for each backends
+    WebdavProppatchRequest **requests = pool_calloc(
+            op->sn->pool,
+            webdav_num_backends(op->dav),
+            sizeof(WebdavProppatchRequest*));
+    if(requests == NULL) {
+        return REQ_ABORTED;
+    }
+    
+    WebdavPList *prev_set = orig_request->set;
+    WebdavPList *prev_remove = orig_request->remove;
+    size_t set_count = orig_request->setcount;
+    size_t remove_count = orig_request->removecount;
+    
+    int ret = REQ_PROCEED;
+    
+    // iterate backends and execute proppatch_do
+    WebdavBackend *dav = op->dav;
+    size_t numrequests = 0;
+    while(dav) {
+        WebdavPList *set = webdav_plist_clone_s(
+                op->sn->pool,
+                prev_set,
+                &set_count);
+        WebdavPList *remove = webdav_plist_clone_s(
+                op->sn->pool,
+                prev_remove,
+                &remove_count);
+        if((prev_set && !set) || (prev_remove && !remove)) {
+            // clone failed, OOM
+            ret = REQ_ABORTED;
+            break;
+        }
+        
+        // create new WebdavProppatchRequest object for this backend
+        WebdavProppatchRequest *req = pool_malloc(
+                op->sn->pool,
+                sizeof(WebdavProppatchRequest));
+        memcpy(req, orig_request, sizeof(WebdavProppatchRequest));
+        req->set = set;
+        req->setcount = set_count;
+        req->remove = remove;
+        req->removecount = remove_count;
+        req->userdata = NULL;
+        
+        // check if we need to open the file because the backend want's it
+        if(!file && (dav->settings & WS_WEBDAV_PROPPATCH_USE_VFS)
+                         == WS_WEBDAV_PROPPATCH_USE_VFS)
+        {
+            ctx = vfs_request_context(op->sn, op->rq);
+            if(!ctx) {
+                ret = REQ_ABORTED;
+                break;
+            }
+            
+            file = vfs_open(ctx, path, O_RDONLY);
+            if(!file) {
+                protocol_status(
+                        op->sn,
+                        op->rq,
+                        util_errno2status(ctx->vfs_errno),
+                        NULL);
+                ret = REQ_ABORTED;
+            }
+        }
+        
+        // execute proppatch_do
+        if(dav->proppatch_do(req, resource, file, &set, &remove)) {
+            // return later, because we need do execute proppatch_finish
+            // for all successfully called backends
+            ret = REQ_ABORTED;
+            break;
+        }
+        
+        // proppatch_do should remove all handled props from set and remove
+        // in the next iteration, the backend must use these reduced lists
+        prev_set = set;
+        prev_remove = remove;
+        
+        requests[numrequests++] = req;
+        
+        // continue with next backend
+        dav = dav->next;
+    }
+    
+    WSBool commit = FALSE;
+    if(ret == REQ_PROCEED && resource->err == 0) {
+        // no errors, no properties with errors -> save the changes
+        commit = TRUE; 
+    }
+    
+    // call proppatch_finish for each successfully called proppatch_do
+    dav = op->dav;
+    int i = 0;
+    while(dav && i < numrequests) {
+        if(dav->proppatch_finish(requests[i], resource, file, commit)) {
+            ret = REQ_ABORTED;
+        }
+        i++;
+        dav = dav->next;
+    }
+    
+    if(file) {
+        vfs_close(file);
+    }
+    
+    if(resource->close(resource)) {
+        ret = REQ_ABORTED;
+    }
+    
+    return ret;
+}
+
 int webdav_op_proppatch_close_resource(
         WebdavOperation *op,
         WebdavResource *resource)
 {
-    return 0;
+    return 0; // NOP
 }
 
--- a/src/server/webdav/operation.h	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/webdav/operation.h	Sat Jan 25 21:37:38 2020 +0100
@@ -42,7 +42,7 @@
     Request                *rq;
     Session                *sn;
     
-    WebdavProppatchRequest *request;      /* proppatch request or NULL */
+    WebdavProppatchRequest *proppatch;    /* proppatch request or NULL */
     WebdavPList            *reqprops;     /* requested properties */
     UcxList                *requests;     /* backend specific request objects */
     
@@ -54,6 +54,11 @@
     struct stat            *stat;         /* current stat object */
 };
 
+/*
+ * counts the number of backends
+ */
+size_t webdav_num_backends(WebdavBackend *dav);
+
 WebdavOperation* webdav_create_propfind_operation(
         Session *sn,
         Request *rq,
@@ -87,6 +92,11 @@
         WebdavProppatchRequest *proppatch,
         WebdavResponse *response);
 
+int webdav_op_proppatch(
+        WebdavOperation *op,
+        const char *href,
+        const char *path);
+
 int webdav_op_proppatch_close_resource(
         WebdavOperation *op,
         WebdavResource *resource);
--- a/src/server/webdav/webdav.c	Sat Jan 25 15:34:30 2020 +0100
+++ b/src/server/webdav/webdav.c	Sat Jan 25 21:37:38 2020 +0100
@@ -64,6 +64,9 @@
     default_backend.propfind_init = default_propfind_init;
     default_backend.propfind_do = default_propfind_do;
     default_backend.propfind_finish = default_propfind_finish;
+    default_backend.proppatch_do = NULL;
+    default_backend.proppatch_finish = NULL;
+    default_backend.settings = WS_WEBDAV_PROPFIND_USE_VFS;
 }
 
 int webdav_init(pblock *pb, Session *sn, Request *rq) {
@@ -242,6 +245,9 @@
     VFSContext *vfs = NULL;
     if(usevfs) {
         vfs = vfs_request_context(sn, rq);
+        if(!vfs) {
+            return REQ_ABORTED;
+        }
         
         if(vfs_stat(vfs, path, &s)) {
             protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL);
@@ -491,14 +497,25 @@
 }
 
 WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list) {
+    return webdav_plist_clone_s(pool, list, NULL);
+}
+
+WebdavPList* webdav_plist_clone_s(
+        pool_handle_t *pool,
+        WebdavPList *list,
+        size_t *newlen)
+{
     WebdavPList *new_list = NULL;     // start of the new list
     WebdavPList *new_list_end = NULL; // end of the new list
     
+    size_t len = 0;
+    
     WebdavPList *elm = list;
     while(elm) {
         // copy list item
         WebdavPList *new_elm = pool_malloc(pool, sizeof(WebdavPList));
         if(!new_elm) {
+            if(newlen) *newlen = 0;
             return NULL;
         }
         new_elm->property = elm->property; // new list contains original ptr
@@ -512,9 +529,11 @@
         }
         new_list_end = new_elm;
         
+        len++;
         elm = elm->next;
     }
     
+    if(newlen) *newlen = len;
     return new_list;
 }
 

mercurial