Sat, 25 Mar 2023 17:18:51 +0100
fix PUT could potentially return a wrong status code
/* * 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 <cx/string.h> #include <cx/utils.h> #include <cx/map.h> #include <cx/hash_map.h> #include "requestparser.h" #include "webdav.h" #include "../util/pool.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, CxMap *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 CxHashKey k = webdav_property_key((const char*)ns, (const char*)name); if(!k.data.bytes) { *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; return 1; } void *c = cxMapGet(propmap, k); if(!c) { if(cxMapPut(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.str); 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; } CxAllocator *a = pool_allocator(sn->pool); CxMap *propmap = cxHashMapCreate(a, 32); // value: intptr_t 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; } cxMapDestroy(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; CxAllocator *a = pool_allocator(sn->pool); // map for duplicate checking // value type: intptr_t CxMap *propmap = cxHashMapCreate(a, 32); 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; } cxMapDestroy(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; }