diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java index aef09e1b3..b9273a66f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java @@ -277,9 +277,8 @@ public class ReconnectionManager { if (e instanceof StreamErrorException) { StreamErrorException xmppEx = (StreamErrorException) e; StreamError error = xmppEx.getStreamError(); - String reason = error.getCode(); - if ("conflict".equals(reason)) { + if (StreamError.Condition.conflict == error.getCondition()) { return; } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java new file mode 100644 index 000000000..9383e60fd --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java @@ -0,0 +1,111 @@ +/** + * + * 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.packet; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.jivesoftware.smack.util.PacketUtil; +import org.jivesoftware.smack.util.XmlStringBuilder; + +public class AbstractError { + + private final String textNamespace; + protected final Map descriptiveTexts; + private final List extensions; + + + protected AbstractError(Map descriptiveTexts) { + this(descriptiveTexts, null); + } + + protected AbstractError(Map descriptiveTexts, List extensions) { + this(descriptiveTexts, null, extensions); + } + + protected AbstractError(Map descriptiveTexts, String textNamespace, List extensions) { + if (descriptiveTexts != null) { + this.descriptiveTexts = descriptiveTexts; + } else { + this.descriptiveTexts = Collections.emptyMap(); + } + this.textNamespace = textNamespace; + if (extensions != null) { + this.extensions = extensions; + } else { + this.extensions = Collections.emptyList(); + } + } + + /** + * 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(""); + } + 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); + } + + /** + * Returns the first packet extension that matches the specified element name and + * namespace, or null if it doesn't exist. + * + * @param elementName the XML element name of the packet extension. + * @param namespace the XML element namespace of the packet extension. + * @return the extension, or null if it doesn't exist. + */ + public PE getExtension(String elementName, String namespace) { + return PacketUtil.packetExtensionfromCollection(extensions, elementName, namespace); + } + + protected void addDescriptiveTextsAndExtensions(XmlStringBuilder xml) { + for (Map.Entry entry : descriptiveTexts.entrySet()) { + String xmllang = entry.getKey(); + String text = entry.getValue(); + xml.halfOpenElement("text").xmlnsAttribute(textNamespace) + .xmllangAttribute(xmllang).rightAngleBracket(); + xml.escape(text); + xml.closeElement("text"); + } + for (PacketExtension packetExtension : extensions) { + xml.append(packetExtension.toXML()); + } + } +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/FullStreamElement.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/FullStreamElement.java index 8666f4d88..86733e64c 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/FullStreamElement.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/FullStreamElement.java @@ -23,6 +23,6 @@ package org.jivesoftware.smack.packet; * * @author Florian Schmaus */ -public abstract class FullStreamElement extends PlainStreamElement implements PacketExtension { +public abstract class FullStreamElement implements PlainStreamElement, PacketExtension { } 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 ff996cf58..ffd6eb7c1 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 @@ -43,7 +43,7 @@ import java.util.concurrent.atomic.AtomicLong; * * @author Matt Tucker */ -public abstract class Packet extends TopLevelStreamElement { +public abstract class Packet implements TopLevelStreamElement { public static final String TEXT = "text"; public static final String ITEM = "item"; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/PlainStreamElement.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/PlainStreamElement.java index fa2394c89..5916028d4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/PlainStreamElement.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/PlainStreamElement.java @@ -29,6 +29,6 @@ package org.jivesoftware.smack.packet; * * @author Florian Schmaus */ -public abstract class PlainStreamElement extends TopLevelStreamElement { +public interface PlainStreamElement extends TopLevelStreamElement { } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java index 1816dd95e..b58c42621 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/StreamError.java @@ -17,6 +17,11 @@ package org.jivesoftware.smack.packet; +import java.util.List; +import java.util.Map; + +import org.jivesoftware.smack.util.XmlStringBuilder; + /** * Represents a stream error packet. Stream errors are unrecoverable errors where the server * will close the unrelying TCP connection after the stream error was sent to the client. @@ -73,48 +78,114 @@ package org.jivesoftware.smack.packet; * not-well-formed the initiating entity has sent XML that is not * well-formed. * + *

+ * Stream error syntax: + *

+ * {@code
+ * 
+ *   
+ *   [
+ *   OPTIONAL descriptive text
+ *   ]
+ *   [OPTIONAL application-specific condition element]
+ * 
+ * }
+ * 
* * @author Gaston Dombiak */ -public class StreamError { +public class StreamError extends AbstractError implements PlainStreamElement { + public static final String ELEMENT = "stream:error"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams"; - private String code; - private String text; + private final Condition condition; + private final String conditionText; - public StreamError(String code) { - super(); - this.code = code; + public StreamError(Condition condition, String conditionText, Map descriptiveTexts, List extensions) { + super(descriptiveTexts, extensions); + if (conditionText != null) { + switch (condition) { + case see_other_host: + break; + default: + throw new IllegalArgumentException("The given condition '" + condition + + "' can not contain a conditionText"); + } + } + this.condition = condition; + this.conditionText = conditionText; } - public StreamError(String code, String text) { - this(code); - this.text = text; + public Condition getCondition() { + return condition; } - /** - * Returns the error code. - * - * @return the error code. - */ - public String getCode() { - return code; - } - - /** - * Returns the error text, which may be null. - * - * @return the error text. - */ - public String getText() { - return text; + public String getConditionText() { + return conditionText; } + @Override public String toString() { - StringBuilder txt = new StringBuilder(); - txt.append("stream:error (").append(code).append(")"); - if (text != null) txt.append(" text: ").append(text); - return txt.toString(); + return toXML().toString(); + } + + @Override + public XmlStringBuilder toXML() { + XmlStringBuilder xml = new XmlStringBuilder(); + xml.openElement(ELEMENT); + xml.rightAngleBracket().append(condition.toString()).xmlnsAttribute(NAMESPACE).closeEmptyElement(); + addDescriptiveTextsAndExtensions(xml); + xml.closeElement(ELEMENT); + return xml; + } + + /** + * The defined stream error condtions, see RFC 6120 § 4.9.3 + * + */ + public enum Condition { + bad_format, + bad_namespace_prefix, + conflict, + connection_timeout, + host_gone, + host_unkown, + improper_addressing, + internal_server_error, + invalid_from, + invalid_namespace, + invalid_xml, + not_authorized, + not_well_formed, + policy_violation, + remote_connection_failed, + reset, + resource_constraint, + restricted_xml, + see_other_host, + system_shutdown, + undeficed_condition, + unsupported_encoding, + unsupported_feature, + unsupported_stanza_type, + unsupported_version; + + @Override + public String toString() { + return this.name().replace('_', '-'); + } + + public static Condition fromString(String string) { + string = string.replace('-', '_'); + Condition condition = null; + try { + condition = Condition.valueOf(string); + } catch (Exception e) { + throw new IllegalStateException("Could not transform string '" + string + "' to XMPPErrorConditoin", e); + } + return condition; + } } } 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 7381281df..ef6516f96 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 @@ -21,11 +21,6 @@ package org.jivesoftware.smack.packet; * A XMPP top level stream element. This is either a stanza ({@link Packet}) or * just a plain stream element ({@link PlainStreamElement}). */ -public abstract class TopLevelStreamElement implements Element { - - @Override - public final String toString() { - return toXML().toString(); - } +public interface TopLevelStreamElement extends Element { } 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 4546b546a..73496a148 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 @@ -16,14 +16,12 @@ */ package org.jivesoftware.smack.packet; -import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; +import java.util.logging.Logger; -import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; /** @@ -32,85 +30,76 @@ import org.jivesoftware.smack.util.XmlStringBuilder; * error condition as well as as an optional text explanation. Typical errors are:

* * - *
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * *
XMPP ErrorType
internal-server-errorWAIT
forbiddenAUTH
bad-requestMODIFY
item-not-foundCANCEL
conflictCANCEL
feature-not-implementedCANCEL
goneMODIFY
jid-malformedMODIFY
not-acceptable MODIFY
not-allowedCANCEL
not-authorizedAUTH
payment-requiredAUTH
recipient-unavailableWAIT
redirectMODIFY
registration-requiredAUTH
remote-server-not-foundCANCEL
remote-server-timeoutWAIT
remote-server-errorCANCEL
resource-constraintWAIT
service-unavailableCANCEL
subscription-requiredAUTH
undefined-conditionWAIT
unexpected-conditionWAIT
request-timeoutCANCEL
XMPP Error ConditionTypeRFC 6120 Section
bad-requestMODIFY8.3.3.1
conflictCANCEL8.3.3.2
feature-not-implementedCANCEL8.3.3.3
forbiddenAUTH8.3.3.4
goneMODIFY8.3.3.5
internal-server-errorWAIT8.3.3.6
item-not-foundCANCEL8.3.3.7
jid-malformedMODIFY8.3.3.8
not-acceptable MODIFY8.3.3.9
not-allowedCANCEL8.3.3.10
not-authorizedAUTH8.3.3.11
policy-violationAUTH8.3.3.12
recipient-unavailableWAIT8.3.3.13
redirectMODIFY8.3.3.14
registration-requiredAUTH8.3.3.15
remote-server-not-foundCANCEL8.3.3.16
remote-server-timeoutWAIT8.3.3.17
resource-constraintWAIT8.3.3.18
service-unavailableCANCEL8.3.3.19
subscription-requiredAUTH8.3.3.20
undefined-conditionWAIT8.3.3.21
unexpected-requestWAIT8.3.3.22
* * @author Matt Tucker * @see RFC 6120 - 8.3.2 Syntax: The Syntax of XMPP error stanzas */ -public class XMPPError { +public class XMPPError extends AbstractError { public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-stanzas"; public static final String ERROR = "error"; + private static final Logger LOGGER = Logger.getLogger(XMPPError.class.getName()); + private static final Map CONDITION_TO_TYPE = new HashMap(); + + static { + CONDITION_TO_TYPE.put(Condition.bad_request, Type.MODIFY); + CONDITION_TO_TYPE.put(Condition.conflict, Type.CANCEL); + CONDITION_TO_TYPE.put(Condition.feature_not_implemented, Type.CANCEL); + CONDITION_TO_TYPE.put(Condition.forbidden, Type.AUTH); + CONDITION_TO_TYPE.put(Condition.gone, Type.CANCEL); + CONDITION_TO_TYPE.put(Condition.internal_server_error, Type.CANCEL); + CONDITION_TO_TYPE.put(Condition.item_not_found, Type.CANCEL); + CONDITION_TO_TYPE.put(Condition.jid_malformed, Type.MODIFY); + CONDITION_TO_TYPE.put(Condition.not_acceptable, Type.MODIFY); + CONDITION_TO_TYPE.put(Condition.not_allowed, Type.CANCEL); + CONDITION_TO_TYPE.put(Condition.not_authorized, Type.AUTH); + CONDITION_TO_TYPE.put(Condition.policy_violation, Type.MODIFY); + CONDITION_TO_TYPE.put(Condition.recipient_unavailable, Type.WAIT); + CONDITION_TO_TYPE.put(Condition.redirect, Type.MODIFY); + CONDITION_TO_TYPE.put(Condition.registration_required, Type.AUTH); + CONDITION_TO_TYPE.put(Condition.remote_server_not_found, Type.CANCEL); + CONDITION_TO_TYPE.put(Condition.remote_server_timeout, Type.WAIT); + CONDITION_TO_TYPE.put(Condition.resource_constraint, Type.WAIT); + CONDITION_TO_TYPE.put(Condition.service_unavailable, Type.WAIT); + CONDITION_TO_TYPE.put(Condition.subscription_required, Type.WAIT); + CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.MODIFY); + } + private final Condition condition; + private final String conditionText; + private final String errorGenerator; private final Type type; - private final String condition; - private String message; - private List applicationExtensions = null; - public XMPPError(String condition) { - this(new Condition(condition)); - } - - /** - * Creates a new error with the specified condition inferring the type. - * If the Condition is predefined, client code should be like: - * new XMPPError(XMPPError.Condition.remote_server_timeout); - * If the Condition is not predefined, invocations should be like - * new XMPPError(new XMPPError.Condition("my_own_error")); - * - * @param condition the error condition. - */ public XMPPError(Condition condition) { - // Look for the condition and its default type - ErrorSpecification defaultErrorSpecification = ErrorSpecification.specFor(condition); - this.condition = condition.value; - if (defaultErrorSpecification != null) { - // If there is a default error specification for the received condition, - // it get configured with the inferred type. - type = defaultErrorSpecification.getType(); - } else { - type = null; - } + this(condition, null, null, null, null, null); } - /** - * Creates a new error with the specified condition and message infering the type. - * If the Condition is predefined, client code should be like: - * new XMPPError(XMPPError.Condition.remote_server_timeout, "Error Explanation"); - * If the Condition is not predefined, invocations should be like - * new XMPPError(new XMPPError.Condition("my_own_error"), "Error Explanation"); - * - * @param condition the error condition. - * @param messageText a message describing the error. - */ - public XMPPError(Condition condition, String messageText) { - this(condition); - this.message = messageText; + public XMPPError(Condition condition, PacketExtension applicationSpecificCondition) { + this(condition, null, null, null, null, Arrays.asList(applicationSpecificCondition)); } /** @@ -121,15 +110,36 @@ public class XMPPError { * * @param type the error type. * @param condition the error condition. - * @param message a message describing the error. - * @param extension list of packet extensions + * @param descriptiveTexts + * @param extensions list of packet extensions */ - public XMPPError(Type type, String condition, String message, - List extension) { - this.type = type; + public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map descriptiveTexts, + List extensions) { + super(descriptiveTexts, NAMESPACE, extensions); this.condition = condition; - this.message = message; - this.applicationExtensions = extension; + if (conditionText != null) { + switch (condition) { + case gone: + case redirect: + break; + default: + throw new IllegalArgumentException( + "Condition text can only be set with condtion types 'gone' and 'redirect', not " + + condition); + } + } + this.conditionText = conditionText; + this.errorGenerator = errorGenerator; + if (type == null) { + Type determinedType = CONDITION_TO_TYPE.get(condition); + if (determinedType == null) { + LOGGER.warning("Could not determine type for condition: " + condition); + determinedType = Type.CANCEL; + } + this.type = determinedType; + } else { + this.type = type; + } } /** @@ -137,7 +147,7 @@ public class XMPPError { * * @return the error condition. */ - public String getCondition() { + public Condition getCondition() { return condition; } @@ -150,13 +160,12 @@ public class XMPPError { return type; } - /** - * Returns the message describing the error, or null if there is no message. - * - * @return the message describing the error, or null if there is no message. - */ - public String getMessage() { - return message; + public String getErrorGenerator() { + return errorGenerator; + } + + public String getConditionText() { + return conditionText; } /** @@ -167,93 +176,24 @@ public class XMPPError { public XmlStringBuilder toXML() { XmlStringBuilder xml = new XmlStringBuilder(); xml.halfOpenElement(ERROR); - xml.optAttribute("type", type.name().toLowerCase(Locale.US)); + xml.attribute("type", type.toString()); + xml.optAttribute("by", errorGenerator); xml.rightAngleBracket(); - if (condition != null) { - xml.halfOpenElement(condition); - xml.xmlnsAttribute(NAMESPACE); - xml.closeEmptyElement(); - } - if (message != null) { - xml.halfOpenElement(Packet.TEXT); - xml.xmllangAttribute("en"); - xml.xmlnsAttribute(NAMESPACE); - xml.rightAngleBracket(); - xml.escape(message); - xml.closeElement(Packet.TEXT); - } - for (PacketExtension element : this.getExtensions()) { - xml.append(element.toXML()); - } + xml.halfOpenElement(condition.toString()); + xml.xmlnsAttribute(NAMESPACE); + xml.closeEmptyElement(); + + addDescriptiveTextsAndExtensions(xml); + xml.closeElement(ERROR); return xml; } - public String toString() { - StringBuilder txt = new StringBuilder(); - if (condition != null) { - txt.append(condition); - } - if (message != null) { - txt.append(" ").append(message); - } - return txt.toString(); - } - - /** - * Returns a List of the error extensions attached to the xmppError. - * An application MAY provide application-specific error information by including a - * properly-namespaced child in the error element. - * - * @return a List of the error extensions. - */ - public synchronized List getExtensions() { - if (applicationExtensions == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(applicationExtensions); - } - - /** - * Returns the first packet extension that matches the specified element name and - * namespace, or null if it doesn't exist. - * - * @param elementName the XML element name of the packet extension. - * @param namespace the XML element namespace of the packet extension. - * @return the extension, or null if it doesn't exist. - */ - public synchronized PacketExtension getExtension(String elementName, String namespace) { - if (applicationExtensions == null || elementName == null || namespace == null) { - return null; - } - for (PacketExtension ext : applicationExtensions) { - if (elementName.equals(ext.getElementName()) && namespace.equals(ext.getNamespace())) { - return ext; - } - } - return null; - } - - /** - * Adds a packet extension to the error. - * - * @param extension a packet extension. - */ - public synchronized void addExtension(PacketExtension extension) { - if (applicationExtensions == null) { - applicationExtensions = new ArrayList(); - } - applicationExtensions.add(extension); - } - - /** - * Set the packet extension to the error. - * - * @param extension a packet extension. - */ - public synchronized void setExtension(List extension) { - applicationExtensions = extension; + public static XMPPError from(Condition condition, String descriptiveText) { + Map descriptiveTexts = new HashMap(); + descriptiveTexts.put("en", descriptiveText); + return new XMPPError(condition, null, null, null, descriptiveTexts, null); } /** @@ -272,161 +212,60 @@ public class XMPPError { CANCEL, MODIFY, AUTH, - CONTINUE - } + CONTINUE; - /** - * A class to represent predefined error conditions. - */ - 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"); - public static final Condition bad_request = new Condition("bad-request"); - public static final Condition conflict = new Condition("conflict"); - public static final Condition feature_not_implemented = new Condition("feature-not-implemented"); - public static final Condition gone = new Condition("gone"); - public static final Condition item_not_found = new Condition("item-not-found"); - public static final Condition jid_malformed = new Condition("jid-malformed"); - public static final Condition not_acceptable = new Condition("not-acceptable"); - public static final Condition not_allowed = new Condition("not-allowed"); - public static final Condition not_authorized = new Condition("not-authorized"); - public static final Condition payment_required = new Condition("payment-required"); - public static final Condition recipient_unavailable = new Condition("recipient-unavailable"); - public static final Condition redirect = new Condition("redirect"); - public static final Condition registration_required = new Condition("registration-required"); - public static final Condition remote_server_error = new Condition("remote-server-error"); - public static final Condition remote_server_not_found = new Condition("remote-server-not-found"); - public static final Condition remote_server_timeout = new Condition("remote-server-timeout"); - public static final Condition resource_constraint = new Condition("resource-constraint"); - public static final Condition service_unavailable = new Condition("service-unavailable"); - public static final Condition subscription_required = new Condition("subscription-required"); - public static final Condition undefined_condition = new Condition("undefined-condition"); - public static final Condition unexpected_request = new Condition("unexpected-request"); - public static final Condition request_timeout = new Condition("request-timeout"); - - private final String value; - - public Condition(String value) { - this.value = value; - } - - @Override + @Override public String toString() { - return value; + // Locale.US not required, since Type consists only of ASCII chars + return name().toLowerCase(); } + public static Type fromString(String string) { + // Locale.US not required, since Type consists only of ASCII chars + string = string.toUpperCase(); + return Type.valueOf(string); + } + } + + public enum Condition { + bad_request, + conflict, + feature_not_implemented, + forbidden, + gone, + internal_server_error, + item_not_found, + jid_malformed, + not_acceptable, + not_allowed, + not_authorized, + policy_violation, + recipient_unavailable, + redirect, + registration_required, + remote_server_not_found, + remote_server_timeout, + resource_constraint, + service_unavailable, + subscription_required, + undefined_condition, + unexpected_request; + @Override - public boolean equals(Object other) { - if (other == null) { - return false; + public String toString() { + return this.name().replace('_', '-'); + } + + public static Condition fromString(String string) { + string = string.replace('-', '_'); + Condition condition = null; + try { + condition = Condition.valueOf(string); + } catch (Exception e) { + throw new IllegalStateException("Could not transform string '" + string + "' to XMPPErrorConditoin", e); } - 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); + return condition; } } - - /** - * A class to represent the error specification used to infer common usage. - */ - private static class ErrorSpecification { - private static Map instances = new HashMap(); - - private final Type type; - @SuppressWarnings("unused") - private final Condition condition; - - private ErrorSpecification(Condition condition, Type type) { - this.type = type; - this.condition = condition; - } - - static { - instances.put(Condition.internal_server_error, new ErrorSpecification( - Condition.internal_server_error, Type.WAIT)); - instances.put(Condition.forbidden, new ErrorSpecification(Condition.forbidden, - Type.AUTH)); - instances.put(Condition.bad_request, new XMPPError.ErrorSpecification( - Condition.bad_request, Type.MODIFY)); - instances.put(Condition.item_not_found, new XMPPError.ErrorSpecification( - Condition.item_not_found, Type.CANCEL)); - instances.put(Condition.conflict, new XMPPError.ErrorSpecification( - Condition.conflict, Type.CANCEL)); - instances.put(Condition.feature_not_implemented, new XMPPError.ErrorSpecification( - Condition.feature_not_implemented, Type.CANCEL)); - instances.put(Condition.gone, new XMPPError.ErrorSpecification( - Condition.gone, Type.MODIFY)); - instances.put(Condition.jid_malformed, new XMPPError.ErrorSpecification( - Condition.jid_malformed, Type.MODIFY)); - instances.put(Condition.not_acceptable, new XMPPError.ErrorSpecification( - Condition.not_acceptable, Type.MODIFY)); - instances.put(Condition.not_allowed, new XMPPError.ErrorSpecification( - Condition.not_allowed, Type.CANCEL)); - instances.put(Condition.not_authorized, new XMPPError.ErrorSpecification( - Condition.not_authorized, Type.AUTH)); - instances.put(Condition.payment_required, new XMPPError.ErrorSpecification( - Condition.payment_required, Type.AUTH)); - instances.put(Condition.recipient_unavailable, new XMPPError.ErrorSpecification( - Condition.recipient_unavailable, Type.WAIT)); - instances.put(Condition.redirect, new XMPPError.ErrorSpecification( - Condition.redirect, Type.MODIFY)); - instances.put(Condition.registration_required, new XMPPError.ErrorSpecification( - Condition.registration_required, Type.AUTH)); - instances.put(Condition.remote_server_not_found, new XMPPError.ErrorSpecification( - Condition.remote_server_not_found, Type.CANCEL)); - instances.put(Condition.remote_server_timeout, new XMPPError.ErrorSpecification( - Condition.remote_server_timeout, Type.WAIT)); - instances.put(Condition.remote_server_error, new XMPPError.ErrorSpecification( - Condition.remote_server_error, Type.CANCEL)); - instances.put(Condition.resource_constraint, new XMPPError.ErrorSpecification( - Condition.resource_constraint, Type.WAIT)); - instances.put(Condition.service_unavailable, new XMPPError.ErrorSpecification( - Condition.service_unavailable, Type.CANCEL)); - instances.put(Condition.subscription_required, new XMPPError.ErrorSpecification( - Condition.subscription_required, Type.AUTH)); - instances.put(Condition.undefined_condition, new XMPPError.ErrorSpecification( - Condition.undefined_condition, Type.WAIT)); - instances.put(Condition.unexpected_request, new XMPPError.ErrorSpecification( - Condition.unexpected_request, Type.WAIT)); - instances.put(Condition.request_timeout, new XMPPError.ErrorSpecification( - Condition.request_timeout, Type.CANCEL)); - } - - protected static ErrorSpecification specFor(Condition condition) { - return instances.get(condition); - } - - /** - * Returns the error type. - * - * @return the error type. - */ - protected Type getType() { - return type; - } - } } 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 b1f8342e1..8e731fabd 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,10 +16,9 @@ */ package org.jivesoftware.smack.sasl.packet; -import java.util.Collections; -import java.util.Locale; import java.util.Map; +import org.jivesoftware.smack.packet.AbstractError; import org.jivesoftware.smack.packet.PlainStreamElement; import org.jivesoftware.smack.sasl.SASLError; import org.jivesoftware.smack.util.StringUtils; @@ -31,7 +30,7 @@ public class SaslStreamElements { /** * Initiating SASL authentication by select a mechanism. */ - public static class AuthMechanism extends PlainStreamElement { + public static class AuthMechanism implements PlainStreamElement { public static final String ELEMENT = "auth"; private final String mechanism; @@ -69,7 +68,7 @@ public class SaslStreamElements { /** * A SASL challenge stream element. */ - public static class Challenge extends PlainStreamElement { + public static class Challenge implements PlainStreamElement { public static final String ELEMENT = "challenge"; private final String data; @@ -91,7 +90,7 @@ public class SaslStreamElements { /** * A SASL response stream element. */ - public static class Response extends PlainStreamElement { + public static class Response implements PlainStreamElement { public static final String ELEMENT = "response"; private final String authenticationText; @@ -121,7 +120,7 @@ public class SaslStreamElements { /** * A SASL success stream element. */ - public static class Success extends PlainStreamElement { + public static class Success implements PlainStreamElement { public static final String ELEMENT = "success"; final private String data; @@ -156,25 +155,21 @@ public class SaslStreamElements { } /** - * A SASL failure stream element. + * A SASL failure stream element, also called "SASL Error" + * @see RFC 6120 6.5 SASL Errors */ - public static class SASLFailure extends PlainStreamElement { + public static class SASLFailure extends AbstractError implements PlainStreamElement { public static final String ELEMENT = "failure"; 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(); - } + super(descriptiveTexts); SASLError error = SASLError.fromString(saslError); if (error == null) { // RFC6120 6.5 states that unknown condition must be treat as generic authentication @@ -203,50 +198,19 @@ 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(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"); - } + addDescriptiveTextsAndExtensions(xml); xml.closeElement(ELEMENT); return xml; } + + @Override + public String toString() { + return toXML().toString(); + } } } 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 1968e163d..ca1736df6 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 @@ -25,7 +25,6 @@ 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; @@ -762,6 +761,18 @@ public class PacketParserUtils { return new Compress.Feature(methods); } + public static Map parseDescriptiveTexts(XmlPullParser parser, Map descriptiveTexts) + throws XmlPullParserException, IOException { + if (descriptiveTexts == null) { + descriptiveTexts = new HashMap(); + } + String xmllang = getLanguageAttribute(parser); + String text = parser.nextText(); + String previousValue = descriptiveTexts.put(xmllang, text); + assert (previousValue == null); + return descriptiveTexts; + } + /** * Parses SASL authentication error packets. * @@ -773,17 +784,14 @@ public class PacketParserUtils { public static SASLFailure parseSASLFailure(XmlPullParser parser) throws XmlPullParserException, IOException { final int initialDepth = parser.getDepth(); String condition = null; - Map descriptiveTexts = new HashMap(); + Map descriptiveTexts = null; outerloop: while (true) { int eventType = parser.next(); 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); + descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts); } else { assert(condition == null); @@ -806,37 +814,51 @@ public class PacketParserUtils { * @param parser the XML parser. * @return an stream error packet. * @throws XmlPullParserException if an exception occurs while parsing the packet. + * @throws SmackException */ - public static StreamError parseStreamError(XmlPullParser parser) throws IOException, - XmlPullParserException { - final int depth = parser.getDepth(); - boolean done = false; - String code = null; - String text = null; - while (!done) { - int eventType = parser.next(); - - if (eventType == XmlPullParser.START_TAG) { - String namespace = parser.getNamespace(); - if (StreamError.NAMESPACE.equals(namespace)) { + public static StreamError parseStreamError(XmlPullParser parser) throws IOException, XmlPullParserException, + SmackException { + final int initialDepth = parser.getDepth(); + List extensions = new ArrayList(); + Map descriptiveTexts = null; + StreamError.Condition condition = null; + String conditionText = null; + outerloop: while (true) { + int eventType = parser.next(); + switch (eventType) { + case XmlPullParser.START_TAG: String name = parser.getName(); - if (name.equals(Packet.TEXT) && !parser.isEmptyElementTag()) { - parser.next(); - text = parser.getText(); + String namespace = parser.getNamespace(); + switch (namespace) { + case StreamError.NAMESPACE: + switch (name) { + case "text": + descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts); + break; + default: + // If it's not a text element, that is qualified by the StreamError.NAMESPACE, + // then it has to be the stream error code + condition = StreamError.Condition.fromString(name); + if (!parser.isEmptyElementTag()) { + conditionText = parser.nextText(); + } + break; + } + break; + default: + PacketParserUtils.addPacketExtension(extensions, parser, name, namespace); + break; } - else { - // If it's not a text element, that is qualified by the StreamError.NAMESPACE, - // then it has to be the stream error code - code = name; + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; } + break; } } - else if (eventType == XmlPullParser.END_TAG && depth == parser.getDepth()) { - done = true; - } + return new StreamError(condition, conditionText, descriptiveTexts, extensions); } - return new StreamError(code, text); -} /** * Parses error sub-packets. @@ -849,50 +871,47 @@ public class PacketParserUtils { */ public static XMPPError parseError(XmlPullParser parser) throws XmlPullParserException, IOException, SmackException { - String type = null; - String message = null; - String condition = null; + final int initialDepth = parser.getDepth(); + Map descriptiveTexts = null; + XMPPError.Condition condition = null; + String conditionText = null; List extensions = new ArrayList(); // Parse the error header - type = parser.getAttributeValue("", "type"); - // Parse the text and condition tags - outerloop: - while (true) { + XMPPError.Type errorType = XMPPError.Type.fromString(parser.getAttributeValue("", "type")); + String errorGenerator = parser.getAttributeValue("", "by"); + + outerloop: while (true) { int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals(Packet.TEXT)) { - message = parser.nextText(); + switch (eventType) { + case XmlPullParser.START_TAG: + String name = parser.getName(); + String namespace = parser.getNamespace(); + switch (namespace) { + case XMPPError.NAMESPACE: + switch (name) { + case Packet.TEXT: + descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts); + break; + default: + condition = XMPPError.Condition.fromString(name); + if (!parser.isEmptyElementTag()) { + conditionText = parser.nextText(); + } + break; + } + break; + default: + PacketParserUtils.addPacketExtension(extensions, parser, name, namespace); } - else { - // Condition tag, it can be xmpp error or an application defined error. - String elementName = parser.getName(); - String namespace = parser.getNamespace(); - if (namespace.equals(XMPPError.NAMESPACE)) { - condition = elementName; - } - else { - PacketParserUtils.addPacketExtension(extensions, parser, elementName, namespace); - } + break; + case XmlPullParser.END_TAG: + if (parser.getDepth() == initialDepth) { + break outerloop; } } - else if (eventType == XmlPullParser.END_TAG) { - if (parser.getName().equals("error")) { - break outerloop; - } - } } - // Parse the error type. - XMPPError.Type errorType = XMPPError.Type.CANCEL; - try { - if (type != null) { - errorType = XMPPError.Type.valueOf(type.toUpperCase(Locale.US)); - } - } - catch (IllegalArgumentException iae) { - LOGGER.log(Level.SEVERE, "Could not find error type for " + type.toUpperCase(Locale.US), iae); - } - return new XMPPError(errorType, condition, message, extensions); + return new XMPPError(condition, conditionText, errorGenerator, errorType, descriptiveTexts, extensions); } /** diff --git a/smack-core/src/test/java/org/jivesoftware/smack/packet/StreamErrorTest.java b/smack-core/src/test/java/org/jivesoftware/smack/packet/StreamErrorTest.java index 33bffd773..905fb773e 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/packet/StreamErrorTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/packet/StreamErrorTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import org.jivesoftware.smack.packet.StreamError.Condition; import org.jivesoftware.smack.util.PacketParserUtils; import org.junit.Test; import org.xmlpull.v1.XmlPullParser; @@ -44,7 +45,7 @@ public class StreamErrorTest { fail(e.getMessage()); } assertNotNull(error); - assertEquals("conflict", error.getCode()); + assertEquals(Condition.conflict, error.getCondition()); } @Test @@ -68,8 +69,8 @@ public class StreamErrorTest { fail(e.getMessage()); } assertNotNull(error); - assertEquals("conflict", error.getCode()); - assertEquals("Replaced by new connection", error.getText()); + assertEquals(Condition.conflict, error.getCondition()); + assertEquals("Replaced by new connection", error.getDescriptiveText()); } @Test @@ -84,7 +85,7 @@ public class StreamErrorTest { "" + "Replaced by new connection" + "" + - "" + + "" + "Text contents of application-specific condition element: Foo Bar" + "" + "" + @@ -96,10 +97,10 @@ public class StreamErrorTest { fail(e.getMessage()); } assertNotNull(error); - assertEquals("conflict", error.getCode()); - assertEquals("Replaced by new connection", error.getText()); - // As of now, Smack ignores application-specific condition elements, so we don't - // test them. + assertEquals(Condition.conflict, error.getCondition()); + assertEquals("Replaced by new connection", error.getDescriptiveText()); + PacketExtension appSpecificElement = error.getExtension("appSpecificElement", "myns"); + assertNotNull(appSpecificElement); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java index 7da37c886..d5cbb2b4e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java @@ -280,7 +280,7 @@ public class Socks5BytestreamRequest implements BytestreamRequest { */ private void cancelRequest() throws XMPPErrorException, NotConnectedException { String errorMessage = "Could not establish socket with any provided host"; - XMPPError error = new XMPPError(XMPPError.Condition.item_not_found, errorMessage); + XMPPError error = XMPPError.from(XMPPError.Condition.item_not_found, errorMessage); IQ errorIQ = IQ.createErrorResponse(this.bytestreamRequest, error); this.manager.getConnection().sendPacket(errorIQ); throw new XMPPErrorException(errorMessage, error); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java index 33820aa60..b96332188 100755 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java @@ -593,8 +593,7 @@ public class AdHocCommandManager extends Manager { private void respondError(AdHocCommandData response, XMPPError.Condition condition, AdHocCommand.SpecificErrorCondition specificCondition) throws NotConnectedException { - XMPPError error = new XMPPError(condition); - error.addExtension(new AdHocCommandData.SpecificError(specificCondition)); + XMPPError error = new XMPPError(condition, new AdHocCommandData.SpecificError(specificCondition)); respondError(response, error); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferException.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferException.java new file mode 100644 index 000000000..71a1b9428 --- /dev/null +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferException.java @@ -0,0 +1,45 @@ +/** + * + * 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.smackx.filetransfer; + +import org.jivesoftware.smack.SmackException; + +public abstract class FileTransferException extends SmackException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public static class NoStreamMethodsOfferedException extends FileTransferException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + } + + public static class NoAcceptableTransferMechanisms extends FileTransferException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + } +} diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java index d5b0e8f50..281d0ac60 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/FileTransferNegotiator.java @@ -37,6 +37,8 @@ import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.filetransfer.FileTransferException.NoAcceptableTransferMechanisms; +import org.jivesoftware.smackx.filetransfer.FileTransferException.NoStreamMethodsOfferedException; import org.jivesoftware.smackx.si.packet.StreamInitiation; import org.jivesoftware.smackx.xdata.Form; import org.jivesoftware.smackx.xdata.FormField; @@ -176,32 +178,32 @@ public class FileTransferNegotiator extends Manager { * * @param request The related file transfer request. * @return The file transfer object that handles the transfer - * @throws XMPPErrorException If there are either no stream methods contained in the packet, or + * @throws NoStreamMethodsOfferedException If there are either no stream methods contained in the packet, or * there is not an appropriate stream method. * @throws NotConnectedException + * @throws NoAcceptableTransferMechanisms */ public StreamNegotiator selectStreamNegotiator( - FileTransferRequest request) throws XMPPErrorException, NotConnectedException { + FileTransferRequest request) throws NotConnectedException, NoStreamMethodsOfferedException, NoAcceptableTransferMechanisms { StreamInitiation si = request.getStreamInitiation(); FormField streamMethodField = getStreamMethodField(si .getFeatureNegotiationForm()); if (streamMethodField == null) { - String errorMessage = "No stream methods contained in packet."; - XMPPError error = new XMPPError(XMPPError.Condition.bad_request, errorMessage); + String errorMessage = "No stream methods contained in stanza."; + XMPPError error = XMPPError.from(XMPPError.Condition.bad_request, errorMessage); IQ iqPacket = IQ.createErrorResponse(si, error); connection().sendPacket(iqPacket); - throw new XMPPErrorException(errorMessage, error); + throw new FileTransferException.NoStreamMethodsOfferedException(); } // select the appropriate protocol - StreamNegotiator selectedStreamNegotiator; try { selectedStreamNegotiator = getNegotiator(streamMethodField); } - catch (XMPPErrorException e) { - IQ iqPacket = IQ.createErrorResponse(si, e.getXMPPError()); + catch (NoAcceptableTransferMechanisms e) { + IQ iqPacket = IQ.createErrorResponse(si, XMPPError.from(XMPPError.Condition.bad_request, "No acceptable transfer mechanism")); connection().sendPacket(iqPacket); throw e; } @@ -221,7 +223,7 @@ public class FileTransferNegotiator extends Manager { } private StreamNegotiator getNegotiator(final FormField field) - throws XMPPErrorException { + throws NoAcceptableTransferMechanisms { String variable; boolean isByteStream = false; boolean isIBB = false; @@ -236,9 +238,7 @@ public class FileTransferNegotiator extends Manager { } if (!isByteStream && !isIBB) { - XMPPError error = new XMPPError(XMPPError.Condition.bad_request, - "No acceptable transfer mechanism"); - throw new XMPPErrorException(error); + throw new FileTransferException.NoAcceptableTransferMechanisms(); } if (isByteStream && isIBB) { @@ -299,10 +299,11 @@ public class FileTransferNegotiator extends Manager { * @throws XMPPErrorException Thrown if there is an error negotiating the file transfer. * @throws NotConnectedException * @throws NoResponseException + * @throws NoAcceptableTransferMechanisms */ public StreamNegotiator negotiateOutgoingTransfer(final String userID, final String streamID, final String fileName, final long size, - final String desc, int responseTimeout) throws XMPPErrorException, NotConnectedException, NoResponseException { + final String desc, int responseTimeout) throws XMPPErrorException, NotConnectedException, NoResponseException, NoAcceptableTransferMechanisms { StreamInitiation si = new StreamInitiation(); si.setSessionID(streamID); si.setMimeType(URLConnection.guessContentTypeFromName(fileName)); @@ -337,8 +338,7 @@ public class FileTransferNegotiator extends Manager { } } - private StreamNegotiator getOutgoingNegotiator(final FormField field) - throws XMPPErrorException { + private StreamNegotiator getOutgoingNegotiator(final FormField field) throws NoAcceptableTransferMechanisms { boolean isByteStream = false; boolean isIBB = false; for (String variable : field.getValues()) { @@ -351,9 +351,7 @@ public class FileTransferNegotiator extends Manager { } if (!isByteStream && !isIBB) { - XMPPError error = new XMPPError(XMPPError.Condition.bad_request, - "No acceptable transfer mechanism"); - throw new XMPPErrorException(error); + throw new FileTransferException.NoAcceptableTransferMechanisms(); } if (isByteStream && isIBB) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java index bdf7a439b..9da7e8c2e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/OutgoingFileTransfer.java @@ -341,16 +341,15 @@ public class OutgoingFileTransfer extends FileTransfer { private void handleXMPPException(XMPPErrorException e) { XMPPError error = e.getXMPPError(); if (error != null) { - String condition = error.getCondition(); - if (XMPPError.Condition.forbidden.equals(condition)) { + switch (error.getCondition()) { + case forbidden: setStatus(Status.refused); return; - } - else if (XMPPError.Condition.bad_request.equals(condition)) { + case bad_request: setStatus(Status.error); setError(Error.not_acceptable); - } - else { + break; + default: setStatus(FileTransfer.Status.error); } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java index 46f09b655..d9fe0d5d0 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/CloseListenerTest.java @@ -73,7 +73,7 @@ public class CloseListenerTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), + assertEquals(XMPPError.Condition.item_not_found, argument.getValue().getError().getCondition()); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java index b0f79caa9..29da9edd2 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/DataListenerTest.java @@ -75,7 +75,7 @@ public class DataListenerTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), + assertEquals(XMPPError.Condition.item_not_found, argument.getValue().getError().getCondition()); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java index a5f79c76e..b7c9b64fe 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManagerTest.java @@ -120,7 +120,7 @@ public class InBandBytestreamManagerTest { fail("exception should be thrown"); } catch (XMPPErrorException e) { - assertEquals(XMPPError.Condition.feature_not_implemented.toString(), + assertEquals(XMPPError.Condition.feature_not_implemented, e.getXMPPError().getCondition()); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java index 718cef3fa..057549a2c 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamRequestTest.java @@ -83,7 +83,7 @@ public class InBandBytestreamRequestTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.not_acceptable.toString(), + assertEquals(XMPPError.Condition.not_acceptable, argument.getValue().getError().getCondition()); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java index 928a92ef9..a946c5175 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamSessionTest.java @@ -368,7 +368,7 @@ public class InBandBytestreamSessionTest { protocol.addResponse(null, Verification.requestTypeERROR, new Verification() { public void verify(IQ request, IQ response) { - assertEquals(XMPPError.Condition.unexpected_request.toString(), + assertEquals(XMPPError.Condition.unexpected_request, request.getError().getCondition()); } @@ -406,7 +406,7 @@ public class InBandBytestreamSessionTest { protocol.addResponse(null, Verification.requestTypeERROR, new Verification() { public void verify(IQ request, IQ response) { - assertEquals(XMPPError.Condition.bad_request.toString(), + assertEquals(XMPPError.Condition.bad_request, request.getError().getCondition()); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java index 48ade4785..d3894e8dc 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java @@ -91,7 +91,7 @@ public class InitiationListenerTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.not_acceptable.toString(), + assertEquals(XMPPError.Condition.not_acceptable, argument.getValue().getError().getCondition()); } @@ -119,7 +119,7 @@ public class InitiationListenerTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.resource_constraint.toString(), + assertEquals(XMPPError.Condition.resource_constraint, argument.getValue().getError().getCondition()); } @@ -209,7 +209,7 @@ public class InitiationListenerTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.not_acceptable.toString(), + assertEquals(XMPPError.Condition.not_acceptable, argument.getValue().getError().getCondition()); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java index c4e9b103c..75bf95bb2 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/InitiationListenerTest.java @@ -98,7 +98,7 @@ public class InitiationListenerTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.not_acceptable.toString(), + assertEquals(XMPPError.Condition.not_acceptable, argument.getValue().getError().getCondition()); } @@ -188,7 +188,7 @@ public class InitiationListenerTest { // assert that reply is the correct error packet assertEquals(initiatorJID, argument.getValue().getTo()); assertEquals(IQ.Type.error, argument.getValue().getType()); - assertEquals(XMPPError.Condition.not_acceptable.toString(), + assertEquals(XMPPError.Condition.not_acceptable, argument.getValue().getError().getCondition()); } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java index 4c00350ad..92512d875 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java @@ -113,7 +113,7 @@ public class Socks5ByteStreamRequestTest { assertTrue(IQ.class.isInstance(targetResponse)); assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), + assertEquals(XMPPError.Condition.item_not_found, ((IQ) targetResponse).getError().getCondition()); } @@ -157,7 +157,7 @@ public class Socks5ByteStreamRequestTest { assertTrue(IQ.class.isInstance(targetResponse)); assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), + assertEquals(XMPPError.Condition.item_not_found, ((IQ) targetResponse).getError().getCondition()); } @@ -205,7 +205,7 @@ public class Socks5ByteStreamRequestTest { assertTrue(IQ.class.isInstance(targetResponse)); assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), + assertEquals(XMPPError.Condition.item_not_found, ((IQ) targetResponse).getError().getCondition()); } @@ -297,7 +297,7 @@ public class Socks5ByteStreamRequestTest { assertTrue(IQ.class.isInstance(targetResponse)); assertEquals(initiatorJID, targetResponse.getTo()); assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); - assertEquals(XMPPError.Condition.item_not_found.toString(), + assertEquals(XMPPError.Condition.item_not_found, ((IQ) targetResponse).getError().getCondition()); } diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java index 96cf47e2b..4b48a5680 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java @@ -33,7 +33,6 @@ import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smackx.jingleold.listeners.JingleListener; import org.jivesoftware.smackx.jingleold.listeners.JingleMediaListener; @@ -1030,9 +1029,8 @@ public class JingleSession extends JingleNegotiator implements MediaReceivedList public IQ createJingleError(IQ iq, JingleError jingleError) { IQ errorPacket = null; if (jingleError != null) { - List extList = new ArrayList(); - extList.add(jingleError); - XMPPError error = new XMPPError(XMPPError.Type.CANCEL, jingleError.toString(), "", extList); + // TODO This is wrong according to XEP-166 § 10, but this jingle implementation is deprecated anyways + XMPPError error = new XMPPError(XMPPError.Condition.undefined_condition, jingleError); errorPacket = IQ.createErrorResponse(iq, error); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/MediaNegotiator.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/MediaNegotiator.java index f0d422baf..aceb9ad6c 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/MediaNegotiator.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/media/MediaNegotiator.java @@ -113,7 +113,7 @@ public class MediaNegotiator extends JingleNegotiator { setNegotiatorState(JingleNegotiatorState.FAILED); triggerMediaClosed(getBestCommonAudioPt()); // This next line seems wrong, and may subvert the normal closing process. - throw new JingleException(iq.getError().getMessage()); + throw new JingleException(iq.getError().getDescriptiveText()); } else if (iq.getType().equals(IQ.Type.result)) { // Process ACKs if (isExpectedId(iq.getPacketID())) { diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java index 54ed04b5a..0de66b148 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/nat/TransportNegotiator.java @@ -603,7 +603,7 @@ public abstract class TransportNegotiator extends JingleNegotiator { setNegotiatorState(JingleNegotiatorState.FAILED); triggerTransportClosed(null); // This next line seems wrong, and may subvert the normal closing process. - throw new JingleException(iq.getError().getMessage()); + throw new JingleException(iq.getError().getDescriptiveText()); } else if (iq.getType().equals(IQ.Type.result)) { // Process ACKs if (isExpectedId(iq.getPacketID())) { diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index cd14c2e2e..5b13c122d 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -1101,7 +1101,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { break; case Failed.ELEMENT: Failed failed = ParseStreamManagement.failed(parser); - XMPPError xmppError = failed.getXMPPError(); + XMPPError xmppError = new XMPPError(failed.getXMPPErrorCondition()); XMPPException xmppException = new XMPPErrorException("Stream Management failed", xmppError); // If only XEP-198 would specify different failure elements for the SM // enable and SM resume failure case. But this is not the case, so we diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/packet/StreamManagement.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/packet/StreamManagement.java index 1fa8de461..ff6e8aa2d 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/packet/StreamManagement.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/packet/StreamManagement.java @@ -189,25 +189,26 @@ public class StreamManagement { public static class Failed extends FullStreamElement { public static final String ELEMENT = "failed"; - private XMPPError error; + private XMPPError.Condition condition; public Failed() { } - public Failed(XMPPError error) { - this.error = error; + public Failed(XMPPError.Condition condition) { + this.condition = condition; } - public XMPPError getXMPPError() { - return error; + public XMPPError.Condition getXMPPErrorCondition() { + return condition; } @Override public CharSequence toXML() { XmlStringBuilder xml = new XmlStringBuilder(this); - if (error != null) { + if (condition != null) { xml.rightAngleBracket(); - xml.append(error.toXML()); + xml.append(condition.toString()); + xml.xmlnsAttribute(XMPPError.NAMESPACE); xml.closeElement(ELEMENT); } else { diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagement.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagement.java index e6290079c..d33c3e3ca 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagement.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagement.java @@ -44,7 +44,7 @@ public class ParseStreamManagement { public static Failed failed(XmlPullParser parser) throws XmlPullParserException, IOException { ParserUtils.assertAtStartTag(parser); String name; - String condition = "unknown"; + XMPPError.Condition condition = null; outerloop: while(true) { int event = parser.next(); @@ -53,7 +53,7 @@ public class ParseStreamManagement { name = parser.getName(); String namespace = parser.getNamespace(); if (XMPPError.NAMESPACE.equals(namespace)) { - condition = name; + condition = XMPPError.Condition.fromString(name); } break; case XmlPullParser.END_TAG: @@ -65,8 +65,7 @@ public class ParseStreamManagement { } } ParserUtils.assertAtEndTag(parser); - XMPPError error = new XMPPError(condition); - return new Failed(error); + return new Failed(condition); } public static Resumed resumed(XmlPullParser parser) throws XmlPullParserException, IOException { diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagementTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagementTest.java index eaccfbdbf..5680ee770 100644 --- a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagementTest.java +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/sm/provider/ParseStreamManagementTest.java @@ -19,6 +19,7 @@ package org.jivesoftware.smack.tcp.sm.provider; import com.jamesmurty.utils.XMLBuilder; + import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.tcp.sm.packet.StreamManagement; import org.jivesoftware.smack.util.PacketParserUtils; @@ -34,6 +35,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public class ParseStreamManagementTest { private static final Properties outputProperties = initOutputProperties(); @@ -83,19 +85,16 @@ public class ParseStreamManagementTest { PacketParserUtils.getParserFor(failedStanza)); assertThat(failedPacket, is(notNullValue())); - XMPPError error = failedPacket.getXMPPError(); - - assertThat(error, is(notNullValue())); - assertThat(error.getCondition(), equalTo("unknown")); + assertTrue(failedPacket.getXMPPErrorCondition() == null); } @Test public void testParseFailedError() throws Exception { - String errorCondition = "failure"; + XMPPError.Condition errorCondition = XMPPError.Condition.unexpected_request; String failedStanza = XMLBuilder.create("failed") .a("xmlns", "urn:xmpp:sm:3") - .element(errorCondition, XMPPError.NAMESPACE) + .element(errorCondition.toString(), XMPPError.NAMESPACE) .asString(outputProperties); System.err.println(failedStanza); @@ -104,10 +103,7 @@ public class ParseStreamManagementTest { PacketParserUtils.getParserFor(failedStanza)); assertThat(failedPacket, is(notNullValue())); - XMPPError error = failedPacket.getXMPPError(); - - assertThat(error, is(notNullValue())); - assertThat(error.getCondition(), equalTo(errorCondition)); + assertTrue(failedPacket.getXMPPErrorCondition() == errorCondition); } @Test