integrate wsgidav into test suite dav-2 tip

Sat, 24 Jan 2026 13:56:19 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 24 Jan 2026 13:56:19 +0100
branch
dav-2
changeset 902
06fa328989ee
parent 901
271103d11d55

integrate wsgidav into test suite

test/Makefile file | annotate | diff | comparison | revisions
test/main.c file | annotate | diff | comparison | revisions
test/webdav.c file | annotate | diff | comparison | revisions
test/webdav.h file | annotate | diff | comparison | revisions
test/webdav_resource.c file | annotate | diff | comparison | revisions
test/webdav_resource.h file | annotate | diff | comparison | revisions
test/wsgidav.c file | annotate | diff | comparison | revisions
test/wsgidav.h file | annotate | diff | comparison | revisions
test/wsgidav/testrepo/hello.txt file | annotate | diff | comparison | revisions
test/wsgidav/wsgidav.yaml file | annotate | diff | comparison | revisions
--- a/test/Makefile	Fri Jan 23 17:38:59 2026 +0100
+++ b/test/Makefile	Sat Jan 24 13:56:19 2026 +0100
@@ -29,9 +29,12 @@
 include ../config.mk
 
 TEST_SRC  = main.c
+TEST_SRC += wsgidav.c
 TEST_SRC += base64.c
 TEST_SRC += crypto.c
 TEST_SRC += utils.c
+TEST_SRC += webdav.c
+TEST_SRC += webdav_resource.c
 
 
 TEST_OBJ = $(TEST_SRC:%.c=../build/test/%$(OBJ_EXT))
--- a/test/main.c	Fri Jan 23 17:38:59 2026 +0100
+++ b/test/main.c	Sat Jan 24 13:56:19 2026 +0100
@@ -29,10 +29,15 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "wsgidav.h"
+
 #include "base64.h"
 #include "crypto.h"
 #include "utils.h"
 
+#include "webdav.h"
+#include "webdav_resource.h"
+
 #include <cx/test.h>
 
 int test_main(int argc, char **argv);
@@ -47,11 +52,18 @@
 }
 #endif
 
