src/server/test/webdav.c

Sun, 29 Dec 2019 22:39:35 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 29 Dec 2019 22:39:35 +0100
branch
webdav
changeset 215
68e824ba4a4f
parent 214
4d7ac67a1c14
child 216
ce2866ec97f6
permissions
-rw-r--r--

add plist iterator

/*
 * 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 <string.h>

#include "testutils.h"

#include "../webdav/requestparser.h"
#include "../webdav/webdav.h"
#include "../webdav/multistatus.h"

#include "webdav.h"

static int test_init(
        Session **out_sn,
        Request **out_rq,
        WebdavPropfindRequest **out_propfind,
        const char *xml)
{
    Session *sn = testutil_session();
    Request *rq = testutil_request(sn->pool, "PROPFIND", "/");
    
    int error = 0;
    
    WebdavPropfindRequest *propfind = propfind_parse(
            sn,
            rq,
            xml,
            strlen(xml),
            &error);
    
    if(error) {
        return 1;
    }
    
    if(!propfind || !propfind->properties) {
        return 1;
    }
    
    *out_sn = sn;
    *out_rq = rq;
    *out_propfind = propfind;
    return 0;
}


UCX_TEST(test_propfind_parse) {
    Session *sn = testutil_session();
    Request *rq = testutil_request(sn->pool, "PROPFIND", "/");
    
    UCX_TEST_BEGIN
    
    int error = 0;
    
    //
    // ----------------- TEST_PROPFIND1 -----------------
    // test basic propfind request
    WebdavPropfindRequest *p1 = propfind_parse(
            sn,
            rq,
            TEST_PROPFIND1,
            strlen(TEST_PROPFIND1),
            &error);
    
    UCX_TEST_ASSERT(p1, "p1 is NULL");
    UCX_TEST_ASSERT(p1->properties, "p1: no props");
    UCX_TEST_ASSERT(!p1->allprop, "p1: allprop is TRUE");
    UCX_TEST_ASSERT(!p1->propname, "p1: propname is TRUE");
    UCX_TEST_ASSERT(p1->propcount == 6, "p1: wrong propcount");
    
    // property 1: DAV:displayname
    WebdavPList *elm = p1->properties;
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "displayname"),
            "p1: property 1 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "DAV:"),
            "p1: property 1 has wrong namespace");
    
    // property 2: DAV:getcontentlength
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p1: property 2 missing");
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "getcontentlength"),
            "p1: property 2 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "DAV:"),
            "p1: property 2 has wrong namespace");
    
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p1: property 3 missing");
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p1: property 4 missing");
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p1: property 5 missing");
    
    // property 6: DAV:getetag
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p1: property 6 missing");
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "getetag"),
            "p1: property 6 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "DAV:"),
            "p1: property 6 has wrong namespace");
    UCX_TEST_ASSERT(!elm->next, "p1: should not have property 7");
    
    //
    // ----------------- TEST_PROPFIND2 -----------------
    // test with multiple namespaces
    WebdavPropfindRequest *p2 = propfind_parse(
            sn,
            rq,
            TEST_PROPFIND2,
            strlen(TEST_PROPFIND2),
            &error);
    
    UCX_TEST_ASSERT(p2, "p2 is NULL");
    UCX_TEST_ASSERT(p2->properties, "p2: no props");
    UCX_TEST_ASSERT(!p2->allprop, "p2: allprop is TRUE");
    UCX_TEST_ASSERT(!p2->propname, "p2: propname is TRUE");
    
    // property 1: DAV:resourcetype
    elm = p2->properties;
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "resourcetype"),
            "p2: property 1 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "DAV:"),
            "p2: property 1 has wrong namespace");
    
    // property 2: X:testprop
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p2: property 2 missing");
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "testprop"),
            "p2: property 2 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "http://example.com/"),
            "p2: property 2 has wrong namespace");
    
    // property 3: X:name
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p2: property 3 missing");
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "name"),
            "p2: property 3 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "http://example.com/"),
            "p2: property 3 has wrong namespace");
    
    // property 4: Z:testprop
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p2: property 4 missing");
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "testprop"),
            "p2: property 4 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "testns"),
            "p2: property 4 has wrong namespace");
    
    
    //
    // ----------------- TEST_PROPFIND3 -----------------
    // test allprop
    WebdavPropfindRequest *p3 = propfind_parse(sn, rq, TEST_PROPFIND3, strlen(TEST_PROPFIND3), &error);
    
    UCX_TEST_ASSERT(p3, "p3 is NULL");
    UCX_TEST_ASSERT(!p3->properties, "p2: has props");
    UCX_TEST_ASSERT(p3->allprop, "p2: allprop is FALSE");
    UCX_TEST_ASSERT(!p3->propname, "p2: propname is TRUE");
    UCX_TEST_ASSERT(p3->propcount == 0, "p2: wrong propcount");
    
    
    //
    // ----------------- TEST_PROPFIND4 -----------------
    // test propname
    WebdavPropfindRequest *p4 = propfind_parse(sn, rq, TEST_PROPFIND4, strlen(TEST_PROPFIND4), &error);
    
    UCX_TEST_ASSERT(p4, "p4 is NULL");
    UCX_TEST_ASSERT(!p4->properties, "p2: has props");
    UCX_TEST_ASSERT(!p4->allprop, "p2: allprop is TRUE");
    UCX_TEST_ASSERT(p4->propname, "p2: propname is FALSE");
    
    
    //
    // ----------------- TEST_PROPFIND5 -----------------
    // test duplicate check
    WebdavPropfindRequest *p5 = propfind_parse(sn, rq, TEST_PROPFIND5, strlen(TEST_PROPFIND5), &error);
    
    UCX_TEST_ASSERT(p5, "p5 is NULL");
    UCX_TEST_ASSERT(p5->properties, "p5: no props");
    UCX_TEST_ASSERT(!p5->allprop, "p5: allprop is TRUE");
    UCX_TEST_ASSERT(!p5->propname, "p5: propname is TRUE");
    UCX_TEST_ASSERT(p5->propcount == 4, "p5: wrong propcount");
    
    // property 1: DAV:displayname
    elm = p5->properties;
    UCX_TEST_ASSERT(elm, "p5: property 1 missing");
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "displayname"),
            "p5: property 1 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "DAV:"),
            "p5: property 1 has wrong namespace");
    
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p5: property 2 missing");
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p5: property 3 missing");
    
    // property 4: DAV:resourcetype
    elm = elm->next;
    UCX_TEST_ASSERT(elm, "p5: property 4 missing");
    UCX_TEST_ASSERT(
            !strcmp(elm->property->name, "resourcetype"),
            "p5: property 4 has wrong name");
    UCX_TEST_ASSERT(
            !strcmp((char*)elm->property->namespace->href, "DAV:"),
            "p5: property 4 has wrong namespace");
    
    
    //
    // ----------------- TEST_PROPFIND6 -----------------
    // test prop/allprop mix
    WebdavPropfindRequest *p6 = propfind_parse(sn, rq, TEST_PROPFIND6, strlen(TEST_PROPFIND6), &error);
    
    UCX_TEST_ASSERT(p6, "p5 is NULL");
    UCX_TEST_ASSERT(!p6->properties, "p5: has props");
    UCX_TEST_ASSERT(p6->allprop, "p5: allprop is FALSE");
    UCX_TEST_ASSERT(!p6->propname, "p5: propname is TRUE");
    UCX_TEST_ASSERT(p6->propcount == 0, "p5: wrong propcount");
    
    UCX_TEST_END
            
    pool_destroy(sn->pool);
}

UCX_TEST(test_proppatch_parse) {
    Session *sn = testutil_session();
    Request *rq = testutil_request(sn->pool, "PROPPATCH", "/");
    
    UCX_TEST_BEGIN
    int error = 0;
    
    WebdavProppatchRequest *p1 = proppatch_parse(sn, rq, TEST_PROPPATCH1, strlen(TEST_PROPPATCH1), &error);
    
    UCX_TEST_ASSERT(p1->set, "p1: missing set props");
    UCX_TEST_ASSERT(!p1->remove, "p1: has remove props");
    UCX_TEST_ASSERT(p1->setcount == 2, "p1: wrong setcount");
    UCX_TEST_ASSERT(p1->set->next, "p1: set plist broken");
    UCX_TEST_ASSERT(!p1->set->next->next, "p1: set plist has no end");
    UCX_TEST_ASSERT(p1->set->property, "p1: missing property ptr in plist");
    UCX_TEST_ASSERT(
            !strcmp(p1->set->property->name, "test"),
            "p1: wrong property 1 name");
    
    WebdavProppatchRequest *p2 = proppatch_parse(sn, rq, TEST_PROPPATCH2, strlen(TEST_PROPPATCH2), &error);
    
    UCX_TEST_ASSERT(p2->set, "p2: missing set props");
    UCX_TEST_ASSERT(p2->remove, "p2: missing remove props");
    UCX_TEST_ASSERT(p2->setcount == 4, "p2: wrong setcount");
    UCX_TEST_ASSERT(p2->removecount == 1, "p2: wrong removecount");
    
    UCX_TEST_ASSERT(
            !strcmp((char*)p2->set->property->namespace->href, "http://example.com/"),
            "p2: set property 1: wrong namespace");
    UCX_TEST_ASSERT(
            !strcmp(p2->set->property->name, "a"),
            "p2: set property 1: wrong name");
    WSXmlNode *p2set1 = p2->set->property->value.node;
    UCX_TEST_ASSERT(
            p2set1->type == WS_NODE_TEXT,
            "p2: set property 1: wrong type");
    UCX_TEST_ASSERT(
            p2set1->content,
            "p2: set property 1: no text");
    UCX_TEST_ASSERT(
            !strcmp((char*)p2set1->content, "test"),
            "p2: set property 1: wrong value");
    
    WSXmlNode *p2set3 = p2->set->next->next->property->value.node;
    UCX_TEST_ASSERT(p2set3, "p2: set property 3 missing");
    UCX_TEST_ASSERT(
            p2set3->type == WS_NODE_TEXT,
            "p2: set property 3: wrong type");
    UCX_TEST_ASSERT(
            p2set3->next,
            "p2: set property 3: missing element X:name");
    
    UCX_TEST_ASSERT(
            xmlHasProp(p2set3->next, BAD_CAST"test"),
            "p2: set property 3: missing attribute 'test'");
    
    UCX_TEST_ASSERT(
            xmlHasProp(p2set3->next, BAD_CAST"abc"),
            "p2: set property 3: missing attribute 'abc");
    
    xmlChar *value1 = xmlGetProp(p2set3->next, BAD_CAST"test");
    UCX_TEST_ASSERT(
            !strcmp((char*) value1, "test1"),
            "p2: set property 3: wrong attribute value 1");
    xmlFree(value1);
    
    xmlChar *value2 = xmlGetProp(p2set3->next, BAD_CAST"abc");
    UCX_TEST_ASSERT(
            !strcmp((char*) value2, "def"),
            "p2: set property 3: wrong attribute value 2");
    xmlFree(value2);
    
    UCX_TEST_ASSERT(
            !strcmp(p2->remove->property->name, "e"),
            "p2: wrong remove property");
    
    UCX_TEST_END
            
    pool_destroy(sn->pool);
}

UCX_TEST(test_lock_parse) {
    Session *sn = testutil_session();
    Request *rq = testutil_request(sn->pool, "LOCK", "/");
    
    UCX_TEST_BEGIN
    int error = 0;
    
    WebdavLockRequest *l1 = lock_parse(sn, rq, TEST_LOCK1, strlen(TEST_LOCK1), &error);
    
    UCX_TEST_ASSERT(l1, "l1 is NULL");
    UCX_TEST_ASSERT(l1->type == WEBDAV_LOCK_WRITE, "l1: wrong type");
    UCX_TEST_ASSERT(l1->scope == WEBDAV_LOCK_SHARED, "l1: wrong scope");
    UCX_TEST_ASSERT(l1->owner, "l1: owner is NULL");
    UCX_TEST_ASSERT(!strcmp((char*)l1->owner->content, "User"), "l1: wrong owner");
    
    UCX_TEST_END
    
    pool_destroy(sn->pool);
}

UCX_TEST(test_rqbody2buffer) {
    Session *sn;
    Request *rq;
    
    UCX_TEST_BEGIN;
    //
    // TEST 1
    sn = testutil_session();
    rq = testutil_request(sn->pool, "PUT", "/");
    testutil_request_body(sn, rq, "Hello World!", 12);
    
    UcxBuffer *b1 = rqbody2buffer(sn, rq);
    UCX_TEST_ASSERT(b1->size == 12, "b1: wrong size");
    UCX_TEST_ASSERT(!memcmp(b1->space,"Hello World!",12), "b1: wrong content");
    
    ucx_buffer_free(b1);
    testutil_destroy_session(sn);
    
    //
    // TEST 2
    size_t len1 = 25000;
    unsigned char *body1 = malloc(len1);
    for(int i=0;i<len1;i++) {
        body1[i] = i;
    }
    sn = testutil_session();
    rq = testutil_request(sn->pool, "PUT", "/");
    testutil_request_body(sn, rq, (char*)body1, len1);
    
    UcxBuffer *b2 = rqbody2buffer(sn, rq);
    UCX_TEST_ASSERT(b2->size == len1, "b2: wrong size");
    UCX_TEST_ASSERT(!memcmp(b2->space, body1, len1), "b2: wrong content");
    
    ucx_buffer_free(b2);
    testutil_destroy_session(sn);
    
    UCX_TEST_END;
}

UCX_TEST(test_webdav_plist_iterator) {
    Session *sn;
    Request *rq;
    WebdavPropfindRequest *propfind;
    
    UCX_TEST_BEGIN;
    UCX_TEST_ASSERT(!test_init(&sn, &rq, &propfind, TEST_PROPFIND1), "init failed");
    
    WebdavPList *properties = propfind->properties;
    size_t count = 0;
    
    WebdavPListIterator i = webdav_plist_iterator(&properties);
    WebdavPList *cur;
    while(webdav_plist_iterator_next(&i, &cur)) {
        switch(i.index) {
            case 0: {
                UCX_TEST_ASSERT(!strcmp(cur->property->name, "displayname"), "wrong property 1");
                break;
            }
            case 1: {
                UCX_TEST_ASSERT(!strcmp(cur->property->name, "getcontentlength"), "wrong property 2");
                break;
            }
            case 2: {
                UCX_TEST_ASSERT(!strcmp(cur->property->name, "getcontenttype"), "wrong property 3");
                break;
            }
            case 3: {
                UCX_TEST_ASSERT(!strcmp(cur->property->name, "getlastmodified"), "wrong property 4");
                break;
            }
            case 4: {
                UCX_TEST_ASSERT(!strcmp(cur->property->name, "resourcetype"), "wrong property 5");
                break;
            }
            case 5: {
                UCX_TEST_ASSERT(!strcmp(cur->property->name, "getetag"), "wrong property 6");
                break;
            }
        }
        count++;
    }
    
    UCX_TEST_ASSERT(count == propfind->propcount, "wrong count");
    
    
    UCX_TEST_END;
    testutil_destroy_session(sn);
}

UCX_TEST(test_msresponse_addproperty) {
    Session *sn = testutil_session();
    Request *rq = testutil_request(sn->pool, "PROPFIND", "/");
    Multistatus *ms = multistatus_response(sn, rq);
    MSResponse *r;
    
    UCX_TEST_BEGIN;
    
    r = (MSResponse*)ms->response.addresource((WebdavResponse*)ms, "/");
    
    WebdavProperty p1;
    WebdavProperty p[16];
    const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"};
    
    // init test data
    p1.namespace = webdav_dav_namespace();
    p1.lang = NULL;
    p1.name = "test1";
    p1.value.data = NULL;
    p1.vtype = 0;
    
    for(int i=0;i<8;i++) {
        p[i].namespace = webdav_dav_namespace();
        p[i].name = names[i];
        p[i].lang = NULL;
        p[i].value.node = NULL;
        p[1].vtype = 0;
    }
    
    UCX_TEST_ASSERT(!r->plist_begin && !r->plist_end, "plist not empty");
    
    r->resource.addproperty((WebdavResource*)r, &p1, 200);
    UCX_TEST_ASSERT(r->plist_begin, "!plist_begin");
    UCX_TEST_ASSERT(r->plist_begin == r->plist_end, "plist begin != end");
    
    r->resource.addproperty((WebdavResource*)r, &p[0], 404);
    r->resource.addproperty((WebdavResource*)r, &p[1], 404);
    r->resource.addproperty((WebdavResource*)r, &p[2], 403);
    r->resource.addproperty((WebdavResource*)r, &p[3], 403);
    r->resource.addproperty((WebdavResource*)r, &p[4], 403);
    r->resource.addproperty((WebdavResource*)r, &p[5], 403);
    r->resource.addproperty((WebdavResource*)r, &p[6], 500);
    
    UCX_TEST_ASSERT(r->plist_begin == r->plist_end, "plist begin != end");
    
    UCX_TEST_ASSERT(r->errors, "no prop errors");
    UCX_TEST_ASSERT(r->errors->next, "no second error code");
    UCX_TEST_ASSERT(r->errors->next->next, "no third error code");
    UCX_TEST_ASSERT(!r->errors->next->next->next, "too many error codes");
    
    UCX_TEST_ASSERT(ucx_list_size(r->errors->begin) == 2, "404 list size != 2");
    UCX_TEST_ASSERT(ucx_list_size(r->errors->next->begin) == 4, "403 list size != 4");
    UCX_TEST_ASSERT(ucx_list_size(r->errors->next->next->begin) == 1, "500 list size != 1");
    
    UCX_TEST_END;
}

/* ----------------------------- Test Backends --------------------------*/

