src/server/webdav/xml.c

changeset 385
a1f4cb076d2f
parent 324
44cf877b3d9f
child 415
d938228c382e
equal deleted inserted replaced
210:21274e5950af 385:a1f4cb076d2f
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2019 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <ucx/string.h>
34 #include <ucx/map.h>
35 #include <ucx/buffer.h>
36
37 #include "../util/util.h"
38 #include "../util/pool.h"
39
40 #include "xml.h"
41
42 /*****************************************************************************
43 * Utility functions
44 *****************************************************************************/
45
46 /*
47 * generates a string key for an xml namespace
48 * format: prefix '\0' href
49 */
50 static sstr_t xml_namespace_key(UcxAllocator *a, WSNamespace *ns) {
51 sstr_t key = sstrcat_a(a, 3,
52 ns->prefix ? sstr((char*)ns->prefix) : S("\0"),
53 S("\0"),
54 sstr((char*)ns->href));
55 return key;
56 }
57
58
59 /*****************************************************************************
60 * Public functions
61 *****************************************************************************/
62
63 /* ------------------------ wsxml_iterator ------------------------ */
64
65 typedef struct StackElm {
66 WSXmlNode *node; // list of nodes
67 //WSXmlNode *parent; // if not NULL, call endcb after node->next is NULL
68 int endonly;
69 struct StackElm *next;
70 } StackElm;
71
72 #define STACK_PUSH(stack, elm) if(stack) { elm->next = stack; } stack = elm;
73
74 int wsxml_iterator(
75 pool_handle_t *pool,
76 WSXmlNode *node,
77 wsxml_func begincb,
78 wsxml_func endcb,
79 void *udata)
80 {
81 if(!node) {
82 return 0;
83 }
84
85 StackElm *stack = pool_malloc(pool, sizeof(StackElm));
86 if(!stack) {
87 return 1; // OOM
88 }
89 stack->next = NULL;
90 stack->node = node;
91 stack->endonly = 0;
92 //stack->parent = NULL;
93
94 int ret = 0;
95 int br = 0;
96 while(stack) {
97 StackElm *cur = stack;
98 WSXmlNode *xmlnode = cur->node; // get top stack element
99 stack = cur->next; // and remove it
100 cur->next = NULL;
101
102 while(xmlnode && !cur->endonly) {
103 // element begin callback
104 if(begincb(xmlnode, udata)) {
105 br = 1;
106 break; // I don't like break with labels - is this wrong?
107 }
108
109 if(xmlnode->children) {
110 // put the children on the stack
111 // the next stack iteration will process the children
112 StackElm *newelm = pool_malloc(pool, sizeof(StackElm));
113 if(!newelm) {
114 ret = 1;
115 br = 1;
116 break;
117 }
118 newelm->next = NULL;
119 newelm->node = xmlnode->children;
120 // setting the parent will make sure endcb will be called
121 // for the current xmlnode after all children are processed
122 //newelm->parent = xmlnode;
123 newelm->endonly = 0;
124
125 // if xmlnode->next is not NULL, there are still nodes at
126 // this level, therefore we have to put these also on the
127 // stack
128 // this way, the remaining nodes are processed after all
129 // children and the end tag are processed
130 if(xmlnode->next) {
131 StackElm *nextelm = pool_malloc(pool, sizeof(StackElm));
132 if(!nextelm) {
133 ret = 1;
134 br = 1;
135 break;
136 }
137 nextelm->node = xmlnode->next;
138 nextelm->next = NULL;
139 nextelm->endonly = 0;
140 STACK_PUSH(stack, nextelm);
141 }
142
143 // we have to put the end tag of the current element
144 // on the stack to ensure endcb is called for the current
145 // element, after all children are processed
146 // reuse cur
147 cur->node = xmlnode;
148 cur->endonly = 1;
149 STACK_PUSH(stack, cur);
150
151 cur = NULL;
152
153 // now we can put the children on the stack
154 STACK_PUSH(stack, newelm);
155 // break, because we don't want to process xmlnode->next now
156 break;
157 } else {
158 // no children means, the end callback can be called directly
159 // after the begin callback (no intermediate nodes)
160 cur->node = NULL;
161 if(endcb(xmlnode, udata)) {
162 br = 1;
163 break;
164 }
165 }
166
167 // continue with next node at this level
168 xmlnode = xmlnode->next;
169 }
170 if(br) {
171 break; // break because of an error
172 }
173
174 if(cur && cur->node) {
175 //xmlNode *endNode = cur->parent ? cur->parent : cur->node;
176 xmlNode *endNode = cur->node;
177 if(endcb(endNode, udata)) {
178 break;
179 }
180 pool_free(pool, cur);
181 }
182 }
183
184 // free all remaining elements
185 StackElm *elm = stack;
186 while(elm) {
187 StackElm *next = elm->next;
188 pool_free(pool, elm);
189 elm = next;
190 }
191
192 return ret;
193 }
194
195 /* ------------------- wsxml_get_required_namespaces ------------------- */
196
197 typedef struct WSNsCollector {
198 UcxAllocator *a;
199 UcxMap *nsmap;
200 WebdavNSList *def;
201 int error;
202 } WSNsCollector;
203
204 static int nslist_node_begin(xmlNode *node, void *userdata) {
205 WSNsCollector *col = userdata;
206 // namespace required for all elements
207 if(node->type == XML_ELEMENT_NODE && node->ns) {
208 // we create a list of unique prefix-href namespaces by putting
209 // all namespaces in a map
210 sstr_t nskey = xml_namespace_key(col->a, node->ns);
211 if(!nskey.ptr) {
212 col->error = 1;
213 return 1;
214 }
215 if(ucx_map_sstr_put(col->nsmap, nskey, node->ns)) {
216 col->error = 1;
217 return 1;
218 }
219
220 // collect all namespace definitions for removing these namespaces
221 // from col->nsmap later
222 WSNamespace *def = node->nsDef;
223 while(def) {
224 WebdavNSList *newdef = col->a->malloc(
225 col->a->pool, sizeof(WebdavNSList));
226 if(!newdef) {
227 col->error = 1;
228 return 1;
229 }
230 newdef->namespace = def;
231 newdef->prev = NULL;
232 newdef->next = NULL;
233 // prepend newdef to the list
234 if(col->def) {
235 newdef->next = col->def;
236 col->def->prev = newdef;
237 }
238 col->def = newdef;
239
240 // continue with next namespace definition
241 def = def->next;
242 }
243 }
244 return 0;
245 }
246
247 static int nslist_node_end(xmlNode *node, void *userdata) {
248 return 0;
249 }
250
251 WebdavNSList* wsxml_get_required_namespaces(
252 pool_handle_t *pool,
253 WSXmlNode *node,
254 int *error)
255 {
256 if(error) *error = 0;
257
258 UcxAllocator a = util_pool_allocator(pool);
259 UcxMap *nsmap = ucx_map_new_a(&a, 16);
260 if(!nsmap) {
261 if(error) *error = 1;
262 return NULL;
263 }
264
265 WSNsCollector col;
266 col.a = &a;
267 col.nsmap = nsmap;
268 col.def = NULL;
269
270 // iterate over all xml elements
271 // this will fill the hashmap with all namespaces
272 // all namespace definitions are added to col.def
273 WebdavNSList *list = NULL;
274 WebdavNSList *end = NULL;
275 if(wsxml_iterator(pool, node, nslist_node_begin, nslist_node_end, &col)) {
276 if(error) *error = 1;
277 } else {
278 // remove all namespace definitions from the map
279 // what we get is a map that contains all missing namespace definitions
280 WebdavNSList *def = col.def;
281 while(def) {
282 sstr_t nskey = xml_namespace_key(&a, def->namespace);
283 if(!nskey.ptr) {
284 if(error) *error = 1;
285 break;
286 }
287 ucx_map_sstr_remove(nsmap, nskey);
288 def = def->next;
289 }
290
291 // convert nsmap to a list
292 UcxMapIterator i = ucx_map_iterator(nsmap);
293 WSNamespace *ns;
294 UCX_MAP_FOREACH(key, ns, i) {
295 WebdavNSList *newelm = pool_malloc(pool, sizeof(WebdavNSList));
296 if(!newelm) {
297 if(error) *error = 1;
298 list = NULL;
299 break;
300 }
301 newelm->namespace = ns;
302 newelm->next = NULL;
303 newelm->prev = end; // NULL or the end of list
304 if(end) {
305 end->next = newelm; // append new element
306 } else {
307 list = newelm; // start new list
308 }
309 end = newelm;
310 }
311 }
312
313 ucx_map_free(nsmap);
314 return list;
315 }
316
317
318 static ssize_t buf_writefunc(void *buf, const char *s, size_t len) {
319 int w = ucx_buffer_write(s, 1, len, buf);
320 return w == 0 ? IO_ERROR : w;
321 }
322
323 WSXmlData* wsxml_node2data(
324 pool_handle_t *pool,
325 WSXmlNode *node)
326 {
327 UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
328 if(!buf) {
329 return NULL;
330 }
331
332 int error = 0;
333 WebdavNSList *nslist = wsxml_get_required_namespaces(pool, node, &error);
334 if(error) {
335 return NULL;
336 }
337
338 Writer writer;
339 char buffer[512];
340 writer_init_with_stream(&writer, buf, buf_writefunc, buffer, 512);
341
342 WSXmlData *data = NULL;
343 if(!wsxml_write_nodes(pool, &writer, NULL, node) && !writer_flush(&writer)) {
344 data = pool_malloc(pool, sizeof(WSXmlData));
345 if(data) {
346 data->data = pool_malloc(pool, buf->size + 1);
347 if(data->data) {
348 memcpy(data->data, buf->space, buf->size);
349 data->data[buf->size] = '\0';
350 data->length = buf->size;
351 data->namespaces = nslist;
352 }
353 }
354 }
355
356 ucx_buffer_free(buf);
357
358 return data;
359 }
360
361 char* wsxml_nslist2string(pool_handle_t *pool, WebdavNSList *nslist) {
362 if(!nslist) return NULL;
363
364 // get required string length
365 size_t len = 0;
366 WebdavNSList *elm = nslist;
367 while(elm) {
368 WSNamespace *ns = elm->namespace;
369 if(ns) {
370 if(ns->prefix) len += strlen((const char*)ns->prefix);
371 if(ns->href) len += strlen((const char*)ns->href);
372 len += 2; // 1 char for ':', 1 char for \n or \0
373 }
374 elm = elm->next;
375 }
376
377 // alloc string
378 char *str = pool_malloc(pool, len);
379 if(!str) {
380 return NULL;
381 }
382 char *pos = str;
383
384 // copy namespace definitions to the string
385 elm = nslist;
386 while(elm) {
387 WSNamespace *ns = elm->namespace;
388 if(ns) {
389 if(ns->prefix) {
390 size_t prefixlen = strlen((const char*)ns->prefix);
391 memcpy(pos, ns->prefix, prefixlen);
392 pos[prefixlen] = ':';
393 pos += prefixlen + 1;
394 } else {
395 pos[0] = ':';
396 pos++;
397 }
398 if(ns->href) {
399 size_t hreflen = strlen((const char*)ns->href);
400 memcpy(pos, ns->href, hreflen);
401 pos[hreflen] = elm->next ? '\n' : '\0';
402 pos += hreflen + 1;
403 } else {
404 pos[0] = elm->next ? '\n' : '\0';
405 pos++;
406 }
407 }
408 elm = elm->next;
409 }
410
411 return str;
412 }
413
414 WebdavNSList* wsxml_string2nslist(pool_handle_t *pool, char *nsliststr) {
415 if(!nsliststr) return NULL;
416 size_t len = strlen(nsliststr);
417 WebdavNSList *list_start = NULL;
418 WebdavNSList *list_current = NULL;
419
420 char *prefix = nsliststr;
421 size_t prefix_start = 0;
422 size_t prefix_len = 0;
423 char *href = NULL;
424 size_t href_start = len;
425 size_t i;
426 for(i=0;i<=len;i++) {
427 char c = nsliststr[i];
428 if(c == '\n' || c == '\0') {
429 if(i > href_start) {
430 WebdavNSList *elm = pool_malloc(pool, sizeof(WebdavNSList));
431 if(!elm) {
432 break;
433 }
434 elm->prev = list_current;
435 elm->next = NULL;
436 WSNamespace *ns = pool_malloc(pool, sizeof(WSNamespace));
437 elm->namespace = ns;
438 if(!ns) {
439 break;
440 }
441 memset(ns, 0, sizeof(WSNamespace));
442 ns->prefix = prefix_len > 0 ? (xmlChar*)sstrdup_pool(pool, sstrn(prefix, prefix_len)).ptr : NULL;
443 ns->href = (xmlChar*)sstrdup_pool(pool, sstrn(href, i-href_start)).ptr;
444 if(list_current) {
445 list_current->next = elm;
446 } else {
447 list_start = elm;
448 }
449 list_current = elm;
450 }
451 prefix_start = i + 1;
452 prefix = nsliststr + prefix_start;
453 prefix_len = 0;
454 href_start = len;
455 href = NULL;
456 } else if(!href && c == ':') {
457 prefix_len = i - prefix_start;
458 href_start = i + 1;
459 href = nsliststr + href_start;
460 }
461 }
462
463 if(i < len) {
464 // error, cleanup
465 while(list_start) {
466 if(list_start->namespace) {
467 WSNamespace *ns = list_start->namespace;
468 if(ns->prefix) {
469 pool_free(pool, (char*)ns->prefix);
470 }
471 if(ns->href) {
472 pool_free(pool, (char*)ns->href);
473 }
474 pool_free(pool, ns);
475 }
476 WebdavNSList *next = list_start->next;
477 pool_free(pool, list_start);
478 list_start = next;
479 }
480 list_start = NULL;
481 }
482
483 return list_start;
484 }
485
486 /*****************************************************************************
487 * Non public functions
488 *****************************************************************************/
489
490 typedef struct XmlWriter {
491 /*
492 * Memory pool for temp memory allocations
493 */
494 pool_handle_t *pool;
495
496 /*
497 * Buffered output stream
498 */
499 Writer *out;
500
501 /*
502 * Map for all previously defined namespaces
503 * key: (char*) namespace prefix
504 * value: WSNamespace*
505 */
506 UcxMap *namespaces;
507
508 /*
509 * Should namespace definitions be created
510 */
511 WSBool define_namespaces;
512 } XmlWriter;
513
514 /*
515 * Serialize an XML text node
516 * This replaces some special characters with entity refs
517 * type: 0 = element text, 1 = attribute text
518 */
519 static void xml_ser_text(Writer *out, int type, const char *text) {
520 size_t start = 0;
521 size_t i;
522 sstr_t entityref = { NULL, 0 };
523 for(i=0;text[i]!='\0';i++) {
524 char c = text[i];
525 if(c == '&') {
526 entityref = S("&amp;");
527 } else if(type == 0) {
528 if(c == '<') {
529 entityref = S("&lt;");
530 } else if(c == '>') {
531 entityref = S("&gt;");
532 }
533 } else {
534 if(c == '\"') {
535 entityref = S("&quot;");
536 } else if(c == '\'') {
537 entityref = S("&apos;");
538 }
539 }
540
541 if(entityref.ptr) {
542 size_t len = i-start;
543 if(len > 0) {
544 writer_put(out, text+start, len);
545 }
546 writer_puts(out, entityref);
547 entityref.ptr = NULL;
548 entityref.length = 0;
549 start = i+1;
550 }
551 }
552 size_t len = i-start;
553 if(len > 0) {
554 writer_put(out, text+start, len);
555 }
556 }
557
558 /*
559 * Serialize an XML element node
560 */
561 static void xml_ser_element(XmlWriter *xw, xmlNode *node) {
562 Writer *out = xw->out;
563 writer_putc(out, '<');
564
565 // write prefix and ':'
566 if(node->ns && node->ns->prefix) {
567 writer_puts(out, sstr((char*)node->ns->prefix));
568 writer_putc(out, ':');
569 }
570
571 // node name
572 writer_puts(out, sstr((char*)node->name));
573
574 // namespace definitions
575 if(xw->define_namespaces) {
576 xmlNs *nsdef = node->nsDef;
577 while(nsdef) {
578 // we define only namespaces without prefix or namespaces
579 // with prefix, that are not already defined
580 // xw->namespaces contains all namespace, that were defined
581 // before xml serialization
582 if(!nsdef->prefix) {
583 writer_puts(out, S(" xmlns=\""));
584 writer_puts(out, sstr((char*)nsdef->href));
585 writer_putc(out, '"');
586 } else {
587 WSNamespace *n = xw->namespaces ?
588 ucx_map_cstr_get(xw->namespaces, (char*)nsdef->prefix) :
589 NULL;
590 if(!n) {
591 writer_puts(out, S(" xmlns:"));
592 writer_puts(out, sstr((char*)nsdef->prefix));
593 writer_puts(out, S("=\""));
594 writer_puts(out, sstr((char*)nsdef->href));
595 writer_putc(out, '"');
596 }
597 }
598
599 nsdef = nsdef->next;
600 }
601 }
602
603 // attributes
604 xmlAttr *attr = node->properties;
605 while(attr) {
606 // format: ' [<prefix>:]<name>="<value>"'
607 writer_putc(out, ' ');
608 // optional namespace
609 if(attr->ns && attr->ns->prefix) {
610 writer_puts(out, sstr((char*)attr->ns->prefix));
611 writer_putc(out, ':');
612 }
613 // <name>="
614 writer_puts(out, sstr((char*)attr->name));
615 writer_puts(out, S("=\""));
616 // value
617 xmlNode *value = attr->children;
618 while(value) {
619 if(value->content) {
620 xml_ser_text(out, 1, (const char*)value->content);
621 }
622 value = value->next;
623 }
624 // trailing quote
625 writer_putc(out, '"');
626
627 attr = attr->next;
628 }
629
630 if(node->children) {
631 writer_putc(out, '>');
632 } else {
633 writer_puts(out, S("/>"));
634 }
635 }
636
637 static int xml_ser_node_begin(xmlNode *node, void *userdata) {
638 XmlWriter *xw = userdata;
639 switch(node->type) {
640 case XML_ELEMENT_NODE: xml_ser_element(xw, node); break;
641 case XML_ATTRIBUTE_NODE: break;
642 case XML_TEXT_NODE: {
643 xml_ser_text(xw->out, 0, (const char*)node->content);
644 break;
645 }
646 case XML_CDATA_SECTION_NODE: {
647 break;
648 }
649 case XML_ENTITY_REF_NODE: break;
650 case XML_ENTITY_NODE: break;
651 case XML_PI_NODE: break;
652 case XML_COMMENT_NODE: break;
653 case XML_DOCUMENT_NODE: break;
654 case XML_DOCUMENT_TYPE_NODE: break;
655 case XML_DOCUMENT_FRAG_NODE: break;
656 case XML_NOTATION_NODE: break;
657 case XML_HTML_DOCUMENT_NODE: break;
658 case XML_DTD_NODE: break;
659 case XML_ELEMENT_DECL: break;
660 case XML_ATTRIBUTE_DECL: break;
661 case XML_ENTITY_DECL: break;
662 case XML_NAMESPACE_DECL: break;
663 case XML_XINCLUDE_START: break;
664 case XML_XINCLUDE_END: break;
665 default: break;
666 }
667 return 0;
668 }
669
670 static int xml_ser_node_end(xmlNode *node, void *userdata) {
671 XmlWriter *xw = userdata;
672 Writer *out = xw->out;
673 if(node->type == XML_ELEMENT_NODE) {
674 if(node->children) {
675 writer_puts(xw->out, S("</"));
676 // write prefix and ':'
677 if(node->ns && node->ns->prefix) {
678 writer_puts(out, sstr((char*)node->ns->prefix));
679 writer_putc(out, ':');
680 }
681 // name and close tag
682 writer_puts(out, sstr((char*)node->name));
683 writer_putc(out, '>');
684
685 } // element was already closed in xml_ser_node_begin
686 }
687 return 0;
688 }
689
690
691 static int xml_write_nodes(
692 pool_handle_t *pool,
693 Writer *out,
694 UcxMap *nsdefs,
695 WSBool createdefs,
696 xmlNode *node)
697 {
698 XmlWriter xmlwriter;
699 xmlwriter.pool = pool;
700 xmlwriter.out = out;
701 xmlwriter.namespaces = nsdefs;
702 xmlwriter.define_namespaces = createdefs;
703
704 // iterate over xml nodes
705 // this includes node->children and node->next
706 int err = wsxml_iterator(
707 pool,
708 node,
709 xml_ser_node_begin,
710 xml_ser_node_end,
711 &xmlwriter);
712 if(err) {
713 return -1;
714 }
715
716 return out->error;
717 }
718
719 int wsxml_write_nodes(
720 pool_handle_t *pool,
721 Writer *out,
722 UcxMap *nsdefs,
723 xmlNode *node)
724 {
725 return xml_write_nodes(pool, out, nsdefs, TRUE, node);
726 }
727
728 int wsxml_write_nodes_without_nsdef(
729 pool_handle_t *pool,
730 Writer *out,
731 xmlNode *node)
732 {
733 return xml_write_nodes(pool, out, NULL, FALSE, node);
734 }

mercurial