src/server/webdav/webdav.c

Mon, 13 Feb 2012 13:49:49 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 13 Feb 2012 13:49:49 +0100
changeset 21
627b09ee74e4
parent 20
7b235fa88008
child 24
1a7853a4257e
permissions
-rw-r--r--

New configuration loader

/*
 * 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/string.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 */
        printf("invalid request\n");
        return REQ_ABORTED;
    }

    xml_body = pool_malloc(sn->pool, xml_len + 1);
    xml_body[xml_len] = 0;
    if(!xml_body) {
        /* server error */
        printf("server error\n");
        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