Sat, 18 Jan 2020 13:48:59 +0100
add xml writer
--- a/src/server/test/main.c Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/test/main.c Sat Jan 18 13:48:59 2020 +0100 @@ -42,6 +42,7 @@ #include <ucx/test.h> #include "vfs.h" +#include "writer.h" #include "xml.h" #include "webdav.h" @@ -69,9 +70,15 @@ ucx_test_register(suite, test_vfs_opendir); ucx_test_register(suite, test_vfs_readdir); + // writer tests + ucx_test_register(suite, test_writer_putc); + ucx_test_register(suite, test_writer_flush); + ucx_test_register(suite, test_writer_put); + // xml tests ucx_test_register(suite, test_wsxml_iterator); ucx_test_register(suite, test_wsxml_get_required_namespaces); + ucx_test_register(suite, test_wsxml_write_nodes); // webdav tests ucx_test_register(suite, test_webdav_plist_add);
--- a/src/server/test/objs.mk Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/test/objs.mk Sat Jan 18 13:48:59 2020 +0100 @@ -35,6 +35,7 @@ TESTOBJ += webdav.o TESTOBJ += vfs.o TESTOBJ += xml.o +TESTOBJ += writer.o TESTOBJS = $(TESTOBJ:%=$(TEST_OBJPRE)%) TESTSOURCE = $(TESTOBJ:%.o=test/%.c)
--- a/src/server/test/testutils.c Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/test/testutils.c Sat Jan 18 13:48:59 2020 +0100 @@ -34,6 +34,8 @@ #include "../util/pblock.h" +#include "../util/io.h" + #include "testutils.h" Session* testutil_session(void) { @@ -92,3 +94,54 @@ void testutil_destroy_session(Session *sn) { pool_destroy(sn->pool); } + + +static ssize_t test_io_write(IOStream *io, void *buf, size_t size) { + TestIOStream *st = (TestIOStream*)io; + return ucx_buffer_write(buf, 1, size, st->buf); +} + +static ssize_t test_io_writev(IOStream *io, struct iovec *iovec, int iovctn) { + return -1; +} + +static ssize_t test_io_read(IOStream *io, void *buf, size_t size) { + return -1; +} + +static void test_io_close(IOStream *io) { + +} + +static void test_io_finish(IOStream *io) { + +} + +static void test_io_setmode(IOStream *io, int mode) { + +} + +static int test_io_poll(IOStream *io, EventHandler *ev, int events , Event *event) { + return 1; +} + +TestIOStream* testutil_iostream(size_t size, int autoextend) { + TestIOStream *stream = calloc(1, sizeof(TestIOStream)); + int flags = 0; + if(autoextend) { + flags = UCX_BUFFER_AUTOEXTEND; + } + stream->buf = ucx_buffer_new(NULL, size, flags); + + stream->io.write = test_io_write; + stream->io.writev = test_io_writev; + stream->io.close = test_io_close; + stream->io.finish = test_io_finish; + + return stream; +} + +void testutil_iostream_destroy(TestIOStream *stream) { + ucx_buffer_free(stream->buf); + free(stream); +}
--- a/src/server/test/testutils.h Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/test/testutils.h Sat Jan 18 13:48:59 2020 +0100 @@ -32,10 +32,18 @@ #include "../public/nsapi.h" #include "../daemon/httprequest.h" +#include "../util/io.h" +#include <ucx/buffer.h> + #ifdef __cplusplus extern "C" { #endif +typedef struct TestIOStream { + IOStream io; + UcxBuffer *buf; +} TestIOStream; + Session* testutil_session(void); Request* testutil_request(pool_handle_t *pool, const char *method, const char *uri); @@ -44,6 +52,9 @@ void testutil_destroy_session(Session *sn); +TestIOStream* testutil_iostream(size_t size, int autoextend); +void testutil_iostream_destroy(TestIOStream *stream); + #ifdef __cplusplus }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/writer.c Sat Jan 18 13:48:59 2020 +0100 @@ -0,0 +1,151 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * 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: + * + * 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 "../util/writer.h" + +#include <ucx/buffer.h> + +#include "writer.h" +#include "testutils.h" + +UCX_TEST(test_writer_putc) { + Session *sn = testutil_session(); + TestIOStream *st = testutil_iostream(2048, TRUE); + UcxBuffer *buf = st->buf; + + UCX_TEST_BEGIN; + + Writer writer; + char wbuf[1024]; + writer_init(&writer, st, wbuf, 4); + Writer *out = &writer; + + writer_putc(out, 'a'); + UCX_TEST_ASSERT(wbuf[0] == 'a', "1: wrong char at pos 0"); + UCX_TEST_ASSERT(writer.pos == 1, "1: wrong pos"); + + writer_putc(out, 'b'); + UCX_TEST_ASSERT(wbuf[1] == 'b', "2: wrong char at pos 1"); + UCX_TEST_ASSERT(writer.pos == 2, "2: wrong pos"); + + writer_putc(out, 'c'); + writer_putc(out, 'd'); + UCX_TEST_ASSERT(wbuf[2] == 'c', "3: wrong char at pos 2"); + UCX_TEST_ASSERT(wbuf[3] == 'd', "4: wrong char at pos 3"); + + writer_putc(out, 'f'); // should flush the buffer + UCX_TEST_ASSERT(wbuf[0] == 'f', "5: wrong char at pos 0"); + UCX_TEST_ASSERT(writer.pos == 1, "5: wrong pos"); + UCX_TEST_ASSERT(buf->space[0] == 'a', "5: wrong char at UcxBuffer pos 0"); + UCX_TEST_ASSERT(buf->space[1] == 'b', "5: wrong char at UcxBuffer pos 1"); + UCX_TEST_ASSERT(buf->pos == 4, "5: wrong UcxBuffer pos"); + + UCX_TEST_END; + testutil_iostream_destroy(st); + testutil_destroy_session(sn); +} + +UCX_TEST(test_writer_flush) { + Session *sn = testutil_session(); + TestIOStream *st = testutil_iostream(2048, TRUE); + UcxBuffer *buf = st->buf; + + UCX_TEST_BEGIN; + + Writer writer; + char wbuf[1024]; + writer_init(&writer, st, wbuf, 4); + Writer *out = &writer; + + writer_putc(out, 'a'); + UCX_TEST_ASSERT(wbuf[0] == 'a', "1: wrong char at pos 0"); + UCX_TEST_ASSERT(writer.pos == 1, "1: wrong pos"); + + writer_flush(out); + UCX_TEST_ASSERT(writer.pos == 0, "wrong pos after flush"); + UCX_TEST_ASSERT(buf->space[0] == 'a', "wrong UcxBuffer content"); + UCX_TEST_ASSERT(buf->pos == 1, "wrong UcxBuffer pos"); + + writer_putc(out, 'b'); + UCX_TEST_ASSERT(wbuf[0] == 'b', "2: wrong char at pos 0"); + UCX_TEST_ASSERT(writer.pos == 1, "2: wrong pos"); + + UCX_TEST_END; + testutil_iostream_destroy(st); + testutil_destroy_session(sn); +} + +UCX_TEST(test_writer_put) { + Session *sn = testutil_session(); + TestIOStream *st = testutil_iostream(2048, TRUE); + UcxBuffer *buf = st->buf; + + UCX_TEST_BEGIN; + + Writer writer; + char wbuf[1024]; + writer_init(&writer, st, wbuf, 8); + Writer *out = &writer; + + writer_put(out, "abcd", 4); + UCX_TEST_ASSERT(!memcmp(wbuf, "abcd", 4), "1: wrong content"); + UCX_TEST_ASSERT(writer.pos == 4, "1: wrong pos"); + + writer_put(out, "efgh", 4); + UCX_TEST_ASSERT(!memcmp(wbuf, "abcdefgh", 8), "2: wrong content"); + UCX_TEST_ASSERT(writer.pos == 8, "2: wrong pos"); + + writer_put(out, "1234", 4); + UCX_TEST_ASSERT(!memcmp(wbuf, "1234", 4), "3: wrong content"); + UCX_TEST_ASSERT(writer.pos == 4, "3: wrong pos"); + UCX_TEST_ASSERT(!memcmp(buf->space, "abcdefgh", 8), "3: wrong UcxBuffer content"); + UCX_TEST_ASSERT(buf->pos == 8, "3: wrong UcxBuffer pos"); + + writer_put(out, "5678xx", 6); + UCX_TEST_ASSERT(!memcmp(wbuf, "xx", 2), "4: wrong content"); + UCX_TEST_ASSERT(writer.pos == 2, "4: wrong pos"); + UCX_TEST_ASSERT(!memcmp(buf->space, "abcdefgh12345678", 16), "4: wrong UcxBuffer content"); + UCX_TEST_ASSERT(buf->pos == 16, "4: wrong UcxBuffer pos"); + + writer_puts(out, S("345678abcdefgh12345678end.")); + UCX_TEST_ASSERT(!memcmp(wbuf, "end.", 4), "5: wrong content"); + UCX_TEST_ASSERT(writer.pos == 4, "5: wrong pos"); + UCX_TEST_ASSERT(!memcmp( + buf->space, + "abcdefgh12345678xx345678abcdefgh12345678", + 40), + "5: wrong UcxBuffer content"); + UCX_TEST_ASSERT(buf->pos == 40, "5: wrong UcxBuffer pos"); + + UCX_TEST_END; + testutil_iostream_destroy(st); + testutil_destroy_session(sn); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/writer.h Sat Jan 18 13:48:59 2020 +0100 @@ -0,0 +1,47 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * 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: + * + * 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 TEST_WRITER_H +#define TEST_WRITER_H + +#include <ucx/test.h> + +#ifdef __cplusplus +extern "C" { +#endif + +UCX_TEST(test_writer_putc); +UCX_TEST(test_writer_flush); +UCX_TEST(test_writer_put); + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_WRITER_H */ +
--- a/src/server/test/xml.c Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/test/xml.c Sat Jan 18 13:48:59 2020 +0100 @@ -36,6 +36,10 @@ #include "testutils.h" #include "../public/webdav.h" +#include "../util/writer.h" + +#include "../webdav/webdav.h" +#include "../webdav/xml.h" typedef struct Test1Data { int beginCounter; @@ -45,6 +49,7 @@ int textErr; int err; int endErr; + int nodesWithAttributesCounter; xmlNode *prev; } Test1Data; @@ -126,6 +131,27 @@ return 0; } +static int test2_begin(xmlNode *node, void *userdata) { + Test1Data *data = userdata; + data->beginCounter++; + if(node->type == XML_ELEMENT_NODE) { + data->elmCounter++; + if(node->properties) { + data->nodesWithAttributesCounter++; + } + } + return 0; +} + +static int test2_end(xmlNode *node, void *userdata) { + Test1Data *data = userdata; + data->endCounter++; + if(node->type == XML_ELEMENT_NODE) { + data->endElmCounter++; + } + return 0; +} + UCX_TEST(test_wsxml_iterator) { Session *sn = testutil_session(); @@ -135,8 +161,11 @@ XML_TESTDATA1, strlen(XML_TESTDATA1), NULL, NULL, 0); xmlDoc *doc2 = xmlReadMemory( XML_TESTDATA2, strlen(XML_TESTDATA2), NULL, NULL, 0); + xmlDoc *doc6 = xmlReadMemory( + XML_TESTDATA6, strlen(XML_TESTDATA6), NULL, NULL, 0); UCX_TEST_ASSERT(doc, "doc is NULL"); UCX_TEST_ASSERT(doc2, "doc2 is NULL"); + UCX_TEST_ASSERT(doc6, "doc6 is NULL"); xmlNode *root = xmlDocGetRootElement(doc); @@ -161,8 +190,18 @@ UCX_TEST_ASSERT(!testdata.textErr, "test2: text order error"); UCX_TEST_ASSERT(testdata.beginCounter == testdata.endCounter, "test2: begin/end counter not equal"); + // Test 3: iterate over document with all kinds of node types + xmlNode *root6 = xmlDocGetRootElement(doc6); + ZERO(&testdata, sizeof(Test1Data)); + ret = wsxml_iterator(sn->pool, root6, test2_begin, test2_end, &testdata); + UCX_TEST_ASSERT(ret == 0, "test3: wsxml_iterator failed"); + UCX_TEST_ASSERT(testdata.elmCounter == testdata.endElmCounter, "test3: begin/end counter not equal"); + UCX_TEST_ASSERT(testdata.elmCounter == 12, "test3: wrong elm counter"); + UCX_TEST_ASSERT(testdata.nodesWithAttributesCounter == 5, "test3: wrong entity ref counter"); + xmlFreeDoc(doc); xmlFreeDoc(doc2); + xmlFreeDoc(doc6); UCX_TEST_END; } @@ -257,5 +296,42 @@ UCX_TEST_ASSERT(!x3, "ns3: x3"); UCX_TEST_ASSERT(!x4, "ns3: x4"); + xmlFreeDoc(doc3); + xmlFreeDoc(doc4); + xmlFreeDoc(doc5); UCX_TEST_END; } + +UCX_TEST(test_wsxml_write_nodes) { + Session *sn = testutil_session(); + TestIOStream *st = testutil_iostream(2048, TRUE); + + UCX_TEST_BEGIN; + xmlDoc *doc = xmlReadMemory( + XML_TESTDATA6, strlen(XML_TESTDATA6), NULL, NULL, 0); + UCX_TEST_ASSERT(doc, "xml parser error"); + xmlNode *root = xmlDocGetRootElement(doc); + + Writer writer; + char buffer[1024]; + writer_init(&writer, st, buffer, 1024); + + int err = wsxml_write_nodes(sn->pool, &writer, NULL, root); + writer_flush(&writer); + UCX_TEST_ASSERT(err == 0, "wsxml_write_nodes error"); + UCX_TEST_ASSERT(st->buf->pos > 0, "buffer is empty"); + + //printf("\n\n"); + //printf("%.*s\n", (int)st->buf->size, st->buf->space); + //printf("\n\n"); + + xmlDoc *genDoc = xmlReadMemory( + st->buf->space, st->buf->size, NULL, NULL, 0); + UCX_TEST_ASSERT(genDoc, "generated doc is not valid xml"); + + xmlFreeDoc(doc); + xmlFreeDoc(genDoc); + + UCX_TEST_END; + testutil_iostream_destroy(st); +}
--- a/src/server/test/xml.h Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/test/xml.h Sat Jan 18 13:48:59 2020 +0100 @@ -38,6 +38,7 @@ UCX_TEST(test_wsxml_iterator); UCX_TEST(test_wsxml_get_required_namespaces); +UCX_TEST(test_wsxml_write_nodes); #define XML_TESTDATA1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \ @@ -98,6 +99,30 @@ <x4:elm4 xmlns:x4=\"http://example.com/ns_0/\" >str1</x4:elm4>\ </x1:prop>" +#define XML_TESTDATA6 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \n\ + <x1:test \n\ + xmlns:x1=\"http://example.com/ns1/\" \n\ + xmlns:x2=\"http://example.com/ns2/\" > \n\ + <x1:elm1>str1</x1:elm1>\n\ + <x2:elm2>str1</x2:elm2>\n\ + <x3:elm3 xmlns:x3=\"http://example.com/ns_0/\" >str1</x3:elm3>\n\ + <x1:sub> \n\ + <x1:a attr1=\"val1\"/> \n\ + <x1:a attr2=\"val2\">text</x1:a>\n\ + <x1:b x2:nsattr=\"nsval\"><x1:c/></x1:b>\n\ + </x1:sub> \n\ + <x1:newns xmlns:x4=\"http://example.com/0/\" x4:attr3=\"val3\">\n\ + </x1:newns>\n\ + <x1:text>Hello\n\ + World\n\ + end.\n\ + </x1:text>\n\ + <x1:entityref ea=\"test & value\">\n\ + entity reference test &quote& \n\ + <xml>\n\ + </x1:entityref>\n\ + </x1:test>\n" + #ifdef __cplusplus } #endif
--- a/src/server/util/writer.c Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/util/writer.c Sat Jan 18 13:48:59 2020 +0100 @@ -71,6 +71,7 @@ return w->error; } + // available bytes size_t a = w->size - w->pos; if(a == 0) { if(writer_flush(w)) { @@ -78,11 +79,13 @@ } } - size_t cplen = len > a ? a : len; - memcpy(w->buffer, s, cplen); + size_t cplen = len > a ? a : len; // number of bytes we can copy + memcpy(w->buffer+w->pos, s, cplen); w->pos += cplen; if(cplen < len) { + // not all bytes copied -> call writer_put again + // the number of available bytes is 0 then, therefore flush is called return writer_put(w, s + cplen, len - cplen); } else { return 0;
--- a/src/server/webdav/xml.c Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/webdav/xml.c Sat Jan 18 13:48:59 2020 +0100 @@ -37,13 +37,15 @@ #include "xml.h" -/* ------------------------ utils ------------------------ */ +/***************************************************************************** + * Utility functions + *****************************************************************************/ /* * generates a string key for an xml namespace * format: prefix '\0' href */ -static sstr_t namespace_key(UcxAllocator *a, WSNamespace *ns) { +static sstr_t xml_namespace_key(UcxAllocator *a, WSNamespace *ns) { sstr_t key = sstrcat_a(a, 3, ns->prefix ? sstr((char*)ns->prefix) : S("\0"), S("\0"), @@ -52,6 +54,10 @@ } +/***************************************************************************** + * Public functions + *****************************************************************************/ + /* ------------------------ wsxml_iterator ------------------------ */ typedef struct StackElm { @@ -184,7 +190,7 @@ return ret; } -/* ------------------------ wsxml_get_namespaces ------------------------ */ +/* ------------------- wsxml_get_required_namespaces ------------------- */ typedef struct WSNsCollector { UcxAllocator *a; @@ -199,7 +205,7 @@ if(node->type == XML_ELEMENT_NODE && node->ns) { // we create a list of unique prefix-href namespaces by putting // all namespaces in a map - sstr_t nskey = namespace_key(col->a, node->ns); + sstr_t nskey = xml_namespace_key(col->a, node->ns); if(!nskey.ptr) { col->error = 1; return 1; @@ -271,7 +277,7 @@ // what we get is a map that contains all missing namespace definitions WebdavNSList *def = col.def; while(def) { - sstr_t nskey = namespace_key(&a, def->namespace); + sstr_t nskey = xml_namespace_key(&a, def->namespace); if(!nskey.ptr) { if(error) *error = 1; break; @@ -305,3 +311,243 @@ ucx_map_free(nsmap); return list; } + + +/***************************************************************************** + * Non public functions + *****************************************************************************/ + +typedef struct XmlWriter { + /* + * Memory pool for temp memory allocations + */ + pool_handle_t *pool; + + /* + * Buffered output stream + */ + Writer *out; + + /* + * Map for all previously defined namespaces + * key: (char*) namespace prefix + * value: WSNamespace* + */ + UcxMap *namespaces; + + /* + * Should namespace definitions be created + */ + WSBool define_namespaces; +} XmlWriter; + +/* + * Serialize an XML text node + * This replaces some special characters with entity refs + */ +static void xml_ser_text(Writer *out, const char *text) { + size_t start = 0; + size_t i; + sstr_t entityref = { NULL, 0 }; + for(i=0;text[i]!='\0';i++) { + switch(text[i]) { + case '<': entityref = S("<"); break; + case '>': entityref = S(">"); break; + case '&': entityref = S("&"); break; + case '\"': entityref = S("""); break; + case '\'': entityref = S("'"); break; + } + if(entityref.ptr) { + size_t len = i-start; + if(len > 0) { + writer_put(out, text+start, len); + } + writer_puts(out, entityref); + entityref.ptr = NULL; + entityref.length = 0; + start = i+1; + } + } + size_t len = i-start; + if(len > 0) { + writer_put(out, text+start, len); + } +} + +/* + * Serialize an XML element node + */ +static void xml_ser_element(XmlWriter *xw, xmlNode *node) { + Writer *out = xw->out; + writer_putc(out, '<'); + + // write prefix and ':' + if(node->ns && node->ns->prefix) { + writer_puts(out, sstr((char*)node->ns->prefix)); + writer_putc(out, ':'); + } + + // node name + writer_puts(out, sstr((char*)node->name)); + + // namespace definitions + if(xw->define_namespaces) { + xmlNs *nsdef = node->nsDef; + while(nsdef) { + // we define only namespaces without prefix or namespaces + // with prefix, that are not already defined + // xw->namespaces contains all namespace, that were defined + // before xml serialization + if(!nsdef->prefix) { + writer_puts(out, S(" xmlns=\"")); + writer_puts(out, sstr((char*)nsdef->href)); + writer_putc(out, '"'); + } else { + WSNamespace *n = xw->namespaces ? + ucx_map_cstr_get(xw->namespaces, (char*)nsdef->prefix) : + NULL; + if(!n) { + writer_puts(out, S(" xmlns:")); + writer_puts(out, sstr((char*)nsdef->prefix)); + writer_puts(out, S("=\"")); + writer_puts(out, sstr((char*)nsdef->href)); + writer_putc(out, '"'); + } + } + + nsdef = nsdef->next; + } + } + + // attributes + xmlAttr *attr = node->properties; + while(attr) { + // format: ' [<prefix>:]<name>="<value>"' + writer_putc(out, ' '); + // optional namespace + if(attr->ns && attr->ns->prefix) { + writer_puts(out, sstr((char*)attr->ns->prefix)); + writer_putc(out, ':'); + } + // <name>=" + writer_puts(out, sstr((char*)attr->name)); + writer_puts(out, S("=\"")); + // value + xmlNode *value = attr->children; + while(value) { + if(value->content) { + xml_ser_text(out, (const char*)value->content); + } + value = value->next; + } + // trailing quote + writer_putc(out, '"'); + + attr = attr->next; + } + + if(node->children) { + writer_putc(out, '>'); + } else { + writer_puts(out, S("/>")); + } +} + +static int xml_ser_node_begin(xmlNode *node, void *userdata) { + XmlWriter *xw = userdata; + switch(node->type) { + case XML_ELEMENT_NODE: xml_ser_element(xw, node); break; + case XML_ATTRIBUTE_NODE: break; + case XML_TEXT_NODE: { + xml_ser_text(xw->out, (const char*)node->content); + break; + } + case XML_CDATA_SECTION_NODE: { + break; + } + case XML_ENTITY_REF_NODE: break; + case XML_ENTITY_NODE: break; + case XML_PI_NODE: break; + case XML_COMMENT_NODE: break; + case XML_DOCUMENT_NODE: break; + case XML_DOCUMENT_TYPE_NODE: break; + case XML_DOCUMENT_FRAG_NODE: break; + case XML_NOTATION_NODE: break; + case XML_HTML_DOCUMENT_NODE: break; + case XML_DTD_NODE: break; + case XML_ELEMENT_DECL: break; + case XML_ATTRIBUTE_DECL: break; + case XML_ENTITY_DECL: break; + case XML_NAMESPACE_DECL: break; + case XML_XINCLUDE_START: break; + case XML_XINCLUDE_END: break; + default: break; + } + return 0; +} + +static int xml_ser_node_end(xmlNode *node, void *userdata) { + XmlWriter *xw = userdata; + Writer *out = xw->out; + if(node->type == XML_ELEMENT_NODE) { + if(node->children) { + writer_puts(xw->out, S("</")); + // write prefix and ':' + if(node->ns && node->ns->prefix) { + writer_puts(out, sstr((char*)node->ns->prefix)); + writer_putc(out, ':'); + } + // name and close tag + writer_puts(out, sstr((char*)node->name)); + writer_putc(out, '>'); + + } // element was already closed in xml_ser_node_begin + } + return 0; +} + + +static int xml_write_nodes( + pool_handle_t *pool, + Writer *out, + UcxMap *nsdefs, + WSBool createdefs, + xmlNode *node) +{ + XmlWriter xmlwriter; + xmlwriter.pool = pool; + xmlwriter.out = out; + xmlwriter.namespaces = nsdefs; + xmlwriter.define_namespaces = createdefs; + + // iterate over xml nodes + // this includes node->children and node->next + int err = wsxml_iterator( + pool, + node, + xml_ser_node_begin, + xml_ser_node_end, + &xmlwriter); + if(err) { + return -1; + } + + return out->error; +} + +int wsxml_write_nodes( + pool_handle_t *pool, + Writer *out, + UcxMap *nsdefs, + xmlNode *node) +{ + return xml_write_nodes(pool, out, nsdefs, TRUE, node); +} + +int wsxml_write_nodes_without_nsdef( + pool_handle_t *pool, + Writer *out, + xmlNode *node) +{ + return xml_write_nodes(pool, out, NULL, FALSE, node); +}
--- a/src/server/webdav/xml.h Fri Jan 17 22:23:30 2020 +0100 +++ b/src/server/webdav/xml.h Sat Jan 18 13:48:59 2020 +0100 @@ -26,22 +26,43 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef XML_H -#define XML_H +#ifndef WEBDAV_XML_H +#define WEBDAV_XML_H #include "../public/webdav.h" #include <libxml/tree.h> +#include <ucx/map.h> + +#include "../util/writer.h" + #ifdef __cplusplus extern "C" { #endif +/* + * Writes the xmlNode, all children and following nodes to the writer 'out'. + */ +int wsxml_write_nodes( + pool_handle_t *pool, + Writer *out, + UcxMap *nsdefs, + xmlNode *node); +/* + * Writes the xmlNode, all children and following nodes to the writer 'out' + * without creating any namespace definitions. Therefore all namespaces must + * be already defined and previously written to 'out'. + */ +int wsxml_write_nodes_without_nsdef( + pool_handle_t *pool, + Writer *out, + xmlNode *node); #ifdef __cplusplus } #endif -#endif /* XML_H */ +#endif /* WEBDAV_XML_H */