add semi functional pg propfind handler webdav

Sun, 24 Apr 2022 18:35:44 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 24 Apr 2022 18:35:44 +0200
branch
webdav
changeset 306
e03737cea6e2
parent 305
4db64fe30588
child 307
8787cb5ebab3

add semi functional pg propfind handler

doc/development/postgresql_vfs.sql file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/pgtest.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/pgtest.h file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/webdav.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/webdav.h file | annotate | diff | comparison | revisions
src/server/public/webdav.h file | annotate | diff | comparison | revisions
src/server/test/webdav.c file | annotate | diff | comparison | revisions
src/server/test/webdav.h file | annotate | diff | comparison | revisions
src/server/webdav/operation.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.c file | annotate | diff | comparison | revisions
--- a/doc/development/postgresql_vfs.sql	Thu Apr 21 17:16:49 2022 +0200
+++ b/doc/development/postgresql_vfs.sql	Sun Apr 24 18:35:44 2022 +0200
@@ -14,3 +14,16 @@
     unique(parent_id, nodename)
 );
 
+create table Property (
+	property_id       serial   primary key,
+	resource_id       int      references Resource(resource_id) on delete cascade,
+	xmlns             text     not null,
+	pname             text     not null,
+	pvalue            text          
+);
+
+create type property_name as (
+ xmlns     text,
+ name      text
+);
+
--- a/src/server/plugins/postgresql/pgtest.c	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/plugins/postgresql/pgtest.c	Sun Apr 24 18:35:44 2022 +0200
@@ -27,19 +27,27 @@
  */
 
 #include <stdio.h>
+#include <inttypes.h>
 
 #include "../../util/util.h"
 #include "../../test/testutils.h"
+#include "../../test/webdav.h"
 #include "../../public/nsapi.h"
+#include "../../public/webdav.h"
+#include "../../webdav/webdav.h"
 
 #include <ucx/string.h>
 #include <ucx/utils.h>
+#include <ucx/buffer.h>
 
 #include "pgtest.h"
 #include "vfs.h"
+#include "webdav.h"
 
 #include <libpq-fe.h>
 
+#include <libxml/tree.h>
+
 
 static char *pg_connstr = "postgresql://localhost/test1";
 static int abort_pg_tests = 0;
@@ -83,6 +91,10 @@
         ucx_test_register(suite, test_pg_vfs_unlink);
         ucx_test_register(suite, test_pg_vfs_rmdir);
         
+        ucx_test_register(suite, test_pg_webdav_create_from_resdata);
+        ucx_test_register(suite, test_pg_prepare_tests);
+        ucx_test_register(suite, test_pg_webdav_propfind);
+        
         PGresult *result = PQexec(test_connection, "BEGIN");
         PQclear(result);
     }
@@ -331,3 +343,137 @@
     
     testutil_destroy_session(sn);
 }
