Fri, 17 Jan 2020 22:23:30 +0100
implement multistatus writer
--- /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; };