|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2018 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 <cx/utils.h> |
|
34 #include <cx/printf.h> |
|
35 |
|
36 #include "xml.h" |
|
37 |
|
38 static DavXmlNodeType convert_type(xmlElementType type) { |
|
39 DavXmlNodeType ct; |
|
40 switch(type) { |
|
41 default: ct = DAV_XML_NONE; break; |
|
42 case XML_ELEMENT_NODE: ct = DAV_XML_ELEMENT; break; |
|
43 case XML_TEXT_NODE: ct = DAV_XML_TEXT; |
|
44 } |
|
45 return ct; |
|
46 } |
|
47 |
|
48 typedef struct { |
|
49 xmlNode *node; |
|
50 DavXmlNode *parent; |
|
51 } ConvXmlElm; |
|
52 |
|
53 DavXmlNode* dav_convert_xml(DavSession *sn, xmlNode *node) { |
|
54 if(!node) { |
|
55 return NULL; |
|
56 } |
|
57 DavXmlNodeType newnt = convert_type(node->type); |
|
58 if(newnt == DAV_XML_NONE) { |
|
59 return NULL; |
|
60 } |
|
61 |
|
62 const CxAllocator *a = sn->mp->allocator; |
|
63 |
|
64 ConvXmlElm ce; |
|
65 ce.node = node; |
|
66 ce.parent = NULL; |
|
67 CxList *stack = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(ConvXmlElm)); |
|
68 if(!stack) { |
|
69 return NULL; |
|
70 } |
|
71 cxListInsert(stack, 0, &ce); |
|
72 |
|
73 DavXmlNode *ret = NULL; |
|
74 |
|
75 while(stack->size > 0) { |
|
76 ConvXmlElm *c = cxListAt(stack, 0); |
|
77 xmlNode *n = c->node; |
|
78 DavXmlNode *c_parent = c->parent; |
|
79 DavXmlNode *prev = NULL; |
|
80 cxListRemove(stack, 0); |
|
81 while(n) { |
|
82 DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode)); |
|
83 if(!ret) { |
|
84 ret = newxn; |
|
85 } |
|
86 newxn->type = convert_type(n->type); |
|
87 newxn->parent = c_parent; |
|
88 if(c_parent && !c_parent->children) { |
|
89 c_parent->children = newxn; |
|
90 } |
|
91 newxn->prev = prev; |
|
92 if(prev) { |
|
93 prev->next = newxn; |
|
94 } |
|
95 |
|
96 if(newxn->type == DAV_XML_ELEMENT) { |
|
97 newxn->name = dav_session_strdup(sn, (char*)n->name); |
|
98 if(n->ns && n->ns->href) { |
|
99 newxn->namespace = dav_session_strdup(sn, (char*)n->ns->href); |
|
100 } |
|
101 |
|
102 xmlAttr *attr = n->properties; |
|
103 DavXmlAttr *newattr = NULL; |
|
104 DavXmlAttr *newattr_last = NULL; |
|
105 while(attr) { |
|
106 DavXmlAttr *na = cxCalloc(a, 1, sizeof(DavXmlAttr)); |
|
107 na->name = dav_session_strdup(sn, (char*)attr->name); |
|
108 if(attr->children && attr->children->type == XML_TEXT_NODE) { |
|
109 na->value = dav_session_strdup(sn, (char*)attr->children->content); |
|
110 } |
|
111 if(!newattr) { |
|
112 newattr = na; |
|
113 } else { |
|
114 newattr_last->next = na; |
|
115 } |
|
116 newattr_last = na; |
|
117 |
|
118 attr = attr->next; |
|
119 } |
|
120 newxn->attributes = newattr; |
|
121 |
|
122 if(n->children) { |
|
123 ConvXmlElm convc; |
|
124 convc.node = n->children; |
|
125 convc.parent = newxn; |
|
126 cxListInsert(stack, 0, &convc); |
|
127 } |
|
128 } else if(newxn->type == DAV_XML_TEXT) { |
|
129 cxmutstr content = cx_strdup_a(a, cx_str((char*)n->content)); |
|
130 newxn->content = content.ptr; |
|
131 newxn->contentlength = content.length; |
|
132 } |
|
133 |
|
134 prev = newxn; |
|
135 n = n->next; |
|
136 } |
|
137 } |
|
138 |
|
139 return ret; |
|
140 } |
|
141 |
|
142 void dav_print_xml(DavXmlNode *node) { |
|
143 if(node->type == DAV_XML_ELEMENT) { |
|
144 printf("<%s", node->name); |
|
145 DavXmlAttr *attr = node->attributes; |
|
146 while(attr) { |
|
147 printf(" %s=\"%s\"", attr->name, attr->value); |
|
148 attr = attr->next; |
|
149 } |
|
150 putchar('>'); |
|
151 |
|
152 DavXmlNode *child = node->children; |
|
153 if(child) { |
|
154 dav_print_xml(child); |
|
155 } |
|
156 |
|
157 printf("</%s>", node->name); |
|
158 } else { |
|
159 fwrite(node->content, 1, node->contentlength, stdout); |
|
160 fflush(stdout); |
|
161 } |
|
162 if(node->next) { |
|
163 dav_print_xml(node->next); |
|
164 } |
|
165 } |
|
166 |
|
167 void dav_print_node(void *stream, cx_write_func writef, CxMap *nsmap, DavXmlNode *node) { |
|
168 while(node) { |
|
169 if(node->type == DAV_XML_ELEMENT) { |
|
170 char *tagend = node->children ? ">" : " />"; |
|
171 char *prefix = NULL; |
|
172 char *prefix_fr = NULL; |
|
173 if(node->namespace) { |
|
174 prefix = cxMapGet(nsmap, cx_hash_key_str(node->namespace)); |
|
175 if(!prefix) { |
|
176 cxmutstr newpre = cx_asprintf("x%d", (int)nsmap->size+1); |
|
177 // TODO: fix namespace declaration |
|
178 //ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr); |
|
179 prefix = newpre.ptr; |
|
180 prefix_fr = prefix; |
|
181 cx_fprintf( |
|
182 stream, |
|
183 writef, |
|
184 "<%s:%s xmlns:%s=\"%s\"", |
|
185 prefix, |
|
186 node->name, |
|
187 prefix, |
|
188 node->namespace); |
|
189 } else { |
|
190 cx_fprintf(stream, writef, "<%s:%s", prefix, node->name); |
|
191 } |
|
192 } else { |
|
193 cx_fprintf(stream, writef, "<%s", node->name); |
|
194 } |
|
195 |
|
196 DavXmlAttr *attr = node->attributes; |
|
197 while(attr) { |
|
198 cx_fprintf(stream, writef, " %s=\"%s\"", attr->name, attr->value); |
|
199 attr = attr->next; |
|
200 } |
|
201 writef(tagend, 1, strlen(tagend), stream); // end xml tag |
|
202 |
|
203 if(node->children) { |
|
204 dav_print_node(stream, writef, nsmap, node->children); |
|
205 if(prefix) { |
|
206 cx_fprintf(stream, writef, "</%s:%s>", prefix, node->name); |
|
207 } else { |
|
208 cx_fprintf(stream, writef, "</%s>", node->name); |
|
209 } |
|
210 } |
|
211 |
|
212 if(prefix_fr) { |
|
213 free(prefix_fr); |
|
214 } |
|
215 } else if(node->type == DAV_XML_TEXT) { |
|
216 writef(node->content, 1, node->contentlength, stream); |
|
217 } |
|
218 |
|
219 node = node->next; |
|
220 } |
|
221 } |
|
222 |
|
223 /* ------------------------- public API ------------------------- */ |
|
224 |
|
225 char* dav_xml_getstring(DavXmlNode *node) { |
|
226 if(node && node->type == DAV_XML_TEXT) { |
|
227 return node->content; |
|
228 } else { |
|
229 return NULL; |
|
230 } |
|
231 } |
|
232 |
|
233 DavBool dav_xml_isstring(DavXmlNode *node) { |
|
234 if(node && node->type == DAV_XML_TEXT && !node->next) { |
|
235 return TRUE; |
|
236 } else { |
|
237 return FALSE; |
|
238 } |
|
239 } |
|
240 |
|
241 DavXmlNode* dav_xml_nextelm(DavXmlNode *node) { |
|
242 node = node->next; |
|
243 while(node) { |
|
244 if(node->type == DAV_XML_ELEMENT) { |
|
245 return node; |
|
246 } |
|
247 node = node->next; |
|
248 } |
|
249 return NULL; |
|
250 } |
|
251 |
|
252 DavXmlNode* dav_text_node(DavSession *sn, const char *text) { |
|
253 const CxAllocator *a = sn->mp->allocator; |
|
254 DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode)); |
|
255 newxn->type = DAV_XML_TEXT; |
|
256 cxmutstr content = cx_strdup_a(a, cx_str(text)); |
|
257 newxn->content = content.ptr; |
|
258 newxn->contentlength = content.length; |
|
259 return newxn; |
|
260 } |
|
261 |
|
262 DavXmlNode* dav_text_element(DavSession *sn, const char *ns, const char *name, const char *text) { |
|
263 const CxAllocator *a = sn->mp->allocator; |
|
264 DavXmlNode *newelm = cxCalloc(a, 1, sizeof(DavXmlNode)); |
|
265 newelm->type = DAV_XML_ELEMENT; |
|
266 newelm->namespace = cx_strdup_a(a, cx_str(ns)).ptr; |
|
267 newelm->name = cx_strdup_a(a, cx_str(name)).ptr; |
|
268 newelm->children = dav_text_node(sn, text); |
|
269 return newelm; |
|
270 } |
|
271 |
|
272 static void dav_free_xml_node_a(const CxAllocator *a, DavXmlNode *node) { |
|
273 if(node->name) cxFree(a, node->name); |
|
274 if(node->namespace) cxFree(a, node->namespace); |
|
275 if(node->content) cxFree(a, node->content); |
|
276 DavXmlAttr *attr = node->attributes; |
|
277 while(attr) { |
|
278 if(attr->name) cxFree(a, attr->name); |
|
279 if(attr->value) cxFree(a, attr->value); |
|
280 attr = attr->next; |
|
281 } |
|
282 DavXmlNode *children = node->children; |
|
283 while(children) { |
|
284 DavXmlNode *next_ch = children->next; |
|
285 dav_free_xml_node_a(a, children); |
|
286 children = next_ch; |
|
287 } |
|
288 cxFree(a, node); |
|
289 } |
|
290 |
|
291 void dav_free_xml_node_sn(DavSession *sn, DavXmlNode *node) { |
|
292 dav_free_xml_node_a(sn->mp->allocator, node); |
|
293 } |
|
294 |
|
295 void dav_free_xml_node(DavXmlNode *node) { |
|
296 dav_free_xml_node_a(cxDefaultAllocator, node); |
|
297 } |
|
298 |
|
299 DavXmlAttr* dav_copy_xml_attr(DavXmlAttr *attr) { |
|
300 if(!attr) { |
|
301 return NULL; |
|
302 } |
|
303 DavXmlAttr *newattr = NULL; |
|
304 DavXmlAttr *prev = NULL; |
|
305 while(attr) { |
|
306 DavXmlAttr *n = calloc(1, sizeof(DavXmlAttr)); |
|
307 n->name = strdup(attr->name); |
|
308 n->value = strdup(attr->value); |
|
309 if(prev) { |
|
310 prev->next = n; |
|
311 } else { |
|
312 newattr = n; |
|
313 } |
|
314 prev = n; |
|
315 attr = attr->next; |
|
316 } |
|
317 return newattr; |
|
318 } |
|
319 |
|
320 DavXmlNode* dav_copy_node(DavXmlNode *node) { |
|
321 DavXmlNode *ret = NULL; |
|
322 DavXmlNode *prev = NULL; |
|
323 while(node) { |
|
324 DavXmlNode *copy = calloc(1, sizeof(DavXmlNode)); |
|
325 copy->type = node->type; |
|
326 if(node->type == DAV_XML_ELEMENT) { |
|
327 copy->namespace = strdup(node->namespace); |
|
328 copy->name = strdup(node->name); |
|
329 copy->children = dav_copy_node(node->children); |
|
330 copy->attributes = dav_copy_xml_attr(node->attributes); |
|
331 } else { |
|
332 copy->contentlength = node->contentlength; |
|
333 copy->content = malloc(node->contentlength+1); |
|
334 memcpy(copy->content, node->content, node->contentlength); |
|
335 copy->content[copy->contentlength] = 0; |
|
336 } |
|
337 if(!ret) { |
|
338 ret = copy; |
|
339 } |
|
340 if(prev) { |
|
341 prev->next = copy; |
|
342 copy->prev = prev; |
|
343 } |
|
344 prev = copy; |
|
345 node = node->next; |
|
346 } |
|
347 return ret; |
|
348 } |
|
349 |
|
350 |
|
351 DavXmlNode* dav_xml_createnode(const char *ns, const char *name) { |
|
352 DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); |
|
353 node->type = DAV_XML_ELEMENT; |
|
354 node->namespace = strdup(ns); |
|
355 node->name = strdup(name); |
|
356 return node; |
|
357 } |
|
358 |
|
359 DavXmlNode* dav_xml_createnode_with_text(const char *ns, const char *name, const char *text) { |
|
360 DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); |
|
361 node->type = DAV_XML_ELEMENT; |
|
362 node->namespace = strdup(ns); |
|
363 node->name = strdup(name); |
|
364 |
|
365 DavXmlNode *textnode = dav_xml_createtextnode(text); |
|
366 node->children = textnode; |
|
367 |
|
368 return node; |
|
369 } |
|
370 |
|
371 DavXmlNode* dav_xml_createtextnode(const char *text) { |
|
372 DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); |
|
373 node->type = DAV_XML_TEXT; |
|
374 cxmutstr content = cx_strdup(cx_str((char*)text)); |
|
375 node->content = content.ptr; |
|
376 node->contentlength = content.length; |
|
377 return node; |
|
378 } |
|
379 |
|
380 void dav_xml_add_child(DavXmlNode *node, DavXmlNode *child) { |
|
381 DavXmlNode *last_child = NULL; |
|
382 DavXmlNode *c = node->children; |
|
383 while(c) { |
|
384 last_child = c; |
|
385 c = c->next; |
|
386 } |
|
387 if(last_child) { |
|
388 last_child->next = child; |
|
389 child->prev = last_child; |
|
390 } else { |
|
391 node->children = child; |
|
392 } |
|
393 } |
|
394 |
|
395 void dav_xml_add_attr(DavXmlNode *node, const char *name, const char *value) { |
|
396 DavXmlAttr *attr = calloc(1, sizeof(DavXmlAttr)); |
|
397 attr->name = strdup(name); |
|
398 attr->value = strdup(value); |
|
399 |
|
400 if(node->attributes) { |
|
401 DavXmlAttr *end = node->attributes; |
|
402 DavXmlAttr* last = end; |
|
403 while(end) { |
|
404 last = end; |
|
405 end = end->next; |
|
406 } |
|
407 last->next = attr; |
|
408 } else { |
|
409 node->attributes = attr; |
|
410 } |
|
411 } |
|
412 |
|
413 char* dav_xml_get_attr(DavXmlNode *node, const char *name) { |
|
414 DavXmlAttr *attr = node->attributes; |
|
415 while(attr) { |
|
416 if(!strcmp(attr->name, name)) { |
|
417 return attr->value; |
|
418 } |
|
419 |
|
420 attr = attr->next; |
|
421 } |
|
422 return NULL; |
|
423 } |
|
424 |
|
425 DavXmlNode* dav_parse_xml(DavSession *sn, const char *str, size_t len) { |
|
426 xmlDoc *doc = xmlReadMemory(str, len, NULL, NULL, 0); |
|
427 if(!doc) { |
|
428 return NULL; |
|
429 } |
|
430 xmlNode *xml_root = xmlDocGetRootElement(doc); |
|
431 if(!xml_root) { |
|
432 xmlFreeDoc(doc); |
|
433 return NULL; |
|
434 } |
|
435 DavXmlNode *x = dav_convert_xml(sn, xml_root); |
|
436 xmlFreeDoc(doc); |
|
437 return x; |
|
438 } |