Mercurial > pidgin
changeset 32038:8d3b5853b017
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
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sun, 04 Sep 2011 21:06:26 +0000 |
parents | 114a98da1a5f |
children | afdbf45fa861 |
files | libpurple/tests/test_xmlnode.c libpurple/xmlnode.c libpurple/xmlnode.h |
diffstat | 3 files changed, 130 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- 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 = "<message xmlns='jabber:client' from='user@gmail.com/resource' to='another_user@darkrain42.org' type='chat' id='purple'>" + "<cha:active xmlns:cha='http://jabber.org/protocol/chatstates'/>" + "<body>xvlc xvlc</body>" + "<im:html xmlns:im='http://jabber.org/protocol/xhtml-im'>" + "<xht:body xmlns:xht='http://www.w3.org/1999/xhtml'>" + "<xht:p>xvlc <xht:span style='font-weight: bold;'>xvlc</xht:span></xht:p>" + "</xht:body>" + "</im:html>" + "</message>"; + const char *out = "<message xmlns='jabber:client' from='user@gmail.com/resource' to='another_user@darkrain42.org' type='chat' id='purple'>" + "<active xmlns:cha='http://jabber.org/protocol/chatstates' xmlns='http://jabber.org/protocol/chatstates'/>" + "<body>xvlc xvlc</body>" + "<html xmlns:im='http://jabber.org/protocol/xhtml-im' xmlns='http://jabber.org/protocol/xhtml-im'>" + "<body xmlns:xht='http://www.w3.org/1999/xhtml' xmlns='http://www.w3.org/1999/xhtml'>" + "<p>xvlc <span style='font-weight: bold;'>xvlc</span></p>" + "</body>" + "</html>" + "</message>"; + 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);
--- 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);
--- 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.