Make XMPPError imutable and add stanza reference

This commit is contained in:
Florian Schmaus 2015-12-08 08:22:50 +01:00
parent 83eda4c58d
commit 45feaecdf7
30 changed files with 308 additions and 94 deletions

View File

@ -1050,8 +1050,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
} }
// If the IQ stanza is of type "get" or "set" with no registered IQ request handler, then answer an // If the IQ stanza is of type "get" or "set" with no registered IQ request handler, then answer an
// IQ of type 'error' with condition 'service-unavailable'. // IQ of type 'error' with condition 'service-unavailable'.
ErrorIQ errorIQ = IQ.createErrorResponse(iq, new XMPPError( ErrorIQ errorIQ = IQ.createErrorResponse(iq, XMPPError.getBuilder((
XMPPError.Condition.service_unavailable)); XMPPError.Condition.service_unavailable)));
try { try {
sendStanza(errorIQ); sendStanza(errorIQ);
} }

View File

@ -73,6 +73,10 @@ public abstract class XMPPException extends Exception {
private static final long serialVersionUID = 212790389529249604L; private static final long serialVersionUID = 212790389529249604L;
private final XMPPError error; private final XMPPError error;
public XMPPErrorException(XMPPError.Builder xmppErrorBuilder) {
this(xmppErrorBuilder.build());
}
/** /**
* Creates a new XMPPException with the XMPPError that was the root case of the exception. * Creates a new XMPPException with the XMPPError that was the root case of the exception.
* *
@ -90,7 +94,9 @@ public abstract class XMPPException extends Exception {
* @param message a description of the exception. * @param message a description of the exception.
* @param error the root cause of the exception. * @param error the root cause of the exception.
* @param wrappedThrowable the root cause of the exception. * @param wrappedThrowable the root cause of the exception.
* @deprecated use {@link XMPPErrorException(XMPPError)} instead.
*/ */
@Deprecated
public XMPPErrorException(String message, XMPPError error, Throwable wrappedThrowable) { public XMPPErrorException(String message, XMPPError error, Throwable wrappedThrowable) {
super(message, wrappedThrowable); super(message, wrappedThrowable);
this.error = error; this.error = error;
@ -102,7 +108,9 @@ public abstract class XMPPException extends Exception {
* *
* @param message a description of the exception. * @param message a description of the exception.
* @param error the root cause of the exception. * @param error the root cause of the exception.
* @deprecated use {@link XMPPErrorException(XMPPError)} instead.
*/ */
@Deprecated
public XMPPErrorException(String message, XMPPError error) { public XMPPErrorException(String message, XMPPError error) {
super(message); super(message);
this.error = error; this.error = error;

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2014 Florian Schmaus * Copyright 2014-2015 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,9 @@
*/ */
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -26,9 +28,9 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
public class AbstractError { public class AbstractError {
private final String textNamespace; protected final String textNamespace;
protected final Map<String, String> descriptiveTexts; protected final Map<String, String> descriptiveTexts;
private final List<ExtensionElement> extensions; protected final List<ExtensionElement> extensions;
protected AbstractError(Map<String, String> descriptiveTexts) { protected AbstractError(Map<String, String> descriptiveTexts) {
@ -108,4 +110,53 @@ public class AbstractError {
xml.append(packetExtension.toXML()); xml.append(packetExtension.toXML());
} }
} }
public static abstract class Builder<B extends Builder<B>> {
protected String textNamespace;
protected Map<String, String> descriptiveTexts;
protected List<ExtensionElement> extensions;
public B setDescriptiveTexts(Map<String, String> descriptiveTexts) {
if (this.descriptiveTexts == null) {
this.descriptiveTexts = descriptiveTexts;
}
else {
this.descriptiveTexts.putAll(descriptiveTexts);
}
return getThis();
}
public B setDescriptiveEnText(String descriptiveEnText) {
if (descriptiveTexts == null) {
descriptiveTexts = new HashMap<>();
}
descriptiveTexts.put("en", descriptiveEnText);
return getThis();
}
public B setTextNamespace(String textNamespace) {
this.textNamespace = textNamespace;
return getThis();
}
public B setExtensions(List<ExtensionElement> extensions) {
if (this.extensions == null) {
this.extensions = extensions;
}
else {
this.extensions.addAll(extensions);
}
return getThis();
}
public B addExtension(ExtensionElement extension) {
if (extensions == null) {
extensions = new ArrayList<>();
}
extensions.add(extension);
return getThis();
}
protected abstract B getThis();
}
} }

View File

@ -27,13 +27,13 @@ public class ErrorIQ extends SimpleIQ {
* <p> * <p>
* According to RFC 6120 § 8.3.1 "4. An error stanza MUST contain an <error/> child element.", so the xmppError argument is mandatory. * According to RFC 6120 § 8.3.1 "4. An error stanza MUST contain an <error/> child element.", so the xmppError argument is mandatory.
* </p> * </p>
* @param xmppError the XMPPError (required). * @param xmppErrorBuilder the XMPPError builder (required).
*/ */
public ErrorIQ(XMPPError xmppError) { public ErrorIQ(XMPPError.Builder xmppErrorBuilder) {
super(ELEMENT, null); super(ELEMENT, null);
Objects.requireNonNull(xmppError, "XMPPError must not be null"); Objects.requireNonNull(xmppErrorBuilder, "xmppErrorBuilder must not be null");
setType(IQ.Type.error); setType(IQ.Type.error);
setError(xmppError); setError(xmppErrorBuilder);
} }
} }

View File

@ -241,7 +241,7 @@ public abstract class IQ extends Stanza {
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}. * {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ. * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
*/ */
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) { public static ErrorIQ createErrorResponse(final IQ request, final XMPPError.Builder error) {
if (!(request.getType() == Type.get || request.getType() == Type.set)) { if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML()); "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
@ -250,9 +250,40 @@ public abstract class IQ extends Stanza {
result.setStanzaId(request.getStanzaId()); result.setStanzaId(request.getStanzaId());
result.setFrom(request.getTo()); result.setFrom(request.getTo());
result.setTo(request.getFrom()); result.setTo(request.getFrom());
error.setStanza(result);
return result; return result;
} }
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError.Condition condition) {
return createErrorResponse(request, XMPPError.getBuilder(condition));
}
/**
* Convenience method to create a new {@link Type#error IQ.Type.error} IQ
* based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
* IQ. The new stanza(/packet) will be initialized with:<ul>
* <li>The sender set to the recipient of the originating IQ.
* <li>The recipient set to the sender of the originating IQ.
* <li>The type set to {@link Type#error IQ.Type.error}.
* <li>The id set to the id of the originating IQ.
* <li>The child element contained in the associated originating IQ.
* <li>The provided {@link XMPPError XMPPError}.
* </ul>
*
* @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet.
* @param error the error to associate with the created IQ packet.
* @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
* @deprecated use {@link #createErrorResponse(IQ, org.jivesoftware.smack.packet.XMPPError.Builder)} instead.
*/
@Deprecated
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) {
return createErrorResponse(request, XMPPError.getBuilder(error));
}
/** /**
* A enum to represent the type of the IQ stanza. * A enum to represent the type of the IQ stanza.
*/ */

View File

@ -248,11 +248,26 @@ public abstract class Stanza implements TopLevelStreamElement {
* Sets the error for this packet. * Sets the error for this packet.
* *
* @param error the error to associate with this packet. * @param error the error to associate with this packet.
* @deprecated use {@link #setError(org.jivesoftware.smack.packet.XMPPError.Builder)} instead.
*/ */
@Deprecated
public void setError(XMPPError error) { public void setError(XMPPError error) {
this.error = error; this.error = error;
} }
/**
* Sets the error for this stanza.
*
* @param xmppErrorBuilder the error to associate with this stanza.
*/
public void setError(XMPPError.Builder xmppErrorBuilder) {
if (xmppErrorBuilder == null) {
return;
}
xmppErrorBuilder.setStanza(this);
error = xmppErrorBuilder.build();
}
/** /**
* Returns the xml:lang of this Stanza, or null if one has not been set. * Returns the xml:lang of this Stanza, or null if one has not been set.
* *

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software. * Copyright 2003-2007 Jive Software, 2015 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
@ -91,17 +92,37 @@ public class XMPPError extends AbstractError {
CONDITION_TO_TYPE.put(Condition.subscription_required, Type.WAIT); CONDITION_TO_TYPE.put(Condition.subscription_required, Type.WAIT);
CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.MODIFY); CONDITION_TO_TYPE.put(Condition.unexpected_request, Type.MODIFY);
} }
private final Condition condition; private final Condition condition;
private final String conditionText; private final String conditionText;
private final String errorGenerator; private final String errorGenerator;
private final Type type; private final Type type;
private final Stanza stanza;
// TODO: Deprecated constructors
// deprecate in 4.3
/**
* Create a new XMPPError.
*
* @param condition
* @deprecated use {@link Builder} instead.
*/
@Deprecated
public XMPPError(Condition condition) { public XMPPError(Condition condition) {
this(condition, null, null, null, null, null); this(condition, null, null, null, null, null, null);
} }
/**
* Create a new XMPPError.
*
* @param condition
* @param applicationSpecificCondition
* @deprecated use {@link Builder} instead.
*/
@Deprecated
public XMPPError(Condition condition, ExtensionElement applicationSpecificCondition) { public XMPPError(Condition condition, ExtensionElement applicationSpecificCondition) {
this(condition, null, null, null, null, Arrays.asList(applicationSpecificCondition)); this(condition, null, null, null, null, Arrays.asList(applicationSpecificCondition), null);
} }
/** /**
@ -114,11 +135,31 @@ public class XMPPError extends AbstractError {
* @param condition the error condition. * @param condition the error condition.
* @param descriptiveTexts * @param descriptiveTexts
* @param extensions list of stanza(/packet) extensions * @param extensions list of stanza(/packet) extensions
* @deprecated use {@link Builder} instead.
*/ */
@Deprecated
public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts, public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts,
List<ExtensionElement> extensions) { List<ExtensionElement> extensions) {
this(condition, conditionText, errorGenerator, type, descriptiveTexts, extensions, null);
}
/**
* Creates a new error with the specified type, condition and message.
* This constructor is used when the condition is not recognized automatically by XMPPError
* i.e. there is not a defined instance of ErrorCondition or it does not apply the default
* specification.
*
* @param type the error type.
* @param condition the error condition.
* @param descriptiveTexts
* @param extensions list of stanza(/packet) extensions
* @param stanza the stanza carrying this XMPP error.
*/
public XMPPError(Condition condition, String conditionText, String errorGenerator, Type type, Map<String, String> descriptiveTexts,
List<ExtensionElement> extensions, Stanza stanza) {
super(descriptiveTexts, NAMESPACE, extensions); super(descriptiveTexts, NAMESPACE, extensions);
this.condition = condition; this.condition = Objects.requireNonNull(condition, "condition must not be null");
this.stanza = stanza;
// Some implementations may send the condition as non-empty element containing the empty string, that is // Some implementations may send the condition as non-empty element containing the empty string, that is
// <condition xmlns='foo'></condition>, in this case the parser may calls this constructor with the empty string // <condition xmlns='foo'></condition>, in this case the parser may calls this constructor with the empty string
// as conditionText, therefore reset it to null if it's the empty string // as conditionText, therefore reset it to null if it's the empty string
@ -176,6 +217,16 @@ public class XMPPError extends AbstractError {
return conditionText; return conditionText;
} }
/**
* Get the stanza carrying the XMPP error.
*
* @return the stanza carrying the XMPP error.
* @since 4.2
*/
public Stanza getStanza() {
return stanza;
}
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("XMPPError: "); StringBuilder sb = new StringBuilder("XMPPError: ");
@ -215,12 +266,81 @@ public class XMPPError extends AbstractError {
return xml; return xml;
} }
public static XMPPError from(Condition condition, String descriptiveText) { public static XMPPError.Builder from(Condition condition, String descriptiveText) {
Map<String, String> descriptiveTexts = new HashMap<String, String>(); Map<String, String> descriptiveTexts = new HashMap<String, String>();
descriptiveTexts.put("en", descriptiveText); descriptiveTexts.put("en", descriptiveText);
return new XMPPError(condition, null, null, null, descriptiveTexts, null); return getBuilder().setCondition(condition).setDescriptiveTexts(descriptiveTexts);
} }
public static Builder getBuilder() {
return new Builder();
}
public static Builder getBuilder(Condition condition) {
return getBuilder().setCondition(condition);
}
public static Builder getBuilder(XMPPError xmppError) {
return getBuilder().copyFrom(xmppError);
}
public static final class Builder extends AbstractError.Builder<Builder> {
private Condition condition;
private String conditionText;
private String errorGenerator;
private Type type;
private Stanza stanza;
private Builder() {
}
public Builder setCondition(Condition condition) {
this.condition = condition;
return this;
}
public Builder setType(Type type) {
this.type = type;
return this;
}
public Builder setConditionText(String conditionText) {
this.conditionText = conditionText;
return this;
}
public Builder setErrorGenerator(String errorGenerator) {
this.errorGenerator = errorGenerator;
return this;
}
public Builder setStanza(Stanza stanza) {
this.stanza = stanza;
return this;
}
public Builder copyFrom(XMPPError xmppError) {
setCondition(xmppError.getCondition());
setType(xmppError.getType());
setConditionText(xmppError.getConditionText());
setErrorGenerator(xmppError.getErrorGenerator());
setStanza(xmppError.getStanza());
setDescriptiveTexts(xmppError.descriptiveTexts);
setTextNamespace(xmppError.textNamespace);
setExtensions(xmppError.extensions);
return this;
}
public XMPPError build() {
return new XMPPError(condition, conditionText, errorGenerator, type, descriptiveTexts,
extensions, stanza);
}
@Override
protected Builder getThis() {
return this;
}
}
/** /**
* A class to represent the type of the Error. The types are: * A class to represent the type of the Error. The types are:
* *

View File

@ -606,7 +606,7 @@ public class PacketParserUtils {
ParserUtils.assertAtStartTag(parser); ParserUtils.assertAtStartTag(parser);
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
IQ iqPacket = null; IQ iqPacket = null;
XMPPError error = null; XMPPError.Builder error = null;
final String id = parser.getAttributeValue("", "id"); final String id = parser.getAttributeValue("", "id");
final Jid to = ParserUtils.getJidAttribute(parser, "to"); final Jid to = ParserUtils.getJidAttribute(parser, "to");
@ -846,17 +846,16 @@ public class PacketParserUtils {
* @return an error sub-packet. * @return an error sub-packet.
* @throws Exception * @throws Exception
*/ */
public static XMPPError parseError(XmlPullParser parser) public static XMPPError.Builder parseError(XmlPullParser parser)
throws Exception { throws Exception {
final int initialDepth = parser.getDepth(); final int initialDepth = parser.getDepth();
Map<String, String> descriptiveTexts = null; Map<String, String> descriptiveTexts = null;
XMPPError.Condition condition = null;
String conditionText = null;
List<ExtensionElement> extensions = new ArrayList<ExtensionElement>(); List<ExtensionElement> extensions = new ArrayList<ExtensionElement>();
XMPPError.Builder builder = XMPPError.getBuilder();
// Parse the error header // Parse the error header
XMPPError.Type errorType = XMPPError.Type.fromString(parser.getAttributeValue("", "type")); builder.setType(XMPPError.Type.fromString(parser.getAttributeValue("", "type")));
String errorGenerator = parser.getAttributeValue("", "by"); builder.setErrorGenerator(parser.getAttributeValue("", "by"));
outerloop: while (true) { outerloop: while (true) {
int eventType = parser.next(); int eventType = parser.next();
@ -871,9 +870,9 @@ public class PacketParserUtils {
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts); descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
break; break;
default: default:
condition = XMPPError.Condition.fromString(name); builder.setCondition(XMPPError.Condition.fromString(name));
if (!parser.isEmptyElementTag()) { if (!parser.isEmptyElementTag()) {
conditionText = parser.nextText(); builder.setConditionText(parser.nextText());
} }
break; break;
} }
@ -888,7 +887,8 @@ public class PacketParserUtils {
} }
} }
} }
return new XMPPError(condition, conditionText, errorGenerator, errorType, descriptiveTexts, extensions); builder.setExtensions(extensions).setDescriptiveTexts(descriptiveTexts);
return builder;
} }
/** /**

View File

@ -62,7 +62,7 @@ public class IQResponseTest {
*/ */
@Test @Test
public void testGeneratingValidErrorResponse() throws XmppStringprepException { public void testGeneratingValidErrorResponse() throws XmppStringprepException {
final XMPPError error = new XMPPError(XMPPError.Condition.bad_request); final XMPPError.Builder error = XMPPError.getBuilder(XMPPError.Condition.bad_request);
final IQ request = new TestIQ(ELEMENT, NAMESPACE); final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setType(IQ.Type.set); request.setType(IQ.Type.set);
@ -75,7 +75,7 @@ public class IQResponseTest {
assertNotNull(result.getStanzaId()); assertNotNull(result.getStanzaId());
assertEquals(request.getStanzaId(), result.getStanzaId()); assertEquals(request.getStanzaId(), result.getStanzaId());
assertEquals(request.getFrom(), result.getTo()); assertEquals(request.getFrom(), result.getTo());
assertEquals(error, result.getError()); assertEquals(error.build().toXML(), result.getError().toXML());
// TODO this test was never valid // TODO this test was never valid
// assertEquals(CHILD_ELEMENT, result.getChildElementXML()); // assertEquals(CHILD_ELEMENT, result.getChildElementXML());
} }
@ -110,7 +110,7 @@ public class IQResponseTest {
*/ */
@Test @Test
public void testGeneratingErrorBasedOnError() throws XmppStringprepException { public void testGeneratingErrorBasedOnError() throws XmppStringprepException {
final XMPPError error = new XMPPError(XMPPError.Condition.bad_request); final XMPPError.Builder error = XMPPError.getBuilder(XMPPError.Condition.bad_request);
final IQ request = new TestIQ(ELEMENT, NAMESPACE); final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setType(IQ.Type.error); request.setType(IQ.Type.error);

View File

@ -444,8 +444,7 @@ public final class InBandBytestreamManager implements BytestreamManager {
* @throws InterruptedException * @throws InterruptedException
*/ */
protected void replyRejectPacket(IQ request) throws NotConnectedException, InterruptedException { protected void replyRejectPacket(IQ request) throws NotConnectedException, InterruptedException {
XMPPError xmppError = new XMPPError(XMPPError.Condition.not_acceptable); IQ error = IQ.createErrorResponse(request, XMPPError.Condition.not_acceptable);
IQ error = IQ.createErrorResponse(request, xmppError);
this.connection.sendStanza(error); this.connection.sendStanza(error);
} }
@ -458,8 +457,7 @@ public final class InBandBytestreamManager implements BytestreamManager {
* @throws InterruptedException * @throws InterruptedException
*/ */
protected void replyResourceConstraintPacket(IQ request) throws NotConnectedException, InterruptedException { protected void replyResourceConstraintPacket(IQ request) throws NotConnectedException, InterruptedException {
XMPPError xmppError = new XMPPError(XMPPError.Condition.resource_constraint); IQ error = IQ.createErrorResponse(request, XMPPError.Condition.resource_constraint);
IQ error = IQ.createErrorResponse(request, xmppError);
this.connection.sendStanza(error); this.connection.sendStanza(error);
} }
@ -472,8 +470,7 @@ public final class InBandBytestreamManager implements BytestreamManager {
* @throws InterruptedException * @throws InterruptedException
*/ */
protected void replyItemNotFoundPacket(IQ request) throws NotConnectedException, InterruptedException { protected void replyItemNotFoundPacket(IQ request) throws NotConnectedException, InterruptedException {
XMPPError xmppError = new XMPPError(XMPPError.Condition.item_not_found); IQ error = IQ.createErrorResponse(request, XMPPError.Condition.item_not_found);
IQ error = IQ.createErrorResponse(request, xmppError);
this.connection.sendStanza(error); this.connection.sendStanza(error);
} }

View File

@ -457,8 +457,8 @@ public class InBandBytestreamSession implements BytestreamSession {
* check if sequence was not used already (see XEP-0047 Section 2.2) * check if sequence was not used already (see XEP-0047 Section 2.2)
*/ */
if (data.getSeq() <= this.lastSequence) { if (data.getSeq() <= this.lastSequence) {
IQ unexpectedRequest = IQ.createErrorResponse((IQ) packet, new XMPPError( IQ unexpectedRequest = IQ.createErrorResponse((IQ) packet,
XMPPError.Condition.unexpected_request)); XMPPError.Condition.unexpected_request);
connection.sendStanza(unexpectedRequest); connection.sendStanza(unexpectedRequest);
return; return;
@ -467,8 +467,8 @@ public class InBandBytestreamSession implements BytestreamSession {
// check if encoded data is valid (see XEP-0047 Section 2.2) // check if encoded data is valid (see XEP-0047 Section 2.2)
if (data.getDecodedData() == null) { if (data.getDecodedData() == null) {
// data is invalid; respond with bad-request error // data is invalid; respond with bad-request error
IQ badRequest = IQ.createErrorResponse((IQ) packet, new XMPPError( IQ badRequest = IQ.createErrorResponse((IQ) packet,
XMPPError.Condition.bad_request)); XMPPError.Condition.bad_request);
connection.sendStanza(badRequest); connection.sendStanza(badRequest);
return; return;
} }

View File

@ -691,7 +691,7 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
* @throws InterruptedException * @throws InterruptedException
*/ */
protected void replyRejectPacket(IQ packet) throws NotConnectedException, InterruptedException { protected void replyRejectPacket(IQ packet) throws NotConnectedException, InterruptedException {
XMPPError xmppError = new XMPPError(XMPPError.Condition.not_acceptable); XMPPError.Builder xmppError = XMPPError.getBuilder(XMPPError.Condition.not_acceptable);
IQ errorIQ = IQ.createErrorResponse(packet, xmppError); IQ errorIQ = IQ.createErrorResponse(packet, xmppError);
connection().sendStanza(errorIQ); connection().sendStanza(errorIQ);
} }

View File

@ -283,10 +283,10 @@ public class Socks5BytestreamRequest implements BytestreamRequest {
*/ */
private void cancelRequest() throws XMPPErrorException, NotConnectedException, InterruptedException { private void cancelRequest() throws XMPPErrorException, NotConnectedException, InterruptedException {
String errorMessage = "Could not establish socket with any provided host"; String errorMessage = "Could not establish socket with any provided host";
XMPPError error = XMPPError.from(XMPPError.Condition.item_not_found, errorMessage); XMPPError.Builder error = XMPPError.from(XMPPError.Condition.item_not_found, errorMessage);
IQ errorIQ = IQ.createErrorResponse(this.bytestreamRequest, error); IQ errorIQ = IQ.createErrorResponse(this.bytestreamRequest, error);
this.manager.getConnection().sendStanza(errorIQ); this.manager.getConnection().sendStanza(errorIQ);
throw new XMPPErrorException(errorMessage, error); throw new XMPPErrorException(error);
} }
/** /**

View File

@ -451,7 +451,7 @@ public final class AdHocCommandManager extends Manager {
response.setStatus(Status.canceled); response.setStatus(Status.canceled);
executingCommands.remove(sessionId); executingCommands.remove(sessionId);
} }
return respondError(response, error); return respondError(response, XMPPError.getBuilder(error));
} }
} }
else { else {
@ -561,7 +561,7 @@ public final class AdHocCommandManager extends Manager {
response.setStatus(Status.canceled); response.setStatus(Status.canceled);
executingCommands.remove(sessionId); executingCommands.remove(sessionId);
} }
return respondError(response, error); return respondError(response, XMPPError.getBuilder(error));
} }
} }
} }
@ -576,7 +576,7 @@ public final class AdHocCommandManager extends Manager {
*/ */
private static IQ respondError(AdHocCommandData response, private static IQ respondError(AdHocCommandData response,
XMPPError.Condition condition) { XMPPError.Condition condition) {
return respondError(response, new XMPPError(condition)); return respondError(response, XMPPError.getBuilder(condition));
} }
/** /**
@ -590,7 +590,7 @@ public final class AdHocCommandManager extends Manager {
private static IQ respondError(AdHocCommandData response, XMPPError.Condition condition, private static IQ respondError(AdHocCommandData response, XMPPError.Condition condition,
AdHocCommand.SpecificErrorCondition specificCondition) AdHocCommand.SpecificErrorCondition specificCondition)
{ {
XMPPError error = new XMPPError(condition, new AdHocCommandData.SpecificError(specificCondition)); XMPPError.Builder error = XMPPError.getBuilder(condition).addExtension(new AdHocCommandData.SpecificError(specificCondition));
return respondError(response, error); return respondError(response, error);
} }
@ -601,7 +601,7 @@ public final class AdHocCommandManager extends Manager {
* @param error the error to send. * @param error the error to send.
* @throws NotConnectedException * @throws NotConnectedException
*/ */
private static IQ respondError(AdHocCommandData response, XMPPError error) { private static IQ respondError(AdHocCommandData response, XMPPError.Builder error) {
response.setType(IQ.Type.error); response.setType(IQ.Type.error);
response.setError(error); response.setError(error);
return response; return response;
@ -626,11 +626,11 @@ public final class AdHocCommandManager extends Manager {
command.setNode(commandInfo.getNode()); command.setNode(commandInfo.getNode());
} }
catch (InstantiationException e) { catch (InstantiationException e) {
throw new XMPPErrorException(new XMPPError( throw new XMPPErrorException(XMPPError.getBuilder(
XMPPError.Condition.internal_server_error)); XMPPError.Condition.internal_server_error));
} }
catch (IllegalAccessException e) { catch (IllegalAccessException e) {
throw new XMPPErrorException(new XMPPError( throw new XMPPErrorException(XMPPError.getBuilder(
XMPPError.Condition.internal_server_error)); XMPPError.Condition.internal_server_error));
} }
return command; return command;

View File

@ -101,7 +101,7 @@ public class AdHocCommandDataProvider extends IQProvider<AdHocCommandData> {
adHocCommandData.addNote(new AdHocCommandNote(type, value)); adHocCommandData.addNote(new AdHocCommandNote(type, value));
} }
else if (parser.getName().equals("error")) { else if (parser.getName().equals("error")) {
XMPPError error = PacketParserUtils.parseError(parser); XMPPError.Builder error = PacketParserUtils.parseError(parser);
adHocCommandData.setError(error); adHocCommandData.setError(error);
} }
} }

View File

@ -143,7 +143,7 @@ public final class ServiceDiscoveryManager extends Manager {
// Return <item-not-found/> error since client doesn't contain // Return <item-not-found/> error since client doesn't contain
// the specified node // the specified node
response.setType(IQ.Type.error); response.setType(IQ.Type.error);
response.setError(new XMPPError(XMPPError.Condition.item_not_found)); response.setError(XMPPError.getBuilder(XMPPError.Condition.item_not_found));
} }
return response; return response;
} }
@ -181,7 +181,7 @@ public final class ServiceDiscoveryManager extends Manager {
} else { } else {
// Return <item-not-found/> error since specified node was not found // Return <item-not-found/> error since specified node was not found
response.setType(IQ.Type.error); response.setType(IQ.Type.error);
response.setError(new XMPPError(XMPPError.Condition.item_not_found)); response.setError(XMPPError.getBuilder(XMPPError.Condition.item_not_found));
} }
} }
return response; return response;

View File

@ -169,7 +169,7 @@ public final class FileTransferManager extends Manager {
// Bytestream rejection as specified in XEP-65 5.3.1 Example 13, which says that // Bytestream rejection as specified in XEP-65 5.3.1 Example 13, which says that
// 'not-acceptable' should be returned. This is done by Smack in // 'not-acceptable' should be returned. This is done by Smack in
// Socks5BytestreamManager.replyRejectPacket(IQ). // Socks5BytestreamManager.replyRejectPacket(IQ).
IQ rejection = IQ.createErrorResponse(initiation, new XMPPError( IQ rejection = IQ.createErrorResponse(initiation, XMPPError.getBuilder(
XMPPError.Condition.forbidden)); XMPPError.Condition.forbidden));
connection().sendStanza(rejection); connection().sendStanza(rejection);
} }

View File

@ -192,7 +192,7 @@ public final class FileTransferNegotiator extends Manager {
if (streamMethodField == null) { if (streamMethodField == null) {
String errorMessage = "No stream methods contained in stanza."; String errorMessage = "No stream methods contained in stanza.";
XMPPError error = XMPPError.from(XMPPError.Condition.bad_request, errorMessage); XMPPError.Builder error = XMPPError.from(XMPPError.Condition.bad_request, errorMessage);
IQ iqPacket = IQ.createErrorResponse(si, error); IQ iqPacket = IQ.createErrorResponse(si, error);
connection().sendStanza(iqPacket); connection().sendStanza(iqPacket);
throw new FileTransferException.NoStreamMethodsOfferedException(); throw new FileTransferException.NoStreamMethodsOfferedException();

View File

@ -36,7 +36,6 @@ import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.packet.XMPPError.Condition; import org.jivesoftware.smack.packet.XMPPError.Condition;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.iqlast.packet.LastActivity; import org.jivesoftware.smackx.iqlast.packet.LastActivity;
@ -169,7 +168,7 @@ public final class LastActivityManager extends Manager {
@Override @Override
public IQ handleIQRequest(IQ iqRequest) { public IQ handleIQRequest(IQ iqRequest) {
if (!enabled) if (!enabled)
return IQ.createErrorResponse(iqRequest, new XMPPError(Condition.not_acceptable)); return IQ.createErrorResponse(iqRequest, Condition.not_acceptable);
LastActivity message = new LastActivity(); LastActivity message = new LastActivity();
message.setType(IQ.Type.result); message.setType(IQ.Type.result);
message.setTo(iqRequest.getFrom()); message.setTo(iqRequest.getFrom());

View File

@ -31,7 +31,6 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.packet.XMPPError.Condition; import org.jivesoftware.smack.packet.XMPPError.Condition;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.iqversion.packet.Version; import org.jivesoftware.smackx.iqversion.packet.Version;
@ -89,7 +88,7 @@ public final class VersionManager extends Manager {
@Override @Override
public IQ handleIQRequest(IQ iqRequest) { public IQ handleIQRequest(IQ iqRequest) {
if (ourVersion == null) { if (ourVersion == null) {
return IQ.createErrorResponse(iqRequest, new XMPPError(Condition.not_acceptable)); return IQ.createErrorResponse(iqRequest, Condition.not_acceptable);
} }
return Version.createResultFor(iqRequest, ourVersion); return Version.createResultFor(iqRequest, ourVersion);

View File

@ -30,7 +30,6 @@ import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.packet.XMPPError.Condition; import org.jivesoftware.smack.packet.XMPPError.Condition;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.time.packet.Time; import org.jivesoftware.smackx.time.packet.Time;
@ -78,7 +77,7 @@ public final class EntityTimeManager extends Manager {
return Time.createResponse(iqRequest); return Time.createResponse(iqRequest);
} }
else { else {
return IQ.createErrorResponse(iqRequest, new XMPPError(Condition.not_acceptable)); return IQ.createErrorResponse(iqRequest, Condition.not_acceptable);
} }
} }
}); });

View File

@ -34,10 +34,11 @@ public class IBBPacketUtils {
* *
* @param from the senders JID * @param from the senders JID
* @param to the recipients JID * @param to the recipients JID
* @param xmppError the XMPP error * @param condition the XMPP error condition
* @return an error IQ * @return an error IQ
*/ */
public static IQ createErrorIQ(Jid from, Jid to, XMPPError xmppError) { public static IQ createErrorIQ(Jid from, Jid to, XMPPError.Condition condition) {
XMPPError.Builder xmppError = XMPPError.getBuilder(condition);
IQ errorIQ = new ErrorIQ(xmppError); IQ errorIQ = new ErrorIQ(xmppError);
errorIQ.setType(IQ.Type.error); errorIQ.setType(IQ.Type.error);
errorIQ.setFrom(from); errorIQ.setFrom(from);

View File

@ -115,9 +115,8 @@ public class InBandBytestreamManagerTest extends InitExtensions {
InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection); InBandBytestreamManager byteStreamManager = InBandBytestreamManager.getByteStreamManager(connection);
try { try {
XMPPError xmppError = new XMPPError( IQ errorIQ = IBBPacketUtils.createErrorIQ(targetJID, initiatorJID,
XMPPError.Condition.feature_not_implemented); XMPPError.Condition.feature_not_implemented);
IQ errorIQ = IBBPacketUtils.createErrorIQ(targetJID, initiatorJID, xmppError);
protocol.addResponse(errorIQ); protocol.addResponse(errorIQ);
// start In-Band Bytestream // start In-Band Bytestream

View File

@ -431,11 +431,10 @@ public class Socks5ByteStreamManagerTest {
Verification.requestTypeGET); Verification.requestTypeGET);
// build error packet to reject SOCKS5 Bytestream // build error packet to reject SOCKS5 Bytestream
XMPPError xmppError = new XMPPError(XMPPError.Condition.not_acceptable); XMPPError.Builder builder = XMPPError.getBuilder(XMPPError.Condition.not_acceptable);
IQ rejectPacket = new ErrorIQ(xmppError); IQ rejectPacket = new ErrorIQ(builder);
rejectPacket.setFrom(targetJID); rejectPacket.setFrom(targetJID);
rejectPacket.setTo(initiatorJID); rejectPacket.setTo(initiatorJID);
rejectPacket.setError(xmppError);
// return error packet as response to the bytestream initiation // return error packet as response to the bytestream initiation
protocol.addResponse(rejectPacket, Verification.correspondingSenderReceiver, protocol.addResponse(rejectPacket, Verification.correspondingSenderReceiver,
@ -450,7 +449,7 @@ public class Socks5ByteStreamManagerTest {
} }
catch (XMPPErrorException e) { catch (XMPPErrorException e) {
protocol.verifyAll(); protocol.verifyAll();
assertEquals(xmppError, e.getXMPPError()); assertEquals(rejectPacket.getError(), e.getXMPPError());
} }
catch (Exception e) { catch (Exception e) {
fail(e.getMessage()); fail(e.getMessage());

View File

@ -109,7 +109,7 @@ public class Socks5ByteStreamRequestTest {
fail("exception should be thrown"); fail("exception should be thrown");
} }
catch (XMPPErrorException e) { catch (XMPPErrorException e) {
assertTrue(e.getMessage().contains("Could not establish socket with any provided host")); assertTrue(e.getXMPPError().getDescriptiveText("en").contains("Could not establish socket with any provided host"));
} }
// verify targets response // verify targets response
@ -153,7 +153,7 @@ public class Socks5ByteStreamRequestTest {
fail("exception should be thrown"); fail("exception should be thrown");
} }
catch (XMPPErrorException e) { catch (XMPPErrorException e) {
assertTrue(e.getMessage().contains("Could not establish socket with any provided host")); assertTrue(e.getXMPPError().getDescriptiveText("en").contains("Could not establish socket with any provided host"));
} }
// verify targets response // verify targets response
@ -200,7 +200,7 @@ public class Socks5ByteStreamRequestTest {
fail("exception should be thrown"); fail("exception should be thrown");
} }
catch (XMPPErrorException e) { catch (XMPPErrorException e) {
assertTrue(e.getMessage().contains( assertTrue(e.getXMPPError().getDescriptiveText("en").contains(
"Could not establish socket with any provided host")); "Could not establish socket with any provided host"));
} }
@ -291,8 +291,8 @@ public class Socks5ByteStreamRequestTest {
fail("exception should be thrown"); fail("exception should be thrown");
} }
catch (XMPPException e) { catch (XMPPErrorException e) {
assertTrue(e.getMessage().contains( assertTrue(e.getXMPPError().getDescriptiveText("en").contains(
"Could not establish socket with any provided host")); "Could not establish socket with any provided host"));
} }

View File

@ -201,8 +201,7 @@ public class Socks5ClientForInitiatorTest {
public void shouldFailIfActivateSocks5ProxyFails() throws Exception { public void shouldFailIfActivateSocks5ProxyFails() throws Exception {
// build error response as reply to the stream activation // build error response as reply to the stream activation
XMPPError xmppError = new XMPPError(XMPPError.Condition.internal_server_error); IQ error = new ErrorIQ(XMPPError.getBuilder(XMPPError.Condition.internal_server_error));
IQ error = new ErrorIQ(xmppError);
error.setFrom(proxyJID); error.setFrom(proxyJID);
error.setTo(initiatorJID); error.setTo(initiatorJID);

View File

@ -62,7 +62,7 @@ public class ConfigureFormTest
Node node = mgr.getNode("princely_musings"); Node node = mgr.getNode("princely_musings");
PubSub errorIq = new PubSub(); PubSub errorIq = new PubSub();
XMPPError error = new XMPPError(Condition.forbidden); XMPPError.Builder error = XMPPError.getBuilder(Condition.forbidden);
errorIq.setError(error); errorIq.setError(error);
con.addIQReply(errorIq); con.addIQReply(errorIq);

View File

@ -54,7 +54,6 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.packet.XMPPError.Condition; import org.jivesoftware.smack.packet.XMPPError.Condition;
import org.jivesoftware.smack.roster.SubscribeListener.SubscribeAnswer; import org.jivesoftware.smack.roster.SubscribeListener.SubscribeAnswer;
import org.jivesoftware.smack.roster.packet.RosterPacket; import org.jivesoftware.smack.roster.packet.RosterPacket;
@ -1538,14 +1537,14 @@ public final class Roster extends Manager {
if (from != null && !from.equals(jid)) { if (from != null && !from.equals(jid)) {
LOGGER.warning("Ignoring roster push with a non matching 'from' ourJid='" + jid + "' from='" + from LOGGER.warning("Ignoring roster push with a non matching 'from' ourJid='" + jid + "' from='" + from
+ "'"); + "'");
return IQ.createErrorResponse(iqRequest, new XMPPError(Condition.service_unavailable)); return IQ.createErrorResponse(iqRequest, Condition.service_unavailable);
} }
// A roster push must contain exactly one entry // A roster push must contain exactly one entry
Collection<Item> items = rosterPacket.getRosterItems(); Collection<Item> items = rosterPacket.getRosterItems();
if (items.size() != 1) { if (items.size() != 1) {
LOGGER.warning("Ignoring roster push with not exaclty one entry. size=" + items.size()); LOGGER.warning("Ignoring roster push with not exaclty one entry. size=" + items.size());
return IQ.createErrorResponse(iqRequest, new XMPPError(Condition.bad_request)); return IQ.createErrorResponse(iqRequest, Condition.bad_request);
} }
Collection<Jid> addedEntries = new ArrayList<>(); Collection<Jid> addedEntries = new ArrayList<>();

View File

@ -1039,13 +1039,11 @@ public class JingleSession extends JingleNegotiator implements MediaReceivedList
IQ errorPacket = null; IQ errorPacket = null;
if (jingleError != null) { if (jingleError != null) {
// TODO This is wrong according to XEP-166 § 10, but this jingle implementation is deprecated anyways // 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); XMPPError.Builder builder = XMPPError.getBuilder(XMPPError.Condition.undefined_condition);
builder.addExtension(jingleError);
errorPacket = IQ.createErrorResponse(iq, error); errorPacket = IQ.createErrorResponse(iq, builder);
// Fill in the fields with the info from the Jingle packet
errorPacket.setStanzaId(iq.getStanzaId());
errorPacket.setError(error);
// errorPacket.addExtension(jingleError); // errorPacket.addExtension(jingleError);
// NO! Let the normal state machinery do all of the sending. // NO! Let the normal state machinery do all of the sending.

View File

@ -180,7 +180,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
/** /**
* *
*/ */
private final SynchronizationPoint<XMPPException> compressSyncPoint = new SynchronizationPoint<XMPPException>( private final SynchronizationPoint<SmackException> compressSyncPoint = new SynchronizationPoint<>(
this, "stream compression"); this, "stream compression");
/** /**
@ -774,11 +774,11 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
* </p> * </p>
* *
* @throws NotConnectedException * @throws NotConnectedException
* @throws XMPPException * @throws SmackException
* @throws NoResponseException * @throws NoResponseException
* @throws InterruptedException * @throws InterruptedException
*/ */
private void maybeEnableCompression() throws NotConnectedException, NoResponseException, XMPPException, InterruptedException { private void maybeEnableCompression() throws NotConnectedException, NoResponseException, SmackException, InterruptedException {
if (!config.isCompressionEnabled()) { if (!config.isCompressionEnabled()) {
return; return;
} }
@ -996,14 +996,14 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
case "urn:ietf:params:xml:ns:xmpp-tls": case "urn:ietf:params:xml:ns:xmpp-tls":
// TLS negotiation has failed. The server will close the connection // TLS negotiation has failed. The server will close the connection
// TODO Parse failure stanza // TODO Parse failure stanza
throw new XMPPErrorException("TLS negotiation has failed", null); throw new SmackException("TLS negotiation has failed");
case "http://jabber.org/protocol/compress": case "http://jabber.org/protocol/compress":
// Stream compression has been denied. This is a recoverable // Stream compression has been denied. This is a recoverable
// situation. It is still possible to authenticate and // situation. It is still possible to authenticate and
// use the connection but using an uncompressed connection // use the connection but using an uncompressed connection
// TODO Parse failure stanza // TODO Parse failure stanza
compressSyncPoint.reportFailure(new XMPPErrorException( compressSyncPoint.reportFailure(new SmackException(
"Could not establish compression", null)); "Could not establish compression"));
break; break;
case SaslStreamElements.NAMESPACE: case SaslStreamElements.NAMESPACE:
// SASL authentication has failed. The server may close the connection // SASL authentication has failed. The server may close the connection
@ -1042,10 +1042,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
if (enabled.isResumeSet()) { if (enabled.isResumeSet()) {
smSessionId = enabled.getId(); smSessionId = enabled.getId();
if (StringUtils.isNullOrEmpty(smSessionId)) { if (StringUtils.isNullOrEmpty(smSessionId)) {
XMPPError.Builder builder = XMPPError.getBuilder(XMPPError.Condition.bad_request);
builder.setDescriptiveEnText("Stream Management 'enabled' element with resume attribute but without session id received");
XMPPErrorException xmppException = new XMPPErrorException( XMPPErrorException xmppException = new XMPPErrorException(
"Stream Management 'enabled' element with resume attribute but without session id received", builder);
new XMPPError(
XMPPError.Condition.bad_request));
smEnabledSyncPoint.reportFailure(xmppException); smEnabledSyncPoint.reportFailure(xmppException);
throw xmppException; throw xmppException;
} }
@ -1061,8 +1061,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
break; break;
case Failed.ELEMENT: case Failed.ELEMENT:
Failed failed = ParseStreamManagement.failed(parser); Failed failed = ParseStreamManagement.failed(parser);
XMPPError xmppError = new XMPPError(failed.getXMPPErrorCondition()); XMPPError.Builder xmppError = XMPPError.getBuilder(failed.getXMPPErrorCondition());
XMPPException xmppException = new XMPPErrorException("Stream Management failed", xmppError); XMPPException xmppException = new XMPPErrorException(xmppError);
// If only XEP-198 would specify different failure elements for the SM // 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 // enable and SM resume failure case. But this is not the case, so we
// need to determine if this is a 'Failed' response for either 'Enable' // need to determine if this is a 'Failed' response for either 'Enable'