src/server/webdav/multistatus.c

branch
webdav
changeset 231
4714468b9b7e
parent 230
ca50e1ebdc4d
child 233
c5985d2fc19a
--- 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;
     }

mercurial