// backend2
static int backend2_propfind_init(
        WebdavPropfindRequest *propfind,
        const char *path,
        WebdavPList **outPList)
{
    return 0;
}

static int backend2_propfind_do(
            WebdavPropfindRequest *propfind,
            WebdavResponse *response,
            VFS_DIR parent,
            const char *path,
            struct stat *s)
{
    return 0;
}

static int backend2_propfind_finish(WebdavPropfindRequest *propfind) {
    return 0;
}

static WebdavBackend backend2 = {
    backend2_propfind_init,
    backend2_propfind_do,
    backend2_propfind_finish,
    0,
    NULL
};

// backend1
static int backend1_propfind_init(
        WebdavPropfindRequest *propfind,
        const char *path,
        WebdavPList **outPList)
{
    WebdavPList *plist = *outPList;
    WebdavProperty *p = plist->property;
    if(!strcmp(p->name, "displayname")) {
        plist->next->prev = NULL;
        *outPList = plist->next; // remove first item from plist
    } else {
        return 1;
    }
    
    return 0;
}

static int backend1_propfind_do(
            WebdavPropfindRequest *propfind,
            WebdavResponse *response,
            VFS_DIR parent,
            const char *path,
            struct stat *s)
{
    return 0;
}

static int backend1_propfind_finish(WebdavPropfindRequest *propfind) {
    return 0;
}

