Tue, 14 Jan 2020 20:05:18 +0100
create namespace definition lists when properties are added to a multistatus response
--- a/src/server/public/webdav.h Tue Dec 31 16:31:29 2019 +0100 +++ b/src/server/public/webdav.h Tue Jan 14 20:05:18 2020 +0100 @@ -98,6 +98,8 @@ struct WebdavProperty { WSNamespace *namespace; + WebdavNSList *nsdef; + const char *name; char *lang; @@ -123,7 +125,7 @@ }; struct WebdavNSList { - WSNamespace *namespace; + WSNamespace *namespace; WebdavNSList *prev; WebdavNSList *next; }; @@ -305,10 +307,16 @@ const char *name); int webdav_property_set_value( - WebdavProperty *p, + WebdavProperty *property, pool_handle_t *pool, char *value); +int webdav_property_add_nsdef( + WebdavProperty *p, + pool_handle_t *pool, + const char *prefix, + const char *nsuri); + WebdavVFSProperties webdav_vfs_properties( WebdavPropfindRequest *rq, WSBool removefromlist,
--- a/src/server/test/webdav.c Tue Dec 31 16:31:29 2019 +0100 +++ b/src/server/test/webdav.c Tue Jan 14 20:05:18 2020 +0100 @@ -200,7 +200,8 @@ } return webdav_operation_create( - (*out_sn)->pool, + (*out_sn), + (*out_rq), &backend1, requests, response);
--- a/src/server/webdav/multistatus.c Tue Dec 31 16:31:29 2019 +0100 +++ b/src/server/webdav/multistatus.c Tue Jan 14 20:05:18 2020 +0100 @@ -56,206 +56,15 @@ 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) @@ -267,6 +76,10 @@ } ZERO(res, sizeof(MSResponse)); + // set href + res->resource.href = pool_strdup(response->op->sn->pool, path); + + // add resource funcs res->resource.addproperty = msresponse_addproperty; res->resource.close = msresponse_close; @@ -275,7 +88,9 @@ res->resource.isclosed = 0; res->closing = 0; + // add new resource to the resource list if(ms->current) { + // before adding a new resource, the current resource must be closed if(!ms->current->resource.isclosed) { msresponse_close((WebdavResource*)ms->current); } @@ -302,26 +117,91 @@ return 0; } + // some WebdavProperty checks to make sure nothing explodes + if(!property->namespace || !property->namespace->href) { + // error: namespace is required + log_ereport( + LOG_FAILURE, + "%s", + "webdav: property '%s' has no namespace", + property->name); + return 1; + } + if(property->nsdef) { + // error: nsdef MUST be NULL, only fill nsdef in this func + log_ereport( + LOG_FAILURE, + "%s", + "webdav: property '%s': nsdef must be null", + property->name); + return 1; + } + // add namespace of this property to the namespace map - if(property->namespace && property->namespace->prefix) { + // the namespace map will be used for global namespace definitions + if(property->namespace->prefix) { char *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->href); if(err) { - return 1; + return 1; // OOM } + } else if(strcmp((char*)property->namespace->href, ns)) { + // global namespace != local namespace + // therefore we need a namespace definition in this element + + if(webdav_property_add_nsdef( + property, + response->multistatus->sn->pool, + (const char*)property->namespace->prefix, + (const char*)property->namespace->href)) + { + return 1; // OOM + } } } - if(status != 200) { + // error properties will be added to a separate list + if(status != 200) { return msresponse_addproperror(response, property, status); } + // add all namespaces used by this property to the nsdef list + if(property->vtype == WS_VALUE_XML_NODE) { + // iterate over xml tree and collect all namespaces + + // TODO: implement + } else if(property->vtype == WS_VALUE_XML_DATA) { + // xml data contains a list of all used namespaces + WebdavNSList *nslist = property->value.data->namespaces; + while(nslist) { + // only add the namespace to the definitions list, if it isn't + // property namespace, because the prop ns is already added + // to the element's def list or global definitions list + if(strcmp( + (const char*)nslist->namespace->prefix, + (const char*)property->namespace->prefix)) + { + // ns-prefix != property-prefix -> add ns to nsdef + if(webdav_property_add_nsdef( + property, + response->multistatus->sn->pool, + (const char*)nslist->namespace->prefix, + (const char*)nslist->namespace->href)) + { + return 1; // OOM + } + } + nslist = nslist->next; + } + } // other value types don't contain xml namespaces + // add property to the list WebdavPList *listelm = pool_malloc( response->multistatus->sn->pool,
--- a/src/server/webdav/multistatus.h Tue Dec 31 16:31:29 2019 +0100 +++ b/src/server/webdav/multistatus.h Tue Jan 14 20:05:18 2020 +0100 @@ -75,6 +75,8 @@ WebdavPList *plist_begin; WebdavPList *plist_end; + UcxMap *nsdef; + MSResponse *next; WSBool closing; };
--- a/src/server/webdav/operation.c Tue Dec 31 16:31:29 2019 +0100 +++ b/src/server/webdav/operation.c Tue Jan 14 20:05:18 2020 +0100 @@ -38,14 +38,17 @@ #define WEBDAV_PATH_MAX 8192 WebdavOperation* webdav_operation_create( - pool_handle_t *pool, + Session *sn, + Request *rq, WebdavBackend *dav, UcxList *requests, WebdavResponse *response) { - WebdavOperation *op = pool_malloc(pool, sizeof(WebdavOperation)); + WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation)); ZERO(op, sizeof(WebdavOperation)); op->dav = dav; + op->sn = sn; + op->rq = rq; op->requests = requests; op->response = response;
--- a/src/server/webdav/operation.h Tue Dec 31 16:31:29 2019 +0100 +++ b/src/server/webdav/operation.h Tue Jan 14 20:05:18 2020 +0100 @@ -37,6 +37,8 @@ struct WebdavOperation { WebdavBackend *dav; + Request *rq; + Session *sn; UcxList *requests; /* backend specific request objects */ WebdavResponse *response; @@ -46,7 +48,8 @@ }; WebdavOperation* webdav_operation_create( - pool_handle_t *pool, + Session *sn, + Request *rq, WebdavBackend *dav, UcxList *requests, WebdavResponse *response);
--- a/src/server/webdav/webdav.c Tue Dec 31 16:31:29 2019 +0100 +++ b/src/server/webdav/webdav.c Tue Jan 14 20:05:18 2020 +0100 @@ -214,7 +214,8 @@ WebdavResponse *response = (WebdavResponse*)ms; WebdavOperation *op = webdav_operation_create( - sn->pool, + sn, + rq, dav, requestObjects, response); @@ -268,6 +269,15 @@ ret = REQ_ABORTED; } + // if propfind was successful, send the result to the client + if(ret == REQ_PROCEED && multistatus_send(ms, sn->csd)) { + ret = REQ_ABORTED; + // TODO: log error + } else { + // TODO: log error + // TODO: error response + } + return ret; } @@ -563,6 +573,44 @@ return 0; } +int webdav_property_add_nsdef( + WebdavProperty *property, + pool_handle_t *pool, + const char *prefix, + const char *nsuri) +{ + // because we're using a memory pool, we don't free in case stuff in + // case of an error (OOM) - stuff will be freed by destroyinig the pool + + WebdavNSList *new_def = pool_malloc(pool, sizeof(WebdavNSList)); + if(!new_def) { + return 1; + } + WSNamespace *new_ns = pool_malloc(pool, sizeof(WSNamespace)); + if(!new_ns) { + return 1; + } + ZERO(new_ns, sizeof(WSNamespace)); + + new_ns->prefix = (xmlChar*)pool_strdup(pool, prefix); + new_ns->href = (xmlChar*)pool_strdup(pool, nsuri); + if(!new_ns->prefix || !new_ns->href) { + return 1; + } + + new_def->namespace = new_ns; + new_def->prev = NULL; + new_def->next = NULL; + + if(property->nsdef) { + property->nsdef->prev = new_def; + new_def->next = property->nsdef; + } + property->nsdef = new_def; + + return 0; +} + WebdavVFSProperties webdav_vfs_properties( WebdavPropfindRequest *rq, WSBool removefromlist,