Thu, 24 May 2012 12:51:52 +0200
new proppatch, mkcol and delete method
--- a/src/server/daemon/config.c Wed May 16 12:47:28 2012 +0200 +++ b/src/server/daemon/config.c Thu May 24 12:51:52 2012 +0200 @@ -43,10 +43,13 @@ #include "config.h" #include "func.h" #include "log.h" +#include "event.h" +#include "threadpools.h" #include "configmanager.h" #include "vserver.h" #include "../util/pblock.h" +#include "../util/util.h" pool_handle_t *cfg_pool; @@ -145,8 +148,8 @@ int hr = 0; if(!sstrcmp(scfgobj->type, sstr("Runtime"))) { hr = cfg_handle_runtime(serverconfig, scfgobj); - } else if(!sstrcmp(scfgobj->type, sstr("LogFile"))) { - //hr = cfg_handle_logfile(serverconfig, scfgobj); + } else if(!sstrcmp(scfgobj->type, sstr("EventHandler"))) { + hr = cfg_handle_eventhandler(serverconfig, scfgobj); } else if(!sstrcmp(scfgobj->type, sstr("AuthDB"))) { hr = cfg_handle_authdb(serverconfig, scfgobj); } else if(!sstrcmp(scfgobj->type, sstr("Listener"))) { @@ -164,7 +167,17 @@ } } - + /* check event handler config */ + if(check_event_handler_cfg() != 0) { + /* critical error */ + return NULL; + } + + /* check thread pool config */ + if(check_thread_pool_cfg() != 0) { + /* critical error */ + return NULL; + } /* set VirtualServer for all listeners */ UcxList *ls = serverconfig->listeners; @@ -237,6 +250,30 @@ return ret; } +int cfg_handle_eventhandler(ServerConfiguration *c, ServerConfigObject *obj) { + EventHandlerConfig evcfg; + + sstr_t name = cfg_directivelist_get_str(obj->directives, sstr("Name")); + sstr_t threads = cfg_directivelist_get_str( + obj->directives, + sstr("Threads")); + sstr_t isdefault = cfg_directivelist_get_str( + obj->directives, + sstr("Default")); + + evcfg.name = name; + + sstr_t s = sstrdup(threads); + evcfg.nthreads = atoi(s.ptr); + free(s.ptr); + + evcfg.isdefault = util_getboolean(isdefault.ptr, 0); + + int ret = create_event_handler(&evcfg); + + return ret; +} + int cfg_handle_authdb(ServerConfiguration *cfg, ServerConfigObject *obj) { /* TODO: authdb*/
--- a/src/server/daemon/config.h Wed May 16 12:47:28 2012 +0200 +++ b/src/server/daemon/config.h Thu May 24 12:51:52 2012 +0200 @@ -77,6 +77,8 @@ int cfg_handle_logfile(ServerConfiguration *cfg, ServerConfigObject *obj); +int cfg_handle_eventhandler(ServerConfiguration *cfg, ServerConfigObject *obj); + int cfg_handle_authdb(ServerConfiguration *cfg, ServerConfigObject *obj); int cfg_handle_listener(ServerConfiguration *cfg, ServerConfigObject *obj);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/event.c Thu May 24 12:51:52 2012 +0200 @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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 "../ucx/map.h" + +#include "event.h" + +UcxMap *event_handler_map = NULL; +int numevhandlers = 0; + +event_handler_t *default_event_handler = NULL; + +event_handler_t *last_handler_c = NULL; + +int create_event_handler(EventHandlerConfig *cfg) { + if(event_handler_map == NULL) { + event_handler_map = ucx_map_new(16); + } + + /* if the event handler already exists, we don't modify it */ + if(ucx_map_sstr_get(event_handler_map, cfg->name)) { + /* TODO: log message */ + /* TODO: set reload status */ + return 1; + } + + /* create new handler */ + event_handler_t *e = evhandler_create(cfg->nthreads); + if(e == NULL) { + return 1; + } + + if(cfg->isdefault) { + if(default_event_handler) { + /* there can be only one default event handler */ + /* TODO: log warning */ + } + default_event_handler = e; + } + + int ret = ucx_map_sstr_put(event_handler_map, cfg->name, e); + if(ret == 0) { + last_handler_c = e; + numevhandlers++; + } + + return ret; +} + +/* + * checks if there is at least one event handler and a default handler + * if necessary, check_event_handler_cfg() creates a default event handler + */ +int check_event_handler_cfg() { + if(numevhandlers > 0 ) { + if(default_event_handler) { + return 0; + } else { + default_event_handler = last_handler_c; + return 0; + } + } + + EventHandlerConfig cfg; + cfg.name = sstr("default"); + cfg.nthreads = 1; + cfg.isdefault = 1; + + return create_event_handler(&cfg); +} +
--- a/src/server/daemon/event.h Wed May 16 12:47:28 2012 +0200 +++ b/src/server/daemon/event.h Thu May 24 12:51:52 2012 +0200 @@ -30,6 +30,7 @@ #define EVENT_H #include "../public/nsapi.h" +#include "../ucx/string.h" #ifdef __cplusplus extern "C" { @@ -49,6 +50,22 @@ void *cookie; }; +typedef struct event_handler_conf { + sstr_t name; + int nthreads; + int isdefault; +} EventHandlerConfig; + +typedef struct event_handler_object { + event_handler_t *handler; + int nthreads; +} EventHandlerObject; + +int create_event_handler(EventHandlerConfig *cfg); + +int check_event_handler_cfg(); + +/* implementation in event_$platform */ event_handler_t* evhandler_create(int numthreads); int ev_pollin(event_handler_t *h, int fd, event_t *event);
--- a/src/server/daemon/event_solaris.c Wed May 16 12:47:28 2012 +0200 +++ b/src/server/daemon/event_solaris.c Thu May 24 12:51:52 2012 +0200 @@ -97,7 +97,7 @@ } for(int i=0;i<nev;i++) { - event_t *event = events[i]->portev_user; + event_t *event = events[i].portev_user; if(event->fn) { event->fn(ev, event); } @@ -107,9 +107,15 @@ /* returns a event handler port */ int ev_get_port(event_handler_t *h) { - int cp = h->lp % h->nports; + int nps = h->nports; + if(nps == 1) { + return 0; + } + + int cp = h->lp % nps; atomic_inc_32(&h->lp); - return cp; + + return h->ports[cp]; } int ev_pollin(event_handler_t *h, int fd, event_t *event) {
--- a/src/server/daemon/objs.mk Wed May 16 12:47:28 2012 +0200 +++ b/src/server/daemon/objs.mk Thu May 24 12:51:52 2012 +0200 @@ -45,6 +45,8 @@ DAEMONOBJ += ws-fn.o DAEMONOBJ += configmanager.o DAEMONOBJ += log.o +DAEMONOBJ += event.o +DAEMONOBJ += threadpools.o #ifeq ($(OS), SunOS) DAEMONOBJ += event_solaris.o
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/threadpools.c Thu May 24 12:51:52 2012 +0200 @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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 "../ucx/map.h" + +#include "threadpools.h" + + +UcxMap *thread_pool_map = NULL; +int numthrpools = 0; + +threadpool_t *default_thread_pool = NULL; + +threadpool_t *last_thrpool_c = NULL; + +int create_threadpool(sstr_t name, ThreadPoolConfig *cfg) { + if(thread_pool_map == NULL) { + thread_pool_map = ucx_map_new(16); + } + + threadpool_t *pool = ucx_map_sstr_get(thread_pool_map, name); + if(pool) { + /* TODO: reconfig thread pool */ + return 0; + } else { + threadpool_t *tp = threadpool_new(cfg->min_threads); + + int ret = ucx_map_sstr_put(thread_pool_map, name, tp); + + if(ret == 0) { + numthrpools++; + last_thrpool_c = tp; + } + + return ret; + } +} + +int check_thread_pool_cfg() { + if(numthrpools > 0 ) { + if(default_thread_pool) { + return 0; + } else { + default_thread_pool = last_thrpool_c; + return 0; + } + } + + ThreadPoolConfig cfg; + cfg.min_threads = 4; + cfg.max_threads = 8; + cfg.queue_size = 64; + cfg.stack_size = 262144; + + return create_threadpool(sstr("default"), &cfg); +} + +threadpool_t* get_default_threadpool() { + return default_thread_pool; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/threadpools.h Thu May 24 12:51:52 2012 +0200 @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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. + */ + +#ifndef THREADPOOLS_H +#define THREADPOOLS_H + +#include "../public/nsapi.h" +#include "../util/thrpool.h" +#include "../ucx/string.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct thread_pool_config { + int min_threads; + int max_threads; + int stack_size; + int queue_size; +} ThreadPoolConfig; + +int create_threadpool(sstr_t name, ThreadPoolConfig *cfg); + +int check_thread_pool_cfg(); + +threadpool_t* get_default_threadpool(); + +#ifdef __cplusplus +} +#endif + +#endif /* THREADPOOLS_H */ +
--- a/src/server/ucx/dlist.c Wed May 16 12:47:28 2012 +0200 +++ b/src/server/ucx/dlist.c Thu May 24 12:51:52 2012 +0200 @@ -45,6 +45,7 @@ nl->data = data; nl->next = NULL; if (l == NULL) { + nl->prev = NULL; return nl; } else { UcxDlist *t = ucx_dlist_last(l); @@ -111,14 +112,6 @@ return s; } -void ucx_dlist_foreach(UcxDlist *l, ucx_callback fnc, void* data) { - UcxDlist *e = l; - while (e != NULL) { - fnc(e, data); - e = e->next; - } -} - /* dlist specific functions */ UcxDlist *ucx_dlist_first(UcxDlist *l) { if (l == NULL) return NULL; @@ -129,3 +122,20 @@ } return e; } + +UcxDlist *ucx_dlist_remove(UcxDlist *l, UcxDlist *e) { + if (e->prev == NULL) { + if(e->next != NULL) { + e->next->prev = NULL; + l = e->next; + } else { + l = NULL; + } + + } else { + e->prev->next = e->next; + e->next->prev = e->prev; + } + free(e); + return l; +}
--- a/src/server/ucx/dlist.h Wed May 16 12:47:28 2012 +0200 +++ b/src/server/ucx/dlist.h Thu May 24 12:51:52 2012 +0200 @@ -29,10 +29,10 @@ UcxDlist *ucx_dlist_last(UcxDlist *l); UcxDlist *ucx_dlist_get(UcxDlist *l, int index); size_t ucx_dlist_size(UcxDlist *l); -void ucx_dlist_foreach(UcxDlist *l, ucx_callback fnc, void* data); /* dlist specific functions */ UcxDlist *ucx_dlist_first(UcxDlist *l); +UcxDlist *ucx_dlist_remove(UcxDlist *l, UcxDlist *e); #ifdef __cplusplus }
--- a/src/server/ucx/string.c Wed May 16 12:47:28 2012 +0200 +++ b/src/server/ucx/string.c Thu May 24 12:51:52 2012 +0200 @@ -146,3 +146,4 @@ return newstring; } +
--- a/src/server/webdav/davparser.cpp Wed May 16 12:47:28 2012 +0200 +++ b/src/server/webdav/davparser.cpp Thu May 24 12:51:52 2012 +0200 @@ -105,6 +105,7 @@ ProppatchRequest *davrq = (ProppatchRequest*)pool_malloc( sn->pool, sizeof(PropfindRequest)); + davrq->nsmap = xmlnsmap_create();
--- a/src/server/webdav/saxhandler.cpp Wed May 16 12:47:28 2012 +0200 +++ b/src/server/webdav/saxhandler.cpp Thu May 24 12:51:52 2012 +0200 @@ -38,6 +38,22 @@ using namespace std; +void xstack_push(UcxDlist **stack, XmlElement *elm) { + *stack = ucx_dlist_prepend(*stack, elm); +} + +XmlElement* xstack_pop(UcxDlist **stack) { + if(*stack == NULL) { + return NULL; + } + XmlElement* ret = (XmlElement*)(*stack)->data; + UcxDlist *newstack = ucx_dlist_remove(*stack, *stack); + *stack = newstack; + return ret; +} + + + PropfindHandler::PropfindHandler(PropfindRequest *rq, pool_handle_t *p) { davrq = rq; pool = p; @@ -132,6 +148,13 @@ ProppatchHandler::ProppatchHandler(ProppatchRequest *rq, pool_handle_t *p) { davrq = rq; pool = p; + + davPropTag = false; + rootElement = NULL; + xmlStack = NULL; + newElement = NULL; + + updateMode = -1; } ProppatchHandler::~ProppatchHandler() { @@ -147,10 +170,34 @@ char *ns = XMLString::transcode(uri); char *name = XMLString::transcode(localname); - if(!strcmp(ns, "DAV:") && !strcmp(name, "prop")) { + if(!strcmp(ns, "DAV:") && !strcmp(name, "set")) { + updateMode = 0; + } else if(!strcmp(ns, "DAV:") && !strcmp(name, "remove")) { + updateMode = 1; + } else if(!strcmp(ns, "DAV:") && !strcmp(name, "prop")) { davPropTag = true; } else if(davPropTag) { + newElement = (XmlElement*)pool_calloc(pool, 1, sizeof(XmlElement)); + newElement->name = sstr(pool_strdup(pool, name)); + newElement->xmlns = xmlnsmap_put(davrq->nsmap, ns); + /* + * the xml stack manages the xml hierarchy + * new elements will be added to the top element on the stack + */ + + XmlElement *currentElm = XSTACK_CUR(); + if(currentElm) { + xmlelm_add_child(currentElm, newElement); + } + + /* newElement is now the parent for future elements */ + XSTACK_PUSH(newElement); + + /* if the root element isn't set, the first new element is the root */ + if(!rootElement) { + rootElement = newElement; + } } XMLString::release(&ns); @@ -167,11 +214,42 @@ char *name = XMLString::transcode(localname); if(!strcmp(ns, "DAV:") && !strcmp(name, "set")) { - updateMode = 0; + updateMode = -1; } else if(!strcmp(ns, "DAV:") && !strcmp(name, "remove")) { - updateMode = 1; + updateMode = -1; } else if(!strcmp(ns, "DAV:") && !strcmp(name, "prop")) { - + davPropTag = false; + } else if(davPropTag) { + XmlElement *elm = XSTACK_POP(); + if(xmlStack == NULL) { + /* property complete */ + + /* + XmlElement *r = rootElement; + printf("<%s>\n", sstrdup(r->name).ptr); + printf("%s\n", r->content); + printf("</%s>\n", sstrdup(r->name).ptr); + */ + + /* add the property to the proppatch request */ + switch(updateMode) { + case 0: { + davrq->setProps = ucx_dlist_append( + davrq->setProps, + rootElement); + break; + } + case 1: { + davrq->removeProps = ucx_dlist_append( + davrq->removeProps, + rootElement); + break; + } + } + + rootElement = NULL; + + } } @@ -184,6 +262,21 @@ const XMLSize_t length) { + XMLString::trim((XMLCh *const)chars); + if(chars[0] == 0) { + return; + } + + XmlElement *currentElm = XSTACK_CUR(); + if(currentElm) { + xmlch_t *str = (xmlch_t*)pool_calloc(pool, sizeof(xmlch_t), length + 1); + for(int i=0;i<length;i++) { + str[i] = chars[i]; + } + + currentElm->content = str; + currentElm->ctlen = length; + } } void ProppatchHandler::startDocument() {
--- a/src/server/webdav/saxhandler.h Wed May 16 12:47:28 2012 +0200 +++ b/src/server/webdav/saxhandler.h Thu May 24 12:51:52 2012 +0200 @@ -35,6 +35,13 @@ using namespace xercesc; +/* stack */ +#define XSTACK_PUSH(elm) xstack_push(&xmlStack, elm) +#define XSTACK_POP() xstack_pop(&xmlStack) +#define XSTACK_CUR() (xmlStack) ? (XmlElement*)xmlStack->data : NULL; +void xstack_push(UcxDlist **stack, XmlElement *elm); +XmlElement* xstack_pop(UcxDlist **stack); + class PropfindHandler : public DefaultHandler { public: PropfindHandler(PropfindRequest *rq, pool_handle_t *p); @@ -91,10 +98,12 @@ pool_handle_t *pool; bool davPropTag; - XmlElement *rxprop; /* root of xml property */ - XmlElement *cxprop; /* current element */ - DavProperty *property; - int updateMode; /* 0 = set, 1 = remove */ + XmlElement *rootElement; + XmlElement *newElement; + UcxDlist *xmlStack; + + //DavProperty *property; + int updateMode; /* 0 = set, 1 = remove, -1 = undefined */ }; #endif /* SAXHANDLER_H */
--- a/src/server/webdav/webdav.c Wed May 16 12:47:28 2012 +0200 +++ b/src/server/webdav/webdav.c Thu May 24 12:51:52 2012 +0200 @@ -36,6 +36,8 @@ #include "../util/pblock.h" #include "../util/date.h" +#include "../daemon/protocol.h" + #include "davparser.h" int webdav_service(pblock *pb, Session *sn, Request *rq) { @@ -50,6 +52,10 @@ return webdav_proppatch(pb, sn, rq); } else if(!strcmp(method, "PUT")) { return webdav_put(pb, sn, rq); + } else if(!strcmp(method, "DELETE")) { + return webdav_delete(pb, sn, rq); + } else if(!strcmp(method, "MKCOL")) { + return webdav_mkcol(pb, sn, rq); } return REQ_NOACTION; @@ -106,6 +112,64 @@ return REQ_PROCEED; } +int webdav_delete(pblock *pb, Session *sn, Request *rq) { + char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); + char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); + + int status = 204; + + struct stat st; + if(stat(ppath, &st) != 0) { + /* ERROR */ + status = 403; /* TODO: check errno */ + } + + if(!strcmp(uri, "/")) { + status = 403; + } else if((st.st_mode & S_IFDIR) == S_IFDIR) { + if(rmdir(ppath) != 0) { + /* ERROR */ + status = 403; + } + } else { + if(unlink(ppath) != 0) { + /* ERROR */ + status = 403; /* TODO: check errno */ + } + } + + protocol_status(sn, rq, status, NULL); + pblock_removekey(pb_key_content_type, rq->srvhdrs); + pblock_nninsert("content-length", 0, rq->srvhdrs); + http_start_response(sn, rq); + + return REQ_PROCEED; +} + +int webdav_mkcol(pblock *pb, Session *sn, Request *rq) { + char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); + + int status = 201; + if(mkdir(ppath, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + status = 403; + } + + protocol_status(sn, rq, status, NULL); + pblock_removekey(pb_key_content_type, rq->srvhdrs); + pblock_nninsert("content-length", 0, rq->srvhdrs); + http_start_response(sn, rq); + + return REQ_ABORTED; +} + +int webdav_copy(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_move(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + int webdav_propfind(pblock *pb, Session *sn, Request *rq) { /* TODO: clean up if errors occurs */ @@ -162,6 +226,7 @@ struct stat st; if(stat(ppath, &st) != 0) { perror("webdav_propfind: stat"); + fprintf(stderr, " file: %s\n", ppath); return REQ_ABORTED; } @@ -246,8 +311,14 @@ } int webdav_proppatch(pblock *pb, Session *sn, Request *rq) { + printf("webdav-proppatch\n"); /* TODO: clean up if errors occurs */ /* TODO: this is the same code as in propfind */ + char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); + if(uri == NULL) { + /* TODO: error */ + return REQ_ABORTED; + } /* Get request body which contains the webdav XML request */ char *xml_body; @@ -282,7 +353,58 @@ xl -= r; } + /* + * parse the xml request and create the proppatch object + */ + ProppatchRequest *davrq = dav_parse_proppatch(sn, rq, xml_body, xml_len); + davrq->sn = sn; + davrq->rq = rq; + davrq->out = sbuf_new(512); + davrq->backend = create_property_backend(); + davrq->propstat = propstat_create(sn->pool); + /* TODO: create prefixes for every namespace */ + XmlNs *ns = xmlnsmap_get(davrq->nsmap, "DAV:"); + ns->prefix = "D"; + ns->prelen = 1; + + /* + * begin multistatus response + * + * The webdav backend does the most work. The backend->proppatch function + * modifies the properties and adds status informations to the propstat + * member of the ProppatchRequest. All we have to do here is to create + * the xml response and send it to the client + */ + + /* write xml response header */ + /* TODO: add possible xml namespaces */ + sbuf_puts(davrq->out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); + sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n"); + + sbuf_puts(davrq->out, "<D:response>\n<D:href>"); + sbuf_puts(davrq->out, uri); + sbuf_puts(davrq->out, "</D:href>\n"); + + /* do proppatch operation */ + davrq->backend->proppatch(davrq->backend, davrq); + + propstat_write(davrq->propstat, davrq->out, 0); + + sbuf_puts(davrq->out, "</D:response>\n"); + sbuf_puts(davrq->out, "</D:multistatus>\n"); + + + /* send the xml response to the client */ + protocol_status(sn, rq, 207, "Multi Status"); + pblock_removekey(pb_key_content_type, rq->srvhdrs); + pblock_nvinsert("content-type", "text/xml", rq->srvhdrs); + pblock_nninsert("content-length", davrq->out->length, rq->srvhdrs); + + pblock_nvinsert("connection", "close", rq->srvhdrs); + http_start_response(sn, rq); + + net_write(sn->csd, davrq->out->ptr, davrq->out->length); return REQ_PROCEED; } @@ -368,7 +490,8 @@ if(pb == NULL) { // } - pb->propfind = dav_rq_propfind; + pb->propfind = dav_rq_propfind; + pb->proppatch = dav_rq_proppatch; return pb; } @@ -404,3 +527,174 @@ } } } + +void dav_rq_proppatch(DAVPropertyBackend *b, ProppatchRequest *rq) { + DAV_FOREACH(p, rq->setProps) { + XmlElement *prop = (XmlElement*)p->data; + propstat_add(rq->propstat, 403, prop); + } + + DAV_FOREACH(p, rq->removeProps) { + XmlElement *prop = (XmlElement*)p->data; + propstat_add(rq->propstat, 403, prop); + } +} + + + +/*---------------------------------- utils ----------------------------------*/ + +/* XmlNsMap */ + +XmlNsMap* xmlnsmap_create() { + XmlNsMap *map = malloc(sizeof(XmlNsMap)); + UcxMap *uxm = ucx_map_new(16); + if(map == NULL || uxm == NULL) { + return NULL; + } + map->map = uxm; + return map; +} + +void xmlnsmap_free(XmlNsMap *map) { + /* TODO: implement */ +} + +XmlNs* xmlnsmap_put(XmlNsMap *map, char *ns) { + XmlNs *xmlns = xmlnsmap_get(map, ns); + if(xmlns != NULL) { + return xmlns; + } + + xmlns = malloc(sizeof(XmlNs)); + if(xmlns == NULL) { + return NULL; + } + + xmlns->xmlns = ns; + xmlns->nslen = strlen(ns); + + xmlns->prefix = NULL; + xmlns->prelen = 0; + + ucx_map_cstr_put(map->map, ns, xmlns); /* TODO: check return value */ + return xmlns; +} + +XmlNs* xmlnsmap_get(XmlNsMap *map, char *ns) { + return ucx_map_cstr_get(map->map, ns); +} + + +/* XmlElement */ + +void xmlelm_add_child(XmlElement *parent, XmlElement *child) { + if(parent->ctlen == 0) { + parent->content = ucx_dlist_append(parent->content, child); + } +} + +void xmlelm_write(XmlElement *elm, sbuf_t *out, int wv) { + sbuf_append(out, sstrn("<", 1)); + sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); + sbuf_append(out, sstrn(":", 1)); + sbuf_append(out, elm->name); + + if(wv) { + if(elm->ctlen == 0) { + if(elm->content == NULL) { + sbuf_append(out, sstrn(" />", 3)); + } else { + sbuf_append(out, sstrn(">", 1)); + DAV_FOREACH(pr, (UcxDlist*)elm->content) { + xmlelm_write((XmlElement*)pr->data, out, 1); + } + sbuf_append(out, sstrn("</", 2)); + sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); + sbuf_append(out, sstrn(":", 1)); + sbuf_append(out, elm->name); + sbuf_append(out, sstrn(">", 1)); + } + } else { + sbuf_append(out, sstrn(" />", 3)); + sbuf_append(out, sstrn((char*)elm->content, elm->ctlen)); + sbuf_append(out, sstrn("</", 2)); + sbuf_append(out, sstrn(elm->xmlns->prefix, elm->xmlns->prelen)); + sbuf_append(out, sstrn(":", 1)); + sbuf_append(out, elm->name); + sbuf_append(out, sstrn(">", 1)); + } + } else { + sbuf_append(out, sstrn(" />", 3)); + } +} + + +/* PropstatMap */ + +Propstat* propstat_create(pool_handle_t *pool) { + Propstat *propstat = (Propstat*)pool_malloc(pool, sizeof(Propstat)); + propstat->map = ucx_map_new(8); + propstat->okprop = NULL; + propstat->pool = pool; + return propstat; +} + +void propstat_add(Propstat *propstat, int status, XmlElement *prop) { + if(status == 200) { + propstat->okprop = ucx_dlist_append(propstat->okprop, prop); + } else { + UcxKey key; + key.data = &status; + key.len = sizeof(int); + + UcxDlist *list = ucx_map_get(propstat->map, key); + list = ucx_dlist_append(list, prop); + + ucx_map_put(propstat->map, key, list); + } +} + +void propstat_write(Propstat *propstat, sbuf_t *out, int wv) { + if(propstat->okprop) { + sbuf_puts(out, "<D:propstat>\n<D:prop>\n"); + + DAV_FOREACH(prop, propstat->okprop) { + xmlelm_write((XmlElement*)prop->data, out, wv); + } + + sbuf_puts(out, "\n</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n"); + sbuf_puts(out, "</D:propstat>\n"); + } + + for(int i=0;i<propstat->map->size;i++) { + UcxMapElement *elm = &propstat->map->map[i]; + while(elm) { + UcxDlist *proplist = (UcxDlist*)elm->data; + + if(proplist) { + sbuf_puts(out, "<D:propstat>\n<D:prop>\n"); + + DAV_FOREACH(prop, proplist) { + xmlelm_write((XmlElement*)prop->data, out, wv); + } + + sbuf_puts(out, "\n</D:prop>\n<D:status>"); + + int status = *(int*)elm->key.data; + if(status < 1000 && status > 0) { + char buf[5]; + buf[4] = 0; + sprintf(buf, "%d ", status); + sbuf_puts(out, "HTTP/1.1 "); + sbuf_puts(out, buf); + sbuf_puts(out, (char*)protocol_status_message(status)); + } + + sbuf_puts(out, "</D:status>\n</D:propstat>\n"); + } + + elm = elm->next; + } + } +}
--- a/src/server/webdav/webdav.h Wed May 16 12:47:28 2012 +0200 +++ b/src/server/webdav/webdav.h Thu May 24 12:51:52 2012 +0200 @@ -34,6 +34,8 @@ #include <sys/file.h> #include <sys/stat.h> +#include <inttypes.h> + #include "../ucx/map.h" #include "../ucx/dlist.h" #include "../util/strbuf.h" @@ -51,10 +53,18 @@ typedef struct PropfindRequest PropfindRequest; typedef struct ProppatchRequest ProppatchRequest; typedef struct DavProperty DavProperty; -typedef struct DavPropertyCt DavPropertyCt; + +typedef struct Propstat Propstat; + +typedef struct XmlNs XmlNs; +typedef struct XmlNsMap XmlNsMap; +typedef struct XmlData XmlData; typedef struct XmlElement XmlElement; +typedef uint8_t xmlch_t; + + struct PropfindRequest { Session *sn; Request *rq; @@ -81,7 +91,19 @@ UcxDlist *setProps; /* XmlElement list, set props */ UcxDlist *removeProps; /* DavProperty list, remove props */ + Propstat *propstat; + XmlNsMap *nsmap; + DAVPropertyBackend *backend; + + + sbuf_t *out; +}; + +struct Propstat { + UcxDlist *okprop; /* properties with status 200 OK */ + UcxMap *map; /* all other properties */ + pool_handle_t *pool; }; struct DavProperty { @@ -89,42 +111,66 @@ char *name; }; -struct DavPropertyCt { - char *xmlns; - char *name; - DavPropertyCt **props; - int npr; +struct XmlData { + XmlNsMap *nsmap; + XmlElement *elm; }; struct XmlElement { - char *xmlns; - char *name; - char *content; - int *ctlen; - int *numelm; - XmlElement **elms; + XmlNs *xmlns; + sstr_t name; + void *content; /* xmlch* or UcxDlist* */ + size_t ctlen; /* content string length. If 0, content is a UcxDlist */ +}; + +struct XmlNs { + char *xmlns; + char *prefix; + int nslen; + int prelen; +}; + +struct XmlNsMap { + UcxMap *map; }; /* - * dav_res_propfind_f + * dav_propfind_f * - * Gets all requested properties of a WebDAV resource(file). This function + * Gets all requested properties for a WebDAV resource(file). This function * should add properties with dav_propfind_add_str_prop or * dav_prop_add_xml_prop. Unavailable properties should be added with + * dav_propfind_add_prop_error * * * arg0: property backend * arg1: current PropfindRequest object * arg2: the webdav resource path */ -typedef void(*dav_res_propfind_f)(DAVPropertyBackend*,PropfindRequest*,char*); +typedef void(*dav_propfind_f)(DAVPropertyBackend*,PropfindRequest*,char*); + +/* + * dav_proppatch_f + * + * Sets properties for a WebDAV resource. It should add all properties to the + * propstat object (member of the ProppatchRequest). + * + * arg0: WebDAV backend + * arg1: current ProppatchRequest object + */ +typedef void(*dav_proppatch_f)(DAVPropertyBackend*,ProppatchRequest*); struct DAVPropertyBackend { - dav_res_propfind_f propfind; + dav_propfind_f propfind; + dav_proppatch_f proppatch; }; int webdav_service(pblock *pb, Session *sn, Request *rq); int webdav_put(pblock *pb, Session *sn, Request *rq); +int webdav_delete(pblock *pb, Session *sn, Request *rq); +int webdav_mkcol(pblock *pb, Session *sn, Request *rq); +int webdav_copy(pblock *pb, Session *sn, Request *rq); +int webdav_move(pblock *pb, Session *sn, Request *rq); int webdav_propfind(pblock *pb, Session *sn, Request *rq); int webdav_proppatch(pblock *pb, Session *sn, Request *rq); @@ -160,7 +206,90 @@ DAVPropertyBackend* create_property_backend(); -void dav_rq_propfind(DAVPropertyBackend *b,PropfindRequest *rq ,char *path); +void dav_rq_propfind(DAVPropertyBackend *b, PropfindRequest *rq, char *path); +void dav_rq_proppatch(DAVPropertyBackend *b, ProppatchRequest *rq); + +/*---------------------------------- utils ----------------------------------*/ + +/* + * XmlNsMap + * + * a map containing xml namespaces + * + * key: namespace uri + * value: XmlNs object, containing namespace uri and the prefix + */ + +XmlNsMap* xmlnsmap_create(); + +void xmlnsmap_free(XmlNsMap *map); + +/* + * Puts a namespace in the map. If the namespace is already in the map, the + * available XmlNs object is returned + */ +XmlNs* xmlnsmap_put(XmlNsMap *map, char *ns); + +/* + * Gets a namespace from the map. Returns NULL if the namespace is not in the + * map + */ +XmlNs* xmlnsmap_get(XmlNsMap *map, char *ns); + + + +/* + * XmlElement + * + * representing a xml element + */ + +/* + * adds a xml element to a parent element + */ +void xmlelm_add_child(XmlElement *parent, XmlElement *child); + +/* + * writes an xml element to an output buffer + * if wv is true, it writes the complete value of the element + */ +void xmlelm_write(XmlElement *elm, sbuf_t *out, int wv); + + +/* + * PropstatMap + * + * A map containing multiple propstat objects + * + * key: status code: int + * value: prop list: UcxDlist* (list of XmlElement*) + */ + + +/* + * creates a new Propstat + */ +Propstat* propstat_create(pool_handle_t *pool); + + +/* + * adds a property to the propstat map + * + * propstat: propstat object + * status: http status code + * prop: WebDAV property + */ +void propstat_add(Propstat *propstat, int status, XmlElement *prop); + +/* + * writes the propstat object to an output buffer + * if wv is true, it writes the values of the 'OK' properties + * + * propstat: propstat object + * out: output buffer + * wv: property output mode + */ +void propstat_write(Propstat *propstat, sbuf_t *out, int wv); #ifdef __cplusplus }