WebdavBackend backend1 = {
    backend1_propfind_init,
    backend1_propfind_do,
    backend1_propfind_finish,
    0,
    &backend2
};

/* ----------------------------------------------------------------------*/

UCX_TEST(test_webdav_propfind_init) {
    Session *sn;
    Request *rq;
    WebdavPropfindRequest *propfind;
    UCX_TEST_BEGIN;
    UCX_TEST_ASSERT(!test_init(&sn, &rq, &propfind, TEST_PROPFIND1), "init failed");
    
    UcxList *requests = NULL;
    int err = webdav_propfind_init(&backend1, propfind, "/", &requests);
    
    UCX_TEST_ASSERT(!err, "webdav_propfind_init failed");
    UCX_TEST_ASSERT(requests, "request list is empty");
    UCX_TEST_ASSERT(ucx_list_size(requests), "request list has wrong size");
    
    WebdavPropfindRequest *p1 = requests->data;
    WebdavPropfindRequest *p2 = requests->next->data;
    
    // backend1 removes the first property from the plist
    // backend2 should have one property less 
    
    UCX_TEST_ASSERT(p1 && p2, "missing requests objects");
    UCX_TEST_ASSERT(p1 != p2, "request objects equal");
    UCX_TEST_ASSERT(p1->properties != p2->properties, "plists equal");
    UCX_TEST_ASSERT(p1->propcount == p2->propcount + 1, "first property not removed");
    
    UCX_TEST_END;
    
    pool_destroy(sn->pool);
}

mercurial