Fri, 17 Jan 2020 22:23:30 +0100
implement multistatus writer
/* * 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( Session *sn, Request *rq, WebdavBackend *dav, UcxList *requests, WebdavResponse *response) { WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation)); ZERO(op, sizeof(WebdavOperation)); op->dav = dav; op->sn = sn; op->rq = rq; op->requests = requests; op->response = response; response->op = op; return op; } int webdav_op_propfind_begin( WebdavOperation *op, const char *href, VFS_DIR parent, struct stat *s) { // create WebdavResource object for requested resource WebdavResource *resource = op->response->addresource(op->response, href); 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 int ret = REQ_PROCEED; if(op->dav->propfind_do(propfind, op->response, NULL, resource, s)) { ret = REQ_ABORTED; } else { // propfind_do successful, close resource if needed // closing the resource will execute propfind_do of all remaining // backends if(!resource->isclosed) { ret = resource->close(resource); } } return ret; } typedef struct PathSearchElm { char *href; char *path; size_t hreflen; size_t pathlen; } PathSearchElm; /* * concats base + / + elm * if baseinit is true, only elm is copied */ static int path_buf_concat( pool_handle_t *pool, char **buf, size_t * restrict len, WSBool * restrict baseinit, const char *base, size_t baselen, const char *elm, size_t elmlen) { if(base[baselen-1] == '/') { baselen--; } size_t newlen = baselen + elmlen + 1; if(newlen > WEBDAV_PATH_MAX) { log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); return 1; } // check if new path + terminator fits in the buffer if(newlen + 1 > *len) { *len = newlen + 128; char *newbuf = pool_realloc(pool, *buf, newlen); if(newbuf) { log_ereport(LOG_FAILURE, "webdav: path memory allocation failed"); return 1; } *baseinit = FALSE; *buf = newbuf; } // if baseinit is true, the parent is already in the buffer // and we don't need to memcpy it again if(!(*baseinit)) { memcpy(*buf, base, baselen); (*buf)[baselen] = '/'; *baseinit = TRUE; } // copy child and terminate string memcpy((*buf) + baselen + 1, elm, elmlen); (*buf)[newlen] = '\0'; return 0; } int webdav_op_propfind_children( WebdavOperation *op, VFSContext *vfs, const char *href, const char *path) { WebdavPropfindRequest *request = op->requests->data; UcxAllocator *a = session_get_allocator(request->sn); pool_handle_t *pool = request->sn->pool; PathSearchElm *start_elm = pool_malloc(pool, sizeof(PathSearchElm)); start_elm->href = pool_strdup(pool, href); start_elm->path = pool_strdup(pool, path); start_elm->hreflen = strlen(href); start_elm->pathlen = strlen(path); UcxList *stack = ucx_list_prepend_a(a, NULL, start_elm); UcxList *stack_end = stack; if(!stack) { return 1; } // reusable buffer for full child path and href char *newpath = pool_malloc(pool, 256); size_t newpathlen = 256; char *newhref = pool_malloc(pool, 256); size_t newhreflen = 256; int err = 0; while(stack && !err) { PathSearchElm *cur_elm = stack->data; // when newpath is initialized with the parent path // set path_buf_init to TRUE WSBool href_buf_init = FALSE; WSBool path_buf_init = FALSE; VFS_DIR dir = vfs_opendir(vfs, cur_elm->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); // create new path and href for the child if(path_buf_concat( pool, &newhref, &newhreflen, &href_buf_init, cur_elm->href, cur_elm->hreflen, f.name, child_len)) { err = 1; break; } if(path_buf_concat( pool, &newpath, &newpathlen, &path_buf_init, cur_elm->path, cur_elm->pathlen, f.name, child_len)) { err = 1; break; } size_t childhreflen = cur_elm->hreflen + 1 + child_len; size_t childpathlen = cur_elm->pathlen + 1 + child_len; // 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 *hrefcp = pool_malloc(pool, childhreflen + 1); memcpy(hrefcp, newhref, childhreflen + 1); hrefcp[childhreflen] = '\0'; char *pathcp = pool_malloc(pool, childpathlen + 1); memcpy(pathcp, newpath, childpathlen + 1); pathcp[childpathlen] = '\0'; PathSearchElm *new_elm = pool_malloc(pool, sizeof(PathSearchElm)); new_elm->href = hrefcp; new_elm->path = pathcp; new_elm->hreflen = childhreflen; new_elm->pathlen = childpathlen; // add the new_elm 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, new_elm); if(!newlistelm) { err = 1; break; } stack_end = newlistelm; } } vfs_closedir(dir); pool_free(pool, cur_elm->path); pool_free(pool, cur_elm->href); pool_free(pool, cur_elm); 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; }