From 4a4248cbfd9c289ea16d68b45d1dbaa8a5daf064 Mon Sep 17 00:00:00 2001 From: Vlad Patrascu Date: Mon, 4 Jul 2022 10:58:20 +0300 Subject: [PATCH] xml: fix accessing node value with CDATA sections --- modules/xml/xml.c | 108 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 10 deletions(-) diff --git a/modules/xml/xml.c b/modules/xml/xml.c index 0f484b9e963..a7556116422 100644 --- a/modules/xml/xml.c +++ b/modules/xml/xml.c @@ -462,7 +462,9 @@ static int get_node_content(xmlNode *node, xmlBufferPtr xml_buf) xmlNode *n_it; for (n_it = node->children; n_it; n_it = n_it->next) - if (n_it->type == XML_TEXT_NODE && xmlBufferCat(xml_buf, n_it->content) < 0) { + if ((n_it->type == XML_TEXT_NODE || + n_it->type == XML_CDATA_SECTION_NODE) && + xmlBufferCat(xml_buf, n_it->content) < 0) { LM_ERR("Unable to append string to xml buffer\n"); return -1; } @@ -686,25 +688,111 @@ static int insert_new_node(xmlDoc *doc, xmlNode *parent, xmlDoc *new_doc, xml_pa return 0; } -static int set_node_content(xmlNode *node, str new_content) +#define TMP_TAG_S "" +#define TMP_TAG_END_S "" +#define TMP_TAG_LEN (sizeof(TMP_TAG_S) - 1) +#define TMP_TAG_END_LEN (sizeof(TMP_TAG_END_S) - 1) + +static int set_node_content_w_cdata(xmlNode *node, str new_content, + xmlDoc *xml_doc) +{ + xmlNode *n, *tmp = NULL, *tmp_root; + xmlDoc *tmp_doc = NULL; + str tmp_doc_str = STR_NULL; + + /* remove all existing CDATA and text nodes */ + for (n = node->children; n; n = tmp) { + tmp = n->next; + + if (n->type == XML_TEXT_NODE || n->type == XML_CDATA_SECTION_NODE) { + xmlUnlinkNode(n); + xmlFreeNode(n); + } + } + + /* build a temporary XML doc just to help us parse the cdata sections and + * potentially extra text; then copy the corresponding nodes to the + * original tree */ + tmp_doc_str.len = new_content.len + TMP_TAG_LEN + TMP_TAG_END_LEN; + tmp_doc_str.s = pkg_malloc(tmp_doc_str.len); + if (!tmp_doc_str.s) { + LM_ERR("no more pkg memory\n"); + return -1; + } + + memcpy(tmp_doc_str.s, TMP_TAG_S, TMP_TAG_LEN); + memcpy(tmp_doc_str.s+TMP_TAG_LEN, new_content.s, new_content.len); + memcpy(tmp_doc_str.s+TMP_TAG_LEN+new_content.len, + TMP_TAG_END_S, TMP_TAG_END_LEN); + + tmp_doc = xmlParseMemory(tmp_doc_str.s, tmp_doc_str.len); + if (!tmp_doc) { + LM_ERR("Failed to parse xml block\n"); + goto error; + } + + tmp_root = xmlDocGetRootElement(tmp_doc); + for (n = tmp_root->children; n; n = n->next) { + if (n->type == XML_TEXT_NODE || n->type == XML_CDATA_SECTION_NODE) { + tmp = xmlDocCopyNode(n, xml_doc, 0); + if (!tmp) { + LM_ERR("Failed to copy node\n"); + goto error; + } + + if (!xmlAddChild(node, tmp)) { + LM_ERR("Unable to link copied node\n"); + goto error; + } + } + } + + xmlFreeDoc(tmp_doc); + pkg_free(tmp_doc_str.s); + + return 0; +error: + if (tmp_doc_str.s) + pkg_free(tmp_doc_str.s); + if (tmp_doc) + xmlFreeDoc(tmp_doc); + return -1; +} + +#define CDATA_PREFIX_S "" +#define CDATA_PREFIX_LEN (sizeof(CDATA_PREFIX_S) - 1) +#define CDATA_SUFFIX_LEN (sizeof(CDATA_SUFFIX_S) - 1) + +static int set_node_content(xmlNode *node, str new_content, xmlDoc *xml_doc) { xmlNode *n_it, *tmp = NULL, *new_txt; int set = 0; - /* remove all text nodes */ + if ((new_content.len > CDATA_PREFIX_LEN + CDATA_SUFFIX_LEN) && + str_strstr(&new_content, &str_init(CDATA_PREFIX_S))) + return set_node_content_w_cdata(node, new_content, xml_doc); + + /* remove all text and CDATA nodes */ if (!new_content.s) set = 1; for (n_it = node->children; n_it; n_it = tmp) { tmp = n_it->next; - if (n_it->type == XML_TEXT_NODE && !xmlIsBlankNode(n_it)) { + if (n_it->type == XML_TEXT_NODE || n_it->type == XML_CDATA_SECTION_NODE) { if (!set) { - /* replace existing text node content with given string */ - xmlNodeSetContentLen(n_it, BAD_CAST new_content.s, new_content.len); - set = 1; + if (n_it->type == XML_CDATA_SECTION_NODE) { + xmlUnlinkNode(n_it); + xmlFreeNode(n_it); + } else { + /* replace existing node content with given string */ + xmlNodeSetContentLen(n_it, + BAD_CAST new_content.s, new_content.len); + set = 1; + } } else { - /* remove any other text node */ + /* remove any other text or CDATA node */ xmlUnlinkNode(n_it); xmlFreeNode(n_it); } @@ -793,7 +881,7 @@ int pv_set_xml(struct sip_msg* msg, pv_param_t* pvp, int flag, pv_value_t* val) return -1; } - if (set_node_content(node, empty_str) < 0) { + if (set_node_content(node, empty_str, obj->xml_doc) < 0) { LM_ERR("Unable to clear text content for element <%s>\n", node->name); return -1; } @@ -895,7 +983,7 @@ int pv_set_xml(struct sip_msg* msg, pv_param_t* pvp, int flag, pv_value_t* val) return -1; } - if (set_node_content(node, val->rs) < 0) { + if (set_node_content(node, val->rs, obj->xml_doc) < 0) { LM_ERR("Unable to set content for element <%s>\n", node->name); return -1; }