refactore webdav backend struct webdav

Sun, 29 Dec 2019 15:09:58 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 29 Dec 2019 15:09:58 +0100
branch
webdav
changeset 212
d7e7ea9c6bc6
parent 211
2160585200ac
child 213
4a6be4f10d5f

refactore webdav backend struct

src/server/public/webdav.h file | annotate | diff | comparison | revisions
src/server/test/webdav.c file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.h file | annotate | diff | comparison | revisions
src/server/webdav/requestparser.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.h file | annotate | diff | comparison | revisions
--- a/src/server/public/webdav.h	Thu Oct 31 10:26:35 2019 +0100
+++ b/src/server/public/webdav.h	Sun Dec 29 15:09:58 2019 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2018 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:
@@ -44,10 +44,13 @@
     
 typedef struct WebdavProperty         WebdavProperty;
 typedef struct WebdavPList            WebdavPList;
+typedef struct WebdavNSList           WebdavNSList;
 
 typedef enum   WebdavLockScope        WebdavLockScope;
 typedef enum   WebdavLockType         WebdavLockType;
 
+typedef enum   WebdavValueType        WebdavValueType;
+
 typedef struct WebdavPropfindRequest  WebdavPropfindRequest;
 typedef struct WebdavProppatchRequest WebdavProppatchRequest;
 typedef struct WebdavLockRequest      WebdavLockRequest;
@@ -57,6 +60,9 @@
 
 typedef struct WebdavVFSProperties    WebdavVFSProperties;
 
+typedef struct WSXmlData              WSXmlData;
+typedef struct WSText                 WSText;
+
 typedef struct _xmlNs   WSNamespace;
 typedef struct _xmlNode WSXmlNode;
 
@@ -72,6 +78,19 @@
  */
 #define WS_PROPFIND_NO_VFS 0x01
 
+
+enum WebdavValueType {
+    WS_VALUE_NO_TYPE = 0,
+    WS_VALUE_XML_NODE,
+    WS_VALUE_XML_DATA,
+    WS_VALUE_TEXT
+};
+
+struct WSText {
+    char   *str;
+    size_t length;
+};
+
 struct WebdavProperty {
     WSNamespace *namespace;
     
@@ -79,7 +98,18 @@
     
     char *lang;
     
-    WSXmlNode *value;
+    union {
+        WSXmlNode *node;
+        WSXmlData *data;
+        WSText    text;
+    } value;
+    WebdavValueType vtype;
+};
+
+struct WSXmlData {
+    WebdavNSList *namespaces;
+    char         *data;
+    size_t       length;
 };
 
 struct WebdavPList {
@@ -88,6 +118,12 @@
     WebdavPList *next;
 };
 
+struct WebdavNSList {
+    WSNamespace *namespace;
+    WebdavNSList *prev;
+    WebdavNSList *next;
+};
+
 enum WebdavLockScope {
     WEBDAV_LOCK_EXCLUSIVE = 0,
     WEBDAV_LOCK_SHARED,
@@ -185,8 +221,6 @@
      * 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 **);
     
@@ -219,10 +253,26 @@
      * See the WS_PROPFIND_ macros for informations about the settings
      */
     uint32_t settings;
+    
+    
+    /*
+     * next Backend
+     */
+    WebdavBackend *next;
 };
 
+/*
+ * gets the requested depth
+ * 
+ * in case of infinity, -1 is returned
+ * if no depth is specified, 0 is returned
+ */
 int webdav_getdepth(Request *rq);
 
+WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list);
+
+size_t webdav_plist_count(WebdavPList *list);
+
 WSNamespace* webdav_dav_namespace(void);
 WebdavProperty* webdav_dav_property(
         pool_handle_t *pool,
--- a/src/server/test/webdav.c	Thu Oct 31 10:26:35 2019 +0100
+++ b/src/server/test/webdav.c	Sun Dec 29 15:09:58 2019 +0100
@@ -262,7 +262,7 @@
     UCX_TEST_ASSERT(
             !strcmp(p2->set->property->name, "a"),
             "p2: set property 1: wrong name");
-    WSXmlNode *p2set1 = p2->set->property->value;
+    WSXmlNode *p2set1 = p2->set->property->value.node;
     UCX_TEST_ASSERT(
             p2set1->type == WS_NODE_TEXT,
             "p2: set property 1: wrong type");
@@ -273,7 +273,7 @@
             !strcmp((char*)p2set1->content, "test"),
             "p2: set property 1: wrong value");
     
