diff -r 34aa8001ea53 -r 1fdbf4170ef4 src/server/webdav.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav.c Sun Jan 08 15:46:47 2012 +0100 @@ -0,0 +1,256 @@ +/* + * 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 +#include +#include + +#include "webdav.h" +#include "sstring.h" +#include "pool.h" +#include "pblock.h" + +#include "davparser.h" + +int webdav_service(pblock *pb, Session *sn, Request *rq) { + /* TODO: + * Dies ist die Implementierung für PROPFIND. Es sollte für jede webdav- + * Methode eine eigene Service-Funktion geben. Solange die anderen + * Methoden nicht implementiert werden, behandelt webdav_service nur + * PROPFIND. + */ + + /* TODO: clean up if errors occurs */ + + /* Get request body which contains the webdav XML request */ + char *xml_body; + size_t xml_len = 0; + + char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers); + if(ctlen) { + xml_len = atoi(ctlen); + } else { + /* invalid request */ + return REQ_ABORTED; + } + + xml_body = pool_malloc(sn->pool, xml_len + 1); + xml_body[xml_len] = 0; + if(!xml_body) { + /* server error */ + return REQ_ABORTED; + } + + /* TODO: bug with multi reads */ + int r = 0; + char *xb = xml_body; + size_t xl = xml_len; + while((r = netbuf_getbytes(sn->inbuf, xb, xl)) != NETBUF_EOF) { + xb += r; + xl -= xml_len; + } + + PropfindRequest *davrq = dav_parse_propfind(sn, rq, xml_body, xml_len); + davrq->sn = sn; + davrq->rq = rq; + davrq->propertyBackend = create_property_backend(); + davrq->notFoundProps = NULL; + davrq->forbiddenProps = NULL; + davrq->out = sbuf_new(512); + + /* write header */ + sbuf_puts(davrq->out, "\n"); + sbuf_puts(davrq->out, "\n"); + + /* get stat of file */ + char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); + char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); + + struct stat st; + if(stat(ppath, &st) != 0) { + perror("webdav_service: stat"); + return REQ_ABORTED; + } + /* TODO: check for more modes */ + if(S_ISDIR(st.st_mode)) { + DIR *dir = opendir(ppath); + if(dir == NULL) { + protocol_status(sn, rq, 500, NULL); + printf("webdav_service: DIR is null\n"); + return REQ_ABORTED; + } + + struct dirent *f; + while((f = readdir(dir)) != NULL) { + if(strcmp(f->d_name, ".") == 0 || strcmp(f->d_name, "..") == 0) { + continue; + } + + sstr_t filename = sstr(f->d_name); + sstr_t _path = sstr(ppath); + sstr_t _uri = sstr(uri); + + sstr_t newuri; + newuri.length = filename.length + _uri.length; + newuri.ptr = alloca(newuri.length + 1); + newuri = sstrncat(2, newuri, _uri, filename); + + sstr_t newpath; + newpath.length = _path.length + filename.length; + newpath.ptr = alloca(newpath.length + 1); + newpath = sstrncat(2, newpath, _path, filename); + + davrq->path = newpath.ptr; + davrq->uri = newuri.ptr; + dav_create_response(davrq); + } + } + davrq->path = ppath; + davrq->uri = uri; + dav_create_response(davrq); + + sbuf_puts(davrq->out, "\n"); + + /* send buffer to client */ + 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); + + protocol_status(sn, rq, 207, NULL); + http_start_response(sn, rq); + + net_write(sn->csd, davrq->out->ptr, davrq->out->length); + + return REQ_PROCEED; +} + +int dav_foreach_reqprop(UcxDlist *list, PropfindRequest *davrq) { + DavProperty *prop = list->data; + int error = 0; + + char *str = davrq->propertyBackend->get_property( + davrq->propertyBackend, + davrq, + davrq->path, + prop->xmlns, + prop->name, + &error); + if(str == NULL) { + UcxDlist **dl = NULL; + if(error == 404) { + dl = &davrq->notFoundProps; + } else { + dl = &davrq->forbiddenProps; + } + *dl = ucx_dlist_append(*dl, prop); + } else { + //printf("dav property: {%s|%s::%s\n", prop->xmlns, prop->name, str); + sbuf_puts(davrq->out, "out, prop->name); + sbuf_puts(davrq->out, ">"); + sbuf_puts(davrq->out, str); + sbuf_puts(davrq->out, "out, prop->name); + sbuf_puts(davrq->out, ">\n"); + } + return 0; +} + +void dav_create_response(PropfindRequest *davrq) { + sbuf_puts(davrq->out, "\n"); + + sbuf_puts(davrq->out, ""); + sbuf_puts(davrq->out, davrq->uri); + sbuf_puts(davrq->out, "\n"); + + sbuf_puts(davrq->out, "\n\n"); + + ucx_dlist_foreach( + davrq->properties, + (ucx_callback)dav_foreach_reqprop, + davrq); + + sbuf_puts(davrq->out, "\nHTTP/1.1 200 OK\n"); + sbuf_puts(davrq->out, "\n"); + + /* 404 props */ + sbuf_puts(davrq->out, "\n\n"); + UcxDlist *dl = davrq->notFoundProps; + while(dl != NULL) { + DavProperty *nfp = dl->data; + sbuf_puts(davrq->out, "out, nfp->name); + sbuf_puts(davrq->out, " />\n"); + dl = dl->next; + } + sbuf_puts(davrq->out, "\n"); + sbuf_puts(davrq->out, "HTTP/1.1 404 Not Found\n"); + sbuf_puts(davrq->out, "\n"); + + /* end */ + sbuf_puts(davrq->out, "\n"); + +} + +char* dav_get_property( + DAVPropertyBackend *b, + PropfindRequest *davrq, + char *path, + char *xmlns, + char *name, + int *error) +{ + DAVDefaultBackend *be = (DAVDefaultBackend*)b; + *error = 200; + + if(strcmp(name, "getcontentlength") == 0) { + struct stat s; + if(stat(davrq->path, &s) != 0) { + *error = 403; /* really? */ + return NULL; + } + if(S_ISDIR(s.st_mode)) { + *error = 404; + return NULL; + } + char *buf = pool_malloc(davrq->sn->pool, 24); + sprintf(buf, "%d", s.st_size); + return buf; + } + + *error = 404; + return NULL; +} + +DAVPropertyBackend* create_property_backend() { + DAVDefaultBackend *backend = malloc(sizeof(DAVDefaultBackend)); + backend->backend.get_property = dav_get_property; + backend->path = NULL; + backend->s = 0; + return (DAVPropertyBackend*)backend; +}