src/server/webdav/webdav.c

Fri, 23 Oct 2015 17:28:09 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 23 Oct 2015 17:28:09 +0200
changeset 104
a8acbb12f27c
parent 94
6b15a094d996
child 107
7e81699d1f77
permissions
-rw-r--r--

implemented range requests

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 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/list.h>
#include <ucx/string.h>
#include "../util/pool.h"
#include "../util/pblock.h"
#include "../util/date.h"
#include "../util/util.h"

#include "../daemon/vfs.h"
#include "../daemon/protocol.h"

#include "davparser.h"
#include "parser.h"
#include "persistence.h"

static UcxMap *pmgr_map; // char*, PersistenceManager

int webdav_init(pblock *pb, Session *sn, Request *rq) {
    pmgr_map = ucx_map_new(8);
    PersistenceManager *defaultmgr = create_property_backend();
    ucx_map_cstr_put(pmgr_map, "default", defaultmgr);
    return REQ_PROCEED;
}

void webdav_add_persistence_manager(char *name, PersistenceManager *mgr) {
    if(!pmgr_map) {
        webdav_init(NULL, NULL, NULL);
    }
    ucx_map_cstr_put(pmgr_map, name, mgr);
}

int webdav_setcollection(pblock *pb, Session *sn, Request *rq) {
    //char *name = pblock_findkeyval(pb_key_name, pb);
    char *db = pblock_findval("db", pb);
    
    if(!db) {
        db = "default";
    }
    
    // setup DavCollection
    DavCollection *dav = pool_malloc(sn->pool, sizeof(DavCollection));
    dav->mgr = ucx_map_cstr_get(pmgr_map, db);
    rq->davCollection = dav;
    
    return REQ_NOACTION;
}