+
+/* ----------------------------- WebDAV tests ----------------------------- */
+
+
+static WebdavBackend* create_test_pgdav(Session *sn, Request *rq) {
+    return pg_webdav_create_from_resdata(sn, rq, &resdata);
+}
+
+UCX_TEST(test_pg_webdav_create_from_resdata) {
+    Session *sn = testutil_session();
+    Request *rq = testutil_request(sn->pool, "PROPFIND", "/");
+    
+    UCX_TEST_BEGIN;
+    
+    WebdavBackend *dav = create_test_pgdav(sn, rq);
+    UCX_TEST_ASSERT(dav, "cannot create pg dav backend");
+    
+    UCX_TEST_END;
+}
+
+UCX_TEST(test_pg_prepare_tests) {
+    Session *sn = testutil_session();
+    Request *rq = testutil_request(sn->pool, "PUT", "/");
+    rq->vfs = create_test_pgvfs(sn, rq);
+    VFSContext *vfs = vfs_request_context(sn, rq);
+    
+    UCX_TEST_BEGIN;
+    
+    vfs_mkdir(vfs, "/propfind");
+    SYS_FILE f1;
+    
+    int64_t res1_id, res2_id;
+    
+    f1 = vfs_open(vfs, "/propfind/res1", O_WRONLY|O_CREAT);
+    UCX_TEST_ASSERT(f1, "res1 create failed");
+    res1_id = ((PgFile*)f1->data)->resource_id;
+    vfs_close(f1);
+    
+    f1 = vfs_open(vfs, "/propfind/res2", O_WRONLY|O_CREAT);
+    UCX_TEST_ASSERT(f1, "res2 create failed");
+    res2_id = ((PgFile*)f1->data)->resource_id;
+    vfs_close(f1);
+    
+    f1 = vfs_open(vfs, "/propfind/res3", O_WRONLY|O_CREAT);
+    UCX_TEST_ASSERT(f1, "res3 create failed");
+    vfs_close(f1);
+    
+    // 2 properties for res1
+    char idstr[32];
+    snprintf(idstr, 32, "%" PRId64, res1_id);
+    const char* params[1] = { idstr };
+    PGresult *result = PQexecParams(
+            test_connection,
+            "insert into Property(resource_id, xmlns, pname, pvalue) values ($1, 'http://example.com/', 'test', 'testvalue');",
+            1,     // number of parameters
+            NULL,
+            params, // parameter value
+            NULL,
+            NULL,
+            0);    // 0: result in text format
+    
+    UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1");
+    PQclear(result);
+    
+    result = PQexecParams(
+            test_connection,
+            "insert into Property(resource_id, xmlns, pname, pvalue) values ($1, 'http://example.com/', 'prop2', 'value2');",
+            1,     // number of parameters
+            NULL,
+            params, // parameter value
+            NULL,
+            NULL,
+            0);    // 0: result in text format
+    
+    UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1");
+    PQclear(result);
+    
+    // 1 property for res2
+    snprintf(idstr, 32, "%" PRId64, res2_id);
+    result = PQexecParams(
+            test_connection,
+            "insert into Property(resource_id, xmlns, pname, pvalue) values ($1, 'http://example.com/', 'test', 'res2test');",
+            1,     // number of parameters
+            NULL,
+            params, // parameter value
+            NULL,
+            NULL,
+            0);    // 0: result in text format
+    
+    UCX_TEST_ASSERT(PQresultStatus(result) == PGRES_COMMAND_OK, "cannot create property 1");
+    PQclear(result);
+    
+    UCX_TEST_END;
+    
+    testutil_destroy_session(sn);
+}
+
+UCX_TEST(test_pg_webdav_propfind) {
+    Session *sn;
+    Request *rq; 
+    TestIOStream *st;
+    pblock *pb;
+    
+    UCX_TEST_BEGIN;
+    
+    // test data:
+    //
+    // /propfind/
+    // /propfind/res1     (2 properties)
+    // /propfind/res2     (1 property)
+    // /propfind/res3
+    
+    int ret;
+    // Test 1
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind", PG_TEST_PROPFIND1);
+    rq->davCollection = create_test_pgdav(sn, rq);
+    
+    ret = webdav_propfind(pb, sn, rq);
+    
+    UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (1) failed");
+    
+    xmlDoc *doc = xmlReadMemory(
+            st->buf->space, st->buf->size, NULL, NULL, 0);
+    UCX_TEST_ASSERT(doc, "propfind1: response is not valid xml");
+    
+    printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space);
+    
+    
+    testutil_destroy_session(sn);
+    xmlFreeDoc(doc);
+    testutil_iostream_destroy(st);
+    
+    UCX_TEST_END;
+}
--- a/src/server/plugins/postgresql/pgtest.h	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/plugins/postgresql/pgtest.h	Sun Apr 24 18:35:44 2022 +0200
@@ -29,6 +29,26 @@
 UCX_TEST(test_pg_vfs_unlink);
 UCX_TEST(test_pg_vfs_rmdir);
 
+UCX_TEST(test_pg_webdav_create_from_resdata);
+UCX_TEST(test_pg_prepare_tests);
+UCX_TEST(test_pg_webdav_propfind);
+
+
+/* --------------------------- PROPFIND --------------------------- */
+
+#define PG_TEST_PROPFIND1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
+        <D:propfind xmlns:D=\"DAV:\"> \
+            <D:prop> \
+                <D:displayname/> \
+                <D:getcontentlength/> \
+                <D:getcontenttype/> \
+                <D:getlastmodified/> \
+                <D:resourcetype/> \
+                <D:getetag/> \
+            </D:prop> \
+        </D:propfind>"
+
+
 #ifdef __cplusplus
 }
 #endif
