src/server/webdav/webdav.c

changeset 14
b8bf95b39952
parent 13
1fdbf4170ef4
child 15
cff9c4101dd7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/webdav/webdav.c	Sat Jan 14 13:53:44 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webdav.h"
+#include "../ucx/sstring.h"
+#include "../util/pool.h"
+#include "../util/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, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+    sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\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, "</D:multistatus>\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, "<D:");
+        sbuf_puts(davrq->out, prop->name);
+        sbuf_puts(davrq->out, ">");
+        sbuf_puts(davrq->out, str);
+        sbuf_puts(davrq->out, "</D:");
+        sbuf_puts(davrq->out, prop->name);
+        sbuf_puts(davrq->out, ">\n");
+    }
+    return 0;
+}
+
+void dav_create_response(PropfindRequest *davrq) {
+    sbuf_puts(davrq->out, "<D:response>\n");
+
+    sbuf_puts(davrq->out, "<D:href>");
+    sbuf_puts(davrq->out, davrq->uri);
+    sbuf_puts(davrq->out, "</D:href>\n");
+
+    sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n");
+    
+    ucx_dlist_foreach(
+            davrq->properties,
+            (ucx_callback)dav_foreach_reqprop,
+            davrq);
+
+    sbuf_puts(davrq->out, "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n");
+    sbuf_puts(davrq->out, "</D:propstat>\n");
+
+    /* 404 props */
+    sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n");
+    UcxDlist *dl = davrq->notFoundProps;
+    while(dl != NULL) {
+        DavProperty *nfp = dl->data;
+        sbuf_puts(davrq->out, "<D:");
+        sbuf_puts(davrq->out, nfp->name);
+        sbuf_puts(davrq->out, " />\n");
+        dl = dl->next;
+    }
+    sbuf_puts(davrq->out, "</D:prop>\n");
+    sbuf_puts(davrq->out, "<D:status>HTTP/1.1 404 Not Found</D:status>\n");
+    sbuf_puts(davrq->out, "</D:propstat>\n");
+
+    /* end */
+    sbuf_puts(davrq->out, "</D:response>\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;
+}

mercurial