int webdav_service(pblock *pb, Session *sn, Request *rq) {
    char *method = pblock_findkeyval(pb_key_method, rq->reqpb);
    if(method == NULL) {
        return REQ_ABORTED;
    }
    
    if(!strcmp(method, "PROPFIND")) {
        return webdav_propfind(pb, sn, rq);
    } else if(!strcmp(method, "PROPPATCH")) {
        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;
}

int webdav_put(pblock *pb, Session *sn, Request *rq) {
    int length = 0;
    
    char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars);
    char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers);
    if(ctlen) {
        length = atoi(ctlen);
    } else {
        /* invalid request */
        printf("invalid request\n");
        return REQ_ABORTED;
    }
    
    printf("PUT length: %d\n", length);
    
    //int status = 201;
    //FILE *out = fopen(ppath, "w");
    
    VFSContext *vfs = vfs_request_context(sn, rq);
    SYS_FILE out = vfs_openWO(vfs, ppath);
    if(out == NULL) {
        fprintf(stderr, "vfs_openWO(%s, \"w\") failed\n", ppath);
        //protocol_status(sn, rq, 500, NULL);
        return REQ_ABORTED;
    }
    
    if(length > 0) {
        size_t len = (length > 4096) ? (4096) : (length);
        char *buffer = pool_malloc(sn->pool, len);
        
        int r;
        int r2 = 0;
        while(r2 < length) {
            r = netbuf_getbytes(sn->inbuf, buffer, len);
            if(r == NETBUF_EOF) {
                break;
            }
            system_fwrite(out, buffer, r);
            
            r2 += r;
        }
        
        pool_free(sn->pool, buffer);
    } else {
        
    }
    vfs_close(out);
    
    protocol_status(sn, rq, 201, 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_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);
    
    VFSContext *vfs = vfs_request_context(sn, rq);
    
    int status = 204;
    
    struct stat st;
    if(vfs_stat(vfs, ppath, &st)) {
        return REQ_ABORTED;
    }
    
    if(!strcmp(uri, "/")) {
        status = 403;
    } else if((st.st_mode & S_IFDIR) == S_IFDIR) {
        if(rmdir(ppath) != 0) {
            /* ERROR */
            status = 403;
        }
    } else {
        if(vfs_unlink(vfs, ppath)) {
            /* ERROR */
            return REQ_ABORTED;
        }
    }
    
    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);
    
    VFSContext *vfs = vfs_request_context(sn, rq);
    if(vfs_mkdir(vfs, ppath)) {
        return REQ_ABORTED;
    }
    
    protocol_status(sn, rq, 201, 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 */

    /* 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);
    if(xml_body == NULL) {
        return REQ_ABORTED;
    }
    xml_body[xml_len] = 0;
    if(!xml_body) {
        /* server error */
        printf("server error\n");
        return REQ_ABORTED;
    }

    /* get request body */
    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 -= r;
    }

    /*
     * get requested properties and initialize some stuff
     */
    DavCollection *collection = rq->davCollection;
    if(!collection) {
        collection = pool_malloc(sn->pool, sizeof(DavCollection));
        collection->mgr = ucx_map_cstr_get(pmgr_map, "default");
    }
    
    PropfindRequest *davrq = dav_parse_propfind2(sn, rq, xml_body, xml_len);  
    davrq->sn = sn;
    davrq->rq = rq;
    davrq->out = sbuf_new(512);
    davrq->persistencemgr = collection->mgr;
    
    /* begin multistatus response */
    char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
    char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars);
    davrq->uri = uri;
    davrq->path = ppath;
    
    VFSContext *vfs = vfs_request_context(sn, rq);
    
    struct stat st;
    if(vfs_stat(vfs, ppath, &st) != 0) {
        return REQ_ABORTED;
    }
    
    // begin propfind
    davrq->isdir = S_ISDIR(st.st_mode);
    davrq->persistencemgr->propfind_begin(davrq->persistencemgr, davrq);
    
    // create the response for the requested resource
    dav_resource_response(davrq, sstr(ppath), sstr(uri));
    
    /*
     * if the requested webdav resource(file) is a directory, we create
     * a response for every child
     */
    if(S_ISDIR(st.st_mode)) {
        VFS_DIR dir = vfs_opendir(vfs, ppath);
        if(dir == NULL) {
            return REQ_ABORTED;
        }
        
        VFS_ENTRY entry;
        while(vfs_readdir(dir, &entry)) {
            sstr_t newpath = util_path_append(sn->pool, ppath, entry.name);
            sstr_t newuri = util_path_append(sn->pool, uri, entry.name); 
            // child response
            dav_resource_response(davrq, newpath, newuri);
        }
    }
    
    // end propfind
    davrq->persistencemgr->propfind_begin(davrq->persistencemgr, davrq);
    
    // end xml
    sbuf_puts(davrq->out, "</D:multistatus>\n");
    
    //printf("%s\n", davrq->out->ptr);
    
    // write xml response header
    sbuf_t *out = sbuf_new(256);
    sbuf_puts(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
    sbuf_puts(out, "<D:multistatus");
    UcxMapIterator nsiter = ucx_map_iterator(davrq->nsmap->map);
    XmlNs *ns;
    UCX_MAP_FOREACH(key, ns, nsiter) {
        sbuf_puts(out, " xmlns:");
        sbuf_puts(out, ns->prefix);
        sbuf_puts(out, "=\"");
        sbuf_puts(out, ns->xmlns);
        sbuf_puts(out, "\"");
    }
    sbuf_puts(out, ">\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",
            out->length + davrq->out->length,
            rq->srvhdrs);
    
    http_start_response(sn, rq);
    
    // write content
    size_t nr;
    nr = net_write(sn->csd, out->ptr, out->length);
    nr = net_write(sn->csd, davrq->out->ptr, davrq->out->length);
    
    sbuf_free(out);
    dav_free_propfind(davrq);
    
    return REQ_PROCEED;
}

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;
    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);
    if(xml_body == NULL) {
        return REQ_ABORTED;
    }
    xml_body[xml_len] = 0;
    if(!xml_body) {
        // server error
        printf("server error\n");
        return REQ_ABORTED;
    }

    // get request body
    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 -= r;
    }
    
    /*
     * parse the xml request and create the proppatch object
     */
    DavCollection *collection = rq->davCollection;
    if(!collection) {
        collection = pool_malloc(sn->pool, sizeof(DavCollection));
        collection->mgr = ucx_map_cstr_get(pmgr_map, "default");
    }
    
    ProppatchRequest *davrq = dav_parse_proppatch(sn, rq, xml_body, xml_len);
    davrq->sn = sn;
    davrq->rq = rq;
    davrq->out = sbuf_new(512);
    davrq->backend = collection->mgr;
    davrq->propstat = propstat_create(sn->pool);
    
    
    /* 
     * 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 */
    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:multistatus");
    UcxMapIterator nsiter = ucx_map_iterator(davrq->nsmap->map);
    XmlNs *ns;
    UCX_MAP_FOREACH(key, ns, nsiter) {
        sbuf_puts(davrq->out, " xmlns:");
        sbuf_puts(davrq->out, ns->prefix);
        sbuf_puts(davrq->out, "=\"");
        sbuf_puts(davrq->out, ns->xmlns);
        sbuf_puts(davrq->out, "\"");
    }
    sbuf_puts(davrq->out, ">\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);
    
    dav_free_proppatch(davrq);
    
    return REQ_PROCEED;
}

