--- 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; }