Mon, 25 Apr 2022 13:54:27 +0200
close last resource in multistatus_send
/* * 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 <string.h> #include <ucx/buffer.h> #include <ucx/list.h> #include "webdav.h" #include "search.h" #include "versioning.h" #include "multistatus.h" #include "requestparser.h" #include "operation.h" #include "../util/pblock.h" #include "../util/util.h" #include "../daemon/session.h" #include "../daemon/http.h" #include "../daemon/protocol.h" #include "../daemon/vfs.h" /* * http method fptr mapping * key: http method name (string) * value: SAF fptr */ static UcxMap *method_handler_map; /* * webdav backend types * key: backend name (string) * value: WebdavBackend* */ static UcxMap *webdav_type_map; static WebdavBackend default_backend; static WSNamespace dav_namespace; static WebdavProperty dav_resourcetype_empty; static WebdavProperty dav_resourcetype_collection; static WSXmlData dav_resourcetype_collection_value; #define WEBDAV_RESOURCE_TYPE_COLLECTION "<D:collection/>" static void init_default_backend(void) { memset(&default_backend, 0, sizeof(WebdavBackend)); 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 = default_proppatch_do; default_backend.proppatch_finish = default_proppatch_finish; default_backend.opt_mkcol = NULL; default_backend.opt_delete = NULL; default_backend.settings = WS_WEBDAV_PROPFIND_USE_VFS; default_backend.instance = NULL; } int webdav_register_backend(const char *name, webdav_create_func webdavCreate) { return ucx_map_cstr_put(webdav_type_map, name, webdavCreate); } static WSBool webdav_is_initialized = FALSE; int webdav_init(pblock *pb, Session *sn, Request *rq) { if(webdav_is_initialized) { return REQ_NOACTION; } webdav_is_initialized = TRUE; webdav_type_map = ucx_map_new(8); if(!webdav_type_map) { return REQ_ABORTED; } method_handler_map = ucx_map_new(64); if(!method_handler_map) { return REQ_ABORTED; } init_default_backend(); ucx_map_cstr_put(webdav_type_map, "default", &default_backend); ucx_map_cstr_put(method_handler_map, "OPTIONS", webdav_options); ucx_map_cstr_put(method_handler_map, "PROPFIND", webdav_propfind); ucx_map_cstr_put(method_handler_map, "PROPPATCH", webdav_proppatch); ucx_map_cstr_put(method_handler_map, "MKCOL", webdav_mkcol); ucx_map_cstr_put(method_handler_map, "POST", webdav_post); ucx_map_cstr_put(method_handler_map, "DELETE", webdav_delete); ucx_map_cstr_put(method_handler_map, "PUT", webdav_put); ucx_map_cstr_put(method_handler_map, "COPY", webdav_copy); ucx_map_cstr_put(method_handler_map, "MOVE", webdav_move); ucx_map_cstr_put(method_handler_map, "LOCK", webdav_lock); ucx_map_cstr_put(method_handler_map, "UNLOCK", webdav_unlock); ucx_map_cstr_put(method_handler_map, "REPORT", webdav_report); ucx_map_cstr_put(method_handler_map, "ACL", webdav_acl); ucx_map_cstr_put(method_handler_map, "SEARCH", webdav_search); ucx_map_cstr_put(method_handler_map, "VERSION-CONTROL", webdav_version_control); ucx_map_cstr_put(method_handler_map, "CHECKOUT", webdav_checkout); ucx_map_cstr_put(method_handler_map, "CHECKIN", webdav_checkin); ucx_map_cstr_put(method_handler_map, "UNCHECKOUT", webdav_uncheckout); ucx_map_cstr_put(method_handler_map, "MKWORKSPACE", webdav_mkworkspace); ucx_map_cstr_put(method_handler_map, "UPDATE", webdav_update); ucx_map_cstr_put(method_handler_map, "LABEL", webdav_label); ucx_map_cstr_put(method_handler_map, "MERGE", webdav_merge); dav_namespace.href = (xmlChar*)"DAV:"; dav_namespace.prefix = (xmlChar*)"D"; dav_resourcetype_empty.namespace = &dav_namespace; dav_resourcetype_empty.name = "resourcetype"; dav_resourcetype_collection.namespace = &dav_namespace; dav_resourcetype_collection.name = "resourcetype"; dav_resourcetype_collection.value.data = &dav_resourcetype_collection_value; dav_resourcetype_collection.vtype = WS_VALUE_XML_DATA; dav_resourcetype_collection_value.data = WEBDAV_RESOURCE_TYPE_COLLECTION; dav_resourcetype_collection_value.length = sizeof(WEBDAV_RESOURCE_TYPE_COLLECTION)-1; return REQ_PROCEED; } int webdav_service(pblock *pb, Session *sn, Request *rq) { if(!method_handler_map) { log_ereport(LOG_FAILURE, "WebDAV module not initialized"); protocol_status(sn, rq, 500, NULL); return REQ_ABORTED; } char *method = pblock_findkeyval(pb_key_method, rq->reqpb); FuncPtr saf = (FuncPtr)ucx_map_cstr_get(method_handler_map, method); if(!saf) { return REQ_NOACTION; } return saf(pb, sn, rq); } UcxBuffer* rqbody2buffer(Session *sn, Request *rq) { if(!sn->inbuf) { //request body required, set http response code protocol_status(sn, rq, 400, NULL); return NULL; } UcxBuffer *buf = ucx_buffer_new( NULL, sn->inbuf->maxsize, UCX_BUFFER_AUTOEXTEND); if(!buf) { protocol_status(sn, rq, 500, NULL); return NULL; } char in[2048]; int r; while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) { if(ucx_buffer_write(in, 1, r, buf) != r) { protocol_status(sn, rq, 500, NULL); ucx_buffer_free(buf); return NULL; } } return buf; } int webdav_options(pblock *pb, Session *sn, Request *rq) { return REQ_ABORTED; } int webdav_propfind(pblock *pb, Session *sn, Request *rq) { char *expect = pblock_findkeyval(pb_key_expect, rq->headers); if(expect) { if(!strcasecmp(expect, "100-continue")) { if(http_send_continue(sn)) { return REQ_ABORTED; } } } UcxBuffer *reqbody = rqbody2buffer(sn, rq); if(!reqbody) { return REQ_ABORTED; } UcxAllocator *a = session_get_allocator(sn); int error = 0; WebdavPropfindRequest *propfind = propfind_parse( sn, rq, reqbody->space, reqbody->size, &error); ucx_buffer_free(reqbody); if(!propfind) { switch(error) { // TODO: handle all errors default: return REQ_ABORTED; } } WebdavBackend *dav = rq->davCollection ? rq->davCollection : &default_backend; // requested uri and path char *path = pblock_findkeyval(pb_key_path, rq->vars); char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); // The multistatus response object contains responses for all // requested resources. At the end the Multistatus object will be // serialized to xml Multistatus *ms = multistatus_response(sn, rq); if(!ms) { return REQ_ABORTED; } int ret = webdav_propfind_do(dav, propfind, (WebdavResponse*)ms, NULL, path, uri); // if propfind was successful, send the result to the client if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) { ret = REQ_ABORTED; // TODO: log error } else { // TODO: error response } // cleanup if(propfind->doc) { xmlFreeDoc(propfind->doc); } return ret; } /* * Initializes Backend Chain * * Calls propfind_init of each Backend and generates a list of custom * WebdavPropfindRequest objects for each backend */ int webdav_propfind_init( WebdavBackend *dav, WebdavPropfindRequest *propfind, const char *path, const char *uri, UcxList **out_req) { pool_handle_t *pool = propfind->sn->pool; UcxAllocator *a = session_get_allocator(propfind->sn); // list of individual WebdavPropfindRequest objects for each Backend UcxList *requestObjects = NULL; // new properties after init, start with clone of original plist WebdavPList *newProp = webdav_plist_clone(pool, propfind->properties); size_t newPropCount = propfind->propcount; // Call propfind_init for each Backend // propfind_init can return a new property list, which // will be passed to the next backend WebdavBackend *davList = dav; while(davList) { // create WebdavPropfindRequest copy WebdavPropfindRequest *pReq = pool_malloc( pool, sizeof(WebdavPropfindRequest)); memcpy(pReq, propfind, sizeof(WebdavPropfindRequest)); // use new plist after previous init (or orig. plist in the first run) pReq->properties = newProp; pReq->propcount = newPropCount; pReq->dav = davList; // add new WebdavPropfindRequest object to list for later use requestObjects = ucx_list_append_a(a, requestObjects, pReq); if(!requestObjects) { return REQ_ABORTED; // OOM } // create plist copy as out-plist for init newProp = webdav_plist_clone(pool, newProp); // run init: this can generate a new properties list (newProp) // which will be passed to the next backend if(davList->propfind_init(pReq, path, uri, &newProp)) { return REQ_ABORTED; } newPropCount = webdav_plist_size(newProp); davList = davList->next; } *out_req = requestObjects; return REQ_PROCEED; } int webdav_propfind_do( WebdavBackend *dav, WebdavPropfindRequest *propfind, WebdavResponse *response, VFSContext *vfs, char *path, char *uri) { Session *sn = propfind->sn; Request *rq = propfind->rq; // VFS settings are only taken from the first backend uint32_t settings = dav->settings; // list of individual WebdavPropfindRequest objects for each Backend UcxList *requestObjects = NULL; // Initialize all Webdav Backends if(webdav_propfind_init(dav, propfind, path, uri, &requestObjects)) { return REQ_ABORTED; } WebdavOperation *op = webdav_create_propfind_operation( sn, rq, dav, propfind->properties, requestObjects, response); // some Backends can list all children by themselves, but some // require the VFS for this WSBool usevfs = (settings & WS_WEBDAV_PROPFIND_USE_VFS) == WS_WEBDAV_PROPFIND_USE_VFS; struct stat s; struct stat *statptr = NULL; if(usevfs && !vfs) { 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); return REQ_ABORTED; } statptr = &s; if(!S_ISDIR(s.st_mode)) { // the file is not a directory, therefore we don't need the VFS usevfs = FALSE; } } if(propfind->depth == 0) { usevfs = FALSE; } int ret = REQ_PROCEED; // create WebdavResource object for requested resource if(!webdav_op_propfind_begin(op, uri, NULL, statptr)) { // propfind for the requested resource was successful // usevfsdir is TRUE if // the webdav backend has not disabled vfs usage // the file is a directory // depth is not 0 // in this case we need to execute propfind_do for all children if(usevfs) { if(webdav_op_propfind_children(op, vfs, uri, path)) { ret = REQ_ABORTED; } } } // finish the propfind request // this function should cleanup all resources, therefore we execute it // even if a previous function failed if(webdav_op_propfind_finish(op)) { // TODO: log error ret = REQ_ABORTED; } return ret; } int webdav_proppatch(pblock *pb, Session *sn, Request *rq) { char *expect = pblock_findkeyval(pb_key_expect, rq->headers); if(expect) { if(!strcasecmp(expect, "100-continue")) { if(http_send_continue(sn)) { return REQ_ABORTED; } } } UcxBuffer *reqbody = rqbody2buffer(sn, rq); if(!reqbody) { return REQ_ABORTED; } int error = 0; WebdavProppatchRequest *proppatch = proppatch_parse( sn, rq, reqbody->space, reqbody->size, &error); ucx_buffer_free(reqbody); if(!proppatch) { switch(error) { // TODO: handle all errors default: return REQ_ABORTED; } } WebdavBackend *dav = rq->davCollection ? rq->davCollection : &default_backend; // requested uri and path char *path = pblock_findkeyval(pb_key_path, rq->vars); char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); // The multistatus response object contains responses for all // requested resources. At the end the Multistatus object will be // serialized to xml Multistatus *ms = multistatus_response(sn, rq); if(!ms) { return REQ_ABORTED; } ms->proppatch = TRUE; // WebdavResponse is the public interface used by Backends // for adding resources to the response WebdavResponse *response = (WebdavResponse*)ms; WebdavOperation *op = webdav_create_proppatch_operation( sn, rq, dav, proppatch, response); int ret = REQ_PROCEED; // Execute proppatch if(webdav_op_proppatch(op, uri, path)) { ret = REQ_ABORTED; } // send response if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) { ret = REQ_ABORTED; // TODO: log error } else { // TODO: error response } // cleanup xmlFreeDoc(proppatch->doc); return ret; } int webdav_mkcol(pblock *pb, Session *sn, Request *rq) { WebdavVFSOperation *op = webdav_vfs_op(sn, rq, rq->davCollection, TRUE); if(!op) { return REQ_ABORTED; } int ret = webdav_vfs_op_do(op, WEBDAV_VFS_MKDIR); return ret; } int webdav_post(pblock *pb, Session *sn, Request *rq) { return REQ_ABORTED; } typedef struct DeleteFile { char *path; struct stat s; } DeleteFile; typedef struct DeleteLists { UcxAllocator *a; UcxList *dirs_begin; UcxList *dirs_end; UcxList *files_begin; UcxList *files_end; } DeleteOp; static int deletelist_add( VFSContext *vfs, const char *href, const char *path, VFSDir *parent, struct stat *s, void *userdata) { DeleteOp *op = userdata; // create object for this file DeleteFile *file = almalloc(op->a, sizeof(DeleteFile)); if(!file) { return 1; } file->path = sstrdup_a(op->a, sstr((char*)path)).ptr; if(!file->path) { return 1; } file->s = *s; // determine which list to use UcxList **begin; UcxList **end; if(S_ISDIR(s->st_mode)) { begin = &op->dirs_begin; end = &op->dirs_end; } else { begin = &op->files_begin; end = &op->files_end; } // add file to list UcxList *elm = ucx_list_append_a(op->a, NULL, file); if(!elm) { alfree(op->a, file->path); // at least do some cleanup, although it alfree(op->a, file); // isn't really necessary return 1; } if(*begin == NULL) { *begin = elm; *end = elm; } else { ucx_list_concat(*end, elm); *end = elm; } return 0; } static int webdav_delete_collection(WebdavVFSOperation *op) { DeleteOp del; ZERO(&del, sizeof(DeleteOp)); del.a = session_get_allocator(op->sn); // get a list of all files if(webdav_op_iterate_children(op->vfs, -1, NULL, op->path, deletelist_add, &del)) { return 1; } // add root to list of dir list DeleteFile root; root.path = op->path; root.s = *op->stat; UcxList root_elm; root_elm.data = &root; root_elm.prev = NULL; root_elm.next = del.dirs_begin; if(del.dirs_begin) { del.dirs_begin->prev = &root_elm; del.dirs_begin = &root_elm; } else { del.dirs_begin = &root_elm; del.dirs_end = &root_elm; } // delete files first UCX_FOREACH(elm, del.files_begin) { DeleteFile *file = elm->data; WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s); if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) { return 1; } } // delete directories, reverse order for(UcxList *elm=del.dirs_end;elm;elm=elm->prev) { DeleteFile *file = elm->data; WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s); if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) { return 1; } } return 0; } int webdav_delete(pblock *pb, Session *sn, Request *rq) { WebdavVFSOperation *op = webdav_vfs_op(sn, rq, rq->davCollection, TRUE); if(!op) { return REQ_ABORTED; } // stat to find out if the resource is a collection struct stat s; if(vfs_stat(op->vfs, op->path, &s)) { sys_set_error_status(op->vfs); return REQ_ABORTED; } op->stat = &s; int ret; if(S_ISDIR(s.st_mode)) { ret = webdav_delete_collection(op); } else { ret = webdav_vfs_op_do(op, WEBDAV_VFS_DELETE); } return ret; } int webdav_put(pblock *pb, Session *sn, Request *rq) { char *path = pblock_findkeyval(pb_key_path, rq->vars); VFSContext *vfs = vfs_request_context(sn, rq); if(!vfs) { protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL); return REQ_ABORTED; } struct stat s; int create_file = 0; if(vfs_stat(vfs, path, &s)) { if(vfs->vfs_errno == ENOENT) { create_file = O_CREAT; } else { protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL); return REQ_ABORTED; } } if(S_ISDIR(s.st_mode)) { // PUT on collections is not allowed protocol_status(sn, rq, PROTOCOL_METHOD_NOT_ALLOWED, NULL); return REQ_ABORTED; } SYS_FILE fd = vfs_open(vfs, path, O_WRONLY | create_file); if(!fd) { // if it fails, vfs_open sets http status code return REQ_ABORTED; } // TODO: check permissions, lock, ... // all checks done char *expect = pblock_findkeyval(pb_key_expect, rq->headers); if(expect) { if(!strcasecmp(expect, "100-continue")) { if(http_send_continue(sn)) { return REQ_ABORTED; } } } char in[4096]; int r; while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) { int w = 0; while(w < r) { w += system_fwrite(fd, in, r); } } system_fclose(fd); int status = create_file ? PROTOCOL_CREATED : PROTOCOL_NO_CONTENT; protocol_status(sn, rq, status, NULL); return REQ_PROCEED; } int webdav_copy(pblock *pb, Session *sn, Request *rq) { char *path = pblock_findkeyval(pb_key_path, rq->vars); char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); char *destination = pblock_findval("destination", rq->headers); if(!destination) { protocol_status(sn, rq, PROTOCOL_BAD_REQUEST, NULL); return REQ_ABORTED; } VFSContext *vfs = vfs_request_context(sn, rq); if(!vfs) { protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL); return REQ_ABORTED; } struct stat src_s; if(vfs_stat(vfs, path, &src_s)) { protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL); return REQ_ABORTED; } // TODO: if src is a directory, make sure the uri has a trailing path separator return REQ_ABORTED; } int webdav_move(pblock *pb, Session *sn, Request *rq) { return REQ_ABORTED; } int webdav_lock(pblock *pb, Session *sn, Request *rq) { return REQ_ABORTED; } int webdav_unlock(pblock *pb, Session *sn, Request *rq) { return REQ_ABORTED; } int webdav_report(pblock *pb, Session *sn, Request *rq) { return REQ_ABORTED; } int webdav_acl(pblock *pb, Session *sn, Request *rq) { return REQ_ABORTED; } /* ------------------------ default webdav backend ------------------------ */ int default_propfind_init( WebdavPropfindRequest *rq, const char* path, const char *href, WebdavPList **outplist) { DefaultWebdavData *data = pool_malloc( rq->sn->pool, sizeof(DefaultWebdavData)); if(!data) { return 1; } rq->userdata = data; data->vfsproperties = webdav_vfs_properties(outplist, TRUE, 0); return 0; } int default_propfind_do( WebdavPropfindRequest *request, WebdavResponse *response, VFS_DIR parent, WebdavResource *resource, struct stat *s) { DefaultWebdavData *data = request->userdata; // add all requested vfs properties like getcontentlength ... if(webdav_add_vfs_properties( resource, request->sn->pool, data->vfsproperties, s)) { return 1; } return 0; } int default_propfind_finish(WebdavPropfindRequest *rq) { return 0; } int default_proppatch_do( WebdavProppatchRequest *request, WebdavResource *response, VFSFile *file, WebdavPList **setInOut, WebdavPList **removeInOut) { return 0; } int default_proppatch_finish( WebdavProppatchRequest *request, WebdavResource *response, VFSFile *file, WSBool commit) { return 0; } /* ------------------------------ public API ------------------------------ */ int webdav_getdepth(Request *rq) { char *depth_str = pblock_findkeyval(pb_key_depth, rq->headers); int depth = 0; if(depth_str) { size_t dlen = strlen(depth_str); if(!memcmp(depth_str, "infinity", dlen)) { depth = -1; } else if(dlen == 1 && depth_str[0] == '1') { depth = 1; } } return depth; } int webdav_plist_add( pool_handle_t *pool, WebdavPList **begin, WebdavPList **end, WebdavProperty *prop) { WebdavPList *elm = pool_malloc(pool, sizeof(WebdavPList)); if(!elm) { return 1; } elm->prev = *end; elm->next = NULL; elm->property = prop; if(!*begin) { *begin = elm; *end = elm; return 0; } (*end)->next = elm; *end = elm; return 0; } 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 new_elm->prev = new_list_end; new_elm->next = NULL; if(new_list_end) { new_list_end->next = new_elm; } else { new_list = new_elm; } new_list_end = new_elm; len++; elm = elm->next; } if(newlen) *newlen = len; return new_list; } size_t webdav_plist_size(WebdavPList *list) { size_t count = 0; WebdavPList *elm = list; while(elm) { count++; elm = elm->next; } return count; } WebdavPListIterator webdav_plist_iterator(WebdavPList **list) { WebdavPListIterator i; i.list = list; i.cur = NULL; i.next = *list; i.index = 0; return i; } int webdav_plist_iterator_next(WebdavPListIterator *i, WebdavPList **cur) { if(i->cur) { i->index++; } i->cur = i->next; i->next = i->cur ? i->cur->next : NULL; *cur = i->cur; return i->cur != NULL; } void webdav_plist_iterator_remove_current(WebdavPListIterator *i) { WebdavPList *cur = i->cur; if(cur->prev) { cur->prev->next = cur->next; if(cur->next) { cur->next->prev = cur->prev; } } else { *i->list = cur->next; if(cur->next) { cur->next->prev = NULL; } } } int webdav_nslist_add( pool_handle_t *pool, WebdavNSList **begin, WebdavNSList **end, WSNamespace *ns) { // same as webdav_plist_add but with different type WebdavNSList *elm = pool_malloc(pool, sizeof(WebdavNSList)); if(!elm) { return 1; } elm->prev = *end; elm->next = NULL; elm->namespace = ns; if(!*begin) { *begin = elm; *end = elm; return 0; } (*end)->next = elm; *end = elm; return 0; } WSNamespace* webdav_dav_namespace(void) { return &dav_namespace; } WebdavProperty* webdav_resourcetype_collection(void) { return &dav_resourcetype_collection; } WebdavProperty* webdav_resourcetype_empty(void) { return &dav_resourcetype_empty; } WebdavProperty* webdav_dav_property( pool_handle_t *pool, const char *name) { WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty)); if(!property) { return NULL; } memset(property, 0, sizeof(WebdavProperty)); property->namespace = &dav_namespace; property->name = name; return property; } int webdav_property_set_value( WebdavProperty *p, pool_handle_t *pool, char *value) { WSXmlNode *node = pool_malloc(pool, sizeof(WSXmlNode)); if(!node) { return 1; } ZERO(node, sizeof(WSXmlNode)); node->content = (xmlChar*)value; node->type = XML_TEXT_NODE; p->value.node = node; p->vtype = WS_VALUE_XML_NODE; return 0; } WebdavVFSProperties webdav_vfs_properties( WebdavPList **plistInOut, WSBool removefromlist, uint32_t flags) { WebdavVFSProperties ret; ZERO(&ret, sizeof(WebdavVFSProperties)); WSBool etag = 1; WSBool creationdate = 1; WebdavPListIterator i = webdav_plist_iterator(plistInOut); WebdavPList *cur; while(webdav_plist_iterator_next(&i, &cur)) { WSNamespace *ns = cur->property->namespace; if(ns && !strcmp((const char*)ns->href, "DAV:")) { const char *name = cur->property->name; WSBool remove_prop = TRUE; if(!strcmp(name, "getlastmodified")) { ret.getlastmodified = 1; } else if(!strcmp(name, "getcontentlength")) { ret.getcontentlength = 1; } else if(!strcmp(name, "resourcetype")) { ret.getresourcetype = 1; } else if(etag && !strcmp(name, "getetag")) { ret.getetag = 1; } else if(creationdate && !strcmp(name, "creationdate")) { ret.creationdate = 1; } else { remove_prop = FALSE; } if(remove_prop) { webdav_plist_iterator_remove_current(&i); } } } return ret; } static inline int w_addprop( WebdavResource *res, pool_handle_t *pool, const char *name, char *value) { WebdavProperty *p = webdav_dav_property(pool, name); if(!p) { return 1; } if(webdav_property_set_value(p, pool, value)) { return 1; } return res->addproperty(res, p, 200); } int webdav_add_vfs_properties( WebdavResource *res, pool_handle_t *pool, WebdavVFSProperties properties, struct stat *s) { if(properties.getresourcetype) { if(S_ISDIR(s->st_mode)) { res->addproperty(res, &dav_resourcetype_collection, 200); } else { res->addproperty(res, &dav_resourcetype_empty, 200); } } if(properties.getcontentlength) { char *buf = pool_malloc(pool, 64); if(!buf) { return 1; } uint64_t contentlength = s->st_size; snprintf(buf, 64, "%" PRIu64, contentlength); if(w_addprop(res, pool, "getcontentlength", buf)) { return 1; } } if(properties.getlastmodified) { char *buf = pool_malloc(pool, HTTP_DATE_LEN+1); if(!buf) { return 1; } buf[HTTP_DATE_LEN] = 0; struct tm mtms; struct tm *mtm = system_gmtime(&s->st_mtim.tv_sec, &mtms); if(mtm) { strftime(buf, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); if(w_addprop(res, pool, "getlastmodified", buf)) { return 1; } } else { return 1; } } if(properties.creationdate) { // TODO } if(properties.getetag) { char *buf = pool_malloc(pool, 96); if(!buf) { return 1; } snprintf(buf, 96, "\"%x-%x\"", (int)s->st_size, (int)s->st_mtim.tv_sec); if(w_addprop(res, pool, "getetag", buf)) { return 1; } } return 0; }