-int test_main(int argc, char **argv) { 
-    printf("libidav tests\n");
-    printf("-------------\n\n");
+int test_main(int argc, char **argv) {   
+    int dav_client_tests = wsgidav_is_available();
+    if(!dav_client_tests) {
+        printf("wsgidav not available: skipping some libidav webdav tests\n");
+    } else {
+        if(wsgidav_start()) {
+            printf("Error: wsgidav_start failed: skipping some libidav webdav tests\n");
+            dav_client_tests = 0;
+        }
+    }
     
-    CxTestSuite* suite = cx_test_suite_new("libidav");
+    CxTestSuite* suite = cx_test_suite_new("libidav basic");
     
     cx_test_register(suite, test_util_base64decode);
     cx_test_register(suite, test_util_base64decode_len);
@@ -70,9 +82,22 @@
     cx_test_register(suite, test_util_path_normalize);
     cx_test_register(suite, test_util_parent_path);
     
+    CxTestSuite* suite_webdav = cx_test_suite_new("libidav webdav");
+    test_webdav_init();
+    if(dav_client_tests) {
+        cx_test_register(suite_webdav, test_dav_load_webdav);
+    }
+    
     cx_test_run_stdout(suite);
     cx_test_suite_free(suite);
     
+    cx_test_run_stdout(suite_webdav);
+    cx_test_suite_free(suite_webdav);
+    
+    if(dav_client_tests) {
+        wsgidav_stop();
+    }
+    
     return 0;
 }
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/webdav.c	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,40 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 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 "webdav.h"
+
+static DavContext *test_webdav_context;
+
+void test_webdav_init(void) {
+    test_webdav_context = dav_context_new();
+}
+
+DavSession* get_test_webdav_session(void) {
+    DavSession *sn = dav_session_new_auth(test_webdav_context, "http://localhost:8182/", "dav", "testdavutils");
+    return sn;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/webdav.h	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,48 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 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.
+ */
+
+#ifndef TEST_WEBDAV_H
+#define TEST_WEBDAV_H
+
+#include <cx/test.h>
+#include <libidav/webdav.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test_webdav_init(void);
+DavSession* get_test_webdav_session(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TEST_WEBDAV_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/webdav_resource.c	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,47 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 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 "webdav.h"
+#include "webdav_resource.h"
+
+CX_TEST(test_dav_load_webdav) {
+    CX_TEST_DO {
+        DavSession *sn = get_test_webdav_session();
+        DavResource *res = dav_resource_new(sn, "/hello.txt");
+        CX_TEST_ASSERT(res);
+        int ret = dav_load(res);
+        CX_TEST_ASSERT(ret == 0);
+        CX_TEST_ASSERT(res->contentlength > 0);
+        CX_TEST_ASSERT(res->creationdate > 0);
+        CX_TEST_ASSERT(res->lastmodified > 0);
+        CX_TEST_ASSERT(res->exists);
+        CX_TEST_ASSERT(res->contenttype);
+        CX_TEST_ASSERT(!res->iscollection);
+        CX_TEST_ASSERT(res->href);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/webdav_resource.h	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 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.
+ */
+
+#ifndef TEST_WEBDAV_RESOURCE_H
+#define TEST_WEBDAV_RESOURCE_H
+
+#include <cx/test.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CX_TEST(test_dav_load_webdav);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TEST_WEBDAV_RESOURCE_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/wsgidav.c	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,306 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 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 "wsgidav.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+//ifndef _WIN32
+
+#include <unistd.h>
+#include <sys/fcntl.h>
+
+#include <spawn.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <cx/string.h>
+#include <cx/buffer.h>
+#include <cx/streams.h>
+#include <cx/printf.h>
+
+
+// -------------------------- Process Utils --------------------------------
+
+typedef struct {
+    pid_t pid;
+    int in;
+    int out;
+    int err;
+} Process;
+
+static Process process_spawn(const char *exec, char **args) {
+    Process proc = { 0 };
+    
+    int pin[2];
+    int pout[2];
+    int perr[2];
+    
+    // child process stdin/stdout pipes
+    if(pipe(pin)) {
+        perror("pipe");
+        return proc;
+    }
+    if(pipe(pout)) {
+        perror("pipe");
+        close(pin[0]);
+        close(pin[1]);
+    }
+    if(pipe(perr)) {
+        perror("pipe");
+        close(pin[0]);
+        close(pin[1]);
+        close(pout[0]);
+        close(pout[1]);
+    }
+    
+    posix_spawn_file_actions_t actions;
+    posix_spawn_file_actions_init(&actions);
+    posix_spawn_file_actions_adddup2(&actions, pin[0], 0);
+    posix_spawn_file_actions_adddup2(&actions, pout[1], 1);
+    posix_spawn_file_actions_adddup2(&actions, perr[1], 2);
+    
+    // child environment variables
+    char *path = getenv("PATH");
+    if(!path) {
+        path = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin";
+    }
+    
+    cxmutstr env_path = cx_asprintf("PATH=%s", path);
+    
+    char *env[10];
+    env[0] = "LC_ALL=C";
+    env[1] = env_path.ptr;
+    env[2] = NULL; // array must be null-terminated
+    
+    // execute posix_spawn to create a child process
+    pid_t child;
+    int ret = posix_spawn(
+            &child,
+            exec,
+            &actions,
+            NULL,            // attributes
+            args,
+            env);
+    posix_spawn_file_actions_destroy(&actions);
+    
+    close(pin[0]);
+    close(pout[1]);
+    close(perr[1]);
+    
+    if(ret) {
+        // posix spawn failed
+        perror("posix_spawn");
+        // close remaining pipe fds
+        close(pin[1]);
+        close(pout[0]);
+        close(perr[0]);
+    } else {
+        proc.pid = child;
+        proc.in = pin[1];
+        proc.out = pout[0];
+        proc.err = perr[0];
+    }
+    
+    return proc;
+}
+
+int process_close(Process *p) {
+    if(p->pid == 0) {
+        return -1;
+    }
+    close(p->in);
+    close(p->out);
+    int status = -1;
+    waitpid(p->pid, &status, 0);
+    return status;
+}
+
+size_t process_read(void *buf, size_t size, size_t n, Process *p) {
+    ssize_t r = read(p->out, buf, size*n);
+    return r > 0 ? r : 0;
+}
+
+size_t process_err_read(void *buf, size_t size, size_t n, Process *p) {
+    ssize_t r = read(p->err, buf, size*n);
+    return r > 0 ? r : 0;
+}
+
+static cxmutstr read_str(Process *p, cx_read_func readf) {
+    if(p->pid == 0) {
+        return CX_NULLSTR;
+    }
+    CxBuffer buffer;
+    cxBufferInit(&buffer, NULL, NULL, 1024, CX_BUFFER_AUTO_EXTEND);
+    cx_stream_copy(p, &buffer, readf, (cx_write_func)cxBufferWrite);
+    cxBufferTerminate(&buffer);
+    return cx_mutstrn(buffer.space, buffer.size);
+}
+
+cxmutstr process_read_string(Process *p) {
+    return read_str(p, (cx_read_func)process_read);
+}
+
+cxmutstr process_err_read_string(Process *p) {
+    return read_str(p, (cx_read_func)process_err_read);
+}
+
+
+// ------------------------------ wsgidav exec -----------------------------
+
+static char *wsgidav_exec_path;
+static Process wsgidav_process;
+static int runtest_root_dir = 0;
+
+int wsgidav_is_available(void) {
+    // check if the wsgidava binary is available
+    char *args[] = {
+        "which",
+        "wsgidav",
+        NULL
+    };
+    Process p = process_spawn("/usr/bin/which", args);
+    if(p.pid == 0) {
+        return 0;
+    }
+    
+    cxmutstr path = process_read_string(&p);
+    int status = process_close(&p);
+    if(status == 0) {
+        wsgidav_exec_path = cx_strdup(cx_strtrim(path)).ptr;
+    }
+    
+    free(path.ptr);
+    
+    if(status != 0) {
+        return 0;
+    }
+    
+    // check if the wsgidav config file is available
+    struct stat s;
+    if(!stat("test/wsgidav/wsgidav.yaml", &s)) {
+        runtest_root_dir = 1; // dav root directory
+    } else {
+        if(!stat("../test/wsgidav/wsgidav.yaml", &s)) {
+            runtest_root_dir = 2; // build directory
+        } else {
+            fprintf(stderr, "Error: wsgidav found, but the config file wsgidav.yaml was not found\nTry running the tests from the dav root directory\n");
+            return 0;
+        }
+    }
+    
+    if(runtest_root_dir == 1) {
+        if(!stat("build", &s)) {
+            if(!S_ISDIR(s.st_mode)) {
+                fprintf(stderr, "Error: build is not a directory: wsgidav disabled\n");
+                return 0;
+            }
+        } else {
+            fprintf(stderr, "Error: build directory not found: wsgidav disabled\n");
+            return 0;
+        }
+    }
+    
+    return 1;
+}
+
+int wsgidav_start(void) {
+    if(!wsgidav_exec_path) {
+        return 1; // wsgidav_start called but wsgidav_is_available return 0
+    }
+    
+    if(runtest_root_dir == 1) {
+        if(chdir("build")) {
+            perror("chdir");
+            return 1;
+        }
+    }
+    
+    // delete previous testrepo directory
+    char *args1[] = { "rm", "-Rf", "testrepo", NULL };
+    Process p_rm = process_spawn("/bin/rm", args1);
+    cxmutstr err = process_err_read_string(&p_rm);
+    if(process_close(&p_rm) != 0) {
+        cxmutstr s = cx_strtrim(err);
+        fprintf(stderr, "rm failed: %.*s\n", (int)s.length, s.ptr);
+        return 1;
+    }
+    free(err.ptr);
+    
+    // copy testrepo to the build directory
+    char *args2[] = { "cp", "-R", "../test/wsgidav/testrepo", "testrepo", NULL };
+    Process p_cp = process_spawn("/bin/cp", args2);
+    err = process_err_read_string(&p_cp);
+    if(process_close(&p_cp) != 0) {
+        cxmutstr s = cx_strtrim(err);
+        fprintf(stderr, "cp failed: %.*s\n", (int)s.length, s.ptr);
+        return 1;
+    }
+    free(err.ptr);
+    
+    // start wsgidav
+    char *args3[] = { "wsgidav", "-c", "../test/wsgidav/wsgidav.yaml", NULL };
+    wsgidav_process = process_spawn(wsgidav_exec_path, args3);
+    
+    // TODO: parse log or try to connect to localhost
+    sleep(1);
+    
+    return 0;
+}
+
+int wsgidav_stop(void) {
+    if(wsgidav_process.pid == 0) {
+        return 0;
+    }
+    kill(wsgidav_process.pid, SIGKILL);
+    (void)process_close(&wsgidav_process);
+    //if(status > 2) {
+    //    printf("wsgidav exit: %d\n", status);
+    //}
+    return 0;
+}
+
+/*
+#else
+
+int wsgidav_is_available(void) {
+    return 0;
+}
+
+int wsgidav_start(void) {
+    return 1;
+}
+
+int wsgidav_stop(void) {
+    return 1;
+}
+
+#endif
+
+*/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/wsgidav.h	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 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.
+ */
+
+#ifndef WSGIDAV_H
+#define WSGIDAV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int wsgidav_is_available(void);
+int wsgidav_start(void);
+int wsgidav_stop(void);
+    
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WSGIDAV_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/wsgidav/testrepo/hello.txt	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,1 @@
+Hello World!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/wsgidav/wsgidav.yaml	Sat Jan 24 13:56:19 2026 +0100
@@ -0,0 +1,360 @@
+# WsgiDAV configuration file
+#
+# 1. Rename this file to `wsgidav.yaml`.
+# 2. Adjust settings as appropriate.
+# 3. Run `wsgidav` from the same directory or pass file path with `--config` option.
+#
+# See https://wsgidav.readthedocs.io/en/latest/user_guide_configure.html
+#
+# ============================================================================
+# SERVER OPTIONS
+
+#: Run WsgiDAV inside this  WSGI server.
+#: Supported servers:
+#:     cheroot, ext-wsgiutils, gevent, gunicorn, paste, uvicorn, wsgiref
+#: 'wsgiref' and 'ext_wsgiutils' are simple builtin servers that should *not* be
+#: used in production.
+#: All other servers must have been installed before, e.g. `pip install cheroot`.
+#: (The binary MSI distribution already includes 'cheroot'.)
+#: NOTE: Using 'gunicorn' with more than 1 worker can cause problems with the
+#: in-memory and shelve-based lock storage as both are not safe for concurrent
+#: access. (see issue #332) Instead, you can use 'gunicorn' with multiple `threads`
+#: or try the 'redis' based lock storage (#186).
+#: Default: 'cheroot', use the `--server` command line option to change this.
+
+server: cheroot
+
+#: Server specific arguments, passed to the server. For example cheroot:
+#:   https://cheroot.cherrypy.dev/en/latest/pkg/cheroot.wsgi.html#cheroot.wsgi.Server
+# server_args:
+#     max: -1
+#     numthreads: 10
+#     request_queue_size: 5
+#     shutdown_timeout: 5
+#     timeout: 10
+
+# Server hostname (default: localhost, use --host on command line)
+host: 127.0.0.1
+
+# Server port (default: 8080, use --port on command line)
+port: 8182
+
+# Transfer block size in bytes
+block_size: 8192
+
+#: Add the MS-Author-Via Response Header to OPTIONS command to allow editing
+#: with Microsoft Office (default: true)
+add_header_MS_Author_Via: true
+
+hotfixes:
+    #: Handle Microsoft's Win32LastModifiedTime property.
+    #: This is useful only in the case when you copy files from a Windows
+    #: client into a WebDAV share. Windows sends the "last modified" time of
+    #: the file in a Microsoft extended property called "Win32LastModifiedTime"
+    #: instead of the standard WebDAV property "getlastmodified". So without
+    #: this config option set to "True", the "last modified" time of the copied
+    #: file will be "now" instead of its original value.
+    #: The proper solution for dealing with the Windows WebDAV client is to use
+    #: a persistent property manager. This setting is merely a work-around.
+    #: NOTE: Works with Win10, can't work with Win7. Other versions untested.
+    emulate_win32_lastmod: false
+    #: Re-encode PATH_INFO using UTF-8 (falling back to ISO-8859-1).
+    #: This seems to be wrong, since per PEP 3333 PATH_INFO is always ISO-8859-1
+    #: encoded (see https://www.python.org/dev/peps/pep-3333/#unicode-issues).
+    #: However it also seems to resolve errors when accessing resources with
+    #: Chinese characters, for example (see issue #73).
+    re_encode_path_info: true
+    #: Force unquoting of PATH_INFO. This should already be done by the WSGI
+    #: Framework, so this setting should only be used to fix unexpected problems
+    #: there (false fixes issue #8, true fixes issue #228).
+    unquote_path_info: false
+    #: Hotfix for WinXP / Vista: accept 'OPTIONS /' for a 'OPTIONS *'
+    #: (default: false)
+    treat_root_options_as_asterisk: false
+
+
+# ----------------------------------------------------------------------------
+# SSL Support
+
+#: The certificate should match the servers hostname, so the bogus certs will
+#: not work in all scenarios.
+#: (Paths can be absolute or relative to this config file.)
+
+# ssl_certificate: 'wsgidav/server/sample_bogo_server.crt'
+# ssl_private_key: 'wsgidav/server/sample_bogo_server.key'
+# ssl_certificate_chain: null
+
+#: Cheroot server supports 'builtin' and 'pyopenssl' (default: 'builtin')
+# ssl_adapter: 'pyopenssl'
+
+# ----------------------------------------------------------------------------
+
+#: Modify to customize the WSGI application stack.
+#: See here for an example how to add custom middlewares:
+#:   https://wsgidav.readthedocs.io/en/latest/user_guide_configure.html#middleware-stack
+middleware_stack:
+    - wsgidav.mw.cors.Cors
+    # - wsgidav.mw.debug_filter.WsgiDavDebugFilter
+    - wsgidav.error_printer.ErrorPrinter
+    - wsgidav.http_authenticator.HTTPAuthenticator
+    # - wsgidav.mw.impersonator.Impersonator
+    - wsgidav.dir_browser.WsgiDavDirBrowser
+    - wsgidav.request_resolver.RequestResolver  # this must be the last middleware item
+
+# ==============================================================================
+# SHARES
+
+#: Application root, applied before provider mapping shares, e.g. 
+#:   <mount_path>/<share_name>/<res_path>
+#: Set this to the mount point (aka location) when WsgiDAV is running behind a 
+#: reverse proxy.
+#: If set, the mount path must have a leading (but not trailing) slash.
+mount_path: null
+
+#: Route share paths to DAVProvider instances
+#: By default a writable `FilesystemProvider` is assumed, but can be forced
+#: to read-only.
+#: Note that a DomainController may still restrict access completely or prevent
+#: editing depending on authentication.
+#:
+#: The following syntax variants are supported to use FilesystemProvider:
+#:     <share_path>: <folder_path>
+#: or
+#:     <share_path>: { 'root': <folder_path>, 'readonly': <bool> }
+#:
+#: or instantiate an arbitrary custom class:
+#:
+#:     <share_path>: { 'class': <class_path>, args: [<arg>, ...], kwargs: {<arg>: <val>, ...} }
+
+provider_mapping:
+    '/': 'testrepo'
+
+#: Additional configuration passed to `FilesystemProvider(..., fs_opts)`
+fs_dav_provider:
+    #: Mapping from request URL to physical file location, e.g.
+    #: make sure that a `/favicon.ico` URL is resolved, even if a `*.html`
+    #: or `*.txt` resource file was opened using the DirBrowser
+    # shadow_map:
+    #     '/favicon.ico': 'file_path/to/favicon.ico'
+    
+    #: Serve symbolic link files and folders (default: false)
+    follow_symlinks: false
+
+# ==============================================================================
+# AUTHENTICATION
+http_authenticator:
+    #: Allow basic authentication
+    accept_basic: true
+    #: Allow digest authentication
+    accept_digest: true
+    #: true (default digest) or false (default basic)
+    default_to_digest: true
+    #: Header field that will be accepted as authorized user.
+    #: Including quotes, for example: trusted_auth_header = 'REMOTE_USER'
+    trusted_auth_header: null
+    #: Domain controller that is used to resolve realms and authorization.
+    #: Default null: which uses SimpleDomainController and the
+    #: `simple_dc.user_mapping` option below.
+    #: (See http://wsgidav.readthedocs.io/en/latest/user_guide_configure.html
+    #: for details.)
+    domain_controller: null
+    # domain_controller: wsgidav.dc.simple_dc.SimpleDomainController
+    # domain_controller: wsgidav.dc.pam_dc.PAMDomainController
+    # domain_controller: wsgidav.dc.nt_dc.NTDomainController
+
+
+# Additional options for SimpleDomainController only:
+simple_dc:
+    # Access control per share.
+    # These routes must match the provider mapping.
+    # NOTE: Provider routes without a matching entry here, are inaccessible.
+    user_mapping:
+        '*':  # default (used for all shares that are not explicitly listed)
+            'dav':
+                password: 'testdavutils'
+                # Optional: passed to downstream middleware as environ["wsgidav.auth.roles"]
+                # roles: ['editor']
+
+# Additional options for NTDomainController only:
+nt_dc:
+    preset_domain: null
+    preset_server: null
+
+# Additional options for PAMDomainController only:
+pam_dc:
+    service: 'login'
+    encoding: 'utf-8'
+    resetcreds: true
+
+
+# ----------------------------------------------------------------------------
+# User/Group Impersonating
+# (Requires `wsgidav.mw.impersonator.Impersonator`, which is disabled by default.)
+impersonator:
+    # enabling impersonating
+    enable: false
+
+    # custom map WebDAV (HTTP) usernames to Unix usernames
+    # custom_user_mapping:
+    #     leonlee: leo
+    #     jenifer: jenny
+
+    # or, use WebDAV (HTTP) usernames as is
+    custom_user_mapping: null
+
+
+# ----------------------------------------------------------------------------
+# CORS
+# (Requires `wsgidav.mw.cors.Cors`, which is enabled by default.)
+cors:
+    #: List of allowed Origins or '*'
+    #: Default: false, i.e. prevent CORS
+    allow_origin: null
+    # allow_origin: '*'
+    # allow_origin:
+    #   - 'https://example.com'
+    #   - 'https://localhost:8081'
+
+    #: List or comma-separated string of allowed methods (returned as
+    #: response to preflight request)
+    allow_methods:
+    # allow_methods: POST,HEAD
+    #: List or comma-separated string of allowed header names (returned as
+    #: response to preflight request)
+    allow_headers:
+    #   - X-PINGOTHER
+    #: List or comma-separated string of allowed headers that JavaScript in
+    #: browsers is allowed to access.
+    expose_headers:
+    #: Set to true to allow responses on requests with credentials flag set
+    allow_credentials: false
+    #: Time in seconds for how long the response to the preflight request can
+    #: be cached (default: 5)
+    max_age: 600
+    #: Add custom response headers (dict of header-name -> header-value items)
+    #: (This is not related to CORS or required to implement CORS functionality)
+    add_always:
+    #    'X-Foo-Header: 'qux'
+
+# ----------------------------------------------------------------------------
+# Property Manager
+# null: (default) no support for dead properties
+# true: Use wsgidav.prop_man.property_manager.PropertyManager
+#       which is an in-memory property manager (NOT persistent)
+#
+# Example: Use persistent shelve based property manager
+#     property_manager:
+#        class: wsgidav.prop_man.property_manager.ShelvePropertyManager
+#        kwargs:
+#            storage_path: 'wsgidav-props.shelve'
+
+property_manager: null
+
+#: Optional additional live property modification
+#: Note: by default live properties like file size and last-modified time are
+#: read-only, but that can be overridden here if the underlying DAV provider
+#: supports it. For now only the FileSystemProvider supports it and only namely
+#: changes to the last-modified timestamp. Enable it with the mutable_live_props
+#: list as below to allow clients to use the utime system call or e.g. the
+#: touch or cp / rsync commands with the preserve-timestamp flags on a mounted
+#: DAV share.
+#: Please note that the timestamp is set on the actual file or directory, so it
+#: is persistent even for in-memory property managers. It should also be noted
+#: that mutable last-modified may not be compliant with the RFC 4918.
+mutable_live_props:
+    # Enable to allow clients to use e.g. the touch or cp / rsync commands with the
+    # preserve-timestamp flags in a mounted DAV share (may be RFC4918 incompliant)
+    - '{DAV:}getlastmodified'
+
+
+# ----------------------------------------------------------------------------
+# Lock Manager Storage
+#
+# null: No lock support
+# true: (default) shortcut for
+#     lock_storage: wsgidav.lock_man.lock_storage.LockStorageDict
+#
+# Note that the default LockStorageDict works in-memory, so it is
+# NOT persistent.
+#
+# Example: Use persistent shelve based lock storage:
+#     lock_storage:
+#         class: wsgidav.lock_man.lock_storage.LockStorageShelve
+#         kwargs:
+#             storage_path: /path/to/wsgidav_locks.shelve
+#
+# Check the documentation on how to develop custom lock storage.
+
+lock_storage: true
+
+
+# ==============================================================================
+# DEBUGGING
+
+#: Set verbosity level (can be overridden by -v or -q arguments)
+verbose: 3
+
+#: Suppress version info in HTTP response headers and error responses
+suppress_version_info: false
+
+logging:
+    #: Enable logging when using wsgidav in library mode (always on, when running as CLI)
+    enable: null
+    #: Set logging output format
+    #: (see https://docs.python.org/3/library/logging.html#logging.Formatter)
+    logger_date_format: '%H:%M:%S'
+    logger_format: '%(asctime)s.%(msecs)03d - %(levelname)-8s: %(message)s'
+    # Example: Add date,thread id, and logger name:
+    # logger_date_format: '%Y-%m-%d %H:%M:%S'
+    # logger_format: '%(asctime)s.%(msecs)03d - <%(thread)05d> %(name)-27s %(levelname)-8s: %(message)s'
+
+    #: Enable specific module loggers
+    #: E.g. ['lock_manager', 'property_manager', 'http_authenticator', ...]
+    # enable_loggers: ['http_authenticator', ]
+
+    # Enable max. logging for certain http methods
+    # E.g. ['COPY', 'DELETE', 'GET', 'HEAD', 'LOCK', 'MOVE', 'OPTIONS', 'PROPFIND', 'PROPPATCH', 'PUT', 'UNLOCK']
+    debug_methods: []
+
+    # Enable max. logging during  litmus suite tests that contain certain strings
+    # E.g. ['lock_excl', 'notowner_modify', 'fail_cond_put_unlocked', ...]
+    debug_litmus: []
+
+
+# ----------------------------------------------------------------------------
+# WsgiDavDirBrowser
+
+dir_browser:
+    enable: true
+    #: List of fnmatch patterns that will be hidden in the directory listing
+    ignore:
+        - '.DS_Store'  # macOS folder meta data
+        - 'Thumbs.db'  # Windows image previews
+        - '._*'  # macOS hidden data files
+    #: Add a trailing slash to directory URLs (by generating a 301 redirect)
+    directory_slash: true
+    #: Display WsgiDAV icon in header
+    icon: true
+    #: Raw HTML code, appended as footer (true: use a default trailer)
+    response_trailer: true
+    #: Display the name and realm of the authenticated user (or 'anomymous')
+    show_user: true
+    show_logout: true
+    #: Send <dm:mount> response if request URL contains '?davmount'
+    #: (See https://tools.ietf.org/html/rfc4709)
+    davmount: true
+    #: Add a 'Mount' link at the top of the listing
+    davmount_links: false
+    #: Invoke MS Office documents for editing using WebDAV by adding a JavaScript
+    #: click handler.
+    #: - For IE 11 and below invokes the SharePoint ActiveXObject("SharePoint.OpenDocuments")
+    #: - If the custom legacy Firefox plugin is available, it will be used
+    #:   https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ff407576(v%3Doffice.14)
+    #: - Otherwise the Office URL prefix is used (e.g. 'ms-word:ofe|u|http://server/path/file.docx')
+    ms_sharepoint_support: true
+    #: Invoke Libre Office documents for editing using WebDAV
+    libre_office_support: true
+    #: The path to the directory that contains template.html and associated
+    #: assets.
+    #: The default is the htdocs directory within the dir_browser directory.
+    htdocs_path: null

mercurial