# HG changeset patch # User Paul Aurich # Date 1315170386 0 # Node ID 8d3b5853b0174d75ed315f3cfe72160305759eff # Parent 114a98da1a5fd4baf1393c15f5574e5351fd2ca8 xmlnode: Add xmlnode_strip_prefixes This is largely based on a patch from Thijs (sphynx/xnyhps) Alkemade, with some modifications by me to try to maintain namespaces of elements as best as we can. I also rewrote xmlnode_get_default_namespace not to use recursion. References #14529 diff -r 114a98da1a5f -r 8d3b5853b017 libpurple/tests/test_xmlnode.c --- a/libpurple/tests/test_xmlnode.c Sun Sep 04 18:52:18 2011 +0000 +++ b/libpurple/tests/test_xmlnode.c Sun Sep 04 21:06:26 2011 +0000 @@ -81,6 +81,41 @@ } END_TEST + +START_TEST(test_strip_prefixes) +{ + const char *xml_doc = "" + "" + "xvlc xvlc" + "" + "" + "xvlc xvlc" + "" + "" + ""; + const char *out = "" + "" + "xvlc xvlc" + "" + "" + "

xvlc xvlc

" + "" + "" + "
"; + char *str; + xmlnode *xml; + + xml = xmlnode_from_str(xml_doc, -1); + fail_if(xml == NULL, "Failed to parse XML"); + + xmlnode_strip_prefixes(xml); + str = xmlnode_to_str(xml, NULL); + assert_string_equal_free(out, str); + + xmlnode_free(xml); +} +END_TEST + Suite * xmlnode_suite(void) { @@ -89,6 +124,7 @@ TCase *tc = tcase_create("xmlnode"); tcase_add_test(tc, test_xmlnode_billion_laughs_attack); tcase_add_test(tc, test_xmlnode_prefixes); + tcase_add_test(tc, test_strip_prefixes); suite_add_tcase(s, tc); diff -r 114a98da1a5f -r 8d3b5853b017 libpurple/xmlnode.c --- a/libpurple/xmlnode.c Sun Sep 04 18:52:18 2011 +0000 +++ b/libpurple/xmlnode.c Sun Sep 04 21:06:26 2011 +0000 @@ -262,10 +262,18 @@ void xmlnode_set_namespace(xmlnode *node, const char *xmlns) { + char *tmp; g_return_if_fail(node != NULL); - g_free(node->xmlns); + tmp = node->xmlns; node->xmlns = g_strdup(xmlns); + + if (node->namespace_map) { + g_hash_table_insert(node->namespace_map, + g_strdup(""), g_strdup(xmlns)); + } + + g_free(tmp); } const char *xmlnode_get_namespace(const xmlnode *node) @@ -277,22 +285,27 @@ const char *xmlnode_get_default_namespace(const xmlnode *node) { + const xmlnode *current_node; const char *ns = NULL; + g_return_val_if_fail(node != NULL, NULL); - /* If this node does *not* have a prefix, node->xmlns is the default - * namespace. Otherwise, it's the prefix namespace. - */ - if (!node->prefix && node->xmlns) { - return node->xmlns; - } else if (node->namespace_map) { - ns = g_hash_table_lookup(node->namespace_map, ""); + current_node = node; + while (current_node) { + /* If this node does *not* have a prefix, node->xmlns is the default + * namespace. Otherwise, it's the prefix namespace. + */ + if (!current_node->prefix && current_node->xmlns) { + return current_node->xmlns; + } else if (current_node->namespace_map) { + ns = g_hash_table_lookup(current_node->namespace_map, ""); + if (ns && *ns) + return ns; + } + + current_node = current_node->parent; } - /* No default ns found? Walk up the tree looking for one */ - if (!(ns && *ns) && node->parent) - ns = xmlnode_get_default_namespace(node->parent); - return ns; } @@ -310,6 +323,53 @@ return node->prefix; } +const char *xmlnode_get_prefix_namespace(const xmlnode *node, const char *prefix) +{ + const xmlnode *current_node; + + g_return_val_if_fail(node != NULL, NULL); + g_return_val_if_fail(prefix != NULL, xmlnode_get_default_namespace(node)); + + current_node = node; + while (current_node) { + if (current_node->prefix && g_str_equal(prefix, current_node->prefix) && + current_node->xmlns) { + return current_node->xmlns; + } else if (current_node->namespace_map) { + const char *ns = g_hash_table_lookup(current_node->namespace_map, prefix); + if (ns && *ns) { + return ns; + } + } + + current_node = current_node->parent; + } + + return NULL; +} + +void xmlnode_strip_prefixes(xmlnode *node) +{ + xmlnode *child; + const char *prefix; + + g_return_if_fail(node != NULL); + + for (child = node->child; child; child = child->next) { + if (child->type == XMLNODE_TYPE_TAG) + xmlnode_strip_prefixes(child); + } + + prefix = xmlnode_get_prefix(node); + if (prefix) { + const char *ns = xmlnode_get_prefix_namespace(node, prefix); + xmlnode_set_namespace(node, ns); + xmlnode_set_prefix(node, NULL); + } else { + xmlnode_set_namespace(node, xmlnode_get_default_namespace(node)); + } +} + xmlnode *xmlnode_get_parent(const xmlnode *child) { g_return_val_if_fail(child != NULL, NULL); diff -r 114a98da1a5f -r 8d3b5853b017 libpurple/xmlnode.h --- a/libpurple/xmlnode.h Sun Sep 04 18:52:18 2011 +0000 +++ b/libpurple/xmlnode.h Sun Sep 04 21:06:26 2011 +0000 @@ -243,6 +243,15 @@ const char *xmlnode_get_default_namespace(const xmlnode *node); /** + * Returns the defined namespace for a prefix. + * + * @param node The node from which to start the search. + * @param prefix The prefix for which to return the associated namespace. + * @return The namespace for this prefix. + */ +const char *xmlnode_get_prefix_namespace(const xmlnode *node, const char *prefix); + +/** * Sets the prefix of a node * * @param node The node to qualify @@ -259,6 +268,19 @@ const char *xmlnode_get_prefix(const xmlnode *node); /** + * Remove all element prefixes from an xmlnode tree. The prefix's + * namespace is transformed into the default namespace for an element. + * + * Note that this will not necessarily remove all prefixes in use + * (prefixed attributes may still exist), and that this usage may + * break some applications (SOAP / XPath apparently often rely on + * the prefixes having the same name. + * + * @param node The node from which to strip prefixes + */ +void xmlnode_strip_prefixes(xmlnode *node); + +/** * Gets the parent node. * * @param child The child node.