--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/requestparser.c Sat Sep 24 16:26:10 2022 +0200 @@ -0,0 +1,496 @@ +/* + * 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/string.h> +#include <ucx/utils.h> +#include <ucx/map.h> + +#include "requestparser.h" +#include "webdav.h" + +#define xstreq(a, b) !strcmp((const char*)a, (const char*)b) + +void proplist_free(pool_handle_t *pool, WebdavPList *list) { + while(list) { + WebdavPList *next = list->next; + pool_free(pool, list); + list = next; + } +} + +WebdavProperty* prop_create( + pool_handle_t *pool, + WSNamespace *ns, + 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; + return prop; +} + +static int parse_prop( + Session *sn, + xmlNode *node, + UcxMap *propmap, + WebdavPList **plist_begin, + WebdavPList **plist_end, + size_t *propcount, + int proppatch, + int *error) +{ + xmlNode *pnode = node->children; + for(;pnode;pnode=pnode->next) { + if(pnode->type != XML_ELEMENT_NODE) { + continue; + } + + const char* ns = (const char*)pnode->ns->href; + const char* name = (const char*)pnode->name; + + // check for prop duplicates + UcxKey k = webdav_property_key((const char*)ns, (const char*)name); + if(!k.data) { + *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + return 1; + } + void *c = ucx_map_get(propmap, k); + if(!c) { + if(ucx_map_put(propmap, k, (void*)1)) { + *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + } + + // no duplicate + // create property elment and add it to the list + WebdavProperty *prop = prop_create(sn->pool, pnode->ns, name); + if(proppatch) { + prop->value.node = pnode->children; + prop->vtype = WS_VALUE_XML_NODE; + } + if(prop) { + if(webdav_plist_add(sn->pool, plist_begin, plist_end, prop)) { + *error = proppatch ? + PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + } + (*propcount)++; + } else { + *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + } + } else if(proppatch) { + *error = PROPPATCH_PARSER_DUPLICATE; + } + + free((void*)k.data); + if(*error) { + return 1; + } + } + return 0; +} + +WebdavPropfindRequest* propfind_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error) +{ + xmlDoc *doc = xmlReadMemory(buf, buflen, NULL, NULL, 0); + if(!doc) { + *error = PROPFIND_PARSER_INVALID_REQUEST; + return NULL; + } + + // ret vars + *error = 0; + + WSBool allprop = FALSE; + WSBool propname = FALSE; + WebdavPList *plist_begin = NULL; + WebdavPList *plist_end = NULL; + size_t propcount = 0; + int depth = webdav_getdepth(rq); + + xmlNode *root = xmlDocGetRootElement(doc); + xmlNode *node = root->children; + + // check if the root element is DAV:propfind + if( + !(root->ns + && xstreq(root->ns->href, "DAV:") + && xstreq(root->name, "propfind"))) + { + *error = PROPFIND_PARSER_NO_PROPFIND; + xmlFreeDoc(doc); + return NULL; + } + + UcxMap *propmap = ucx_map_new(32); + if(!propmap) { + *error = PROPFIND_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + + int ret = 0; + while(node && !ret) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->ns->href, "DAV:") && !allprop && !propname) { + // a propfind request can contain a prop element + // with specified properties or the allprop or propname + // element + if(xstreq(node->name, "prop")) { + ret = parse_prop( + sn, + node, + propmap, + &plist_begin, + &plist_end, + &propcount, + 0, // proppatch = false + error); + } else if(xstreq(node->name, "allprop")) { + allprop = TRUE; + } else if(xstreq(node->name, "propname")) { + propname = TRUE; + } + } + } + node = node->next; + } + + ucx_map_free(propmap); // no allocated content must be freed + + if(ret) { + // parse_prop failed + // in this case, error is already set + xmlFreeDoc(doc); + return NULL; + } + + if(!allprop && !propname && propcount == 0) { + *error = PROPFIND_PARSER_NO_PROPERTIES; + xmlFreeDoc(doc); + return NULL; + } + + WebdavPropfindRequest *request = pool_malloc( + sn->pool, + sizeof(WebdavPropfindRequest)); + if(!request) { + *error = PROPFIND_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + request->sn = sn; + request->rq = rq; + request->properties = NULL; + request->propcount = 0; + request->depth = depth; + request->doc = doc; + if(allprop) { + request->allprop = TRUE; + request->propname = FALSE; // we cannot have allprop and propname + } else if(propname) { + request->allprop = FALSE; + request->propname = TRUE; + } else { + request->allprop = FALSE; + request->propname = FALSE; + request->properties = plist_begin; + request->propcount = propcount; + } + + if(!request->properties && plist_begin) { + proplist_free(sn->pool, plist_begin); + } + return request; +} + +WebdavProppatchRequest* proppatch_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error) +{ + return webdav_parse_set( + sn, rq, buf, buflen, "DAV:", "propertyupdate", TRUE, error); +} + +static xmlNode* find_child(xmlNode *node, const char *name) { + xmlNode *c = node->children; + while(c) { + if(c->ns) { + if(xstreq(c->ns->href, "DAV:") && xstreq(c->name, name)) { + return c; + } + } + c = c->next; + } + return NULL; +} + +WebdavProppatchRequest* webdav_parse_set( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + const char *rootns, + const char *rootname, + WSBool allowremove, + int *error) +{ + xmlDoc *doc = xmlReadMemory(buf, buflen, NULL, NULL, 0); + if(!doc) { + *error = PROPPATCH_PARSER_INVALID_REQUEST; + return NULL; + } + + xmlNode *root = xmlDocGetRootElement(doc); + xmlNode *node = root->children; + + // check if the root element is correct + if( + !(root->ns + && xstreq(root->ns->href, rootns) + && xstreq(root->name, rootname))) + { + *error = PROPPATCH_PARSER_NO_PROPERTYUPDATE; + xmlFreeDoc(doc); + return NULL; + } + + // ret vars + *error = 0; + + UcxMap *propmap = ucx_map_new(32); // map for duplicate checking + if(!propmap) { + *error = PROPPATCH_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + + WebdavPList *set_begin = NULL; + WebdavPList *set_end = NULL; + WebdavPList *remove_begin = NULL; + WebdavPList *remove_end = NULL; + size_t set_count = 0; + size_t remove_count = 0; + + int ret = 0; + while(node && !ret) { + if(node->type == XML_ELEMENT_NODE) { + if(node->ns && xstreq(node->ns->href, "DAV:")) { + // a propfind request can contain a prop element + // with specified properties or the allprop or propname + // element + if(xstreq(node->name, "set")) { + xmlNode *prop = find_child(node, "prop"); + ret = parse_prop( + sn, + prop, + propmap, + &set_begin, + &set_end, + &set_count, + TRUE, // proppatch = true + error); + } else if(xstreq(node->name, "remove")) { + if(!allowremove) { + *error = PROPPATCH_PARSER_INVALID_REQUEST; + ret = 1; + break; + } + xmlNode *prop = find_child(node, "prop"); + ret = parse_prop( + sn, + prop, + propmap, + &remove_begin, + &remove_end, + &remove_count, + TRUE, // proppatch = true + error); + } + + } + } + node = node->next; + } + + ucx_map_free(propmap); // allocated content must not be freed + + if(set_count + remove_count == 0) { + *error = PROPPATCH_PARSER_NO_PROPERTIES; + ret = 1; + } + + if(ret) { + xmlFreeDoc(doc); + return NULL; + } + + WebdavProppatchRequest *request = pool_malloc( + sn->pool, + sizeof(WebdavProppatchRequest)); + if(!request) { + *error = PROPPATCH_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + request->sn = sn; + request->rq = rq; + request->doc = doc; + request->set = set_begin; + request->setcount = set_count; + request->remove = remove_begin; + request->removecount = remove_count; + return request; +} + +WebdavLockRequest* lock_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error) +{ + xmlDoc *doc = xmlReadMemory(buf, buflen, NULL, NULL, 0); + if(!doc) { + *error = LOCK_PARSER_INVALID_REQUEST; + return NULL; + } + + xmlNode *root = xmlDocGetRootElement(doc); + xmlNode *node = root->children; + + // check if the root element is correct + if( + !(root->ns + && xstreq(root->ns->href, "DAV:") + && xstreq(root->name, "lockinfo"))) + { + *error = LOCK_PARSER_NO_LOCKINFO; + xmlFreeDoc(doc); + return NULL; + } + + WebdavLockScope lockscope = WEBDAV_LOCK_SCOPE_UNKNOWN; + WebdavLockType locktype = WEBDAV_LOCK_TYPE_UNKNOWN; + WSXmlNode *owner = NULL; + + int ret = 0; + while(node && !ret) { + if( + node->type == XML_ELEMENT_NODE + && node->ns + && xstreq(node->ns->href, "DAV:")) + { + char *name = (char*)node->name; + if(xstreq(name, "lockscope")) { + xmlNode *s = node->children; + while(s) { + if( + s->type == XML_ELEMENT_NODE + && s->ns + && xstreq(s->ns->href, "DAV:")) + { + if(xstreq(s->name, "exclusive")) { + lockscope = WEBDAV_LOCK_EXCLUSIVE; + } else if(xstreq(s->name, "shared")) { + lockscope = WEBDAV_LOCK_SHARED; + } else { + // don't ignore unknown lockscope + *error = LOCK_PARSER_UNKNOWN_ELEMENT; + ret = 1; + break; + } + } + s = s->next; + } + } else if(xstreq(name, "locktype")) { + xmlNode *t = node->children; + while(t) { + if( + t->type == XML_ELEMENT_NODE + && t->ns + && xstreq(t->ns->href, "DAV:")) + { + if(xstreq(t->name, "write")) { + locktype = WEBDAV_LOCK_WRITE; + } else { + *error = LOCK_PARSER_UNKNOWN_ELEMENT; + ret = 1; + break; + } + } + t = t->next; + } + } else if(xstreq(name, "owner")) { + owner = node->children; + } + } + node = node->next; + } + + if(ret) { + xmlFreeDoc(doc); + return NULL; + } + + WebdavLockRequest *request = pool_malloc( + sn->pool, + sizeof(WebdavLockRequest)); + if(!request) { + *error = LOCK_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + request->sn = sn; + request->rq = rq; + request->doc = doc; + + if(locktype == WEBDAV_LOCK_TYPE_UNKNOWN) { + locktype = WEBDAV_LOCK_WRITE; + } + if(lockscope == WEBDAV_LOCK_SCOPE_UNKNOWN) { + lockscope = WEBDAV_LOCK_EXCLUSIVE; + } + request->scope = lockscope; + request->type = locktype; + request->owner = owner; + return request; +} +