implement multistatus writer webdav

Fri, 17 Jan 2020 22:23:30 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 17 Jan 2020 22:23:30 +0100
branch
webdav
changeset 231
4714468b9b7e
parent 230
ca50e1ebdc4d
child 232
499711b2a970

implement multistatus writer

src/server/util/platform.h file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.c file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.h file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/util/platform.h	Fri Jan 17 22:23:30 2020 +0100
@@ -0,0 +1,50 @@
+/*
+ * 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 BASE_PLATFORM_H
+#define BASE_PLATFORM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__clang__)
+#define WS_FALLTHROUGH [[clang::fallthrough]]
+#elif defined(__GNUC__)
+#define WS_FALLTHROUGH __attribute__((fallthrough))
+#else
+#define WS_FALLTHROUGH
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BASE_PLATFORM_H */
+
--- a/src/server/webdav/multistatus.c	Fri Jan 17 19:37:24 2020 +0100
+++ b/src/server/webdav/multistatus.c	Fri Jan 17 22:23:30 2020 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2019 Olaf Wintermann. All rights reserved.
+ * Copyright 2020 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,9 @@
 #include <stdlib.h>
 
 #include "../daemon/session.h"
+#include "../util/platform.h"
+
+#include <ucx/string.h>
 
 #include "operation.h"
 
@@ -50,12 +53,167 @@
     if(!ms->namespaces) {
         return NULL;
     }
-    if(ucx_map_cstr_put(ms->namespaces, "D", "DAV:")) {
+    if(ucx_map_cstr_put(ms->namespaces, "D", webdav_dav_namespace())) {
         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);
+    WSNamespace *ns;
+    UCX_MAP_FOREACH(key, ns, i) {
+        writer_puts(out, S(" xmlns:"));
+        writer_put(out, key.data, key.len);
+        writer_puts(out, S("=\""));
+        writer_puts(out, sstr((char*)ns->href));
+        writer_puts(out, S("\""));
+    }
+    
+    writer_puts(out, S(">\n"));
+    
+    return out->error;
+}
+
+static void send_nsdef(WSNamespace *ns, Writer *out) {
+    writer_puts(out, S(" xmlns:"));
+    writer_puts(out, sstr((char*)ns->prefix));
+    writer_puts(out, S("=\""));
+    writer_puts(out, sstr((char*)ns->href));
+    writer_putc(out, '\"');
+}
+
+static int send_property(
+        Multistatus *ms,
+        WebdavProperty *property,
+        WebdavNSList *nsdef,
+        WSBool writeContent,
+        Writer *out)
+{
+    // write: "<prefix:name"
+    writer_puts(out, S("    <"));
+    writer_puts(out, sstr((char*)property->namespace->prefix));
+    writer_putc(out, ':');
+    writer_puts(out, sstr((char*)property->name));
+    
+    // check if the namespace is already defined
+    WSBool need_nsdef = TRUE;
+    WSNamespace *ns = ucx_map_cstr_get(
+            ms->namespaces,
+            (char*)property->namespace->prefix);
+    if(ns && !strcmp(
+            (const char*)ns->href,
+            (const char*)property->namespace->href))
+    {
+        need_nsdef = FALSE; // prefix and href are the same, no need for nsdef
+    }
+    
+    // send definition for the element's namespace
+    if(need_nsdef) {
+        send_nsdef(property->namespace, out);
+    }
+    
+    // send additional namespace definitions required for the value
+    WebdavNSList *def = nsdef;
+    while(def) {
+        send_nsdef(def->namespace, out);
+        def = def->next;
+    }
+    
+    // send xml lang attribute
+    if(property->lang) {
+        writer_puts(out, S(" xml:lang=\""));
+        writer_puts(out, sstr((char*)property->lang));
+        writer_putc(out, '\"');
+    }
+    
+    // end property tag and write content
+    if(writeContent) {
+        writer_putc(out, '>');
+        
+        // content
+        switch(property->vtype) {
+            case WS_VALUE_NO_TYPE: break;
+            case WS_VALUE_XML_NODE: {
+                // TODO
+                break;
+            }
+            case WS_VALUE_XML_DATA: {
+                // only write data, data->namespaces is already handled
+                writer_put(
+                        out,
+                        property->value.data->data,
+                        property->value.data->length);
+                break;
+            }
+            case WS_VALUE_TEXT: {
+                // asume the text is already escaped
+                writer_put(
+                        out,
+                        property->value.text.str,
+                        property->value.text.length);
+                break;
+            }
+        }
+        
+        // end tag
+        writer_putc(out, '<');
+        writer_puts(out, sstr((char*)property->namespace->prefix));
+        writer_putc(out, ':');
+        writer_puts(out, sstr((char*)property->name));
+        writer_putc(out, '>');
+    } else {
+        writer_putc(out, '/>');
+    }
+    
+    return out->error;
+}
+
+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"));
+        // send properties
+        PropertyOkList *p = rp->plist_begin;
+        while(p) {
+            if(send_property(ms, p->property, p->nsdef, TRUE, out)) {
+                return out->error;
+            }
+            p = p->next;
+        }
+        
+        writer_puts(out, S("   </D:prop>\n"
+                           "   <D:status>HTTP/1.1 200 OK</D:status>"
+                           "  </D:propstat>\n"));
+    }
+    
+    // send error properties
+    PropertyErrorList *error = rp->errors;
+    while(error) {
+        WebdavPList *errprop = error->begin;
+        while(errprop) {
+            if(send_property(ms, errprop->property, NULL, FALSE, out)) {
+                return out->error;
+            }
+            errprop = errprop->next;
+        }
+        error = error->next;
+    }
+    
+    return out->error;
+}
+
 int multistatus_send(Multistatus *ms, SYS_NETFD net) {
     char buffer[MULTISTATUS_BUFFER_LENGTH];
     // create a writer, that flushes the buffer when it is filled
@@ -63,7 +221,19 @@
     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;
 }
