From 646a4a6f900a2b817c2a23c6eb5020017bbcf701 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 18 Nov 2014 19:48:02 +0100 Subject: [PATCH] Add support for SASL failure 'text' elements - Also move toString() into TopLevelStreamElement. - Fix SASLFailure toXML xmlnsAttribute(NAMESPACE) - Improve SASLFailure parsing - And introduce XmlUnitUtils --- .../org/jivesoftware/smack/packet/Packet.java | 5 -- .../jivesoftware/smack/packet/Presence.java | 12 ----- .../smack/packet/TopLevelStreamElement.java | 5 ++ .../smack/sasl/packet/SaslStreamElements.java | 53 ++++++++++++++++++- .../smack/util/PacketParserUtils.java | 37 ++++++++----- .../smack/PacketCollectorTest.java | 6 --- .../smack/packet/MessageTest.java | 7 +-- .../smack/test/util/XmlUnitUtils.java | 35 ++++++++++++ .../smack/util/PacketParserUtilsTest.java | 47 +++++++++++++--- 9 files changed, 160 insertions(+), 47 deletions(-) create mode 100644 smack-core/src/test/java/org/jivesoftware/smack/test/util/XmlUnitUtils.java diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Packet.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Packet.java index c33f9299e..ff996cf58 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Packet.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Packet.java @@ -372,11 +372,6 @@ public abstract class Packet extends TopLevelStreamElement { return DEFAULT_LANGUAGE; } - @Override - public String toString() { - return toXML().toString(); - } - /** * Add to, from, id and 'xml:lang' attributes * diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java index 1f84755aa..69852d3f1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java @@ -230,18 +230,6 @@ public final class Presence extends Packet { return buf; } - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append(type); - if (mode != null) { - buf.append(": ").append(mode); - } - if (getStatus() != null) { - buf.append(" (").append(getStatus()).append(")"); - } - return buf.toString(); - } - /** * An enum to represent the presence type. Note that presence type is often confused * with presence mode. Generally, if a user is signed in to a server, they have a presence diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/TopLevelStreamElement.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/TopLevelStreamElement.java index f978f0418..7381281df 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/TopLevelStreamElement.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/TopLevelStreamElement.java @@ -23,4 +23,9 @@ package org.jivesoftware.smack.packet; */ public abstract class TopLevelStreamElement implements Element { + @Override + public final String toString() { + return toXML().toString(); + } + } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/packet/SaslStreamElements.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/packet/SaslStreamElements.java index b7d44963b..b1f8342e1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/packet/SaslStreamElements.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/packet/SaslStreamElements.java @@ -16,6 +16,10 @@ */ package org.jivesoftware.smack.sasl.packet; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; + import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.sasl.SASLError; import org.jivesoftware.smack.util.StringUtils; @@ -159,8 +163,18 @@ public class SaslStreamElements { private final SASLError saslError; private final String saslErrorString; + private final Map descriptiveTexts; public SASLFailure(String saslError) { + this(saslError, null); + } + + public SASLFailure(String saslError, Map descriptiveTexts) { + if (descriptiveTexts != null) { + this.descriptiveTexts = descriptiveTexts; + } else { + this.descriptiveTexts = Collections.emptyMap(); + } SASLError error = SASLError.fromString(saslError); if (error == null) { // RFC6120 6.5 states that unknown condition must be treat as generic authentication @@ -189,11 +203,48 @@ public class SaslStreamElements { return saslErrorString; } + /** + * Get the descriptive text of this SASLFailure. + *

+ * Returns the descriptive text of this SASLFailure in the system default language if possible. May return null. + *

+ * + * @return the descriptive text or null. + */ + public String getDescriptiveText() { + String defaultLocale = Locale.getDefault().getLanguage(); + String descriptiveText = getDescriptiveText(defaultLocale); + if (descriptiveText == null) { + descriptiveText = getDescriptiveText(null); + } + return descriptiveText; + } + + /** + * Get the descriptive test of this SASLFailure. + *

+ * Returns the descriptive text of this SASLFailure in the given language. May return null if not available. + *

+ * + * @param xmllang the language. + * @return the descriptive text or null. + */ + public String getDescriptiveText(String xmllang) { + return descriptiveTexts.get(xmllang); + } + @Override public XmlStringBuilder toXML() { XmlStringBuilder xml = new XmlStringBuilder(); - xml.halfOpenElement(ELEMENT).xmlnsAttribute(ELEMENT).rightAngleBracket(); + xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket(); xml.emptyElement(saslErrorString); + for (Map.Entry entry : descriptiveTexts.entrySet()) { + String xmllang = entry.getKey(); + String text = entry.getValue(); + xml.halfOpenElement("text").xmllangAttribute(xmllang).rightAngleBracket(); + xml.escape(text); + xml.closeElement("text"); + } xml.closeElement(ELEMENT); return xml; } 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 a56a81289..1968e163d 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 @@ -22,9 +22,11 @@ import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -765,26 +767,37 @@ public class PacketParserUtils { * * @param parser the XML parser. * @return a SASL Failure packet. - * @throws Exception if an exception occurs while parsing the packet. + * @throws IOException + * @throws XmlPullParserException */ - public static SASLFailure parseSASLFailure(XmlPullParser parser) throws Exception { + public static SASLFailure parseSASLFailure(XmlPullParser parser) throws XmlPullParserException, IOException { + final int initialDepth = parser.getDepth(); String condition = null; - boolean done = false; - while (!done) { + Map descriptiveTexts = new HashMap(); + outerloop: while (true) { int eventType = parser.next(); - - if (eventType == XmlPullParser.START_TAG) { - if (!parser.getName().equals("failure")) { + switch (eventType) { + case XmlPullParser.START_TAG: + String name = parser.getName(); + if (name.equals("text")) { + String xmllang = getLanguageAttribute(parser); + String text = parser.nextText(); + String previousValue = descriptiveTexts.put(xmllang, text); + assert(previousValue == null); + } + else { + assert(condition == null); condition = parser.getName(); } - } - else if (eventType == XmlPullParser.END_TAG) { - if (parser.getName().equals("failure")) { - done = true; + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; } + break; } } - return new SASLFailure(condition); + return new SASLFailure(condition, descriptiveTexts); } /** diff --git a/smack-core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java b/smack-core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java index 0ffcefd5f..cb6a5067f 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/PacketCollectorTest.java @@ -200,12 +200,6 @@ public class PacketCollectorTest setPacketID(String.valueOf(i)); } - @Override - public String toString() - { - return toXML(); - } - @Override public String toXML() { diff --git a/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java b/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java index d96438f95..c6c99d5af 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/packet/MessageTest.java @@ -21,8 +21,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import org.custommonkey.xmlunit.Diff; -import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier; +import org.jivesoftware.smack.test.util.XmlUnitUtils; import org.junit.Test; import org.xml.sax.SAXException; @@ -150,9 +149,7 @@ public class MessageTest { message.addBody(null, messageBody1); message.addBody(lang2, messageBody2); message.addBody(lang3, messageBody3); - Diff xmlDiff = new Diff(control, message.toXML().toString()); - xmlDiff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); - assertTrue(xmlDiff.similar()); + XmlUnitUtils.assertSimilar(control, message.toXML()); Collection languages = message.getBodyLanguages(); List controlLanguages = new ArrayList(); diff --git a/smack-core/src/test/java/org/jivesoftware/smack/test/util/XmlUnitUtils.java b/smack-core/src/test/java/org/jivesoftware/smack/test/util/XmlUnitUtils.java new file mode 100644 index 000000000..7a25fe455 --- /dev/null +++ b/smack-core/src/test/java/org/jivesoftware/smack/test/util/XmlUnitUtils.java @@ -0,0 +1,35 @@ +/** + * + * Copyright 2014 Florian Schmaus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.test.util; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; + +import java.io.IOException; + +import org.custommonkey.xmlunit.Diff; +import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier; +import org.xml.sax.SAXException; + +public class XmlUnitUtils { + + public static void assertSimilar(CharSequence expected, CharSequence actual) throws SAXException, IOException { + Diff diff = new Diff(expected.toString(), actual.toString()); + diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); + assertXMLEqual(diff, true); + } + +} 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 1815b7bb0..c4d6bea6e 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 @@ -32,12 +32,14 @@ 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; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.sasl.SASLError; +import org.jivesoftware.smack.sasl.packet.SaslStreamElements; +import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smack.test.util.XmlUnitUtils; import org.junit.Test; import org.xml.sax.SAXException; import org.xmlpull.v1.XmlPullParser; @@ -745,11 +747,9 @@ public class PacketParserUtilsTest { .a("xml:lang", "sp") .t("This is a test of the emergency broadcast system, 3.") .asString(outputProperties); - + Packet message = PacketParserUtils.parseStanza(control); - Diff xmlDiff = new Diff(control, message.toXML().toString()); - xmlDiff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); - assertTrue(xmlDiff.similar()); + XmlUnitUtils.assertSimilar(control, message.toXML()); } @Test @@ -813,6 +813,41 @@ public class PacketParserUtilsTest { assertXMLEqual(stanza, result.toString()); } + @Test + public void parseSASLFailureSimple() throws FactoryConfigurationError, SAXException, IOException, + TransformerException, ParserConfigurationException, XmlPullParserException { + // @formatter:off + final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslStreamElements.NAMESPACE) + .e(SASLError.account_disabled.toString()) + .asString(); + // @formatter:on + XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT); + SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser); + assertXMLEqual(saslFailureString, saslFailure.toString()); + } + + @Test + public void parseSASLFailureExtended() throws FactoryConfigurationError, TransformerException, + ParserConfigurationException, XmlPullParserException, IOException, SAXException { + // @formatter:off + final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslStreamElements.NAMESPACE) + .e(SASLError.account_disabled.toString()) + .up() + .e("text").a("xml:lang", "en") + .t("Call 212-555-1212 for assistance.") + .up() + .e("text").a("xml:lang", "de") + .t("Bitte wenden sie sich an (04321) 123-4444") + .up() + .e("text") + .t("Wusel dusel") + .asString(); + // @formatter:on + XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT); + SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser); + XmlUnitUtils.assertSimilar(saslFailureString, saslFailure.toXML()); + } + private String determineNonDefaultLanguage() { String otherLanguage = "jp"; Locale[] availableLocales = Locale.getAvailableLocales();