--- a/src/server/plugins/postgresql/webdav.c	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/plugins/postgresql/webdav.c	Sun Apr 24 18:35:44 2022 +0200
@@ -27,6 +27,11 @@
  */
 
 #include "webdav.h"
+#include "vfs.h"
+
+#include "../../util/util.h"
+
+#include <libxml/tree.h>
 
 static WebdavBackend pg_webdav_backend = {
     pg_dav_propfind_init,
@@ -43,6 +48,162 @@
     NULL
 };
 
+
+/*
+ * SQL Queries
+ */
+
+// propfind with depth = 0
+// params: $1: resource_id
+static const char *sql_propfind_allprop_depth0 = "\
+select\n\
+    NULL as ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+where r.resource_id = $1;";
+
+// propfind with depth = 0 for specific properties
+// params: $1: resource_id
+static const char *sql_propfind_depth0 = "\
+select\n\
+    NULL as ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
+   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+where r.resource_id = $1;";
+
+// propfind with depth = 1
+// params: $1: resource_id
+static const char *sql_propfind_allprop_depth1 = "\
+select\n\
+    NULL ass ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+where r.resource_id = $1 or r.parent_id = $1\n\
+order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
+
+
+// propfind with depth = 1 for specific properties
+// params: $1: resource_id
+static const char *sql_propfind_depth1 = "\
+select\n\
+    NULL as ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from Resource r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
+   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+where r.resource_id = $1 or r.parent_id = $1\n\
+order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
+
+// recursive propfind
+// params: $1: resource_id
+static const char *sql_propfind_allprop_recursive = "\
+with recursive resolvepath as (\n\
+    select\n\
+        '' as ppath,\n\
+        *\n\
+    from Resource\n\
+    where resource_id = $1 \n\
+    union\n\
+    select\n\
+        p.ppath || '/' || r.nodename,\n\
+        r.*\n\
+    from Resource r\n\
+    inner join resolvepath p on r.parent_id = p.resource_id\n\
+    )\n\
+select\n\
+    r.ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+    from resolvepath r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+order by replace(ppath, '/', chr(1)), resource_id;";
+
+// recursive propfind for specific properties
+// params: $1: resource_id
+//         $2: array of property xmlns
+//         $3: array of property names
+static const char *sql_propfind_recursive = "\
+with recursive resolvepath as (\n\
+    select\n\
+        '' as ppath,\n\
+        *\n\
+    from Resource\n\
+    where resource_id = $1 \n\
+    union\n\
+    select\n\
+        p.ppath || '/' || r.nodename,\n\
+        r.*\n\
+    from Resource r\n\
+    inner join resolvepath p on r.parent_id = p.resource_id\n\
+    )\n\
+select\n\
+    r.ppath,\n\
+    r.resource_id,\n\
+    r.parent_id,\n\
+    r.nodename,\n\
+    r.iscollection,\n\
+    r.lastmodified,\n\
+    r.creationdate,\n\
+    r.contentlength,\n\
+    p.xmlns,\n\
+    p.pname,\n\
+    p.pvalue\n\
+from resolvepath r\n\
+left join Property p on r.resource_id = p.resource_id\n\
+inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
+   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+order by replace(ppath, '/', chr(1)), resource_id;";
+
 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) {
     // resourcepool is required
     char *resource_pool = pblock_findval("resourcepool", pb);
@@ -69,7 +230,7 @@
     *webdav = pg_webdav_backend;
     
     PgWebdavBackend *instance = pool_malloc(sn->pool, sizeof(PgWebdavBackend));
-    if(instance) {
+    if(!instance) {
         pool_free(sn->pool, webdav);
         return NULL;
     }
@@ -91,10 +252,75 @@
         const char *path,
         WebdavPList **outplist)
 {
+    PgWebdavBackend *pgdav = rq->dav->instance;
+    
+    // check if the resource exists
+    int64_t parent_id;
+    int64_t resource_id;
+    const char *resourcename;
+    WSBool iscollection;
+    int res_errno = 0;
+    int err = pg_resolve_path(
+            pgdav->connection,
+            path,
+            &parent_id,
+            &resource_id,
+            NULL, // OID
+            &resourcename,
+            &iscollection,
+            NULL, // stat
+            &res_errno);
+    
+    if(err) {
+        if(res_errno == ENOENT) {
+            protocol_status(rq->sn, rq->rq, PROTOCOL_NOT_FOUND, NULL);
+        }
+        return 1;
+    }
+    
+    // choose sql query
+    const char *query = NULL;
+    if(!iscollection || rq->depth == 0) {
+        query = rq->allprop ? sql_propfind_allprop_depth0 : sql_propfind_depth0;
+    } else if(rq->depth == 1) {
+        query = rq->allprop ? sql_propfind_allprop_depth1 : sql_propfind_depth1;
+    } else if(rq->depth == -1) {
+        query = rq->allprop ? sql_propfind_allprop_recursive : sql_propfind_recursive;
+    } else {
+        log_ereport(LOG_FAILURE, "%s", "pg_dav_propfind_init: invalid depth");
+        return 1;
+    }
+    
+    // get all resources and properties
+    char resource_id_str[32];
+    snprintf(resource_id_str, 32, "%" PRId64, resource_id);
+    
+    const char* params[1] = { resource_id_str };
+    PGresult *result = PQexecParams(
+            pgdav->connection,
+            sql_propfind_allprop_recursive,
+            1,     // number of parameters
+            NULL,
+            params, // parameter value
+            NULL,
+            NULL,
+            0);    // 0: result in text format
+    int nrows = PQntuples(result);
+    if(nrows < 1) {
+        PQclear(result);
+        return 1;
+    }
+    
     PgPropfind *pg = pool_malloc(rq->sn->pool, sizeof(PgPropfind));
     rq->userdata = pg;
     
-    return 1;
+    pg->path = path;
+    pg->resource_id = resource_id;
+    pg->vfsproperties = webdav_vfs_properties(outplist, TRUE, 0);
+    pg->result = result;
+    pg->nrows = nrows;
+    
+    return 0;
 }
 
 int pg_dav_propfind_do(
@@ -104,11 +330,103 @@
 	WebdavResource *resource,
 	struct stat *s)
 {
-    return 1;
+    PgPropfind *pg = rq->userdata;
+    pool_handle_t *pool = rq->sn->pool;
+    PGresult *result = pg->result;
+    WebdavVFSProperties vfsprops = pg->vfsproperties;
+    
+    WSBool vfsprops_set = 0;
+    int64_t current_resource_id = pg->resource_id;
+    for(int r=0;r<pg->nrows;r++) {
+        // columns:
+        //  0: path
+        //  1: resource_id
+        //  2: parent_id
+        //  3: nodename
+        //  4: iscollection
+        //  5: lastmodified
+        //  6: creationdate
+        //  7: contentlength
+        //  8: property xmlns
+        //  9: property name
+        // 10: property value
+        
+        char *path = PQgetvalue(result, r, 0);
+        char *res_id = PQgetvalue(result, r, 1);
+        int64_t resource_id;
+        if(!util_strtoint(res_id, &resource_id)) {
+            log_ereport(LOG_FAILURE, "pg_dav_propfind_do: cannot convert resource_id '%s' to int", res_id);
+            return 1;
+        }
+        
+        char *nodename = PQgetvalue(result, r, 3);
+        if(resource_id != current_resource_id) {
+            // new resource
+            resource = response->addresource(response, nodename);
+            vfsprops_set = FALSE;
+            current_resource_id = resource_id;
+        }
+        
+        // standard webdav properties
+        if(!vfsprops_set) {
+            if(vfsprops.getresourcetype) {
+                char *iscollection = PQgetvalue(result, r, 4);
+                if(iscollection && iscollection[0] == 't') {
+                    resource->addproperty(resource, webdav_resourcetype_collection(), 200);
+                } else {
+                    resource->addproperty(resource, webdav_resourcetype_empty(), 200);
+                }
+            }
+            if(vfsprops.getlastmodified) {
+                char *lastmodified = PQgetvalue(result, r, 5);
+            }
+            if(vfsprops.creationdate) {
+                char *creationdate = PQgetvalue(result, r, 6);
+            }
+            if(vfsprops.getcontentlength) {
+                char *contentlength = PQgetvalue(result, r, 7);
+            }
+            if(vfsprops.getetag) {
+                
+            }
+            
+            
+            vfsprops_set = TRUE;
+        }
+        
+        // dead properties
+        if(!PQgetisnull(result, r, 9)) {
+            char *xmlns = PQgetvalue(result, r, 8);
+            char *pname = PQgetvalue(result, r, 9);
+            char *pvalue = PQgetvalue(result, r, 10);
+            
+            WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty));
+            property->lang = NULL;
+            property->name = pool_strdup(pool, pname);
+            
+            xmlNs *namespace = pool_malloc(pool, sizeof(xmlNs));
+            memset(namespace, 0, sizeof(struct _xmlNs));
+            namespace->href = pool_strdup(pool, xmlns);
+            namespace->prefix = "zx1";
+            property->namespace = namespace;
+            
+            property->vtype = WS_VALUE_TEXT;
+            property->value.text.str = pool_strdup(pool, pvalue);
+            property->value.text.length = strlen(pvalue);
+            
+            resource->addproperty(resource, property, 200);
+        }
+        
+        
+    }
+    
+
+    
+    return 0;
 }
 
 int pg_dav_propfind_finish(WebdavPropfindRequest *rq) {
-    return 1;
+    return 0;
 }
 
 int pg_dav_proppatch_do(
--- a/src/server/plugins/postgresql/webdav.h	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/plugins/postgresql/webdav.h	Sun Apr 24 18:35:44 2022 +0200
@@ -44,8 +44,11 @@
 } PgWebdavBackend;
     
 typedef struct PgPropfind {
-    ResourceData *pg_resource;
-    PGconn *connection;
+    const char *path;
+    int64_t resource_id;
+    WebdavVFSProperties vfsproperties;
+    PGresult *result;
+    int nrows;
 } PgPropfind;
 
 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb);
