Sun, 29 Dec 2019 15:09:58 +0100
refactore webdav backend struct
--- a/src/server/public/webdav.h Thu Oct 31 10:26:35 2019 +0100 +++ b/src/server/public/webdav.h Sun Dec 29 15:09:58 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2018 Olaf Wintermann. All rights reserved. + * 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: @@ -44,10 +44,13 @@ typedef struct WebdavProperty WebdavProperty; typedef struct WebdavPList WebdavPList; +typedef struct WebdavNSList WebdavNSList; typedef enum WebdavLockScope WebdavLockScope; typedef enum WebdavLockType WebdavLockType; +typedef enum WebdavValueType WebdavValueType; + typedef struct WebdavPropfindRequest WebdavPropfindRequest; typedef struct WebdavProppatchRequest WebdavProppatchRequest; typedef struct WebdavLockRequest WebdavLockRequest; @@ -57,6 +60,9 @@ typedef struct WebdavVFSProperties WebdavVFSProperties; +typedef struct WSXmlData WSXmlData; +typedef struct WSText WSText; + typedef struct _xmlNs WSNamespace; typedef struct _xmlNode WSXmlNode; @@ -72,6 +78,19 @@ */ #define WS_PROPFIND_NO_VFS 0x01 + +enum WebdavValueType { + WS_VALUE_NO_TYPE = 0, + WS_VALUE_XML_NODE, + WS_VALUE_XML_DATA, + WS_VALUE_TEXT +}; + +struct WSText { + char *str; + size_t length; +}; + struct WebdavProperty { WSNamespace *namespace; @@ -79,7 +98,18 @@ char *lang; - WSXmlNode *value; + union { + WSXmlNode *node; + WSXmlData *data; + WSText text; + } value; + WebdavValueType vtype; +}; + +struct WSXmlData { + WebdavNSList *namespaces; + char *data; + size_t length; }; struct WebdavPList { @@ -88,6 +118,12 @@ WebdavPList *next; }; +struct WebdavNSList { + WSNamespace *namespace; + WebdavNSList *prev; + WebdavNSList *next; +}; + enum WebdavLockScope { WEBDAV_LOCK_EXCLUSIVE = 0, WEBDAV_LOCK_SHARED, @@ -185,8 +221,6 @@ * request and should initialize everything needed for generating the * multistatus response. * - * Optionally, the function can store a pointer to a list of all properties, - * which will be processed by this backend, in the outplist argument. */ int (*propfind_init)(WebdavPropfindRequest *, const char *, WebdavPList **); @@ -219,10 +253,26 @@ * See the WS_PROPFIND_ macros for informations about the settings */ uint32_t settings; + + + /* + * next Backend + */ + WebdavBackend *next; }; +/* + * gets the requested depth + * + * in case of infinity, -1 is returned + * if no depth is specified, 0 is returned + */ int webdav_getdepth(Request *rq); +WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list); + +size_t webdav_plist_count(WebdavPList *list); + WSNamespace* webdav_dav_namespace(void); WebdavProperty* webdav_dav_property( pool_handle_t *pool,
--- a/src/server/test/webdav.c Thu Oct 31 10:26:35 2019 +0100 +++ b/src/server/test/webdav.c Sun Dec 29 15:09:58 2019 +0100 @@ -262,7 +262,7 @@ UCX_TEST_ASSERT( !strcmp(p2->set->property->name, "a"), "p2: set property 1: wrong name"); - WSXmlNode *p2set1 = p2->set->property->value; + WSXmlNode *p2set1 = p2->set->property->value.node; UCX_TEST_ASSERT( p2set1->type == WS_NODE_TEXT, "p2: set property 1: wrong type"); @@ -273,7 +273,7 @@ !strcmp((char*)p2set1->content, "test"), "p2: set property 1: wrong value"); - WSXmlNode *p2set3 = p2->set->next->next->property->value; + WSXmlNode *p2set3 = p2->set->next->next->property->value.node; UCX_TEST_ASSERT(p2set3, "p2: set property 3 missing"); UCX_TEST_ASSERT( p2set3->type == WS_NODE_TEXT, @@ -382,6 +382,22 @@ WebdavProperty p1; WebdavProperty p[16]; + const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"}; + + // init test data + p1.namespace = webdav_dav_namespace(); + p1.lang = NULL; + p1.name = "test1"; + p1.value.data = NULL; + p1.vtype = 0; + + 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_ASSERT(!r->plist_begin && !r->plist_end, "plist not empty");
--- a/src/server/webdav/multistatus.h Thu Oct 31 10:26:35 2019 +0100 +++ b/src/server/webdav/multistatus.h Sun Dec 29 15:09:58 2019 +0100 @@ -55,8 +55,7 @@ MSResponse *current; /* - * this map contains some namespaces of property elements, but not all - * only the namespace of the property name element is added + * map of document namespace definitions * * key: (char*) namespace prefix * value: (char*) namespace href
--- a/src/server/webdav/requestparser.c Thu Oct 31 10:26:35 2019 +0100 +++ b/src/server/webdav/requestparser.c Sun Dec 29 15:09:58 2019 +0100 @@ -77,10 +77,10 @@ const char *name) { WebdavProperty *prop = pool_malloc(pool, sizeof(WebdavProperty)); + memset(prop, 0, sizeof(WebdavProperty)); prop->lang = NULL; prop->name = (char*)name; prop->namespace = ns; - prop->value = NULL; return prop; } @@ -128,7 +128,8 @@ // create property elment and add it to the list WebdavProperty *prop = prop_create(sn->pool, pnode->ns, name); if(proppatch) { - prop->value = pnode->children; + prop->value.node = pnode->children; + prop->vtype = WS_VALUE_XML_NODE; } if(prop) { if(proplist_add(sn->pool, plist_begin, plist_end, prop)) {
--- 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 {
--- a/src/server/webdav/webdav.h Thu Oct 31 10:26:35 2019 +0100 +++ b/src/server/webdav/webdav.h Sun Dec 29 15:09:58 2019 +0100 @@ -49,16 +49,32 @@ int webdav_service(pblock *pb, Session *sn, Request *rq); +/* + * returns a buffer containing the request body + * + * this function sets an http response code in case of an error + * or missing request body + */ UcxBuffer* rqbody2buffer(Session *sn, Request *rq); -int webdav_getdepth(Request *rq); int webdav_options(pblock *pb, Session *sn, Request *rq); int webdav_propfind(pblock *pb, Session *sn, Request *rq); + +int webdav_propfind_do( + WebdavBackend *webdav, + UcxList *requests, + WebdavResponse *response, + VFS_DIR parent, + const char *path, + struct stat *s); + +int webdav_propfind_finish(WebdavBackend *webdav, UcxList *requests); + int propfind_children( WebdavBackend *webdav, - WebdavPropfindRequest *request, + UcxList *requests, WebdavResponse *response, VFSContext *vfs, char *path); @@ -78,7 +94,8 @@ int default_propfind_init( WebdavPropfindRequest *rq, - const char* path); + const char* path, + WebdavPList **outplist); int default_propfind_do( WebdavPropfindRequest *request, WebdavResponse *response,