Mon, 30 Dec 2019 16:33:20 +0100
add webdav_plist_iterator_remove_current tests and fix some plist related bugs
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * 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: * * 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. */ #include <stdio.h> #include <stdlib.h> #include "../daemon/session.h" #include "multistatus.h" #define MULTISTATUS_BUFFER_LENGTH 2048 Multistatus* multistatus_response(Session *sn, Request *rq) { Multistatus *ms = pool_malloc(sn->pool, sizeof(Multistatus)); if(!ms) { return NULL; } ZERO(ms, sizeof(Multistatus)); ms->response.addresource = multistatus_addresource; ms->sn = sn; ms->rq = rq; ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8); if(!ms->namespaces) { return NULL; } if(ucx_map_cstr_put(ms->namespaces, "D", "DAV:")) { 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); char *href; UCX_MAP_FOREACH(key, href, i) { writer_puts(out, S(" xmlns:")); writer_put(out, key.data, key.len); writer_puts(out, S("=\"")); writer_puts(out, sstr(href)); writer_puts(out, S("\"")); } writer_puts(out, S(">\n")); return out->error; } static int send_prop_name( Multistatus *ms, WebdavProperty *p, Writer *out, char *prefixbuf, int *prefixbuflen) { int in_prefix_len = *prefixbuflen; *prefixbuflen = 0; writer_putc(out, '<'); const char *prefix = NULL; const char *href = NULL; if(p->namespace && p->namespace->href) { href = (const char*)p->namespace->href; if(p->namespace->prefix) { // check if there is a namespace with this prefix already defined // and has the same namespace uri prefix = (const char*)p->namespace->prefix; char *nshref = ucx_map_cstr_get( ms->namespaces, (const char*)p->namespace->prefix); if(!strcmp(nshref, href)) { href = NULL; // we don't need a new xmlns def } } else { // generate new prefix for(int i=0;i<1024;i++) { int len = snprintf(prefixbuf, in_prefix_len, "x%d\0", i); char *test = ucx_map_cstr_get(ms->namespaces, prefixbuf); if(!test) { prefix = prefixbuf; *prefixbuflen = len; break; // found an unused prefix } if(!prefix) { // What? Can't find a free prefix? return 1; } } } } if(prefix) { writer_put(out, prefix, strlen(prefix)); writer_put(out, ":", 1); } // write xml element name writer_put(out, (const char*)p->name, strlen((const char*)p->name)); if(href) { writer_puts(out, S(" xmlns:")); writer_put(out, prefix, strlen(prefix)); writer_puts(out, S("=\"")); writer_put(out, href, strlen(href)); writer_putc(out, '\"'); } if(p->lang) { writer_puts(out, S(" lang=\"")); writer_puts(out, sstr(p->lang)); writer_putc(out, '\"'); } writer_putc(out, '>'); return out->error; } #define MAX_XML_TREE_DEPTH 128 static int send_xml(Multistatus *ms, Writer *out, WSXmlNode *node, WSNamespace *rootns, int depth) { int ret = 0; const char *s; while(node) { switch(node->type) { case XML_ELEMENT_NODE: { writer_putc(out, '<'); if(node->ns && node->ns->prefix) { s = (const char*)node->ns->prefix; writer_put(out, s, strlen(s)); writer_putc(out, ':'); } s = (const char*)node->name; writer_put(out, s, strlen(s)); } } node = node->next; } return ret; } 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")); WebdavPList *p = rp->plist_begin; char prefix[16]; while(p) { WebdavProperty *prop = p->property; int prefixlen = 16; if(send_prop_name(ms, prop, out, prefix, &prefixlen)) { return 1; } // send content // send end tag writer_put(out, "<", 1); if(prop->namespace && prop->namespace->href) { const char *pre = NULL; if(prop->namespace->prefix) { pre = (const char*)prop->namespace->prefix; } else if(prefixlen > 0) { pre = prefix; } if(pre) { writer_put(out, pre, strlen(pre)); writer_put(out, ":", 1); } } writer_put(out, prop->name, strlen(prop->name)); writer_put(out, ">", 1); if(out->error) { return 1; } p = p->next; } writer_puts(out, S(" </D:prop>\n" " <D:status>HTTP/1.1 200 OK</D:status>" " </D:propstat>\n")); } return out->error; } int multistatus_send(Multistatus *ms, SYS_NETFD net) { char buffer[MULTISTATUS_BUFFER_LENGTH]; Writer writer; 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; } WebdavResource * multistatus_addresource( WebdavResponse *response, const char *path) { Multistatus *ms = (Multistatus*)response; MSResponse *res = pool_malloc(ms->sn->pool, sizeof(MSResponse)); if(!res) { return NULL; } ZERO(res, sizeof(MSResponse)); res->resource.addproperty = msresponse_addproperty; res->multistatus = ms; res->errors = NULL; res->end = 0; if(ms->current) { ms->current->end = 1; ms->current->next = res; } else { ms->first = res; } ms->current = res; return (WebdavResource*)res; } int msresponse_addproperty( WebdavResource *res, WebdavProperty *property, int status) { MSResponse *response = (MSResponse*)res; if(response->end) { log_ereport( LOG_WARN, "%s", "webdav: cannot add property to closed response tag"); return 0; } // add namespace of this property to the namespace map if(property->namespace && property->namespace->prefix) { char *ns = ucx_map_cstr_get( response->multistatus->namespaces, (const char*)property->namespace->prefix); if(!ns) { int err = ucx_map_cstr_put( response->multistatus->namespaces, (const char*)property->namespace->prefix, property->namespace->href); if(err) { return 1; } } } if(status != 200) { return msresponse_addproperror(response, property, status); } // add property to the list WebdavPList *listelm = pool_malloc( response->multistatus->sn->pool, sizeof(WebdavPList)); if(!listelm) { return 1; } listelm->property = property; listelm->next = NULL; if(response->plist_end) { response->plist_end->next = listelm; } else { response->plist_begin = listelm; } response->plist_end = listelm; return 0; } int msresponse_addproperror( MSResponse *response, WebdavProperty *property, int statuscode) { pool_handle_t *pool = response->multistatus->sn->pool; UcxAllocator *a = session_get_allocator(response->multistatus->sn); // MSResponse contains a list of properties for each status code // at first find the list for this status code PropertyErrorList *errlist = NULL; PropertyErrorList *list = response->errors; PropertyErrorList *last = NULL; while(list) { if(list->status == statuscode) { errlist = list; break; } last = list; list = list->next; } if(!errlist) { // no list available for this statuscode PropertyErrorList *newelm = pool_malloc(pool, sizeof(PropertyErrorList)); if(!newelm) { return 1; } newelm->begin = NULL; newelm->end = NULL; newelm->next = NULL; newelm->status = statuscode; if(last) { last->next = newelm; } else { response->errors = newelm; } errlist = newelm; } // we have the list -> add the new element UcxList *newlistelm = ucx_list_append_a(a, errlist->end, property); if(!newlistelm) { return 1; } errlist->end = newlistelm; if(!errlist->begin) { errlist->begin = newlistelm; } return 0; }