src/server/test/webdav.c

Thu, 31 Oct 2019 10:26:35 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 31 Oct 2019 10:26:35 +0100
branch
webdav
changeset 211
2160585200ac
child 212
d7e7ea9c6bc6
permissions
-rw-r--r--

add propfind/proppatch parser and first iteration of the new webdav api

/*
 * 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"

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;
    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;
    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_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];
    
    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;
}

mercurial