Sat, 25 Jan 2020 21:37:38 +0100
add proppatch op
--- 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; }