Skip to content

Commit

Permalink
xml: fix accessing node value with CDATA sections
Browse files Browse the repository at this point in the history
  • Loading branch information
rvlad-patrascu committed Jul 4, 2022
1 parent d7da7d2 commit 4a4248c
Showing 1 changed file with 98 additions and 10 deletions.
108 changes: 98 additions & 10 deletions modules/xml/xml.c
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 "<x>"
#define TMP_TAG_END_S "</x>"
#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 "<![CDATA["
#define CDATA_SUFFIX_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);
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 4a4248c

Please sign in to comment.