Thu, 31 Oct 2019 10:26:35 +0100
add propfind/proppatch parser and first iteration of the new webdav api
--- a/src/server/daemon/acl.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/acl.c Thu Oct 31 10:26:35 2019 +0100 @@ -100,6 +100,7 @@ } User* acllist_getuser(Session *sn, Request *rq, ACLListHandle *list) { + // TODO: cache result if(!sn || !rq || !list) { return NULL; } @@ -461,6 +462,11 @@ return 1; } +int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { + // TODO: + return 1; +} + int solaris_acl_check( char *path, struct stat *s, @@ -571,6 +577,10 @@ return 1; } +int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { + return 1; +} + void fs_acl_finish() { } @@ -583,6 +593,10 @@ return 1; } +int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { + return 1; +} + void fs_acl_finish() { } @@ -638,6 +652,11 @@ return 1; } +int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { + // TODO + return 1; +} + void fs_acl_finish() { struct passwd *pw = conf_getglobals()->Vuserpw; if(!pw) {
--- a/src/server/daemon/acl.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/acl.h Thu Oct 31 10:26:35 2019 +0100 @@ -49,6 +49,7 @@ // file system acl functions int fs_acl_check(SysACL *acl, User *user, char *path, uint32_t access_mask); +int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask); void fs_acl_finish(); #ifdef __cplusplus
--- a/src/server/daemon/httprequest.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/httprequest.c Thu Oct 31 10:26:35 2019 +0100 @@ -87,6 +87,46 @@ return S("/"); } +NSAPISession* nsapisession_create(pool_handle_t *pool) { + NSAPISession *sn = pool_malloc(pool, sizeof(NSAPISession)); + if(!sn) { + return NULL; + } + + ZERO(sn, sizeof(NSAPISession)); + + sn->sn.pool = pool; + sn->allocator = util_pool_allocator(pool); + + sn->sn.client = pblock_create_pool(sn->sn.pool, 8); + if(!sn->sn.client) { + pool_free(pool, sn); + return NULL; + } + sn->sn.fill = 1; + + return sn; +} + +int nsapisession_setconnection(NSAPISession *sn, Connection *conn, netbuf *inbuf, IOStream **io) { + SessionHandler *sh = conn->session_handler; + WSBool ssl; + IOStream *sio = sh->create_iostream(sh, conn, sn->sn.pool, &ssl); + if(!sio) { + return 1; + } + *io = sio; + IOStream *http = httpstream_new(sn->sn.pool, sio); + if(!http) { + return 1; + } + sn->connection = conn; + sn->netbuf = inbuf; + sn->sn.csd = http; + sn->sn.ssl = ssl; + return 0; +} + int handle_request(HTTPRequest *request, threadpool_t *thrpool, EventHandler *ev) { // handle nsapi request @@ -94,11 +134,11 @@ pool_handle_t *pool = pool_create(); // create nsapi data structures - NSAPISession *sn = pool_malloc(pool, sizeof(NSAPISession)); + NSAPISession *sn = nsapisession_create(pool); if(sn == NULL) { /* TODO: error */ } - ZERO(sn, sizeof(NSAPISession)); + NSAPIRequest *rq = pool_malloc(pool, sizeof(NSAPIRequest)); if(rq == NULL) { /* TODO: error */ @@ -108,25 +148,16 @@ rq->phase = NSAPIAuthTrans; // fill session structure - sn->connection = request->connection; - sn->netbuf = request->netbuf; - sn->sn.pool = pool; - SessionHandler *sh = request->connection->session_handler; - WSBool ssl; - IOStream *io = sh->create_iostream(sh, request->connection, pool, &ssl); - sn->sn.csd = httpstream_new(pool, io); - sn->sn.ssl = ssl; - - sn->sn.client = pblock_create_pool(sn->sn.pool, 8); - sn->sn.next = NULL; - sn->sn.fill = 1; - sn->sn.subject = NULL; + IOStream *io = NULL; + if(nsapisession_setconnection(sn, request->connection, request->netbuf, &io)) { + // TODO: error + } if(!ev) { ev = ev_instance(get_default_event_handler()); } sn->sn.ev = ev; - + // the session needs the current server configuration sn->config = request->connection->listener->cfg; @@ -342,7 +373,7 @@ // check for request body and prepare input buffer char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers); if(ctlen_str) { - int ctlen = atoi(ctlen_str); + int ctlen = atoi(ctlen_str); // TODO: use other func //printf("request body length: %d\n", ctlen);
--- a/src/server/daemon/httprequest.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/httprequest.h Thu Oct 31 10:26:35 2019 +0100 @@ -73,6 +73,10 @@ sstr_t http_request_get_abspath(HTTPRequest *req); + +NSAPISession* nsapisession_create(pool_handle_t *pool); +int nsapisession_setconnection(NSAPISession *sn, Connection *conn, netbuf *inbuf, IOStream **io); + /* * starts request processing after reading the request header *
--- a/src/server/daemon/session.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/session.c Thu Oct 31 10:26:35 2019 +0100 @@ -40,3 +40,8 @@ NSAPISession *sn = (NSAPISession*)s; return sn->config; } + +NSAPI_PUBLIC void* session_get_allocator(Session *sn) { + return &((NSAPISession*)sn)->allocator; +} +
--- a/src/server/daemon/session.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/session.h Thu Oct 31 10:26:35 2019 +0100 @@ -47,6 +47,8 @@ threadpool_t *currentpool; threadpool_t *defaultpool; + UcxAllocator allocator; + ServerConfiguration *config; }; @@ -57,6 +59,8 @@ // get the server configuration of this session NSAPI_PUBLIC void* session_get_config(Session *s); +NSAPI_PUBLIC void* session_get_allocator(Session *sn); + #ifdef __cplusplus } #endif
--- a/src/server/daemon/vfs.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/vfs.c Thu Oct 31 10:26:35 2019 +0100 @@ -52,9 +52,11 @@ sys_vfs_stat, sys_vfs_fstat, sys_vfs_opendir, + sys_vfs_fdopendir, sys_vfs_mkdir, sys_vfs_unlink, - VFS_CHECKS_ACL + VFS_CHECKS_ACL, + NULL }; static VFS_IO sys_file_io = { @@ -202,6 +204,27 @@ return dir; } +VFS_DIR vfs_fdopendir(VFSContext *ctx, SYS_FILE fd) { + WS_ASSERT(ctx); + WS_ASSERT(path); + + uint32_t access_mask = ctx->aclreqaccess | ACL_LIST; + + // ctx->aclreqaccess should be the complete access mask + uint32_t m = ctx->aclreqaccess; // save original access mask + ctx->aclreqaccess = access_mask; // set mask for vfs->open call + if((ctx->vfs->flags & VFS_CHECKS_ACL) != VFS_CHECKS_ACL) { + // VFS does not evaluates the ACL itself, so we have to do it here + SysACL sysacl; + if(sys_acl_check(ctx, access_mask, &sysacl)) { + return NULL; + } + } + VFS_DIR dir = ctx->vfs->fdopendir(ctx, fd); + ctx->aclreqaccess = m; // restore original access mask + return dir; +} + int vfs_readdir(VFS_DIR dir, VFS_ENTRY *entry) { WS_ASSERT(dir); WS_ASSERT(entry); @@ -422,6 +445,62 @@ return dir; } +VFS_DIR sys_vfs_fdopendir(VFSContext *ctx, SYS_FILE fd) { + uint32_t access_mask = ctx->aclreqaccess; + pool_handle_t *pool = ctx->pool; + + // check ACLs + SysACL sysacl; + if(sys_acl_check(ctx, access_mask, &sysacl)) { + return NULL; + } + + if(sysacl.acl) { + if(!fs_acl_check_fd(&sysacl, ctx->user, fd->fd, access_mask)) { + acl_set_error_status(ctx->sn, ctx->rq, sysacl.acl, ctx->user); + return NULL; + } + } + + // open directory + DIR *sys_dir = fdopendir(fd->fd); + if(!sys_dir) { + if(ctx) { + ctx->vfs_errno = errno; + sys_set_error_status(ctx); + } + return NULL; + } + + SysVFSDir *dir_data = VFS_MALLOC(pool, sizeof(SysVFSDir)); + if(!dir_data) { + closedir(sys_dir); + return NULL; + } + long maxfilelen = fpathconf(fd->fd, _PC_NAME_MAX); + size_t entry_len = offsetof(struct dirent, d_name) + maxfilelen + 1; + dir_data->cur = VFS_MALLOC(pool, entry_len); + if(!dir_data->cur) { + closedir(sys_dir); + VFS_FREE(pool, dir_data); + return NULL; + } + dir_data->dir = sys_dir; + + VFSDir *dir = VFS_MALLOC(pool, sizeof(VFSDir)); + if(!dir) { + closedir(sys_dir); + VFS_FREE(pool, dir_data->cur); + VFS_FREE(pool, dir_data); + return NULL; + } + dir->ctx = ctx; + dir->data = dir_data; + dir->fd = fd->fd; + dir->io = &sys_dir_io; + return dir; +} + int sys_vfs_mkdir(VFSContext *ctx, char *path) { return sys_path_op(ctx, path, sys_mkdir); } @@ -539,6 +618,7 @@ entry->name = name; if(getstat) { // TODO: check ACLs again for new path + entry->stat_errno = 0; if(fstatat(dir->fd, result->d_name, &entry->stat, 0)) { entry->stat_errno = errno; }
--- a/src/server/daemon/vfs.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/vfs.h Thu Oct 31 10:26:35 2019 +0100 @@ -57,6 +57,7 @@ int sys_vfs_stat(VFSContext *ctx, char *path, struct stat *buf); int sys_vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf); VFS_DIR sys_vfs_opendir(VFSContext *ctx, char *path); +VFS_DIR sys_vfs_fdopendir(VFSContext *ctx, SYS_FILE fd); int sys_vfs_mkdir(VFSContext *ctx, char *path); int sys_vfs_unlink(VFSContext *ctx, char *path);
--- a/src/server/daemon/ws-fn.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/ws-fn.c Thu Oct 31 10:26:35 2019 +0100 @@ -70,5 +70,7 @@ { "set-variable", set_variable, NULL, NULL, 0}, { "common-log", common_log, NULL, NULL, 0}, { "send-cgi", send_cgi, NULL, NULL, 0}, + { "webdav-init", webdav_init, NULL, NULL, 0}, + { "webdav-service", webdav_service, NULL, NULL, 0}, {NULL, NULL, NULL, NULL, 0} };
--- a/src/server/public/acl.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/public/acl.h Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2013 Olaf Wintermann. All rights reserved. + * Copyright 2018 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:
--- a/src/server/public/auth.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/public/auth.h Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2013 Olaf Wintermann. All rights reserved. + * Copyright 2018 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:
--- a/src/server/public/nsapi.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/public/nsapi.h Thu Oct 31 10:26:35 2019 +0100 @@ -110,6 +110,9 @@ /* --- Begin miscellaneous definitions --- */ +#define WS_TRUE 1 +#define WS_FALSE 0 + /* Used in some places as a length limit on error messages */ #define MAGNUS_ERROR_LEN 1024 @@ -404,6 +407,7 @@ // they are VFSFile* // TODO: fix NOTE +typedef int WSBool; #ifndef SYS_FILE_T typedef struct VFSFile *SYS_FILE;
--- a/src/server/public/vfs.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/public/vfs.h Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2013 Olaf Wintermann. All rights reserved. + * Copyright 2018 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: @@ -52,9 +52,11 @@ int (*stat)(VFSContext *ctx, char *path, struct stat *buf); int (*fstat)(VFSContext *ctx, SYS_FILE fd, struct stat *buf); VFS_DIR (*opendir)(VFSContext *ctx, char *path); + VFS_DIR (*fdopendir)(VFSContext *ctx, SYS_FILE fd); int (*mkdir)(VFSContext *ctx, char *path); int (*unlink)(VFSContext *ctx, char *path); uint32_t flags; + void *instance; }; struct VFSContext { @@ -70,16 +72,16 @@ struct VFSFile { VFSContext *ctx; - VFS_IO *io; // IO functions - void *data; // private data used by the VFSFile implementation - int fd; // native file descriptor if available, or -1 + VFS_IO *io; /* IO functions */ + void *data; /* private data used by the VFSFile implementation */ + int fd; /* native file descriptor if available, or -1 */ }; struct VFSDir { VFSContext *ctx; VFS_DIRIO *io; - void *data; // private data used by the VFSDir implementation - int fd; // native file descriptor if available, or -1 + void *data; /* private data used by the VFSDir implementation */ + int fd; /* native file descriptor if available, or -1 */ }; struct VFSEntry { @@ -124,6 +126,7 @@ int vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf); void vfs_close(SYS_FILE fd); VFS_DIR vfs_opendir(VFSContext *ctx, char *path); +VFS_DIR vfs_fdopendir(VFSContext *ctx, SYS_FILE fd); int vfs_readdir(VFS_DIR dir, VFS_ENTRY *entry); int vfs_readdir_stat(VFS_DIR dir, VFS_ENTRY *entry); void vfs_closedir(VFS_DIR dir);
--- a/src/server/public/webdav.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/public/webdav.h Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2013 Olaf Wintermann. All rights reserved. + * Copyright 2018 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: @@ -30,6 +30,7 @@ #define WS_WEBDAV_H #include "nsapi.h" +#include "vfs.h" #include <sys/file.h> #include <sys/stat.h> @@ -39,7 +40,209 @@ extern "C" { #endif +typedef struct WebdavBackend WebdavBackend; + +typedef struct WebdavProperty WebdavProperty; +typedef struct WebdavPList WebdavPList; +typedef enum WebdavLockScope WebdavLockScope; +typedef enum WebdavLockType WebdavLockType; + +typedef struct WebdavPropfindRequest WebdavPropfindRequest; +typedef struct WebdavProppatchRequest WebdavProppatchRequest; +typedef struct WebdavLockRequest WebdavLockRequest; + +typedef struct WebdavResponse WebdavResponse; +typedef struct WebdavResource WebdavResource; + +typedef struct WebdavVFSProperties WebdavVFSProperties; + +typedef struct _xmlNs WSNamespace; +typedef struct _xmlNode WSXmlNode; + +#define WS_NODE_ELEMENT 1 +#define WS_NODE_TEXT 3 +#define WS_NODE_CDATA 4 +#define WS_NODE_ENTITY_REF 5 + +/* propfind settings */ + +/* + * Don't use the vfs to stat files or read the directory children + */ +#define WS_PROPFIND_NO_VFS 0x01 + +struct WebdavProperty { + WSNamespace *namespace; + + const char *name; + + char *lang; + + WSXmlNode *value; +}; + +struct WebdavPList { + WebdavProperty *property; + WebdavPList *prev; + WebdavPList *next; +}; + +enum WebdavLockScope { + WEBDAV_LOCK_EXCLUSIVE = 0, + WEBDAV_LOCK_SHARED, + WEBDAV_LOCK_SCOPE_UNKNOWN +}; + +enum WebdavLockType { + WEBDAV_LOCK_WRITE = 0, + WEBDAV_LOCK_TYPE_UNKNOWN +}; + +struct WebdavPropfindRequest { + Session *sn; + Request *rq; + + void *doc; + + /* + * list of requested properties + */ + WebdavPList *properties; + + /* + * number of properties + */ + size_t propcount; + + WSBool allprop; + WSBool propname; + + int depth; + + /* + * custom userdata for the backend + */ + void *userdata; +}; + +struct WebdavProppatchRequest { + Session *sn; + Request *rq; + + void *doc; + + WebdavPList *set; + size_t setcount; + + WebdavPList *remove; + size_t removecount; +}; + +struct WebdavLockRequest { + Session *sn; + Request *rq; + + void *doc; + + WebdavLockScope scope; + WebdavLockType type; + + WSXmlNode *owner; +}; + +struct WebdavVFSProperties { + uint32_t getcontentlength:1; + uint32_t getlastmodified:1; + uint32_t getresourcetype:1; + uint32_t getetag:1; + uint32_t creationdate:1; +}; + +struct WebdavResponse { + WebdavResource* (*addresource)(WebdavResponse*, const char*); +}; + +struct WebdavResource { + char *href; + + /* + * int addprop(WebdavResource *res, WebdavProperty *property, int status); + * + * Adds a property to the resource + */ + int (*addproperty)(WebdavResource*, WebdavProperty*, int); +}; + +struct WebdavBackend { + /* + * int propfind_init( + * WebdavPropfindRequest *rq, + * const char *path, + * WebdavPList **outplist); + * + * Initializes a propfind request. This is called once for each propfind + * request and should initialize everything needed for generating the + * multistatus response. + * + * Optionally, the function can store a pointer to a list of all properties, + * which will be processed by this backend, in the outplist argument. + */ + int (*propfind_init)(WebdavPropfindRequest *, const char *, WebdavPList **); + + /* + * int propfind_do( + * WebdavPropfindRequest *rq, + * WebdavResponse *response, + * VFS_DIR parent, + * const char *path, + * struct stat *s); + * + * This function is called for the requsted resource and for all children + * if WS_PROPFIND_NO_VFS_CHILDREN is not set. + */ + int (*propfind_do)( + WebdavPropfindRequest *, + WebdavResponse *, + VFS_DIR, + const char *, + struct stat *); + + /* + * int propfind_finish(WebdavPropfindRequest *rq); + * + * Finishes a propfind request. + */ + int (*propfind_finish)(WebdavPropfindRequest *); + + /* + * See the WS_PROPFIND_ macros for informations about the settings + */ + uint32_t settings; +}; + +int webdav_getdepth(Request *rq); + +WSNamespace* webdav_dav_namespace(void); +WebdavProperty* webdav_dav_property( + pool_handle_t *pool, + const char *name); + +int webdav_property_set_value( + WebdavProperty *p, + pool_handle_t *pool, + char *value); + +WebdavVFSProperties webdav_vfs_properties( + WebdavPropfindRequest *rq, + WSBool removefromlist, + uint32_t flags); + +int webdav_add_vfs_properties( + WebdavResource *res, + pool_handle_t *pool, + WebdavVFSProperties properties, + struct stat *s); #ifdef __cplusplus }
--- a/src/server/test/main.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/test/main.c Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2013 Olaf Wintermann. All rights reserved. + * 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: @@ -39,10 +39,9 @@ #include "../util/plist.h" #include "../util/date.h" -#include "../../ucx/string.h" +#include <ucx/test.h> -static int std_pipe_fds[2]; -static WSBool is_daemon; +#include "webdav.h" void test() { @@ -50,7 +49,7 @@ // needed for linking WSBool main_is_daemon(void) { - return is_daemon; + return 0; } int main(int argc, char **argv) { @@ -59,7 +58,20 @@ //test(); printf("%s", "Webserver Test Suite\n====================\n\n"); - + + UcxTestSuite* suite = ucx_test_suite_new(); + + // webdav tests + ucx_test_register(suite, test_propfind_parse); + ucx_test_register(suite, test_proppatch_parse); + ucx_test_register(suite, test_lock_parse); + ucx_test_register(suite, test_rqbody2buffer); + ucx_test_register(suite, test_msresponse_addproperty); + + // run tests + ucx_test_run(suite, stdout); + fflush(stdout); + return EXIT_SUCCESS; }
--- a/src/server/test/objs.mk Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/test/objs.mk Thu Oct 31 10:26:35 2019 +0100 @@ -31,6 +31,8 @@ TEST_OBJPRE = $(OBJ_DIR)$(TEST_SRC_DIR) TESTOBJ = main.o +TESTOBJ += testutils.o +TESTOBJ += webdav.o TESTOBJS = $(TESTOBJ:%=$(TEST_OBJPRE)%) TESTSOURCE = $(TESTOBJ:%.o=test/%.c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/testutils.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,94 @@ +/* + * 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 <ucx/string.h> +#include <ucx/utils.h> + +#include "../util/pblock.h" + +#include "testutils.h" + +Session* testutil_session(void) { + pool_handle_t *pool = pool_create(); + NSAPISession *sn = nsapisession_create(pool); + + return &sn->sn; +} + +Request* testutil_request(pool_handle_t *pool, const char *method, const char *uri) { + NSAPIRequest *rq = pool_malloc(pool, sizeof(NSAPIRequest)); + + HTTPRequest httprequest; + ZERO(&httprequest, sizeof(HTTPRequest)); + request_initialize(pool, &httprequest, rq); + + sstr_t clf = ucx_sprintf("%s %s HTTP/1.1", method, uri); + pblock_kvinsert( + pb_key_clf_request, + clf.ptr, + clf.length, + rq->rq.reqpb); + free(clf.ptr); + + pblock_nvinsert( + "method", + method, + rq->rq.reqpb); + + pblock_nvinsert( + "protocol", + "HTTP/1.1", + rq->rq.reqpb); + + pblock_nvinsert("uri", uri, rq->rq.reqpb); + + return &rq->rq; +} + +void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len) { + sstr_t cl = ucx_sprintf("%d", (int)len); + pblock_nvreplace("content-length", cl.ptr, rq->headers); + free(cl.ptr); + + netbuf *inbuf = pool_malloc(sn->pool, sizeof(netbuf)); + inbuf->sd = NULL; + inbuf->inbuf = pool_malloc(sn->pool, len); + inbuf->pos = 0; + inbuf->maxsize = len; + inbuf->cursize = len; + sn->inbuf = inbuf; + + memcpy(inbuf->inbuf, body, len); +} + +void testutil_destroy_session(Session *sn) { + pool_destroy(sn->pool); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/testutils.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef TESTUTILS_H +#define TESTUTILS_H + +#include "../public/nsapi.h" +#include "../daemon/httprequest.h" + +#ifdef __cplusplus +extern "C" { +#endif + +Session* testutil_session(void); + +Request* testutil_request(pool_handle_t *pool, const char *method, const char *uri); + +void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len); + +void testutil_destroy_session(Session *sn); + + +#ifdef __cplusplus +} +#endif + +#endif /* TESTUTILS_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/webdav.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,412 @@ +/* + * 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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/webdav.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#ifndef TEST_WEBDAV_H +#define TEST_WEBDAV_H + +#include "../public/nsapi.h" +#include "../public/webdav.h" + +#include <ucx/test.h> + +#ifdef __cplusplus +extern "C" { +#endif + +UCX_TEST(test_propfind_parse); +UCX_TEST(test_proppatch_parse); +UCX_TEST(test_lock_parse); + +UCX_TEST(test_rqbody2buffer); + +UCX_TEST(test_msresponse_addproperty); + +/* --------------------------- PROPFIND --------------------------- */ + +#define 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>" + +#define TEST_PROPFIND2 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:propfind xmlns:D=\"DAV:\"> \ + <D:prop xmlns:X=\"http://example.com/\"> \ + <D:resourcetype/> \ + <X:testprop/> \ + <X:name/> \ + <Z:testprop xmlns:Z=\"testns\"/>\ + </D:prop> \ + </D:propfind>" + +#define TEST_PROPFIND3 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:propfind xmlns:D=\"DAV:\"> \ + <D:allprop/> \ + </D:propfind>" + +#define TEST_PROPFIND4 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:propfind xmlns:D=\"DAV:\"> \ + <D:propname/> \ + </D:propfind>" + +#define TEST_PROPFIND5 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:propfind xmlns:D=\"DAV:\"> \ + <D:prop> \ + <D:displayname/> \ + <D:getcontentlength/> \ + <D:getetag/> \ + <D:getcontentlength/> \ + <D:resourcetype/> \ + </D:prop> \ + </D:propfind>" + +#define TEST_PROPFIND6 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:propfind xmlns:D=\"DAV:\"> \ + <D:prop> \ + <D:displayname/> \ + <D:getcontentlength/> \ + <D:getetag/> \ + <D:resourcetype/> \ + </D:prop> \ + <D:allprop/> \ + </D:propfind>" + +/* --------------------------- PROPPATCH --------------------------- */ + +#define TEST_PROPPATCH1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:propertyupdate xmlns:D=\"DAV:\" xmlns:X=\"http://example.com/\"> \ + <D:set> \ + <D:prop><X:test>test</X:test><D:creationdate>123</D:creationdate></D:prop> \ + </D:set> \ + </D:propertyupdate>" + +#define TEST_PROPPATCH2 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:propertyupdate xmlns:D=\"DAV:\" xmlns:X=\"http://example.com/\"> \ + <D:set> \ + <D:prop> \ + <X:a>test</X:a> \ + <X:b>15</X:b> \ + <X:c> \ + <X:name test='test1' abc='def'>User</X:name><X:mail>user@host</X:mail> \ + </X:c> \ + <X:d><X:name>Test</X:name></X:d> \ + </D:prop> \ + </D:set> \ + <D:remove> \ + <D:prop> \ + <X:e/> \ + </D:prop> \ + </D:remove> \ + </D:propertyupdate>" + +/* --------------------------- LOCK --------------------------- */ + +#define TEST_LOCK1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ + <D:lockinfo xmlns:D=\"DAV:\"> \ + <D:lockscope><D:shared/></D:lockscope> \ + <D:locktype>D:write/></D:locktype> \ + <D:owner>User</D:owner> \ + </D:lockinfo>" + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_WEBDAV_H */ +
--- a/src/server/util/netbuf.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/util/netbuf.c Thu Oct 31 10:26:35 2019 +0100 @@ -143,7 +143,11 @@ return bytes_in_buffer; } } - + + if(!buf->sd) { + return NETBUF_EOF; + } + /* The netbuf is empty. Read data directly into the caller's buffer */ bytes = net_read(buf->sd, buffer, size); if (bytes == 0)
--- a/src/server/util/objs.mk Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/util/objs.mk Thu Oct 31 10:26:35 2019 +0100 @@ -42,6 +42,7 @@ UTILOBJ += thrpool.o UTILOBJ += util.o UTILOBJ += date.o +UTILOBJ += writer.o UTILOBJS = $(UTILOBJ:%=$(UTIL_OBJPRE)%) UTILSOURCE = $(UTILOBJ:%.o=util/%.c)
--- a/src/server/util/pblock.cpp Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/util/pblock.cpp Thu Oct 31 10:26:35 2019 +0100 @@ -324,7 +324,8 @@ const pb_key *const pb_key_vary = _create_key("vary"); const pb_key *const pb_key_via = _create_key("via"); const pb_key *const pb_key_warning = _create_key("warning"); - +const pb_key *const pb_key_depth = _create_key("depth"); +const pb_key *const pb_key_if = _create_key("if"); /* ------------------------------ _find_key ------------------------------- */
--- a/src/server/util/pblock.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/util/pblock.h Thu Oct 31 10:26:35 2019 +0100 @@ -301,6 +301,8 @@ extern const pb_key *const pb_key_vary; extern const pb_key *const pb_key_via; extern const pb_key *const pb_key_warning; +extern const pb_key *const pb_key_depth; +extern const pb_key *const pb_key_if; NSAPI_PUBLIC pool_handle_t *pblock_pool(pblock *pb);
--- a/src/server/util/systems.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/util/systems.h Thu Oct 31 10:26:35 2019 +0100 @@ -58,10 +58,6 @@ typedef int PRBool; #define PR_TRUE 1 #define PR_FALSE 0 - -typedef int WSBool; -#define WS_TRUE 1 -#define WS_FALSE 0 /* end new types */ /* --- End common definitions for all supported platforms --- */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/util/writer.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,104 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 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 "writer.h" + +void writer_init(Writer *w, SYS_NETFD fd, char *buf, size_t len) { + w->fd = fd; + w->buffer = buf; + w->size = len; + w->pos = 0; + w->error = 0; +} + +int writer_flush(Writer *w) { + if(w->error) { + return w->error; + } + + size_t pos = 0; + size_t len = w->pos; + + while(len > 0) { + ssize_t r = net_write(w->fd, w->buffer + pos, len); + if(r <= 0) { + break; + } + len -= r; + pos += r; + } + + if(pos != w->pos) { + w->error = 1; + return 1; + } + + w->pos = 0; + return 0; +} + +int writer_put(Writer *w, const char *s, size_t len) { + if(w->error) { + return w->error; + } + + size_t a = w->size - w->pos; + if(a == 0) { + if(writer_flush(w)) { + return 1; + } + } + + size_t cplen = len > a ? a : len; + memcpy(w->buffer, s, cplen); + w->pos += cplen; + + if(cplen < len) { + return writer_put(w, s + cplen, len - cplen); + } else { + return 0; + } +} + +int writer_puts(Writer *w, sstr_t s) { + return writer_put(w, s.ptr, s.length); +} + +int writer_putc(Writer *w, char c) { + if(w->pos == w->size) { + if(writer_flush(w)) { + return 1; + } + } + w->buffer[w->pos++] = c; + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/util/writer.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 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 WRITER_H +#define WRITER_H + +#include "../public/nsapi.h" +#include <ucx/string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Writer { + SYS_NETFD fd; + char *buffer; + size_t size; + size_t pos; + int error; +} Writer; + +void writer_init(Writer *w, SYS_NETFD fd, char *buf, size_t len); + +int writer_flush(Writer *w); + +int writer_put(Writer *w, const char *s, size_t len); + +int writer_puts(Writer *w, sstr_t s); + +int writer_putc(Writer *w, char c); + +#ifdef __cplusplus +} +#endif + +#endif /* WRITER_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/multistatus.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,391 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2019 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "../daemon/session.h" + +#include "multistatus.h" + +#define MULTISTATUS_BUFFER_LENGTH 2048 + +Multistatus* multistatus_response(Session *sn, Request *rq) { + Multistatus *ms = pool_malloc(sn->pool, sizeof(Multistatus)); + if(!ms) { + return NULL; + } + ZERO(ms, sizeof(Multistatus)); + ms->response.addresource = multistatus_addresource; + ms->sn = sn; + ms->rq = rq; + ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8); + if(!ms->namespaces) { + return NULL; + } + if(ucx_map_cstr_put(ms->namespaces, "D", "DAV:")) { + return NULL; + } + return ms; +} + +static int send_xml_root(Multistatus *ms, Writer *out) { + writer_puts(out, S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + "<D:multistatus")); + + // write the namespaces definitions + // key is the namespace prefix + // the map always contains the "DAV:" namespace with the prefix "D" + UcxMapIterator i = ucx_map_iterator(ms->namespaces); + char *href; + UCX_MAP_FOREACH(key, href, i) { + writer_puts(out, S(" xmlns:")); + writer_put(out, key.data, key.len); + writer_puts(out, S("=\"")); + writer_puts(out, sstr(href)); + writer_puts(out, S("\"")); + } + + writer_puts(out, S(">\n")); + + return out->error; +} + +static int send_prop_name( + Multistatus *ms, + WebdavProperty *p, + Writer *out, + char *prefixbuf, + int *prefixbuflen) +{ + int in_prefix_len = *prefixbuflen; + *prefixbuflen = 0; + + writer_putc(out, '<'); + + const char *prefix = NULL; + const char *href = NULL; + + if(p->namespace && p->namespace->href) { + href = (const char*)p->namespace->href; + + if(p->namespace->prefix) { + // check if there is a namespace with this prefix already defined + // and has the same namespace uri + prefix = (const char*)p->namespace->prefix; + char *nshref = ucx_map_cstr_get( + ms->namespaces, + (const char*)p->namespace->prefix); + if(!strcmp(nshref, href)) { + href = NULL; // we don't need a new xmlns def + } + } else { + // generate new prefix + for(int i=0;i<1024;i++) { + int len = snprintf(prefixbuf, in_prefix_len, "x%d\0", i); + char *test = ucx_map_cstr_get(ms->namespaces, prefixbuf); + if(!test) { + prefix = prefixbuf; + *prefixbuflen = len; + break; // found an unused prefix + } + if(!prefix) { + // What? Can't find a free prefix? + return 1; + } + } + } + } + + if(prefix) { + writer_put(out, prefix, strlen(prefix)); + writer_put(out, ":", 1); + } + + // write xml element name + writer_put(out, (const char*)p->name, strlen((const char*)p->name)); + + if(href) { + writer_puts(out, S(" xmlns:")); + writer_put(out, prefix, strlen(prefix)); + writer_puts(out, S("=\"")); + writer_put(out, href, strlen(href)); + writer_putc(out, '\"'); + } + + if(p->lang) { + writer_puts(out, S(" lang=\"")); + writer_puts(out, sstr(p->lang)); + writer_putc(out, '\"'); + } + + writer_putc(out, '>'); + + return out->error; +} + + + +#define MAX_XML_TREE_DEPTH 128 +static int send_xml(Multistatus *ms, Writer *out, WSXmlNode *node, WSNamespace *rootns, int depth) { + int ret = 0; + const char *s; + while(node) { + switch(node->type) { + case XML_ELEMENT_NODE: { + writer_putc(out, '<'); + if(node->ns && node->ns->prefix) { + s = (const char*)node->ns->prefix; + writer_put(out, s, strlen(s)); + writer_putc(out, ':'); + } + s = (const char*)node->name; + writer_put(out, s, strlen(s)); + + + } + + } + node = node->next; + } + + return ret; +} + +static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) { + writer_puts(out, S(" <D:response>\n" + " <D:href>")); + writer_puts(out, sstr(rp->resource.href)); + writer_puts(out, S("</href>\n")); + + if(rp->plist_begin) { + writer_puts(out, S(" <D:propstat>" + " <D:prop>\n")); + WebdavPList *p = rp->plist_begin; + char prefix[16]; + while(p) { + WebdavProperty *prop = p->property; + int prefixlen = 16; + if(send_prop_name(ms, prop, out, prefix, &prefixlen)) { + return 1; + } + + // send content + + + // send end tag + writer_put(out, "<", 1); + if(prop->namespace && prop->namespace->href) { + const char *pre = NULL; + if(prop->namespace->prefix) { + pre = (const char*)prop->namespace->prefix; + } else if(prefixlen > 0) { + pre = prefix; + } + + if(pre) { + writer_put(out, pre, strlen(pre)); + writer_put(out, ":", 1); + } + } + writer_put(out, prop->name, strlen(prop->name)); + writer_put(out, ">", 1); + + if(out->error) { + return 1; + } + + p = p->next; + } + writer_puts(out, S(" </D:prop>\n" + " <D:status>HTTP/1.1 200 OK</D:status>" + " </D:propstat>\n")); + } + + return out->error; +} + +int multistatus_send(Multistatus *ms, SYS_NETFD net) { + char buffer[MULTISTATUS_BUFFER_LENGTH]; + Writer writer; + Writer *out = &writer; + writer_init(out, net, buffer, MULTISTATUS_BUFFER_LENGTH); + + // send the xml root element with namespace defs + if(send_xml_root(ms, out)) { + return 1; + } + + // send response tags + MSResponse *response = ms->first; + while(response) { + if(send_response_tag(ms, response, out)) { + return 1; + } + response = response->next; + } + + return 0; +} + + +WebdavResource * multistatus_addresource( + WebdavResponse *response, + const char *path) +{ + Multistatus *ms = (Multistatus*)response; + MSResponse *res = pool_malloc(ms->sn->pool, sizeof(MSResponse)); + if(!res) { + return NULL; + } + ZERO(res, sizeof(MSResponse)); + + res->resource.addproperty = msresponse_addproperty; + + res->multistatus = ms; + res->errors = NULL; + res->end = 0; + + if(ms->current) { + ms->current->end = 1; + ms->current->next = res; + } else { + ms->first = res; + } + ms->current = res; + + return (WebdavResource*)res; +} + +int msresponse_addproperty( + WebdavResource *res, + WebdavProperty *property, + int status) +{ + MSResponse *response = (MSResponse*)res; + if(response->end) { + log_ereport( + LOG_WARN, + "%s", + "webdav: cannot add property to closed response tag"); + return 0; + } + + // add namespace of this property to the namespace map + if(property->namespace && property->namespace->prefix) { + char *ns = ucx_map_cstr_get( + response->multistatus->namespaces, + (const char*)property->namespace->prefix); + if(!ns) { + int err = ucx_map_cstr_put( + response->multistatus->namespaces, + (const char*)property->namespace->prefix, + property->namespace->href); + if(err) { + return 1; + } + } + } + + if(status != 200) { + return msresponse_addproperror(response, property, status); + } + + // add property to the list + WebdavPList *listelm = pool_malloc( + response->multistatus->sn->pool, + sizeof(WebdavPList)); + if(!listelm) { + return 1; + } + + listelm->property = property; + listelm->next = NULL; + + if(response->plist_end) { + response->plist_end->next = listelm; + } else { + response->plist_begin = listelm; + } + response->plist_end = listelm; + return 0; +} + +int msresponse_addproperror( + MSResponse *response, + WebdavProperty *property, + int statuscode) +{ + pool_handle_t *pool = response->multistatus->sn->pool; + UcxAllocator *a = session_get_allocator(response->multistatus->sn); + + // MSResponse contains a list of properties for each status code + // at first find the list for this status code + PropertyErrorList *errlist = NULL; + PropertyErrorList *list = response->errors; + PropertyErrorList *last = NULL; + while(list) { + if(list->status == statuscode) { + errlist = list; + break; + } + last = list; + list = list->next; + } + + if(!errlist) { + // no list available for this statuscode + PropertyErrorList *newelm = pool_malloc(pool, + sizeof(PropertyErrorList)); + if(!newelm) { + return 1; + } + newelm->begin = NULL; + newelm->end = NULL; + newelm->next = NULL; + newelm->status = statuscode; + + if(last) { + last->next = newelm; + } else { + response->errors = newelm; + } + errlist = newelm; + } + + // we have the list -> add the new element + UcxList *newlistelm = ucx_list_append_a(a, errlist->end, property); + if(!newlistelm) { + return 1; + } + errlist->end = newlistelm; + if(!errlist->begin) { + errlist->begin = newlistelm; + } + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/multistatus.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,113 @@ +/* + * 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. + */ +#ifndef MULTISTATUS_H +#define MULTISTATUS_H + +#include "../public/webdav.h" + +#include <ucx/map.h> +#include <ucx/buffer.h> +#include <libxml/tree.h> +#include "../util/writer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Multistatus Multistatus; +typedef struct MSResponse MSResponse; + +typedef struct PropertyErrorList PropertyErrorList; + +/* + * implements the WebdavResponse interface + */ +struct Multistatus { + WebdavResponse response; + Session *sn; + Request *rq; + MSResponse *first; + MSResponse *current; + + /* + * this map contains some namespaces of property elements, but not all + * only the namespace of the property name element is added + * + * key: (char*) namespace prefix + * value: (char*) namespace href + */ + UcxMap *namespaces; +}; + +/* + * implements the WebdavResource interface + */ +struct MSResponse { + WebdavResource resource; + Multistatus *multistatus; + + PropertyErrorList *errors; + + WebdavPList *plist_begin; + WebdavPList *plist_end; + + MSResponse *next; + WSBool end; +}; + +struct PropertyErrorList { + PropertyErrorList *next; + UcxList *begin; + UcxList *end; + int status; +}; + +Multistatus* multistatus_response(Session *sn, Request *rq); + +int multistatus_send(Multistatus *ms, SYS_NETFD out); + +WebdavResource * multistatus_addresource( + WebdavResponse *response, + const char *path); + +int msresponse_addproperty( + WebdavResource *res, + WebdavProperty *property, + int status); + +int msresponse_addproperror( + MSResponse *response, + WebdavProperty *property, + int statuscode); + +#ifdef __cplusplus +} +#endif + +#endif /* MULTISTATUS_H */ +
--- a/src/server/webdav/objs.mk Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/webdav/objs.mk Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. # -# Copyright 2013 Olaf Wintermann. All rights reserved. +# 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: @@ -31,6 +31,10 @@ DAV_OBJPRE = $(OBJ_DIR)$(DAV_SRC_DIR) DAVOBJ = webdav.o +DAVOBJ += requestparser.o +DAVOBJ += multistatus.o +DAVOBJ += search.o +DAVOBJ += versioning.o DAVOBJS = $(DAVOBJ:%=$(DAV_OBJPRE)%) DAVSOURCE = $(DAVOBJ:%.o=webdav/%.c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/requestparser.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,528 @@ +/* + * 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 <ucx/string.h> +#include <ucx/utils.h> +#include <ucx/map.h> + +#include "requestparser.h" + +#define xstreq(a, b) !strcmp((const char*)a, (const char*)b) + +int proplist_add( + pool_handle_t *pool, + WebdavPList **begin, + WebdavPList **end, + WebdavProperty *prop) +{ + WebdavPList *elm = pool_malloc(pool, sizeof(WebdavPList)); + if(!elm) { + return 1; + } + elm->next = NULL; + elm->property = prop; + + if(!*begin) { + *begin = elm; + *end = elm; + return 0; + } + + (*end)->next = elm; + *end = elm; + + return 0; +} + +void proplist_free(pool_handle_t *pool, WebdavPList *list) { + while(list) { + WebdavPList *next = list->next; + pool_free(pool, list); + list = next; + } +} + +WebdavProperty* prop_create( + pool_handle_t *pool, + WSNamespace *ns, + const char *name) +{ + WebdavProperty *prop = pool_malloc(pool, sizeof(WebdavProperty)); + prop->lang = NULL; + prop->name = (char*)name; + prop->namespace = ns; + prop->value = NULL; + return prop; +} + +static UcxKey propkey(const char *ns, const char *name) { + UcxKey key; + sstr_t data = ucx_sprintf("%s\n%s", name, ns); + key.data = data.ptr; + key.len = data.length; + key.hash = ucx_hash(data.ptr, data.length); + return key; +} + +static int parse_prop( + Session *sn, + xmlNode *node, + UcxMap *propmap, + WebdavPList **plist_begin, + WebdavPList **plist_end, + size_t *propcount, + int proppatch, + int *error) +{ + xmlNode *pnode = node->children; + for(;pnode;pnode=pnode->next) { + if(pnode->type != XML_ELEMENT_NODE) { + continue; + } + + const char* ns = (const char*)pnode->ns->href; + const char* name = (const char*)pnode->name; + + // check for prop duplicates + UcxKey k = propkey((const char*)ns, (const char*)name); + if(!k.data) { + *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + return 1; + } + void *c = ucx_map_get(propmap, k); + if(!c) { + if(ucx_map_put(propmap, k, (void*)1)) { + *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + } + + // no duplicate + // create property elment and add it to the list + WebdavProperty *prop = prop_create(sn->pool, pnode->ns, name); + if(proppatch) { + prop->value = pnode->children; + } + if(prop) { + if(proplist_add(sn->pool, plist_begin, plist_end, prop)) { + *error = proppatch ? + PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + } + (*propcount)++; + } else { + *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; + } + } else if(proppatch) { + *error = PROPPATCH_PARSER_DUPLICATE; + } + + free(k.data); + if(*error) { + return 1; + } + } + return 0; +} + +WebdavPropfindRequest* propfind_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error) +{ + xmlDoc *doc = xmlReadMemory(buf, buflen, NULL, NULL, 0); + if(!doc) { + *error = PROPFIND_PARSER_INVALID_REQUEST; + return NULL; + } + + // ret vars + *error = 0; + + WSBool allprop = FALSE; + WSBool propname = FALSE; + WebdavPList *plist_begin = NULL; + WebdavPList *plist_end = NULL; + size_t propcount = 0; + int depth = webdav_getdepth(rq); + + xmlNode *root = xmlDocGetRootElement(doc); + xmlNode *node = root->children; + + // check if the root element is DAV:propfind + if( + !(root->ns + && xstreq(root->ns->href, "DAV:") + && xstreq(root->name, "propfind"))) + { + *error = PROPFIND_PARSER_NO_PROPFIND; + xmlFreeDoc(doc); + return NULL; + } + + UcxMap *propmap = ucx_map_new(32); + if(!propmap) { + *error = PROPFIND_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + + int ret = 0; + while(node && !ret) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->ns->href, "DAV:") && !allprop && !propname) { + // a propfind request can contain a prop element + // with specified properties or the allprop or propname + // element + if(xstreq(node->name, "prop")) { + ret = parse_prop( + sn, + node, + propmap, + &plist_begin, + &plist_end, + &propcount, + 0, // proppatch = false + error); + } else if(xstreq(node->name, "allprop")) { + allprop = TRUE; + } else if(xstreq(node->name, "propname")) { + propname = TRUE; + } + } + } + node = node->next; + } + + ucx_map_free(propmap); // no allocated content must be freed + + if(ret) { + // parse_prop failed + // in this case, error is already set + xmlFreeDoc(doc); + return NULL; + } + + if(!allprop && !propname && propcount == 0) { + *error = PROPFIND_PARSER_NO_PROPERTIES; + xmlFreeDoc(doc); + return NULL; + } + + WebdavPropfindRequest *request = pool_malloc( + sn->pool, + sizeof(WebdavPropfindRequest)); + if(!request) { + *error = PROPFIND_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + request->sn = sn; + request->rq = rq; + request->properties = NULL; + request->propcount = 0; + request->depth = depth; + request->doc = doc; + if(allprop) { + request->allprop = TRUE; + request->propname = FALSE; // we cannot have allprop and propname + } else if(propname) { + request->allprop = FALSE; + request->propname = TRUE; + } else { + request->allprop = FALSE; + request->propname = FALSE; + request->properties = plist_begin; + request->propcount = propcount; + } + + if(!request->properties && plist_begin) { + proplist_free(sn->pool, plist_begin); + } + return request; +} + +WebdavProppatchRequest* proppatch_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error) +{ + return webdav_parse_set( + sn, rq, buf, buflen, "DAV:", "propertyupdate", TRUE, error); +} + +static xmlNode* find_child(xmlNode *node, const char *name) { + xmlNode *c = node->children; + while(c) { + if(c->ns) { + if(xstreq(c->ns->href, "DAV:") && xstreq(c->name, name)) { + return c; + } + } + c = c->next; + } + return NULL; +} + +WebdavProppatchRequest* webdav_parse_set( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + const char *rootns, + const char *rootname, + WSBool allowremove, + int *error) +{ + xmlDoc *doc = xmlReadMemory(buf, buflen, NULL, NULL, 0); + if(!doc) { + *error = PROPPATCH_PARSER_INVALID_REQUEST; + return NULL; + } + + xmlNode *root = xmlDocGetRootElement(doc); + xmlNode *node = root->children; + + // check if the root element is correct + if( + !(root->ns + && xstreq(root->ns->href, rootns) + && xstreq(root->name, rootname))) + { + *error = PROPPATCH_PARSER_NO_PROPERTYUPDATE; + xmlFreeDoc(doc); + return NULL; + } + + // ret vars + *error = 0; + + UcxMap *propmap = ucx_map_new(32); // map for duplicate checking + if(!propmap) { + *error = PROPPATCH_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + + WebdavPList *set_begin = NULL; + WebdavPList *set_end = NULL; + WebdavPList *remove_begin = NULL; + WebdavPList *remove_end = NULL; + size_t set_count = 0; + size_t remove_count = 0; + + int ret = 0; + while(node && !ret) { + if(node->type == XML_ELEMENT_NODE) { + if(node->ns && xstreq(node->ns->href, "DAV:")) { + // a propfind request can contain a prop element + // with specified properties or the allprop or propname + // element + if(xstreq(node->name, "set")) { + xmlNode *prop = find_child(node, "prop"); + ret = parse_prop( + sn, + prop, + propmap, + &set_begin, + &set_end, + &set_count, + TRUE, // proppatch = true + error); + } else if(xstreq(node->name, "remove")) { + if(!allowremove) { + *error = PROPPATCH_PARSER_INVALID_REQUEST; + ret = 1; + break; + } + xmlNode *prop = find_child(node, "prop"); + ret = parse_prop( + sn, + prop, + propmap, + &remove_begin, + &remove_end, + &remove_count, + TRUE, // proppatch = true + error); + } + + } + } + node = node->next; + } + + ucx_map_free(propmap); // no allocated content must be freed + + if(set_count + remove_count == 0) { + *error = PROPPATCH_PARSER_NO_PROPERTIES; + ret = 1; + } + + if(ret) { + xmlFreeDoc(doc); + return NULL; + } + + WebdavProppatchRequest *request = pool_malloc( + sn->pool, + sizeof(WebdavProppatchRequest)); + if(!request) { + *error = PROPPATCH_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + request->sn = sn; + request->rq = rq; + request->doc = doc; + request->set = set_begin; + request->setcount = set_count; + request->remove = remove_begin; + request->removecount = remove_count; + return request; +} + +WebdavLockRequest* lock_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error) +{ + xmlDoc *doc = xmlReadMemory(buf, buflen, NULL, NULL, 0); + if(!doc) { + *error = LOCK_PARSER_INVALID_REQUEST; + return NULL; + } + + xmlNode *root = xmlDocGetRootElement(doc); + xmlNode *node = root->children; + + // check if the root element is correct + if( + !(root->ns + && xstreq(root->ns->href, "DAV:") + && xstreq(root->name, "lockinfo"))) + { + *error = LOCK_PARSER_NO_LOCKINFO; + xmlFreeDoc(doc); + return NULL; + } + + WebdavLockScope lockscope = WEBDAV_LOCK_SCOPE_UNKNOWN; + WebdavLockType locktype = WEBDAV_LOCK_TYPE_UNKNOWN; + WSXmlNode *owner = NULL; + + int ret = 0; + while(node && !ret) { + if( + node->type == XML_ELEMENT_NODE + && node->ns + && xstreq(node->ns->href, "DAV:")) + { + char *name = (char*)node->name; + if(xstreq(name, "lockscope")) { + xmlNode *s = node->children; + while(s) { + if( + s->type == XML_ELEMENT_NODE + && s->ns + && xstreq(s->ns->href, "DAV:")) + { + if(xstreq(s->name, "exclusive")) { + lockscope = WEBDAV_LOCK_EXCLUSIVE; + } else if(xstreq(s->name, "shared")) { + lockscope = WEBDAV_LOCK_SHARED; + } else { + // don't ignore unknown lockscope + *error = LOCK_PARSER_UNKNOWN_ELEMENT; + ret = 1; + break; + } + } + s = s->next; + } + } else if(xstreq(name, "locktype")) { + xmlNode *t = node->children; + while(t) { + if( + t->type == XML_ELEMENT_NODE + && t->ns + && xstreq(t->ns->href, "DAV:")) + { + if(xstreq(t->name, "write")) { + locktype = WEBDAV_LOCK_WRITE; + } else { + *error = LOCK_PARSER_UNKNOWN_ELEMENT; + ret = 1; + break; + } + } + t = t->next; + } + } else if(xstreq(name, "owner")) { + owner = node->children; + } + } + node = node->next; + } + + if(ret) { + xmlFreeDoc(doc); + return NULL; + } + + WebdavLockRequest *request = pool_malloc( + sn->pool, + sizeof(WebdavLockRequest)); + if(!request) { + *error = LOCK_PARSER_OOM; + xmlFreeDoc(doc); + return NULL; + } + request->sn = sn; + request->rq = rq; + request->doc = doc; + + if(locktype == WEBDAV_LOCK_TYPE_UNKNOWN) { + locktype = WEBDAV_LOCK_WRITE; + } + if(lockscope == WEBDAV_LOCK_SCOPE_UNKNOWN) { + lockscope = WEBDAV_LOCK_EXCLUSIVE; + } + request->scope = lockscope; + request->type = locktype; + request->owner = owner; + return request; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/requestparser.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#ifndef REQUESTPARSER_H +#define REQUESTPARSER_H + +#include "../public/webdav.h" + +#include <libxml/tree.h> +#include <ucx/buffer.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define PROPFIND_PARSER_OK 0 +#define PROPFIND_PARSER_NO_PROPFIND 1 +#define PROPFIND_PARSER_NO_PROPERTIES 2 +#define PROPFIND_PARSER_INVALID_REQUEST 3 +#define PROPFIND_PARSER_OOM 4 +#define PROPFIND_PARSER_ERROR 5 + +#define PROPPATCH_PARSER_OK 0 +#define PROPPATCH_PARSER_NO_PROPERTYUPDATE 1 +#define PROPPATCH_PARSER_NO_PROPERTIES 2 +#define PROPPATCH_PARSER_INVALID_REQUEST 3 +#define PROPPATCH_PARSER_DUPLICATE 4 +#define PROPPATCH_PARSER_OOM 5 +#define PROPPATCH_PARSER_ERROR 6 + +#define LOCK_PARSER_OK 0 +#define LOCK_PARSER_NO_LOCKINFO 1 +#define LOCK_PARSER_INVALID_REQUEST 2 +#define LOCK_PARSER_UNKNOWN_ELEMENT 3 +#define LOCK_PARSER_OOM 4 +#define LOCK_PARSER_ERROR 5 + +int proplist_add( + pool_handle_t *pool, + WebdavPList **begin, + WebdavPList **end, + WebdavProperty *prop); + +WebdavProperty* prop_create( + pool_handle_t *pool, + WSNamespace *ns, + const char *name); + +WebdavPropfindRequest* propfind_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error); + +WebdavProppatchRequest* proppatch_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error); + +WebdavProppatchRequest* webdav_parse_set( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + const char *rootns, + const char *rootname, + WSBool allowremove, + int *error); + +WebdavLockRequest* lock_parse( + Session *sn, + Request *rq, + const char *buf, + size_t buflen, + int *error); + + +#ifdef __cplusplus +} +#endif + +#endif /* REQUESTPARSER_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/search.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,33 @@ +/* + * 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 "search.h" + +int webdav_search (pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/search.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef SEARCH_H +#define SEARCH_H + +#include "../public/webdav.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int webdav_search (pblock *pb, Session *sn, Request *rq); + + +#ifdef __cplusplus +} +#endif + +#endif /* SEARCH_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/versioning.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,61 @@ +/* + * 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 "versioning.h" + +int webdav_version_control(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_checkout(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_checkin(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_uncheckout(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_mkworkspace(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_update(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_label(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_merge(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/versioning.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef VERSIONING_H +#define VERSIONING_H + +#include "../public/webdav.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int webdav_version_control(pblock *pb, Session *sn, Request *rq); +int webdav_checkout(pblock *pb, Session *sn, Request *rq); +int webdav_checkin(pblock *pb, Session *sn, Request *rq); +int webdav_uncheckout(pblock *pb, Session *sn, Request *rq); +int webdav_mkworkspace(pblock *pb, Session *sn, Request *rq); +int webdav_update(pblock *pb, Session *sn, Request *rq); +int webdav_label(pblock *pb, Session *sn, Request *rq); +int webdav_merge(pblock *pb, Session *sn, Request *rq); + + +#ifdef __cplusplus +} +#endif + +#endif /* VERSIONING_H */ +
--- a/src/server/webdav/webdav.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/webdav/webdav.c Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2013 Olaf Wintermann. All rights reserved. + * 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: @@ -30,6 +30,622 @@ #include <stdlib.h> #include <string.h> +#include <ucx/buffer.h> +#include <ucx/list.h> + #include "webdav.h" +#include "search.h" +#include "versioning.h" +#include "multistatus.h" +#include "requestparser.h" +#include "../util/pblock.h" +#include "../util/util.h" +#include "../daemon/session.h" +#include "../daemon/http.h" + +static UcxMap *method_handler_map; + +static WebdavBackend default_backend; + +static WSNamespace dav_namespace; + +static WebdavProperty dav_resourcetype_empty; +static WebdavProperty dav_resourcetype_collection; +static WSXmlNode dav_resourcetype_collection_value; + +static void init_default_backend(void) { + memset(&default_backend, 0, sizeof(WebdavBackend)); + default_backend.propfind_init = default_propfind_init; + default_backend.propfind_do = default_propfind_do; + default_backend.propfind_finish = default_propfind_finish; +} + +int webdav_init(pblock *pb, Session *sn, Request *rq) { + init_default_backend(); + + method_handler_map = ucx_map_new(64); + + ucx_map_cstr_put(method_handler_map, "OPTIONS", webdav_options); + ucx_map_cstr_put(method_handler_map, "PROPFIND", webdav_propfind); + ucx_map_cstr_put(method_handler_map, "PROPPATCH", webdav_proppatch); + ucx_map_cstr_put(method_handler_map, "MKCOL", webdav_mkcol); + ucx_map_cstr_put(method_handler_map, "POST", webdav_post); + ucx_map_cstr_put(method_handler_map, "DELETE", webdav_delete); + ucx_map_cstr_put(method_handler_map, "PUT", webdav_put); + ucx_map_cstr_put(method_handler_map, "COPY", webdav_copy); + ucx_map_cstr_put(method_handler_map, "MOVE", webdav_move); + ucx_map_cstr_put(method_handler_map, "LOCK", webdav_lock); + ucx_map_cstr_put(method_handler_map, "UNLOCK", webdav_unlock); + ucx_map_cstr_put(method_handler_map, "REPORT", webdav_report); + ucx_map_cstr_put(method_handler_map, "ACL", webdav_acl); + + ucx_map_cstr_put(method_handler_map, "SEARCH", webdav_search); + + ucx_map_cstr_put(method_handler_map, "VERSION-CONTROL", webdav_version_control); + ucx_map_cstr_put(method_handler_map, "CHECKOUT", webdav_checkout); + ucx_map_cstr_put(method_handler_map, "CHECKIN", webdav_checkin); + ucx_map_cstr_put(method_handler_map, "UNCHECKOUT", webdav_uncheckout); + ucx_map_cstr_put(method_handler_map, "MKWORKSPACE", webdav_mkworkspace); + ucx_map_cstr_put(method_handler_map, "UPDATE", webdav_update); + ucx_map_cstr_put(method_handler_map, "LABEL", webdav_label); + ucx_map_cstr_put(method_handler_map, "MERGE", webdav_merge); + + dav_namespace.href = (xmlChar*)"DAV:"; + dav_namespace.prefix = (xmlChar*)"D"; + + dav_resourcetype_empty.namespace = &dav_namespace; + dav_resourcetype_empty.name = "resourcetype"; + + dav_resourcetype_collection.namespace = &dav_namespace; + dav_resourcetype_collection.name = "resourcetype"; + dav_resourcetype_collection.value = &dav_resourcetype_collection_value; + dav_resourcetype_collection_value.content = (xmlChar*)"<D:collection/>"; + dav_resourcetype_collection_value.type = XML_TEXT_NODE; + + + return REQ_PROCEED; +} + + +int webdav_service(pblock *pb, Session *sn, Request *rq) { + if(!method_handler_map) { + log_ereport(LOG_FAILURE, "WebDAV module not initialized"); + protocol_status(sn, rq, 500, NULL); + return REQ_ABORTED; + } + char *method = pblock_findkeyval(pb_key_method, rq->reqpb); + + FuncPtr saf = (FuncPtr)ucx_map_cstr_get(method_handler_map, method); + if(!saf) { + return REQ_NOACTION; + } + + return saf(pb, sn, rq); +} + +UcxBuffer* rqbody2buffer(Session *sn, Request *rq) { + if(!sn->inbuf) { + protocol_status(sn, rq, 400, NULL); + return NULL; + } + + UcxBuffer *buf = ucx_buffer_new( + NULL, + sn->inbuf->maxsize, + UCX_BUFFER_AUTOEXTEND); + if(!buf) { + protocol_status(sn, rq, 500, NULL); + return NULL; + } + + char in[2048]; + int r; + while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) { + if(ucx_buffer_write(in, 1, r, buf) != r) { + protocol_status(sn, rq, 500, NULL); + ucx_buffer_free(buf); + return NULL; + } + } + + return buf; +} + +int webdav_options(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_propfind(pblock *pb, Session *sn, Request *rq) { + UcxBuffer *reqbody = rqbody2buffer(sn, rq); + if(!reqbody) { + return REQ_ABORTED; + } + + int error = 0; + WebdavPropfindRequest *propfind = propfind_parse( + sn, + rq, + reqbody->space, + reqbody->size, + &error); + ucx_buffer_free(reqbody); + if(!propfind) { + switch(error) { + // TODO: handle all errors + default: return REQ_ABORTED; + } + } + + + Multistatus *ms = multistatus_response(sn, rq); + if(!ms) { + return REQ_ABORTED; + } + WebdavResponse *response = (WebdavResponse*)ms; + + WebdavBackend *dav = + rq->davCollection ? rq->davCollection : &default_backend; + + char *path = pblock_findkeyval(pb_key_path, rq->vars); + + uint32_t settings = dav->settings; + if(dav->propfind_init(propfind, path)) { + return REQ_ABORTED; + } + + WSBool usevfs = (settings & WS_PROPFIND_NO_VFS) != WS_PROPFIND_NO_VFS; + struct stat s; + struct stat *statptr = NULL; + + VFSContext *vfs = NULL; + if(usevfs) { + vfs = vfs_request_context(sn, rq); + + if(vfs_stat(vfs, path, &s)) { + return REQ_ABORTED; + } + statptr = &s; + if(!S_ISDIR(s.st_mode)) { + usevfs = FALSE; + } + } + if(propfind->depth == 0) { + usevfs = FALSE; + } + + int ret = REQ_ABORTED; + if(!dav->propfind_do(propfind, response, NULL, path, statptr)) { + // propfind for the requested resource was successful + + // usevfsdir is TRUE if + // the webdav backend has not disabled vfs usage + // the file is a directory + // depth is not 0 + // in this case we need to execute propfind_do for all children + if(usevfs && !propfind_children(dav, propfind, response, vfs, path)) { + ret = REQ_PROCEED; + } + } + + // finish the propfind request + // this function should cleanup all resources, therefore we execute it + // even if a previous function failed + if(dav->propfind_finish(propfind)) { + ret = REQ_ABORTED; + } + + return ret; +} + +int propfind_children( + WebdavBackend *dav, + WebdavPropfindRequest *request, + WebdavResponse *response, + VFSContext *vfs, + char *path) +{ + UcxAllocator *a = session_get_allocator(request->sn); + pool_handle_t *pool = request->sn->pool; + UcxList *stack = ucx_list_prepend_a(a, NULL, path); + UcxList *stack_end = stack; + if(!stack) { + return 1; + } + + // reusable buffer for full child path + char *newpath = NULL; + size_t newpathlen = 0; + + int err = 0; + while(stack && !err) { + char *cur_path = stack->data; + size_t parent_len = strlen(cur_path); + if(parent_len > WEBDAV_PATH_MAX) { + log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); + err = 1; + break; + } + if(cur_path[parent_len-1] == '/') { + parent_len--; + } + size_t max_child_len = WEBDAV_PATH_MAX - parent_len; + + // when newpath is initialized with the parent path + // set path_buf_init to TRUE + WSBool path_buf_init = FALSE; + + VFS_DIR dir = vfs_opendir(vfs, path); + if(!dir) { + log_ereport( + LOG_FAILURE, + "webdav: propfind: cannot open directory %d", + vfs->vfs_errno); + err = 1; + break; + } + + VFS_ENTRY f; + while(vfs_readdir_stat(dir, &f)) { + if(f.stat_errno != 0) { + continue; + } + + size_t child_len = strlen(f.name); + if(child_len > max_child_len) { + log_ereport(LOG_FAILURE, "webdav: maximal path length exceeded"); + err = 1; + break; + } + size_t childpathlen = parent_len + child_len + 1; // +1 '/' + if(childpathlen > newpathlen) { + // we're gonna need a bigger boa^H^H^Hbuffer + if(newpath) { + pool_free(pool, newpath); + } + newpath = pool_malloc(pool, childpathlen + 1); + if(!newpath) { + err = 1; + break; + } + newpathlen = childpathlen; + path_buf_init = FALSE; + } + // create full path string for this child + if(!path_buf_init) { + memcpy(newpath, cur_path, parent_len); + newpath[parent_len] = '/'; + } + memcpy(newpath+parent_len+1, f.name, child_len); + newpath[childpathlen] = 0; + + // propfind for this child + if(dav->propfind_do(request, response, dir, newpath, &f.stat)) { + err = 1; + break; + } + + // depth of -1 means infinity + if(request->depth == -1 && S_ISDIR(f.stat.st_mode)) { + char *pathcp = pool_malloc(pool, childpathlen + 1); + memcpy(pathcp, newpath, childpathlen + 1); + + // add the newpath copy to the stack + // stack_end is always not NULL here, because we remove + // the first stack element at the end of the loop + UcxList *newlistelm = ucx_list_append_a(a, stack_end, pathcp); + if(!newlistelm) { + err = 1; + break; + } + stack_end = newlistelm; + } + } + + vfs_closedir(dir); + + if(cur_path != path) { + pool_free(pool, cur_path); + } + stack = ucx_list_remove_a(a, stack, stack); + } + + // in case of an error, we have to free all remaining stack elements + UCX_FOREACH(elm, stack) { + char *data = elm->data; + if(data != path) { + pool_free(pool, data); + } + } + + return err; +} + +int webdav_proppatch(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_mkcol(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_post(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_delete(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_put(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_copy(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_move(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_lock(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_unlock(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_report(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + +int webdav_acl(pblock *pb, Session *sn, Request *rq) { + return REQ_ABORTED; +} + + +/* ------------------------ default webdav backend ------------------------ */ + +int default_propfind_init( + WebdavPropfindRequest *rq, + const char* path) +{ + DefaultWebdavData *data = pool_malloc( + rq->sn->pool, + sizeof(DefaultWebdavData)); + if(!data) { + return 1; + } + rq->userdata = data; + + data->vfsproperties = webdav_vfs_properties(rq, TRUE, 0); + + return 0; +} + +int default_propfind_do( + WebdavPropfindRequest *request, + WebdavResponse *response, + VFS_DIR parent, + const char *path, + struct stat *s) +{ + DefaultWebdavData *data = request->userdata; + + // add a resource to the response + // usually this will lead to a <response> ... </response> tag in the + // multistatus response + WebdavResource *resource = response->addresource(response, path); + if(!resource) { + return 1; + } + + // add all requested vfs properties like getcontentlength ... + if(webdav_add_vfs_properties( + resource, + request->sn->pool, + data->vfsproperties, + s)) + { + return 1; + } + + // all remaining properties are not available + WebdavPList *p = request->properties; + while(p) { + resource->addproperty(resource, p->property, 404); + p = p->next; + } + + return 0; +} + +int default_propfind_finish(WebdavPropfindRequest *rq) { + return 0; +} + + +/* ------------------------------ public API ------------------------------ */ + +int webdav_getdepth(Request *rq) { + char *depth_str = pblock_findkeyval(pb_key_depth, rq->headers); + int depth = 0; + if(depth_str) { + size_t dlen = strlen(depth_str); + if(!memcmp(depth_str, "infinity", dlen)) { + depth = -1; + } else if(dlen == 1 && depth_str[0] == '1') { + depth = 1; + } + } + return depth; +} + +WSNamespace* webdav_dav_namespace(void) { + return &dav_namespace; +} + +WebdavProperty* webdav_dav_property( + pool_handle_t *pool, + const char *name) +{ + WebdavProperty *property = pool_malloc(pool, sizeof(WebdavProperty)); + if(!property) { + return NULL; + } + + property->namespace = &dav_namespace; + property->lang = NULL; + property->name = name; + property->value = NULL; + return property; +} + +int webdav_property_set_value( + WebdavProperty *p, + pool_handle_t *pool, + char *value) +{ + WSXmlNode *node = pool_malloc(pool, sizeof(WSXmlNode)); + if(!node) { + return 1; + } + ZERO(node, sizeof(WSXmlNode)); + + node->content = (xmlChar*)value; + node->type = XML_TEXT_NODE; + + p->value = node; + return 0; +} + +WebdavVFSProperties webdav_vfs_properties( + WebdavPropfindRequest *rq, + WSBool removefromlist, + uint32_t flags) +{ + WebdavVFSProperties ret; + ZERO(&ret, sizeof(WebdavVFSProperties)); + + WSBool etag = 1; + WSBool creationdate = 1; + + WebdavPList *property = rq->properties; + WebdavPList *prev = NULL; + while(property) { + WebdavPList *next = property->next; + WSNamespace *ns = property->property->namespace; + if(ns && !strcmp((char*)ns->href, "DAV:")) { + const char *name = property->property->name; + WebdavPList *removethis = property; + if(!strcmp(name, "getlastmodified")) { + ret.getlastmodified = 1; + } else if(!strcmp(name, "getcontentlength")) { + ret.getcontentlength = 1; + } else if(!strcmp(name, "resourcetype")) { + ret.getresourcetype = 1; + } else if(etag && !strcmp(name, "getetag")) { + ret.getetag = 1; + } else if(creationdate && !strcmp(name, "creationdate")) { + ret.creationdate = 1; + } else { + removethis = NULL; + } + + if(removefromlist && removethis) { + if(prev) { + prev->next = next; + } else { + rq->properties = next; + } + } + } + prev = property; + property = next; + } + + return ret; +} + +static inline int w_addprop( + WebdavResource *res, + pool_handle_t *pool, + const char *name, + char *value) +{ + WebdavProperty *p = webdav_dav_property(pool, name); + if(!p) { + return 1; + } + if(webdav_property_set_value(p, pool, value)) { + return 1; + } + return res->addproperty(res, p, 200); +} + +int webdav_add_vfs_properties( + WebdavResource *res, + pool_handle_t *pool, + WebdavVFSProperties properties, + struct stat *s) +{ + if(properties.getresourcetype) { + if(S_ISDIR(s->st_mode)) { + res->addproperty(res, &dav_resourcetype_collection, 200); + } else { + res->addproperty(res, &dav_resourcetype_empty, 200); + } + } + if(properties.getcontentlength) { + char *buf = pool_malloc(pool, 64); + if(!buf) { + return 1; + } + uint64_t contentlength = s->st_size; + snprintf(buf, 64, "%" PRIu64 "\0", contentlength); + if(w_addprop(res, pool, "getcontentlength", buf)) { + return 1; + } + } + if(properties.getlastmodified) { + char *buf = pool_malloc(pool, HTTP_DATE_LEN+1); + if(!buf) { + return 1; + } + buf[HTTP_DATE_LEN] = 0; + + struct tm mtms; + struct tm *mtm = system_gmtime(&s->st_mtim.tv_sec, &mtms); + + if(mtm) { + strftime(buf, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); + if(w_addprop(res, pool, "getlastmodified", buf)) { + return 1; + } + } else { + return 1; + } + } + if(properties.creationdate) { + // TODO + } + if(properties.getetag) { + char *buf = pool_malloc(pool, 96); + if(!buf) { + return 1; + } + snprintf(buf, + 96, + "\"%x-%x\"\0", + (int)s->st_size, + (int)s->st_mtim.tv_sec); + if(w_addprop(res, pool, "getetag", buf)) { + return 1; + } + } + + return 0; +}
--- a/src/server/webdav/webdav.h Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/webdav/webdav.h Thu Oct 31 10:26:35 2019 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2013 Olaf Wintermann. All rights reserved. + * 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: @@ -30,7 +30,6 @@ #define WEBDAV_H #include "../public/webdav.h" -#include "../util/strbuf.h" #include <ucx/map.h> #include <ucx/list.h> @@ -39,7 +38,54 @@ extern "C" { #endif +#define WEBDAV_PATH_MAX 8192 + +typedef struct DefaultWebdavData { + WebdavVFSProperties vfsproperties; +} DefaultWebdavData; + + +int webdav_init(pblock *pb, Session *sn, Request *rq); + +int webdav_service(pblock *pb, Session *sn, Request *rq); +UcxBuffer* rqbody2buffer(Session *sn, Request *rq); + +int webdav_getdepth(Request *rq); + +int webdav_options(pblock *pb, Session *sn, Request *rq); + +int webdav_propfind(pblock *pb, Session *sn, Request *rq); +int propfind_children( + WebdavBackend *webdav, + WebdavPropfindRequest *request, + WebdavResponse *response, + VFSContext *vfs, + char *path); + +int webdav_proppatch(pblock *pb, Session *sn, Request *rq); +int webdav_mkcol(pblock *pb, Session *sn, Request *rq); +int webdav_post(pblock *pb, Session *sn, Request *rq); +int webdav_delete(pblock *pb, Session *sn, Request *rq); +int webdav_put(pblock *pb, Session *sn, Request *rq); +int webdav_copy(pblock *pb, Session *sn, Request *rq); +int webdav_move(pblock *pb, Session *sn, Request *rq); +int webdav_lock(pblock *pb, Session *sn, Request *rq); +int webdav_unlock(pblock *pb, Session *sn, Request *rq); +int webdav_report(pblock *pb, Session *sn, Request *rq); +int webdav_acl(pblock *pb, Session *sn, Request *rq); +int webdav_search (pblock *pb, Session *sn, Request *rq); + +int default_propfind_init( + WebdavPropfindRequest *rq, + const char* path); +int default_propfind_do( + WebdavPropfindRequest *request, + WebdavResponse *response, + VFS_DIR parent, + const char *path, + struct stat *s); +int default_propfind_finish(WebdavPropfindRequest *rq); #ifdef __cplusplus }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/xml.c Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,36 @@ +/* + * 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 "xml.h" + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/webdav/xml.h Thu Oct 31 10:26:35 2019 +0100 @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef XML_H +#define XML_H + +#include "../public/webdav.h" +#include <libxml/tree.h> + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /* XML_H */ +