void dav_resource_response(PropfindRequest *davrq, sstr_t path, sstr_t uri) {
    printf("dav_resource_response %s %s\n", sstrdup(path).ptr, sstrdup(uri).ptr);
    
    sbuf_puts(davrq->out, "<D:response>\n");
    sbuf_puts(davrq->out, "<D:href>");
    sbuf_append(davrq->out, uri);
    sbuf_puts(davrq->out, "</D:href>\n");
    
    if(davrq->persistencemgr->vfs_props) {
        // get some DAV properties from the file system
        dav_rq_propfind(davrq->persistencemgr, davrq, path.ptr);
    }
    davrq->persistencemgr->propfind(davrq->persistencemgr, davrq, path.ptr);
    
    if(davrq->prop) {
        /* 
         * there are some properties written, so we close the
         * prop and propstat tag
         */
        sbuf_puts(davrq->out, "</D:prop>\n");
        sbuf_puts(davrq->out, "<D:status>HTTP/1.1 200 OK</D:status>\n");
        sbuf_puts(davrq->out, "</D:propstat>\n");
    }
    
    if(davrq->notFoundProps != NULL) {
        sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n");
        DAV_FOREACH(elm, davrq->notFoundProps) {
            DavProperty *prop = (DavProperty*)elm->data;
            sbuf_put(davrq->out, '<');
            sbuf_puts(davrq->out, prop->xmlns->prefix);
            sbuf_put(davrq->out, ':');
            
            sbuf_puts(davrq->out, prop->name);
            sbuf_puts(davrq->out, " />\n");
        }
        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");
    }
    
    sbuf_puts(davrq->out, "</D:response>\n");
    
    /* reset */
    davrq->prop = 0;
    davrq->notFoundProps = NULL;
    davrq->forbiddenProps = NULL;
    
}

void dav_propfind_add_str_prop(
        PropfindRequest *davrq,
        DavProperty* prop,
        char *str,
        size_t len)
{
    if(!davrq->prop) {
        sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n");
        davrq->prop = 1;
    }
    
    sbuf_put(davrq->out, '<');
    sbuf_puts(davrq->out, prop->xmlns->prefix);
    sbuf_put(davrq->out, ':');
    sbuf_puts(davrq->out, prop->name);
    sbuf_put(davrq->out, '>');
    
    sbuf_append(davrq->out, sstrn(str, len));
    
    sbuf_puts(davrq->out, "</");
    sbuf_puts(davrq->out, prop->xmlns->prefix);
    sbuf_put(davrq->out, ':');
    sbuf_puts(davrq->out, prop->name);
    sbuf_puts(davrq->out, ">\n");
}

void dav_propfind_add_prop_error(
        PropfindRequest *davrq,
        DavProperty *prop,
        int error)
{
    // TODO: different errors
    davrq->notFoundProps = ucx_list_append(davrq->notFoundProps, prop);
}




/* WebDAV Default Backend */
static PersistenceManager dav_file_backend = {
    dav_rq_propfind_begin,
    dav_rq_propfind_end,
    dav_rq_propfind,
    dav_rq_proppatch,
    0
};

PersistenceManager* create_property_backend() {
    return &dav_file_backend;
}

void dav_rq_propfind_begin(PersistenceManager *mgr, PropfindRequest *rq) {
    
}

void dav_rq_propfind_end(PersistenceManager *mgr, PropfindRequest *rq) {
    
}

