From b5b134f56952c97f6ae96e24e38fbd49feb99ab3 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 29 Aug 2014 14:59:54 +0200 Subject: [PATCH 1/4] Process all XML namespaces in PubSub ItemProvider Fixes SMACK-601 --- .../smack/util/PacketParserUtils.java | 28 ++++++++++++++----- .../smack/parsing/ParsingExceptionTest.java | 2 +- .../smack/util/PacketParserUtilsTest.java | 23 +++++++++++++++ .../smackx/pubsub/provider/ItemProvider.java | 2 +- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java index f6df91214..dd98e7b80 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java @@ -217,8 +217,14 @@ public class PacketParserUtils { * @throws IOException */ public static String parseElement(XmlPullParser parser) throws XmlPullParserException, IOException { - assert(parser.getEventType() == XmlPullParser.START_TAG); - return parseContentDepth(parser, parser.getDepth()); + return parseElement(parser, false); + } + + public static String parseElement(XmlPullParser parser, + boolean fullNamespaces) throws XmlPullParserException, + IOException { + assert (parser.getEventType() == XmlPullParser.START_TAG); + return parseContentDepth(parser, parser.getDepth(), fullNamespaces); } /** @@ -245,28 +251,36 @@ public class PacketParserUtils { } // Advance the parser, since we want to parse the content of the current element parser.next(); - return parseContentDepth(parser, parser.getDepth()); + return parseContentDepth(parser, parser.getDepth(), false); + } + + public static String parseContentDepth(XmlPullParser parser, int depth) + throws XmlPullParserException, IOException { + return parseContentDepth(parser, depth, false); } /** * Returns the content from the current position of the parser up to the closing tag of the * given depth. Note that only the outermost namespace attributes ("xmlns") will be returned, - * not nested ones. + * not nested ones, if fullNamespaces is false. If it is true, then namespaces of + * parent elements will be added to child elements that don't define a different namespace. *

* This method is able to parse the content with MX- and KXmlParser. In order to achieve * this some trade-off has to be make, because KXmlParser does not support xml-roundtrip (ie. * return a String on getText() on START_TAG and END_TAG). We are therefore required to work * around this limitation, which results in only partial support for XML namespaces ("xmlns"): - * Only the outermost namespace of elements will be included in the resulting String. + * Only the outermost namespace of elements will be included in the resulting String, if + * fullNamespaces is set to false. *

* * @param parser * @param depth + * @param fullNamespaces * @return the content of the current depth * @throws XmlPullParserException * @throws IOException */ - public static String parseContentDepth(XmlPullParser parser, int depth) throws XmlPullParserException, IOException { + public static String parseContentDepth(XmlPullParser parser, int depth, boolean fullNamespaces) throws XmlPullParserException, IOException { XmlStringBuilder xml = new XmlStringBuilder(); int event = parser.getEventType(); boolean isEmptyElement = false; @@ -277,7 +291,7 @@ public class PacketParserUtils { while (true) { if (event == XmlPullParser.START_TAG) { xml.halfOpenElement(parser.getName()); - if (namespaceElement == null) { + if (namespaceElement == null || fullNamespaces) { String namespace = parser.getNamespace(); if (StringUtils.isNotEmpty(namespace)) { xml.attribute("xmlns", namespace); diff --git a/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java b/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java index ebf801c23..a78412280 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/parsing/ParsingExceptionTest.java @@ -67,7 +67,7 @@ public class ParsingExceptionTest { try { PacketParserUtils.parseMessage(parser); } catch (Exception e) { - content = PacketParserUtils.parseContentDepth(parser, parserDepth); + content = PacketParserUtils.parseContentDepth(parser, parserDepth, false); } assertNotNull(content); assertEquals(MESSAGE_EXCEPTION_ELEMENT + EXTENSION2 + "", content); diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java b/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java index 39e484ee2..95db3aa2a 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/util/PacketParserUtilsTest.java @@ -24,9 +24,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.IOException; import java.util.Locale; import java.util.Properties; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier; import org.jivesoftware.smack.packet.Message; @@ -34,6 +39,7 @@ import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.test.util.TestUtils; import org.junit.Test; +import org.xml.sax.SAXException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -790,6 +796,23 @@ public class PacketParserUtilsTest { assertEquals("", content); } + @Test + public void parseElementMultipleNamespace() + throws ParserConfigurationException, + FactoryConfigurationError, XmlPullParserException, + IOException, TransformerException, SAXException { + // @formatter:off + final String stanza = XMLBuilder.create("outer", "outerNamespace").a("outerAttribute", "outerValue") + .element("inner", "innerNamespace").a("innverAttribute", "innerValue") + .element("innermost") + .t("some text") + .asString(); + // @formatter:on + XmlPullParser parser = TestUtils.getParser(stanza, "outer"); + String result = PacketParserUtils.parseElement(parser, true); + assertXMLEqual(stanza, result); + } + private String determineNonDefaultLanguage() { String otherLanguage = "jp"; Locale[] availableLocales = Locale.getAvailableLocales(); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java index 3dc3b1cb7..530863269 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java @@ -54,7 +54,7 @@ public class ItemProvider implements PacketExtensionProvider if (ProviderManager.getExtensionProvider(payloadElemName, payloadNS) == null) { - String payloadText = PacketParserUtils.parseElement(parser); + String payloadText = PacketParserUtils.parseElement(parser, true); return new PayloadItem(id, node, new SimplePayload(payloadElemName, payloadNS, payloadText)); } else From 72557dd35483b36e28042bd0e6829c8785108f7d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 29 Aug 2014 18:06:22 +0200 Subject: [PATCH 2/4] Make PacketCollector handle InterruptedException Fixes SMACK-602 --- .../jivesoftware/smack/PacketCollector.java | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java index d402f6db0..15b6ab73a 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java @@ -19,6 +19,8 @@ package org.jivesoftware.smack; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; @@ -42,6 +44,8 @@ import org.jivesoftware.smack.packet.XMPPError; */ public class PacketCollector { + private static final Logger LOGGER = Logger.getLogger(PacketCollector.class.getName()); + private PacketFilter packetFilter; private ArrayBlockingQueue resultQueue; private XMPPConnection connection; @@ -114,12 +118,16 @@ public class PacketCollector { * @return the next available packet. */ public Packet nextResultBlockForever() { - try { - return resultQueue.take(); - } - catch (InterruptedException e) { - throw new RuntimeException(e); + Packet res = null; + while (res == null) { + try { + res = resultQueue.take(); + } catch (InterruptedException e) { + LOGGER.log(Level.FINE, + "nextResultBlockForever was interrupted", e); + } } + return res; } /** @@ -139,13 +147,19 @@ public class PacketCollector { * * @return the next available packet. */ - public Packet nextResult(long timeout) { - try { - return resultQueue.poll(timeout, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) { - throw new RuntimeException(e); - } + public Packet nextResult(final long timeout) { + Packet res = null; + long remainingWait = timeout; + final long waitStart = System.currentTimeMillis(); + while (res == null && remainingWait > 0) { + try { + res = resultQueue.poll(remainingWait, TimeUnit.MILLISECONDS); + remainingWait = timeout - (System.currentTimeMillis() - waitStart); + } catch (InterruptedException e) { + LOGGER.log(Level.FINE, "nextResult was interrupted", e); + } + } + return res; } /** From 5342aaf137e4cf8b9e41cb296f6460274f3f5398 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 1 Sep 2014 10:13:23 +0200 Subject: [PATCH 3/4] Make XMPPError.Condition.equals null-safe also make Condition implement CharSequence. SMACK-603 --- .../jivesoftware/smack/packet/XMPPError.java | 27 ++++++++++++++++++- .../jivesoftware/smack/util/StringUtils.java | 14 ++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java index c3343759b..c25e8066d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import org.jivesoftware.smack.util.StringUtils; + /** * Represents a XMPP error sub-packet. Typically, a server responds to a request that has * problems by sending the packet back and including an error packet. Each error has a type, @@ -57,6 +59,7 @@ import java.util.Map; * * * @author Matt Tucker + * @see RFC 6120 - 8.3.2 Syntax: The Syntax of XMPP error stanzas */ public class XMPPError { @@ -266,7 +269,7 @@ public class XMPPError { /** * A class to represent predefined error conditions. */ - public static class Condition { + public static class Condition implements CharSequence { public static final Condition internal_server_error = new Condition("internal-server-error"); public static final Condition forbidden = new Condition("forbidden"); @@ -306,13 +309,35 @@ public class XMPPError { @Override public boolean equals(Object other) { + if (other == null) { + return false; + } return toString().equals(other.toString()); } + public boolean equals(CharSequence other) { + return StringUtils.nullSafeCharSequenceEquals(this, other); + } + @Override public int hashCode() { return value.hashCode(); } + + @Override + public int length() { + return value.length(); + } + + @Override + public char charAt(int index) { + return value.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return value.subSequence(start, end); + } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index 796b7ff1f..9e4dbdb92 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java @@ -546,4 +546,18 @@ public class StringUtils { public static boolean isEmpty(CharSequence cs) { return cs.length() == 0; } + + public static boolean nullSafeCharSequenceEquals(CharSequence csOne, CharSequence csTwo) { + return nullSafeCharSequenceComperator(csOne, csTwo) == 0; + } + + public static int nullSafeCharSequenceComperator(CharSequence csOne, CharSequence csTwo) { + if (csOne == null ^ csTwo == null) { + return (csOne == null) ? -1 : 1; + } + if (csOne == null && csTwo == null) { + return 0; + } + return csOne.toString().compareTo(csTwo.toString()); + } } From 7bf31bc2e4a46110f37130530331661d7e0d0c54 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 5 Sep 2014 21:59:35 +0200 Subject: [PATCH 4/4] Smack 4.0.4 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5f1fc7fa2..a3578b827 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ allprojects { ext { shortVersion = '4.0.4' - isSnapshot = true + isSnapshot = false gitCommit = getGitCommit() javadocAllDir = new File(buildDir, 'javadoc') documentationDir = new File(buildDir, 'documentation')