--- a/src/server/webdav/webdav.c Thu Oct 31 10:26:35 2019 +0100 +++ b/src/server/webdav/webdav.c Sun Dec 29 15:09:58 2019 +0100 @@ -53,7 +53,7 @@ static WebdavProperty dav_resourcetype_empty; static WebdavProperty dav_resourcetype_collection; -static WSXmlNode dav_resourcetype_collection_value; +static WSXmlNode dav_resourcetype_collection_value; // TODO: change type to WSXmlData static void init_default_backend(void) { memset(&default_backend, 0, sizeof(WebdavBackend)); @@ -100,7 +100,8 @@ dav_resourcetype_collection.namespace = &dav_namespace; dav_resourcetype_collection.name = "resourcetype"; - dav_resourcetype_collection.value = &dav_resourcetype_collection_value; + dav_resourcetype_collection.value.node = &dav_resourcetype_collection_value; + dav_resourcetype_collection.vtype = WS_VALUE_XML_NODE; dav_resourcetype_collection_value.content = (xmlChar*)"<D:collection/>"; dav_resourcetype_collection_value.type = XML_TEXT_NODE; @@ -127,6 +128,7 @@ UcxBuffer* rqbody2buffer(Session *sn, Request *rq) { if(!sn->inbuf) { + //request body required, set http response code protocol_status(sn, rq, 400, NULL); return NULL; } @@ -163,6 +165,8 @@ return REQ_ABORTED; } + UcxAllocator *a = session_get_allocator(sn); + int error = 0; WebdavPropfindRequest *propfind = propfind_parse( sn, @@ -178,23 +182,72 @@ } } - + // 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; } + // WebdavResponse is the public interface used by Backends + // for adding resources to the response WebdavResponse *response = (WebdavResponse*)ms; WebdavBackend *dav = rq->davCollection ? rq->davCollection : &default_backend; + // requested uri path char *path = pblock_findkeyval(pb_key_path, rq->vars); + // Initialize WebDAV Backend Chain + // + // Call propfind_init of each Backend + // propfind_init can return a new property list, which + // will be passed to the next backend + // + // VFS settings are only taken from the first backend uint32_t settings = dav->settings; - if(dav->propfind_init(propfind, path)) { - return REQ_ABORTED; + + // list of individual WebdavPropfindRequest objects for each Backend + UcxList *backendPropfind = NULL; + + // new properties after init, start with clone of original plist + WebdavPList *newProp = webdav_plist_clone(sn->pool, propfind->properties); + size_t newPropCount = propfind->propcount; + + WebdavBackend *davList = dav; + while(davList) { + // create WebdavPropfindRequest copy + WebdavPropfindRequest *pReq = pool_malloc( + sn->pool, + sizeof(WebdavPropfindRequest)); + memcpy(propfind, pReq, sizeof(WebdavPropfindRequest)); + // use new plist after previous init (or orig. plist in the first run) + pReq->properties = newProp; + pReq->propcount = newPropCount; + + // add new WebdavPropfindRequest object to list for later use + backendPropfind = ucx_list_append_a(a, backendPropfind, pReq); + if(!backendPropfind) { + return REQ_ABORTED; // OOM + } + + // create plist copy as out-plist for init + newProp = webdav_plist_clone(sn->pool, newProp); + + // run init: this can generate a new properties list (newProp) + // which will be passed to the next backend + if(dav->propfind_init(pReq, path, &newProp)) { + return REQ_ABORTED; + } + + newPropCount = webdav_plist_count(newProp); + + davList = davList->next; } + // some Backends can list all children by themselves, but some + // require the VFS for this WSBool usevfs = (settings & WS_PROPFIND_NO_VFS) != WS_PROPFIND_NO_VFS; struct stat s; struct stat *statptr = NULL; @@ -204,10 +257,12 @@ vfs = vfs_request_context(sn, rq); 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; } } @@ -216,7 +271,7 @@ } int ret = REQ_ABORTED; - if(!dav->propfind_do(propfind, response, NULL, path, statptr)) { + if(!webdav_propfind_do(dav, backendPropfind, response, NULL, path, statptr)) { // propfind for the requested resource was successful // usevfsdir is TRUE if @@ -224,7 +279,7 @@ // the file is a directory // depth is not 0 // in this case we need to execute propfind_do for all children - if(usevfs && !propfind_children(dav, propfind, response, vfs, path)) { + if(usevfs && !propfind_children(dav, backendPropfind, response, vfs, path)) { ret = REQ_PROCEED; } } @@ -239,13 +294,60 @@ return ret; } +/* + * Executes propfind_do for each Backend + * The list requests must contain all WebdavPropfindRequest objects + * of all backends + */ +int webdav_propfind_do( + WebdavBackend *webdav, + UcxList *requests, + WebdavResponse *response, + VFS_DIR parent, + const char *path, + struct stat *s) +{ + while(webdav && requests) { + if(webdav->propfind_do(requests->data, response, parent, path, s)) { + return REQ_ABORTED; + } + + webdav = webdav->next; + requests = requests->next; + } + return REQ_PROCEED; +} + +/* + * Executes propfind_finish for each Backend + */ +int webdav_propfind_finish(WebdavBackend *webdav, UcxList *requests) { + int ret = REQ_PROCEED; + while(webdav && requests) { + if(webdav->propfind_finish(requests->data)) { + ret = REQ_ABORTED; + } + + webdav = webdav->next; + requests = requests->next; + } + return ret; +} + + +/* + * Uses the VFS to iterate over all children of the requsted resource + * and executes propfind for each child + */ int propfind_children( WebdavBackend *dav, - WebdavPropfindRequest *request, + UcxList *requests, WebdavResponse *response, VFSContext *vfs, char *path) { + WebdavPropfindRequest *request = requests->data; + UcxAllocator *a = session_get_allocator(request->sn); pool_handle_t *pool = request->sn->pool; UcxList *stack = ucx_list_prepend_a(a, NULL, path); @@ -321,7 +423,7 @@ newpath[childpathlen] = 0; // propfind for this child - if(dav->propfind_do(request, response, dir, newpath, &f.stat)) { + if(webdav_propfind_do(dav, requests, response, dir, newpath, &f.stat)) { err = 1; break; } @@ -411,7 +513,8 @@ int default_propfind_init( WebdavPropfindRequest *rq, - const char* path) + const char* path, + WebdavPList **outplist) { DefaultWebdavData *data = pool_malloc( rq->sn->pool, @@ -484,6 +587,47 @@ return depth; } +WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list) { + WebdavPList *new_list = NULL; // start of the new list + WebdavPList *new_list_end = NULL; // end of the new list + + WebdavPList *elm = list; + while(elm) { + // copy list item + WebdavPList *new_elm = pool_malloc(pool, sizeof(WebdavPList)); + if(!new_elm) { + return NULL; + } + new_elm->property = elm->property; // new list contains original ptr + new_elm->prev = NULL; + new_elm->next = NULL; + + if(new_list_end) { + new_list_end->next = elm; + elm->prev = new_list_end; + + new_list_end = elm; + } else { + new_list = new_elm; + new_list_end = new_elm; + } + + elm = elm->next; + } + + return new_list; +} + +size_t webdav_plist_count(WebdavPList *list) { + size_t count = 0; + WebdavPList *elm = list; + while(elm) { + count++; + elm = elm->next; + } + return count; +} + WSNamespace* webdav_dav_namespace(void) { return &dav_namespace; } @@ -496,11 +640,10 @@ if(!property) { return NULL; } + memset(property, 0, sizeof(WebdavProperty)); property->namespace = &dav_namespace; - property->lang = NULL; property->name = name; - property->value = NULL; return property; } @@ -518,7 +661,8 @@ node->content = (xmlChar*)value; node->type = XML_TEXT_NODE; - p->value = node; + p->value.node = node; + p->vtype = WS_VALUE_XML_NODE; return 0; } @@ -556,6 +700,7 @@ } if(removefromlist && removethis) { + if(prev) { prev->next = next; } else {