@@ -180,6 +350,44 @@
     }
     a->free(a->pool, key.ptr);
     
+    // list of namespace definitions for this property
+    WebdavNSList *nsdef_begin = NULL;
+    WebdavNSList *nsdef_end = NULL;
+    
+    // add namespace of this property to the namespace map
+    // the namespace map will be used for global namespace definitions
+    if(property->namespace->prefix) {
+        WSNamespace *ns = ucx_map_cstr_get(
+                response->multistatus->namespaces,
+                (const char*)property->namespace->prefix);
+        if(!ns) {
+            // prefix is not in use -> we can add the namespace to the ns map
+            int err = ucx_map_cstr_put(
+                    response->multistatus->namespaces,
+                    (const char*)property->namespace->prefix,
+                    property->namespace);
+            if(err) {
+                return 1; // OOM
+            }
+        } else if(
+                strcmp((const char*)property->namespace->href,
+                (const char*)ns->href))
+        {
+            // global namespace != local namespace
+            // therefore we need a namespace definition in this element
+            
+            // ns-prefix != property-prefix -> add ns to nsdef
+            if(webdav_nslist_add(
+                    sn->pool,
+                    &nsdef_begin,
+                    &nsdef_end,
+                    property->namespace))
+            {
+                return 1; // OOM
+            }
+        }
+    }
+    
     // error properties will be added to a separate list
     if(status != 200) { 
         return msresponse_addproperror(response, property, status);
@@ -202,8 +410,6 @@
         nslist = property->value.data->namespaces;
     } // other value types don't contain xml namespaces
     
-    WebdavNSList *nsdef_begin = NULL;
-    WebdavNSList *nsdef_end = NULL;
     while(nslist) {
         // only add the namespace to the definitions list, if it isn't a
         // property namespace, because the prop ns is already added
@@ -220,7 +426,7 @@
                     nslist->namespace))
             {
                 return 1; // OOM
-            }    
+            }
         }
         nslist = nslist->next;
     }
--- a/src/server/webdav/multistatus.h	Fri Jan 17 19:37:24 2020 +0100
+++ b/src/server/webdav/multistatus.h	Fri Jan 17 22:23:30 2020 +0100
@@ -59,7 +59,7 @@
      * map of document namespace definitions
      * 
      * key: (char*) namespace prefix
-     * value: (char*) namespace href
+     * value: WSNamespace*
      */
     UcxMap *namespaces;
 };

mercurial