adium: b7a1ac61: *** Plucked rev d9b2d73b4d6e64c7768adff6...
thijsalkemade at gmail.com
thijsalkemade at gmail.com
Wed Oct 12 12:15:58 EDT 2011
----------------------------------------------------------------------
Revision: b7a1ac61019d1553d175719453fb16d2e991cb80
Parent: f69b22b713390e0c56594cd15494c119222f7ca2
Author: thijsalkemade at gmail.com
Date: 10/12/11 12:13:08
Branch: im.pidgin.adium
URL: http://d.pidgin.im/viewmtn/revision/info/b7a1ac61019d1553d175719453fb16d2e991cb80
Changelog:
*** Plucked rev d9b2d73b4d6e64c7768adff67303cfa8816d6d2e (darkrain42 at pidgin.im):
xmlnode: Fix some brokeness in xmlnode serialization with prefixed elements.
Basically we were treating node->xmlns as the default namespace, but
that isn't the case with prefexed elements. In our serialization,
I believe we were adding an extraneous xmlns='' to a prefixed element,
which changes the (default) namespace for its children. (It's been
a bit too long with this in my tree, so I've forgotten the exact details)
*** Plucked rev 5cd85b37b438c361ff191199de99c6ba9b7b0e49 (darkrain42 at pidgin.im):
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
*** Plucked rev f6804c803da32abd1dafe2a767e3c877a0521e1c (darkrain42 at pidgin.im):
jabber: Strip element prefixes on XHTML-IM content.
I'm a little late to the party in completing this code, as
I believe Google's actually reverted the change. But the consensus
was we should present saner content to the core/UIs.
Fixes #14529
Changes against parent f69b22b713390e0c56594cd15494c119222f7ca2
patched libpurple/protocols/jabber/message.c
patched libpurple/tests/test_xmlnode.c
patched libpurple/xmlnode.c
patched libpurple/xmlnode.h
-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/message.c 56cb48ae5198a3d7b0180dd2a2facf6999ed83c5
+++ libpurple/protocols/jabber/message.c 54ac5226906b4c78beafd9090decf1e6570e77d2
@@ -638,6 +638,8 @@ void jabber_message_parse(JabberStream *
jabber_message_add_remote_smileys(js, to, packet);
}
+ xmlnode_strip_prefixes(child);
+
/* reformat xhtml so that img tags with a "cid:" src gets
translated to the bare text of the emoticon (the "alt" attrib) */
/* this is done also when custom smiley retrieval is turned off,
============================================================
--- libpurple/xmlnode.c 62f65e9ad8e60c0016637ebe74f8d6e37dcae5c5
+++ libpurple/xmlnode.c 4d0aba18947dc2bb9b32cd6f49880955409abbe7
@@ -78,6 +78,19 @@ xmlnode_new_child(xmlnode *parent, const
node = new_node(name, XMLNODE_TYPE_TAG);
xmlnode_insert_child(parent, node);
+#if 0
+ /* This would give xmlnodes more appropriate namespacing
+ * when creating them. Otherwise, unless an explicit namespace
+ * is set, xmlnode_get_namespace() will return NULL, when
+ * there may be a default namespace.
+ *
+ * I'm unconvinced that it's useful, and concerned it may break things.
+ *
+ * _insert_child would need the same thing, probably (assuming
+ * xmlns->node == NULL)
+ */
+ xmlnode_set_namespace(node, xmlnode_get_default_namespace(node))
+#endif
return node;
}
@@ -261,19 +274,53 @@ 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(xmlnode *node)
+const char *xmlnode_get_namespace(const xmlnode *node)
{
g_return_val_if_fail(node != NULL, NULL);
return node->xmlns;
}
+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);
+
+ 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;
+ }
+
+ return ns;
+}
+
void xmlnode_set_prefix(xmlnode *node, const char *prefix)
{
g_return_if_fail(node != NULL);
@@ -288,6 +335,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);
@@ -455,12 +549,22 @@ xmlnode_to_str_helper(const xmlnode *nod
if (node->namespace_map) {
g_hash_table_foreach(node->namespace_map,
(GHFunc)xmlnode_to_str_foreach_append_ns, text);
- } else if (node->xmlns) {
- if(!node->parent || !purple_strequal(node->xmlns, node->parent->xmlns))
+ } else {
+ /* Figure out if this node has a different default namespace from parent */
+ const char *xmlns = NULL;
+ const char *parent_xmlns = NULL;
+ if (!prefix)
+ xmlns = node->xmlns;
+
+ if (!xmlns)
+ xmlns = xmlnode_get_default_namespace(node);
+ if (node->parent)
+ parent_xmlns = xmlnode_get_default_namespace(node->parent);
+ if (!purple_strequal(xmlns, parent_xmlns))
{
- char *xmlns = g_markup_escape_text(node->xmlns, -1);
- g_string_append_printf(text, " xmlns='%s'", xmlns);
- g_free(xmlns);
+ char *escaped_xmlns = g_markup_escape_text(xmlns, -1);
+ g_string_append_printf(text, " xmlns='%s'", escaped_xmlns);
+ g_free(escaped_xmlns);
}
}
for(c = node->child; c; c = c->next)
============================================================
--- libpurple/xmlnode.h 1662f69e937ac1d3b99d0d161d7f0b10fa7d4005
+++ libpurple/xmlnode.h 6f763b135f4bd68d6d9618f29e18dff510b64633
@@ -249,9 +249,37 @@ void xmlnode_set_namespace(xmlnode *node
* @param node The node to get the namepsace from
* @return The namespace of this node
*/
-const char *xmlnode_get_namespace(xmlnode *node);
+const char *xmlnode_get_namespace(const xmlnode *node);
/**
+ * Returns the current default namespace. The default
+ * namespace is the current namespace which applies to child
+ * elements which are unprefixed and which do not contain their
+ * own namespace.
+ *
+ * For example, given:
+ * <iq type='get' xmlns='jabber:client' xmlns:ns1='http://example.org/ns1'>
+ * <ns1:element><child1/></ns1:element>
+ * </iq>
+ *
+ * The default namespace of all nodes (including 'child1') is "jabber:client",
+ * though the namespace for 'element' is "http://example.org/ns1".
+ *
+ * @param node The node for which to return the default namespace
+ * @return The default namespace of this node
+ */
+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
@@ -268,6 +296,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 ef8840551902714078a88e375c19a8caf02be7d8
+++ libpurple/tests/test_xmlnode.c 15242821df719edc44b882586ccfface0905aa11
@@ -21,6 +21,101 @@ END_TEST
}
END_TEST
+#define check_doc_structure(x) { \
+ xmlnode *ping, *child1, *child2; \
+ fail_if(x == NULL, "Failed to parse document"); \
+ ping = xmlnode_get_child(x, "ping"); \
+ fail_if(ping == NULL, "Failed to find 'ping' child"); \
+ child1 = xmlnode_get_child(ping, "child1"); \
+ fail_if(child1 == NULL, "Failed to find 'child1'"); \
+ child2 = xmlnode_get_child(child1, "child2"); \
+ fail_if(child2 == NULL, "Failed to find 'child2'"); \
+ xmlnode_new_child(child2, "a"); \
+ \
+ assert_string_equal("jabber:client", xmlnode_get_namespace(x)); \
+ /* NOTE: xmlnode_get_namespace() returns the namespace of the element, not the
+ * current default namespace. See http://www.w3.org/TR/xml-names/#defaulting and
+ * http://www.w3.org/TR/xml-names/#dt-defaultNS.
+ */ \
+ assert_string_equal("urn:xmpp:ping", xmlnode_get_namespace(ping)); \
+ assert_string_equal("jabber:client", xmlnode_get_namespace(child1)); \
+ assert_string_equal("urn:xmpp:ping", xmlnode_get_namespace(child2)); \
+ /*
+ * This fails (well, actually crashes [the ns is NULL]) unless
+ * xmlnode_new_child() actually sets the element namespace.
+ assert_string_equal("jabber:client", xmlnode_get_namespace(xmlnode_get_child(child2, "a")));
+ */ \
+ \
+ assert_string_equal("jabber:client", xmlnode_get_default_namespace(x)); \
+ assert_string_equal("jabber:client", xmlnode_get_default_namespace(ping)); \
+ assert_string_equal("jabber:client", xmlnode_get_default_namespace(child1)); \
+ assert_string_equal("jabber:client", xmlnode_get_default_namespace(child2)); \
+}
+
+START_TEST(test_xmlnode_prefixes)
+{
+ const char *xml_doc =
+ "<iq type='get' xmlns='jabber:client' xmlns:ping='urn:xmpp:ping'>"
+ "<ping:ping>"
+ "<child1>"
+ "<ping:child2></ping:child2>" /* xmlns='jabber:child' */
+ "</child1>"
+ "</ping:ping>"
+ "</iq>";
+ char *str;
+ xmlnode *xml, *reparsed;
+
+ xml = xmlnode_from_str(xml_doc, -1);
+ check_doc_structure(xml);
+
+ /* Check that xmlnode_from_str(xmlnode_to_str(xml, NULL), -1) is idempotent. */
+ str = xmlnode_to_str(xml, NULL);
+ fail_if(str == NULL, "Failed to serialize XMLnode");
+ reparsed = xmlnode_from_str(str, -1);
+ fail_if(reparsed == NULL, "Failed to reparse xml document");
+ check_doc_structure(reparsed);
+
+ g_free(str);
+ xmlnode_free(xml);
+ xmlnode_free(reparsed);
+}
+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)
{
@@ -28,6 +123,9 @@ 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);
return s;
More information about the Commits
mailing list