void dav_rq_propfind(PersistenceManager *b, PropfindRequest *rq ,char *path) {
    struct stat st;
    if(stat(path, &st) != 0) {
        perror("dav_be_propfind");
        fprintf(stderr, "Cannot get stat of file: %s\n", path);
    }
    
    if(rq->allprop) {
        DavProperty prop;
        prop.xmlns = xmlnsmap_get(rq->nsmap, "DAV:");
        
        prop.name = "resourcetype";
        if(S_ISDIR(st.st_mode)) {
            dav_propfind_add_str_prop(rq, &prop, "<D:collection/>", 15);
        } else {
            dav_propfind_add_str_prop(rq, &prop, NULL, 0);
        }
        
        if(!S_ISDIR(st.st_mode)) {
            prop.name = "getcontentlength";
            char buf[32];
            size_t n = snprintf(buf, 32, "%jd", st.st_size);
            dav_propfind_add_str_prop(rq, &prop, buf, n);
        }
        
        prop.name = "getlastmodified";

        sstr_t s = date_format_http(st.st_mtime, rq->sn->pool);
        dav_propfind_add_str_prop(rq, &prop, s.ptr, s.length);
        
        prop.name = "creationdate";
        s = date_format_iso8601(st.st_ctime, rq->sn->pool);
        dav_propfind_add_str_prop(rq, &prop, s.ptr, s.length);
        
        return;
    }
    
    DAV_FOREACH(elm, rq->properties) {
        DavProperty *prop = (DavProperty*)elm->data;
        
        char *s = prop->name;
        if(!strcmp(s, "resourcetype")) {
            if(S_ISDIR(st.st_mode)) {
                dav_propfind_add_str_prop(rq, prop, "<D:collection/>", 15);
            } else {
                dav_propfind_add_str_prop(rq, prop, NULL, 0);
            }
        } else if(!strcmp(s, "getcontentlength") && !S_ISDIR(st.st_mode)) {
            char buf[32];
            size_t n = snprintf(buf, 32, "%jd", st.st_size);
            dav_propfind_add_str_prop(rq, prop, buf, n);
        } else if(!strcmp(s, "getlastmodified")) {
            sstr_t s = date_format_http(st.st_mtime, rq->sn->pool);
            dav_propfind_add_str_prop(rq, prop, s.ptr, s.length);
        } else if(!strcmp(s, "creationdate")) {
            sstr_t s = date_format_iso8601(st.st_ctime, rq->sn->pool);
            dav_propfind_add_str_prop(rq, prop, s.ptr, s.length);
        } else {
            dav_propfind_add_prop_error(rq, prop, 404);
        }
    }
}

void dav_rq_proppatch(PersistenceManager *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(pool_handle_t *pool) {
    XmlNsMap *map = pool_malloc(pool, sizeof(XmlNsMap));
    UcxMap *uxm = ucx_map_new(16); // TODO: use pool for map
    if(map == NULL || uxm == NULL) {
        return NULL;
    }
    map->map = uxm;
    map->pool = pool;
    map->num = 0;
    
    // create DAV: namespace
    XmlNs *ns = pool_malloc(map->pool, sizeof(XmlNs));
    ns->xmlns = "DAV:";
    ns->prefix = "D";
    ns->nslen = 4;
    ns->prelen = 1;
    
    ucx_map_cstr_put(uxm, "DAV:", ns);
    
    return map;
}

void xmlnsmap_free(XmlNsMap *map) {
    ucx_map_free(map->map);
}

XmlNs* xmlnsmap_put(XmlNsMap *map, char *ns) {
    if(!ns) {
        return NULL;
    }
    
    XmlNs *xmlns = xmlnsmap_get(map, ns);
    if(xmlns != NULL) {
        return xmlns;
    }
    
    xmlns = pool_malloc(map->pool, sizeof(XmlNs));
    if(xmlns == NULL) {
        return NULL;
    }
    
    sstr_t newns = sstrdup(sstr(ns));
    
    xmlns->xmlns = newns.ptr;
    xmlns->nslen = newns.length;
    
    xmlns->prefix = pool_calloc(map->pool, 1, 8);
    xmlns->prelen = snprintf(xmlns->prefix, 7, "x%d", map->num);
    
    ucx_map_cstr_put(map->map, ns, xmlns); // TODO: check return value
    map->num++;
    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_list_append(parent->content, child);
    }
}

void xmlelm_write(XmlElement *elm, Buffer *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, sstr(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, (UcxList*)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, sstr(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, sstr(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_list_append(propstat->okprop, prop);
    } else {
        UcxKey key;
        key.data = &status;
        key.len = sizeof(int);

        UcxList *list = ucx_map_get(propstat->map, key);
        list = ucx_list_append(list, prop);

        ucx_map_put(propstat->map, key, list);
    }
}

void propstat_write(Propstat *propstat, Buffer *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");
    }
    
    UcxMapIterator iter = ucx_map_iterator(propstat->map);
    UcxList *proplist;
    UCX_MAP_FOREACH(key, proplist, iter) { 
        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*)iter.cur->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");
        }
    }
}

mercurial