src/server/webdav/multistatus.c

Tue, 14 Jan 2020 20:05:18 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 14 Jan 2020 20:05:18 +0100
branch
webdav
changeset 222
5f05e56cb8e2
parent 217
8ed14d76db42
child 223
bbaec8415c10
permissions
-rw-r--r--

create namespace definition lists when properties are added to a multistatus response

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2019 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 "../daemon/session.h"

#include "operation.h"

#include "multistatus.h"

#define MULTISTATUS_BUFFER_LENGTH 2048

Multistatus* multistatus_response(Session *sn, Request *rq) {
    Multistatus *ms = pool_malloc(sn->pool, sizeof(Multistatus));
    if(!ms) {
        return NULL;
    }
    ZERO(ms, sizeof(Multistatus)); 
    ms->response.addresource = multistatus_addresource;
    ms->sn = sn;
    ms->rq = rq;
    ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8);
    if(!ms->namespaces) {
        return NULL;
    }
    if(ucx_map_cstr_put(ms->namespaces, "D", "DAV:")) {
        return NULL;
    }
    return ms;
}

int multistatus_send(Multistatus *ms, SYS_NETFD net) {
    char buffer[MULTISTATUS_BUFFER_LENGTH];
    Writer writer;
    Writer *out = &writer;
    writer_init(out, net, buffer, MULTISTATUS_BUFFER_LENGTH);
    
    
}

WebdavResource * multistatus_addresource(
        WebdavResponse *response,
        const char *path)
{
    Multistatus *ms = (Multistatus*)response;
    MSResponse *res = pool_malloc(ms->sn->pool, sizeof(MSResponse));
    if(!res) {
        return NULL;
    }
    ZERO(res, sizeof(MSResponse));
    
    // set href
    res->resource.href = pool_strdup(response->op->sn->pool, path);
    
    // add resource funcs
    res->resource.addproperty = msresponse_addproperty;
    res->resource.close = msresponse_close;
    
    res->multistatus = ms;
    res->errors = NULL;
    res->resource.isclosed = 0;
    res->closing = 0;
    
    // add new resource to the resource list
    if(ms->current) {
        // before adding a new resource, the current resource must be closed
        if(!ms->current->resource.isclosed) {
            msresponse_close((WebdavResource*)ms->current);
        }
        ms->current->next = res;
    } else {
        ms->first = res;
    }
    ms->current = res;
    
    return (WebdavResource*)res;
}

int msresponse_addproperty(
        WebdavResource *res,
        WebdavProperty *property,
        int status)
{
    MSResponse *response = (MSResponse*)res;
    if(response->resource.isclosed) {
        log_ereport(
                LOG_WARN,
                "%s",
                "webdav: cannot add property to closed response tag");
        return 0;
    }
    
    // some WebdavProperty checks to make sure nothing explodes  
    if(!property->namespace || !property->namespace->href) {
        // error: namespace is required
        log_ereport(
                LOG_FAILURE,
                "%s",
                "webdav: property '%s' has no namespace",
                property->name);
        return 1;
    }
    if(property->nsdef) {
        // error: nsdef MUST be NULL, only fill nsdef in this func
        log_ereport(
                LOG_FAILURE,
                "%s",
                "webdav: property '%s': nsdef must be null",
                property->name);
        return 1;
    }
    
    // add namespace of this property to the namespace map
    // the namespace map will be used for global namespace definitions
    if(property->namespace->prefix) {
        char *ns = ucx_map_cstr_get(
                response->multistatus->namespaces,
                (const char*)property->namespace->prefix);
        if(!ns) {
            // prefix is not in use -> we can add the namespace to the ns map
            int err = ucx_map_cstr_put(
                    response->multistatus->namespaces,
                    (const char*)property->namespace->prefix,
                    property->namespace->href);
            if(err) {
                return 1; // OOM
            }
        } else if(strcmp((char*)property->namespace->href, ns)) {
            // global namespace != local namespace
            // therefore we need a namespace definition in this element
            
            if(webdav_property_add_nsdef(
                    property,
                    response->multistatus->sn->pool,
                    (const char*)property->namespace->prefix,
                    (const char*)property->namespace->href))
            {
                return 1; // OOM
            }          
        }
    }
    
    // error properties will be added to a separate list
    if(status != 200) { 
        return msresponse_addproperror(response, property, status);
    }
    
    // add all namespaces used by this property to the nsdef list
    if(property->vtype == WS_VALUE_XML_NODE) {
        // iterate over xml tree and collect all namespaces
        
        // TODO: implement
    } else if(property->vtype == WS_VALUE_XML_DATA) {
        // xml data contains a list of all used namespaces
        WebdavNSList *nslist = property->value.data->namespaces;
        while(nslist) {
            // only add the namespace to the definitions list, if it isn't
            // property namespace, because the prop ns is already added
            // to the element's def list or global definitions list
            if(strcmp(
                    (const char*)nslist->namespace->prefix,
                    (const char*)property->namespace->prefix))
            {
                // ns-prefix != property-prefix -> add ns to nsdef
                if(webdav_property_add_nsdef(
                        property,
                        response->multistatus->sn->pool,
                        (const char*)nslist->namespace->prefix,
                        (const char*)nslist->namespace->href))
                {
                    return 1; // OOM
                }    
            }
            nslist = nslist->next;
        }
    } // other value types don't contain xml namespaces
    
    // add property to the list
    WebdavPList *listelm = pool_malloc(
            response->multistatus->sn->pool,
            sizeof(WebdavPList));
    if(!listelm) {
        return 1;
    }
    
    listelm->property = property;
    listelm->next = NULL;
    
    if(response->plist_end) {
        response->plist_end->next = listelm;
    } else {
        response->plist_begin = listelm;
    }
    response->plist_end = listelm;
    return 0;
}

int msresponse_addproperror(
        MSResponse *response,
        WebdavProperty *property,
        int statuscode)
{
    pool_handle_t *pool = response->multistatus->sn->pool;
    UcxAllocator *a = session_get_allocator(response->multistatus->sn);
    
    // MSResponse contains a list of properties for each status code
    // at first find the list for this status code
    PropertyErrorList *errlist = NULL;
    PropertyErrorList *list = response->errors;
    PropertyErrorList *last = NULL;
    while(list) {
        if(list->status == statuscode) {
            errlist = list;
            break;
        }
        last = list;
        list = list->next;
    }
    
    if(!errlist) {
        // no list available for this statuscode
        PropertyErrorList *newelm = pool_malloc(pool,
                sizeof(PropertyErrorList));
        if(!newelm) {
            return 1;
        }
        newelm->begin = NULL;
        newelm->end = NULL;
        newelm->next = NULL;
        newelm->status = statuscode;
        
        if(last) {
            last->next = newelm;
        } else {
            response->errors = newelm;
        }
        errlist = newelm;
    }
    
    // we have the list -> add the new element
    UcxList *newlistelm = ucx_list_append_a(a, errlist->end, property);
    if(!newlistelm) {
        return 1;
    }
    errlist->end = newlistelm;
    if(!errlist->begin) {
        errlist->begin = newlistelm;
    }
    return 0;
}

int msresponse_close(WebdavResource *res) {
    MSResponse *response = (MSResponse*)res;
    if(response->closing) {
        return 0; // close already in progress
    }
    
    int ret = REQ_PROCEED;
    WebdavOperation *op = response->multistatus->response.op;
    if(webdav_op_propfiond_close_resource(op, res)) {
        ret = REQ_ABORTED;
    }
    
    response->resource.isclosed = TRUE;
    response->closing = FALSE;
    return ret;
}

mercurial