diff -r 2483f517c562 -r b5bb7b3cd597 libidav/xml.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/xml.c Mon Jan 22 17:27:47 2024 +0100 @@ -0,0 +1,438 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2018 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 +#include +#include + +#include +#include + +#include "xml.h" + +static DavXmlNodeType convert_type(xmlElementType type) { + DavXmlNodeType ct; + switch(type) { + default: ct = DAV_XML_NONE; break; + case XML_ELEMENT_NODE: ct = DAV_XML_ELEMENT; break; + case XML_TEXT_NODE: ct = DAV_XML_TEXT; + } + return ct; +} + +typedef struct { + xmlNode *node; + DavXmlNode *parent; +} ConvXmlElm; + +DavXmlNode* dav_convert_xml(DavSession *sn, xmlNode *node) { + if(!node) { + return NULL; + } + DavXmlNodeType newnt = convert_type(node->type); + if(newnt == DAV_XML_NONE) { + return NULL; + } + + const CxAllocator *a = sn->mp->allocator; + + ConvXmlElm ce; + ce.node = node; + ce.parent = NULL; + CxList *stack = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(ConvXmlElm)); + if(!stack) { + return NULL; + } + cxListInsert(stack, 0, &ce); + + DavXmlNode *ret = NULL; + + while(stack->size > 0) { + ConvXmlElm *c = cxListAt(stack, 0); + xmlNode *n = c->node; + DavXmlNode *c_parent = c->parent; + DavXmlNode *prev = NULL; + cxListRemove(stack, 0); + while(n) { + DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode)); + if(!ret) { + ret = newxn; + } + newxn->type = convert_type(n->type); + newxn->parent = c_parent; + if(c_parent && !c_parent->children) { + c_parent->children = newxn; + } + newxn->prev = prev; + if(prev) { + prev->next = newxn; + } + + if(newxn->type == DAV_XML_ELEMENT) { + newxn->name = dav_session_strdup(sn, (char*)n->name); + if(n->ns && n->ns->href) { + newxn->namespace = dav_session_strdup(sn, (char*)n->ns->href); + } + + xmlAttr *attr = n->properties; + DavXmlAttr *newattr = NULL; + DavXmlAttr *newattr_last = NULL; + while(attr) { + DavXmlAttr *na = cxCalloc(a, 1, sizeof(DavXmlAttr)); + na->name = dav_session_strdup(sn, (char*)attr->name); + if(attr->children && attr->children->type == XML_TEXT_NODE) { + na->value = dav_session_strdup(sn, (char*)attr->children->content); + } + if(!newattr) { + newattr = na; + } else { + newattr_last->next = na; + } + newattr_last = na; + + attr = attr->next; + } + newxn->attributes = newattr; + + if(n->children) { + ConvXmlElm convc; + convc.node = n->children; + convc.parent = newxn; + cxListInsert(stack, 0, &convc); + } + } else if(newxn->type == DAV_XML_TEXT) { + cxmutstr content = cx_strdup_a(a, cx_str((char*)n->content)); + newxn->content = content.ptr; + newxn->contentlength = content.length; + } + + prev = newxn; + n = n->next; + } + } + + return ret; +} + +void dav_print_xml(DavXmlNode *node) { + if(node->type == DAV_XML_ELEMENT) { + printf("<%s", node->name); + DavXmlAttr *attr = node->attributes; + while(attr) { + printf(" %s=\"%s\"", attr->name, attr->value); + attr = attr->next; + } + putchar('>'); + + DavXmlNode *child = node->children; + if(child) { + dav_print_xml(child); + } + + printf("", node->name); + } else { + fwrite(node->content, 1, node->contentlength, stdout); + fflush(stdout); + } + if(node->next) { + dav_print_xml(node->next); + } +} + +void dav_print_node(void *stream, cx_write_func writef, CxMap *nsmap, DavXmlNode *node) { + while(node) { + if(node->type == DAV_XML_ELEMENT) { + char *tagend = node->children ? ">" : " />"; + char *prefix = NULL; + char *prefix_fr = NULL; + if(node->namespace) { + prefix = cxMapGet(nsmap, cx_hash_key_str(node->namespace)); + if(!prefix) { + cxmutstr newpre = cx_asprintf("x%d", (int)nsmap->size+1); + // TODO: fix namespace declaration + //ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr); + prefix = newpre.ptr; + prefix_fr = prefix; + cx_fprintf( + stream, + writef, + "<%s:%s xmlns:%s=\"%s\"", + prefix, + node->name, + prefix, + node->namespace); + } else { + cx_fprintf(stream, writef, "<%s:%s", prefix, node->name); + } + } else { + cx_fprintf(stream, writef, "<%s", node->name); + } + + DavXmlAttr *attr = node->attributes; + while(attr) { + cx_fprintf(stream, writef, " %s=\"%s\"", attr->name, attr->value); + attr = attr->next; + } + writef(tagend, 1, strlen(tagend), stream); // end xml tag + + if(node->children) { + dav_print_node(stream, writef, nsmap, node->children); + if(prefix) { + cx_fprintf(stream, writef, "", prefix, node->name); + } else { + cx_fprintf(stream, writef, "", node->name); + } + } + + if(prefix_fr) { + free(prefix_fr); + } + } else if(node->type == DAV_XML_TEXT) { + writef(node->content, 1, node->contentlength, stream); + } + + node = node->next; + } +} + +/* ------------------------- public API ------------------------- */ + +char* dav_xml_getstring(DavXmlNode *node) { + if(node && node->type == DAV_XML_TEXT) { + return node->content; + } else { + return NULL; + } +} + +DavBool dav_xml_isstring(DavXmlNode *node) { + if(node && node->type == DAV_XML_TEXT && !node->next) { + return TRUE; + } else { + return FALSE; + } +} + +DavXmlNode* dav_xml_nextelm(DavXmlNode *node) { + node = node->next; + while(node) { + if(node->type == DAV_XML_ELEMENT) { + return node; + } + node = node->next; + } + return NULL; +} + +DavXmlNode* dav_text_node(DavSession *sn, const char *text) { + const CxAllocator *a = sn->mp->allocator; + DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode)); + newxn->type = DAV_XML_TEXT; + cxmutstr content = cx_strdup_a(a, cx_str(text)); + newxn->content = content.ptr; + newxn->contentlength = content.length; + return newxn; +} + +DavXmlNode* dav_text_element(DavSession *sn, const char *ns, const char *name, const char *text) { + const CxAllocator *a = sn->mp->allocator; + DavXmlNode *newelm = cxCalloc(a, 1, sizeof(DavXmlNode)); + newelm->type = DAV_XML_ELEMENT; + newelm->namespace = cx_strdup_a(a, cx_str(ns)).ptr; + newelm->name = cx_strdup_a(a, cx_str(name)).ptr; + newelm->children = dav_text_node(sn, text); + return newelm; +} + +static void dav_free_xml_node_a(const CxAllocator *a, DavXmlNode *node) { + if(node->name) cxFree(a, node->name); + if(node->namespace) cxFree(a, node->namespace); + if(node->content) cxFree(a, node->content); + DavXmlAttr *attr = node->attributes; + while(attr) { + if(attr->name) cxFree(a, attr->name); + if(attr->value) cxFree(a, attr->value); + attr = attr->next; + } + DavXmlNode *children = node->children; + while(children) { + DavXmlNode *next_ch = children->next; + dav_free_xml_node_a(a, children); + children = next_ch; + } + cxFree(a, node); +} + +void dav_free_xml_node_sn(DavSession *sn, DavXmlNode *node) { + dav_free_xml_node_a(sn->mp->allocator, node); +} + +void dav_free_xml_node(DavXmlNode *node) { + dav_free_xml_node_a(cxDefaultAllocator, node); +} + +DavXmlAttr* dav_copy_xml_attr(DavXmlAttr *attr) { + if(!attr) { + return NULL; + } + DavXmlAttr *newattr = NULL; + DavXmlAttr *prev = NULL; + while(attr) { + DavXmlAttr *n = calloc(1, sizeof(DavXmlAttr)); + n->name = strdup(attr->name); + n->value = strdup(attr->value); + if(prev) { + prev->next = n; + } else { + newattr = n; + } + prev = n; + attr = attr->next; + } + return newattr; +} + +DavXmlNode* dav_copy_node(DavXmlNode *node) { + DavXmlNode *ret = NULL; + DavXmlNode *prev = NULL; + while(node) { + DavXmlNode *copy = calloc(1, sizeof(DavXmlNode)); + copy->type = node->type; + if(node->type == DAV_XML_ELEMENT) { + copy->namespace = strdup(node->namespace); + copy->name = strdup(node->name); + copy->children = dav_copy_node(node->children); + copy->attributes = dav_copy_xml_attr(node->attributes); + } else { + copy->contentlength = node->contentlength; + copy->content = malloc(node->contentlength+1); + memcpy(copy->content, node->content, node->contentlength); + copy->content[copy->contentlength] = 0; + } + if(!ret) { + ret = copy; + } + if(prev) { + prev->next = copy; + copy->prev = prev; + } + prev = copy; + node = node->next; + } + return ret; +} + + +DavXmlNode* dav_xml_createnode(const char *ns, const char *name) { + DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); + node->type = DAV_XML_ELEMENT; + node->namespace = strdup(ns); + node->name = strdup(name); + return node; +} + +DavXmlNode* dav_xml_createnode_with_text(const char *ns, const char *name, const char *text) { + DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); + node->type = DAV_XML_ELEMENT; + node->namespace = strdup(ns); + node->name = strdup(name); + + DavXmlNode *textnode = dav_xml_createtextnode(text); + node->children = textnode; + + return node; +} + +DavXmlNode* dav_xml_createtextnode(const char *text) { + DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); + node->type = DAV_XML_TEXT; + cxmutstr content = cx_strdup(cx_str((char*)text)); + node->content = content.ptr; + node->contentlength = content.length; + return node; +} + +void dav_xml_add_child(DavXmlNode *node, DavXmlNode *child) { + DavXmlNode *last_child = NULL; + DavXmlNode *c = node->children; + while(c) { + last_child = c; + c = c->next; + } + if(last_child) { + last_child->next = child; + child->prev = last_child; + } else { + node->children = child; + } +} + +void dav_xml_add_attr(DavXmlNode *node, const char *name, const char *value) { + DavXmlAttr *attr = calloc(1, sizeof(DavXmlAttr)); + attr->name = strdup(name); + attr->value = strdup(value); + + if(node->attributes) { + DavXmlAttr *end = node->attributes; + DavXmlAttr* last = end; + while(end) { + last = end; + end = end->next; + } + last->next = attr; + } else { + node->attributes = attr; + } +} + +char* dav_xml_get_attr(DavXmlNode *node, const char *name) { + DavXmlAttr *attr = node->attributes; + while(attr) { + if(!strcmp(attr->name, name)) { + return attr->value; + } + + attr = attr->next; + } + return NULL; +} + +DavXmlNode* dav_parse_xml(DavSession *sn, const char *str, size_t len) { + xmlDoc *doc = xmlReadMemory(str, len, NULL, NULL, 0); + if(!doc) { + return NULL; + } + xmlNode *xml_root = xmlDocGetRootElement(doc); + if(!xml_root) { + xmlFreeDoc(doc); + return NULL; + } + DavXmlNode *x = dav_convert_xml(sn, xml_root); + xmlFreeDoc(doc); + return x; +}