diff -r cd74667f6c85 -r 4adad7faf452 src/server/webdav/operation.c --- 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 }