--- a/src/server/public/webdav.h	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/public/webdav.h	Sun Apr 24 18:35:44 2022 +0200
@@ -160,6 +160,8 @@
 struct WebdavPropfindRequest {
     Session *sn;
     Request *rq;
+    
+    WebdavBackend *dav;
 
     void *doc;
       
@@ -189,6 +191,8 @@
     Session *sn;
     Request *rq;
     
+    WebdavBackend *dav;
+    
     void *doc;
     
     WebdavPList *set;
@@ -422,6 +426,8 @@
 void webdav_plist_iterator_remove_current(WebdavPListIterator *i);
 
 WSNamespace* webdav_dav_namespace(void);
+WebdavProperty* webdav_resourcetype_collection(void);
+WebdavProperty* webdav_resourcetype_empty(void);
 WebdavProperty* webdav_dav_property(
         pool_handle_t *pool,
         const char *name);
--- a/src/server/test/webdav.c	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/test/webdav.c	Sun Apr 24 18:35:44 2022 +0200
@@ -1086,12 +1086,13 @@
     testutil_destroy_session(sn);
 }
 
-static void init_test_webdav_method(
+void init_test_webdav_method(
         Session **out_sn,
         Request **out_rq,
         TestIOStream **out_st,
         pblock **out_pb,
         const char *method,
+        const char *path,
         const char *request_body)
 {
     Session *sn;
@@ -1102,8 +1103,8 @@
     sn = testutil_session();
     rq = testutil_request(sn->pool, method, "/");
     
-    pblock_nvinsert("path", "/", rq->vars);
-    pblock_nvinsert("uri", "/", rq->reqpb);
+    pblock_nvinsert("path", path, rq->vars);
+    pblock_nvinsert("uri", path, rq->reqpb);
     
     st = testutil_iostream(2048, TRUE);
     sn->csd = (IOStream*)st;
@@ -1130,7 +1131,7 @@
     
     int ret;
     // Test 1
-    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", TEST_PROPFIND1);
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/", TEST_PROPFIND1);
     
     ret = webdav_propfind(pb, sn, rq);
     
@@ -1147,7 +1148,7 @@
     testutil_iostream_destroy(st);
     
     // Test2
-    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", TEST_PROPFIND2);
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/", TEST_PROPFIND2);
     
     ret = webdav_propfind(pb, sn, rq);
     
@@ -1420,7 +1421,7 @@
     
     int ret;
     // Test 1
-    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPPATCH", TEST_PROPPATCH2);
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPPATCH", "/", TEST_PROPPATCH2);
     rq->davCollection = &backend1;
     ret = webdav_proppatch(pb, sn, rq);
     
@@ -1584,7 +1585,7 @@
     // behaves the same for both operations
     // the only difference are the callbacks
     
-    init_test_webdav_method(&sn, &rq, &st, &pb, "MKCOL", NULL);
+    init_test_webdav_method(&sn, &rq, &st, &pb, "MKCOL", "/", NULL);
     VFS *testvfs = testvfs_create(sn);
     rq->vfs = testvfs;
     
@@ -1667,7 +1668,7 @@
     TestIOStream *st;
     pblock *pb;
     
-    init_test_webdav_method(&sn, &rq, &st, &pb, "DELETE", NULL);
+    init_test_webdav_method(&sn, &rq, &st, &pb, "DELETE", "/", NULL);
     rq->vfs = testvfs_create(sn);
     
     WebdavBackend dav1;
@@ -1735,7 +1736,7 @@
     
     const char *content_const = "Hello World";
     
-    init_test_webdav_method(&sn, &rq, &st, &pb, "PUT", content_const);
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PUT", "/", content_const);
     rq->vfs = testvfs_create(sn);
     
     UCX_TEST_BEGIN;
--- a/src/server/test/webdav.h	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/test/webdav.h	Sun Apr 24 18:35:44 2022 +0200
@@ -38,6 +38,15 @@
 extern "C" {
 #endif
     
+void init_test_webdav_method(
+        Session **out_sn,
+        Request **out_rq,
+        TestIOStream **out_st,
+        pblock **out_pb,
+        const char *method,
+        const char *path,
+        const char *request_body);
+    
 UCX_TEST(test_webdav_plist_add);
 UCX_TEST(test_webdav_plist_size);
     
--- a/src/server/webdav/operation.c	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/webdav/operation.c	Sun Apr 24 18:35:44 2022 +0200
@@ -324,6 +324,7 @@
                 op->sn->pool,
                 sizeof(WebdavProppatchRequest));
         memcpy(req, orig_request, sizeof(WebdavProppatchRequest));
+        req->dav = dav;
         req->set = set;
         req->setcount = set_count;
         req->remove = remove;
--- a/src/server/webdav/webdav.c	Thu Apr 21 17:16:49 2022 +0200
+++ b/src/server/webdav/webdav.c	Sun Apr 24 18:35:44 2022 +0200
@@ -89,7 +89,14 @@
     return ucx_map_cstr_put(webdav_type_map, name, webdavCreate);
 }
 
+static WSBool webdav_is_initialized = FALSE;
+
 int webdav_init(pblock *pb, Session *sn, Request *rq) {
+    if(webdav_is_initialized) {
+        return REQ_NOACTION;
+    }
+    webdav_is_initialized = TRUE;
+    
     webdav_type_map = ucx_map_new(8);
     if(!webdav_type_map) {
         return REQ_ABORTED;
@@ -297,6 +304,7 @@
         // use new plist after previous init (or orig. plist in the first run)
         pReq->properties = newProp;
         pReq->propcount = newPropCount;
+        pReq->dav = davList;
         
         // add new WebdavPropfindRequest object to list for later use
         requestObjects = ucx_list_append_a(a, requestObjects, pReq);
@@ -979,6 +987,14 @@
     return &dav_namespace;
 }
 
+WebdavProperty* webdav_resourcetype_collection(void) {
+    return &dav_resourcetype_collection;
+}
+
+WebdavProperty* webdav_resourcetype_empty(void) {
+    return &dav_resourcetype_empty;
+}
+
 WebdavProperty* webdav_dav_property(
         pool_handle_t *pool,
         const char *name)

mercurial