pidgin: 5cd85b37: xmlnode: Add xmlnode_strip_prefixes

darkrain42 at pidgin.im darkrain42 at pidgin.im
Sun Sep 4 17:15:49 EDT 2011


----------------------------------------------------------------------
Revision: 5cd85b37b438c361ff191199de99c6ba9b7b0e49
Parent:   d9b2d73b4d6e64c7768adff67303cfa8816d6d2e
Author:   darkrain42 at pidgin.im
Date:     09/04/11 17:06:26
Branch:   im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/5cd85b37b438c361ff191199de99c6ba9b7b0e49

Changelog: 

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

Changes against parent d9b2d73b4d6e64c7768adff67303cfa8816d6d2e

  patched  libpurple/tests/test_xmlnode.c
  patched  libpurple/xmlnode.c
  patched  libpurple/xmlnode.h

-------------- next part --------------
============================================================
--- libpurple/xmlnode.c	aee6abd361df1eb41d15d5ce1333f73a26723fa6
+++ libpurple/xmlnode.c	788fc4f961ccbbd827cecb0c0fca0b164de0520d
@@ -262,10 +262,18 @@ void xmlnode_set_namespace(xmlnode *node
 
 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_namespac
 
 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 @@ const char *xmlnode_get_prefix(const xml
 	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);
============================================================
--- libpurple/xmlnode.h	c2dc393a2b8e3667e6b762a942f25f6c1db453dc
+++ libpurple/xmlnode.h	549855bb896a6d1ef06998c61f089d0adee65d6e
@@ -243,6 +243,15 @@ const char *xmlnode_get_default_namespac
 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 xml
 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.
============================================================
--- libpurple/tests/test_xmlnode.c	a64c414376163c6a17fb2bb21b31fd782e6061d8
+++ libpurple/tests/test_xmlnode.c	15242821df719edc44b882586ccfface0905aa11
@@ -81,6 +81,41 @@ END_TEST
 }
 END_TEST
 
+
+START_TEST(test_strip_prefixes)
+{
+	const char *xml_doc = "<message xmlns='jabber:client' from='user at gmail.com/resource' to='another_user at 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 at gmail.com/resource' to='another_user at 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 @@ xmlnode_suite(void)
 	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);
 


More information about the Commits mailing list