-    WSXmlNode *p2set3 = p2->set->next->next->property->value;
+    WSXmlNode *p2set3 = p2->set->next->next->property->value.node;
     UCX_TEST_ASSERT(p2set3, "p2: set property 3 missing");
     UCX_TEST_ASSERT(
             p2set3->type == WS_NODE_TEXT,
@@ -382,6 +382,22 @@
     
     WebdavProperty p1;
     WebdavProperty p[16];
+    const char *names[] = {"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9"};
+    
+    // init test data
+    p1.namespace = webdav_dav_namespace();
+    p1.lang = NULL;
+    p1.name = "test1";
+    p1.value.data = NULL;
+    p1.vtype = 0;
+    
+    for(int i=0;i<8;i++) {
+        p[i].namespace = webdav_dav_namespace();
+        p[i].name = names[i];
+        p[i].lang = NULL;
+        p[i].value.node = NULL;
+        p[1].vtype = 0;
+    }
     
     UCX_TEST_ASSERT(!r->plist_begin && !r->plist_end, "plist not empty");
     
--- a/src/server/webdav/multistatus.h	Thu Oct 31 10:26:35 2019 +0100
+++ b/src/server/webdav/multistatus.h	Sun Dec 29 15:09:58 2019 +0100
@@ -55,8 +55,7 @@
     MSResponse *current;
     
     /*
-     * this map contains some namespaces of property elements, but not all
-     * only the namespace of the property name element is added
+     * map of document namespace definitions
      * 
      * key: (char*) namespace prefix
      * value: (char*) namespace href
--- a/src/server/webdav/requestparser.c	Thu Oct 31 10:26:35 2019 +0100
+++ b/src/server/webdav/requestparser.c	Sun Dec 29 15:09:58 2019 +0100
@@ -77,10 +77,10 @@
         const char *name)
 {
     WebdavProperty *prop = pool_malloc(pool, sizeof(WebdavProperty));
+    memset(prop, 0, sizeof(WebdavProperty));
     prop->lang = NULL;
     prop->name = (char*)name;
     prop->namespace = ns;
-    prop->value = NULL;
     return prop;
 }
 
@@ -128,7 +128,8 @@
             // create property elment and add it to the list
             WebdavProperty *prop = prop_create(sn->pool, pnode->ns, name);
             if(proppatch) {
-                prop->value = pnode->children;
+                prop->value.node = pnode->children;
+                prop->vtype = WS_VALUE_XML_NODE;
             }
             if(prop) {
                 if(proplist_add(sn->pool, plist_begin, plist_end, prop)) {
--- a/src/server/webdav/webdav.c	Thu Oct 31 10:26:35 2019 +0100
+++ b/src/server/webdav/webdav.c	Sun Dec 29 15:09:58 2019 +0100
@@ -53,7 +53,7 @@
 
 static WebdavProperty dav_resourcetype_empty;
 static WebdavProperty dav_resourcetype_collection;
-static WSXmlNode dav_resourcetype_collection_value;
+static WSXmlNode dav_resourcetype_collection_value; // TODO: change type to WSXmlData
 
 static void init_default_backend(void) {
     memset(&default_backend, 0, sizeof(WebdavBackend));
@@ -100,7 +100,8 @@
     
     dav_resourcetype_collection.namespace = &dav_namespace;
     dav_resourcetype_collection.name = "resourcetype";
-    dav_resourcetype_collection.value = &dav_resourcetype_collection_value;
+    dav_resourcetype_collection.value.node = &dav_resourcetype_collection_value;
+    dav_resourcetype_collection.vtype = WS_VALUE_XML_NODE;
     dav_resourcetype_collection_value.content = (xmlChar*)"<D:collection/>";
     dav_resourcetype_collection_value.type = XML_TEXT_NODE;
     
@@ -127,6 +128,7 @@
 
 UcxBuffer* rqbody2buffer(Session *sn, Request *rq) {
     if(!sn->inbuf) {
+        //request body required, set http response code
         protocol_status(sn, rq, 400, NULL);
         return NULL;
     }
@@ -163,6 +165,8 @@
         return REQ_ABORTED;
     }
     
+    UcxAllocator *a = session_get_allocator(sn);
+    
     int error = 0;
     WebdavPropfindRequest *propfind = propfind_parse(
             sn,
@@ -178,23 +182,72 @@
         }
     }
     
-    
+    // The multistatus response object contains responses for all
+    // requested resources. At the end the Multistatus object will be
+    // serialized to xml
     Multistatus *ms = multistatus_response(sn, rq);
     if(!ms) {
         return REQ_ABORTED;
     }
+    // WebdavResponse is the public interface used by Backends
+    // for adding resources to the response
     WebdavResponse *response = (WebdavResponse*)ms;
     
     WebdavBackend *dav = 
             rq->davCollection ? rq->davCollection : &default_backend;
     
+    // requested uri path
     char *path = pblock_findkeyval(pb_key_path, rq->vars);
     
+    // Initialize WebDAV Backend Chain
+    //
+    // Call propfind_init of each Backend
+    // propfind_init can return a new property list, which
+    // will be passed to the next backend
+    //
+    // VFS settings are only taken from the first backend
     uint32_t settings = dav->settings;
-    if(dav->propfind_init(propfind, path)) {
-        return REQ_ABORTED;
+    
+    // list of individual WebdavPropfindRequest objects for each Backend
+    UcxList *backendPropfind = NULL;
+    
+    // new properties after init, start with clone of original plist
+    WebdavPList *newProp = webdav_plist_clone(sn->pool, propfind->properties);
+    size_t newPropCount = propfind->propcount;
+    
+    WebdavBackend *davList = dav;
+    while(davList) {
+        // create WebdavPropfindRequest copy
+        WebdavPropfindRequest *pReq = pool_malloc(
+                sn->pool,
+                sizeof(WebdavPropfindRequest));
+        memcpy(propfind, pReq, sizeof(WebdavPropfindRequest));
+        // use new plist after previous init (or orig. plist in the first run)
+        pReq->properties = newProp;
+        pReq->propcount = newPropCount;
+        
+        // add new WebdavPropfindRequest object to list for later use
+        backendPropfind = ucx_list_append_a(a, backendPropfind, pReq);
+        if(!backendPropfind) {
+            return REQ_ABORTED; // OOM
+        }
+        
+        // create plist copy as out-plist for init
+        newProp = webdav_plist_clone(sn->pool, newProp);
+        
+        // run init: this can generate a new properties list (newProp)
+        //           which will be passed to the next backend
+        if(dav->propfind_init(pReq, path, &newProp)) {
+            return REQ_ABORTED;
+        }
+        
+        newPropCount = webdav_plist_count(newProp);
+        
+        davList = davList->next;
     }
     
+    // some Backends can list all children by themselves, but some
+    // require the VFS for this
     WSBool usevfs = (settings & WS_PROPFIND_NO_VFS) != WS_PROPFIND_NO_VFS;
     struct stat s;
     struct stat *statptr = NULL;
@@ -204,10 +257,12 @@
         vfs = vfs_request_context(sn, rq);
         
         if(vfs_stat(vfs, path, &s)) {
+            protocol_status(sn, rq, util_errno2status(vfs->vfs_errno), NULL);
             return REQ_ABORTED;
         }
         statptr = &s;
         if(!S_ISDIR(s.st_mode)) {
+            // the file is not a directory, therefore we don't need the VFS
             usevfs = FALSE;
         }
     }
@@ -216,7 +271,7 @@
     }
     
     int ret = REQ_ABORTED;
-    if(!dav->propfind_do(propfind, response, NULL, path, statptr)) {
+    if(!webdav_propfind_do(dav, backendPropfind, response, NULL, path, statptr)) {
         // propfind for the requested resource was successful
         
         // usevfsdir is TRUE if
@@ -224,7 +279,7 @@
         //   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)) {
+        if(usevfs && !propfind_children(dav, backendPropfind, response, vfs, path)) {
             ret = REQ_PROCEED;
         }
     }
@@ -239,13 +294,60 @@
     return ret;
 }
 
+/*
+ * Executes propfind_do for each Backend
+ * The list requests must contain all WebdavPropfindRequest objects
+ * of all backends
+ */
+int webdav_propfind_do(
+        WebdavBackend *webdav,
+        UcxList *requests,
+        WebdavResponse *response,
+        VFS_DIR parent,
+        const char *path,
+        struct stat *s)
+{
+    while(webdav && requests) {
+        if(webdav->propfind_do(requests->data, response, parent, path, s)) {
+            return REQ_ABORTED;
+        }
+        
+        webdav = webdav->next;
+        requests = requests->next;
+    }
+    return REQ_PROCEED;
+}
+
+/*
+ * Executes propfind_finish for each Backend
+ */
+int webdav_propfind_finish(WebdavBackend *webdav, UcxList *requests) {
+    int ret = REQ_PROCEED;
+    while(webdav && requests) {
+        if(webdav->propfind_finish(requests->data)) {
+            ret = REQ_ABORTED;
+        }
+        
+        webdav = webdav->next;
+        requests = requests->next;
+    }
+    return ret;
+}
+
+
+/*
+ * Uses the VFS to iterate over all children of the requsted resource
+ * and executes propfind for each child
+ */
 int propfind_children(
         WebdavBackend *dav,
-        WebdavPropfindRequest *request,
+        UcxList *requests,
         WebdavResponse *response,
         VFSContext *vfs,
         char *path)
 {
+    WebdavPropfindRequest *request = requests->data;
+    
     UcxAllocator *a = session_get_allocator(request->sn);
     pool_handle_t *pool = request->sn->pool;
     UcxList *stack = ucx_list_prepend_a(a, NULL, path);
@@ -321,7 +423,7 @@
             newpath[childpathlen] = 0;
             
             // propfind for this child
-            if(dav->propfind_do(request, response, dir, newpath, &f.stat)) {
+            if(webdav_propfind_do(dav, requests, response, dir, newpath, &f.stat)) {
                 err = 1;
                 break;
             }
@@ -411,7 +513,8 @@
 
 int default_propfind_init(
         WebdavPropfindRequest *rq,
-        const char* path)
+        const char* path,
+        WebdavPList **outplist)
 {
     DefaultWebdavData *data = pool_malloc(
             rq->sn->pool,
@@ -484,6 +587,47 @@
     return depth;
 }
 
+WebdavPList* webdav_plist_clone(pool_handle_t *pool, WebdavPList *list) {
+    WebdavPList *new_list = NULL;     // start of the new list
+    WebdavPList *new_list_end = NULL; // end of the new list
+    
+    WebdavPList *elm = list;
+    while(elm) {
+        // copy list item
+        WebdavPList *new_elm = pool_malloc(pool, sizeof(WebdavPList));
+        if(!new_elm) {
+            return NULL;
+        }
+        new_elm->property = elm->property; // new list contains original ptr
+        new_elm->prev = NULL;
+        new_elm->next = NULL;
+        
+        if(new_list_end) {
+            new_list_end->next = elm;
+            elm->prev = new_list_end;
+            
+            new_list_end = elm;
+        } else {
+            new_list = new_elm;
+            new_list_end = new_elm;
+        }
+        
+        elm = elm->next;
+    }
+    
+    return new_list;
+}
+
+size_t webdav_plist_count(WebdavPList *list) {
+    size_t count = 0;
+    WebdavPList *elm = list;
+    while(elm) {
+        count++;
+        elm = elm->next;
+    }
+    return count;
+}
+
 WSNamespace* webdav_dav_namespace(void) {
     return &dav_namespace;
 }
@@ -496,11 +640,10 @@
     if(!property) {
         return NULL;
     }
+    memset(property, 0, sizeof(WebdavProperty));
     
     property->namespace = &dav_namespace;
-    property->lang = NULL;
     property->name = name;
-    property->value = NULL;
     return property;
 }
 
@@ -518,7 +661,8 @@
     node->content = (xmlChar*)value;
     node->type = XML_TEXT_NODE;
     
-    p->value = node;
+    p->value.node = node;
+    p->vtype = WS_VALUE_XML_NODE;
     return 0;
 }
 
@@ -556,6 +700,7 @@
             }
             
             if(removefromlist && removethis) {
+                
                 if(prev) {
                     prev->next = next;
                 } else {
--- a/src/server/webdav/webdav.h	Thu Oct 31 10:26:35 2019 +0100
+++ b/src/server/webdav/webdav.h	Sun Dec 29 15:09:58 2019 +0100
@@ -49,16 +49,32 @@
     
 int webdav_service(pblock *pb, Session *sn, Request *rq);
 
+/*
+ * returns a buffer containing the request body
+ * 
+ * this function sets an http response code in case of an error
+ * or missing request body
+ */
 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 webdav_propfind_do(
+        WebdavBackend *webdav,
+        UcxList *requests,
+        WebdavResponse *response,
+        VFS_DIR parent,
+        const char *path,
+        struct stat *s);
+
+int webdav_propfind_finish(WebdavBackend *webdav, UcxList *requests);
+
 int propfind_children(
         WebdavBackend *webdav,
-        WebdavPropfindRequest *request,
+        UcxList *requests,
         WebdavResponse *response,
         VFSContext *vfs,
         char *path);
@@ -78,7 +94,8 @@
 
 int default_propfind_init(
         WebdavPropfindRequest *rq,
-        const char* path);
+        const char* path,
+        WebdavPList **outplist);
 int default_propfind_do(
         WebdavPropfindRequest *request,
         WebdavResponse *response,

mercurial