1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-27 22:04:50 +02:00

Compare commits

..

No commits in common. "f0300dc906089564d57135f791c4e4900cfe224d" and "b83dacc60a3a23ea0b432079f7e70ffd455720f3" have entirely different histories.

35 changed files with 427 additions and 661 deletions

View file

@ -35,6 +35,10 @@
<property name="format" value="\.append\(&quot;(.|\\.)&quot;\)"/>
<property name="message" value="Don&apos;t use StringBuilder.append(String) when you can use StringBuilder.append(char). Solution: Replace double quotes of append&apos;s argument with single quotes."/>
</module>
<module name="RegexpSingleline">
<property name="format" value="^\s+$"/>
<property name="message" value="Line containing only whitespace character(s)"/>
</module>
<module name="FileTabCharacter"/>
<module name="RegexpSingleline">
<!--

View file

@ -29,7 +29,6 @@ import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.SmackWrappedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.StreamErrorException;
@ -40,6 +39,8 @@ import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jivesoftware.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
@ -217,7 +218,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
protected void loginInternal(String username, String password, Resourcepart resource) throws XMPPException,
SmackException, IOException, InterruptedException {
// Authenticate using SASL
authenticate(username, password, config.getAuthzid(), null);
saslAuthentication.authenticate(username, password, config.getAuthzid(), null);
bindResourceAndEstablishSession(resource);
@ -394,26 +395,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
readerConsumer.start();
}
@Override
protected void afterSaslAuthenticationSuccess()
throws NotConnectedException, InterruptedException, SmackWrappedException {
// XMPP over BOSH is unusual when it comes to SASL authentication: Instead of sending a new stream open, it
// requires a special XML element ot be send after successful SASL authentication.
// See XEP-0206 § 5., especially the following is example 5 of XEP-0206.
ComposableBody composeableBody = ComposableBody.builder().setNamespaceDefinition("xmpp",
XMPPBOSHConnection.XMPP_BOSH_NS).setAttribute(
BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart",
"xmpp"), "true").setAttribute(
BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), getXMPPServiceDomain().toString()).build();
try {
send(composeableBody);
} catch (BOSHException e) {
// jbosh's exception API does not really match the one of Smack.
throw new SmackException.SmackWrappedException(e);
}
}
/**
* A listener class which listen for a successfully established connection
* and connection errors and notifies the BOSHConnection.
@ -509,9 +490,30 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
case Presence.ELEMENT:
parseAndProcessStanza(parser);
break;
case "challenge":
// The server is challenging the SASL authentication
// made by the client
final String challengeData = parser.nextText();
getSASLAuthentication().challengeReceived(challengeData);
break;
case "success":
send(ComposableBody.builder().setNamespaceDefinition("xmpp",
XMPPBOSHConnection.XMPP_BOSH_NS).setAttribute(
BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart",
"xmpp"), "true").setAttribute(
BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), getXMPPServiceDomain().toString()).build());
Success success = new Success(parser.nextText());
getSASLAuthentication().authenticated(success);
break;
case "features":
parseFeatures(parser);
break;
case "failure":
if ("urn:ietf:params:xml:ns:xmpp-sasl".equals(parser.getNamespace(null))) {
final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser);
getSASLAuthentication().authenticationFailed(failure);
}
break;
case "error":
// Some BOSH error isn't stream error.
if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) {
@ -520,9 +522,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
StanzaError.Builder builder = PacketParserUtils.parseError(parser);
throw new XMPPException.XMPPErrorException(null, builder.build());
}
default:
parseAndProcessNonza(parser);
break;
}
break;
default:

View file

@ -60,7 +60,6 @@ import java.util.logging.Logger;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.callback.Callback;
@ -79,7 +78,6 @@ import org.jivesoftware.smack.SmackException.NotLoggedInException;
import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException;
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
import org.jivesoftware.smack.SmackException.SmackSaslException;
import org.jivesoftware.smack.SmackException.SmackWrappedException;
import org.jivesoftware.smack.SmackFuture.InternalSmackFuture;
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
@ -115,14 +113,9 @@ import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.provider.NonzaProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.sasl.SASLErrorException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.CollectionUtil;
import org.jivesoftware.smack.util.DNSUtil;
import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.util.ParserUtils;
@ -134,7 +127,6 @@ import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
@ -243,9 +235,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
private XmlEnvironment incomingStreamXmlEnvironment;
protected XmlEnvironment outgoingStreamXmlEnvironment;
final MultiMap<QName, NonzaCallback> nonzaCallbacksMap = new MultiMap<>();
final Map<QName, NonzaCallback> nonzaCallbacks = new HashMap<>();
protected final Lock connectionLock = new ReentrantLock();
@ -316,7 +306,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
/**
* The SASLAuthentication manager that is responsible for authenticating with the server.
*/
private final SASLAuthentication saslAuthentication;
protected final SASLAuthentication saslAuthentication;
/**
* A number to uniquely identify connections that are created. This is distinct from the
@ -410,26 +400,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
protected AbstractXMPPConnection(ConnectionConfiguration configuration) {
saslAuthentication = new SASLAuthentication(this, configuration);
config = configuration;
// Install the SASL Nonza callbacks.
buildNonzaCallback()
.listenFor(SaslNonza.Challenge.class, c -> {
try {
saslAuthentication.challengeReceived(c);
} catch (SmackException | InterruptedException e) {
saslAuthentication.authenticationFailed(e);
}
})
.listenFor(SaslNonza.Success.class, s -> {
try {
saslAuthentication.authenticated(s);
} catch (SmackSaslException | NotConnectedException | InterruptedException e) {
saslAuthentication.authenticationFailed(e);
}
})
.listenFor(SaslNonza.SASLFailure.class, f -> saslAuthentication.authenticationFailed(f))
.install();
SmackDebuggerFactory debuggerFactory = configuration.getDebuggerFactory();
if (debuggerFactory != null) {
debugger = debuggerFactory.create(this);
@ -528,6 +498,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
// Reset the connection state
initState();
saslAuthentication.init();
streamId = null;
try {
@ -838,50 +809,14 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}
/**
* Authenticate a connection.
* Returns the SASLAuthentication manager that is responsible for authenticating with
* the server.
*
* @param username the username that is authenticating with the server.
* @param password the password to send to the server.
* @param authzid the authorization identifier (typically null).
* @param sslSession the optional SSL/TLS session (if one was established)
* @return the used SASLMechanism.
* @throws XMPPErrorException if there was an XMPP error returned.
* @throws SASLErrorException if a SASL protocol error was returned.
* @throws IOException if an I/O error occured.
* @throws InterruptedException if the calling thread was interrupted.
* @throws SmackSaslException if a SASL specific error occured.
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws NoResponseException if there was no response from the remote entity.
* @throws SmackWrappedException in case of an exception.
* @see SASLAuthentication#authenticate(String, String, EntityBareJid, SSLSession)
* @return the SASLAuthentication manager that is responsible for authenticating with
* the server.
*/
protected final SASLMechanism authenticate(String username, String password, EntityBareJid authzid,
SSLSession sslSession) throws XMPPErrorException, SASLErrorException, SmackSaslException,
NotConnectedException, NoResponseException, IOException, InterruptedException, SmackWrappedException {
SASLMechanism saslMechanism = saslAuthentication.authenticate(username, password, authzid, sslSession);
afterSaslAuthenticationSuccess();
return saslMechanism;
}
/**
* Hook for subclasses right after successful SASL authentication. RFC 6120 § 6.4.6. specifies a that the initiating
* entity, needs to initiate a new stream in this case. But some transports, like BOSH, requires a special handling.
* <p>
* Note that we can not reset XMPPTCPConnection's parser here, because this method is invoked by the thread calling
* {@link #login()}, but the parser reset has to be done within the reader thread.
* </p>
*
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
* @throws SmackWrappedException in case of an exception.
*/
protected void afterSaslAuthenticationSuccess()
throws NotConnectedException, InterruptedException, SmackWrappedException {
sendStreamOpen();
}
protected final boolean isSaslAuthenticated() {
return saslAuthentication.authenticationSuccessful();
protected SASLAuthentication getSASLAuthentication() {
return saslAuthentication;
}
/**
@ -1289,9 +1224,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}
protected final void parseAndProcessNonza(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
ParserUtils.assertAtStartTag(parser);
final int initialDepth = parser.getDepth();
final String element = parser.getName();
final String namespace = parser.getNamespace();
final QName key = new QName(namespace, element);
@ -1299,26 +1231,21 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
NonzaProvider<? extends Nonza> nonzaProvider = ProviderManager.getNonzaProvider(key);
if (nonzaProvider == null) {
LOGGER.severe("Unknown nonza: " + key);
ParserUtils.forwardToEndTagOfDepth(parser, initialDepth);
return;
}
List<NonzaCallback> nonzaCallbacks;
synchronized (nonzaCallbacksMap) {
nonzaCallbacks = nonzaCallbacksMap.getAll(key);
nonzaCallbacks = CollectionUtil.newListWith(nonzaCallbacks);
NonzaCallback nonzaCallback;
synchronized (nonzaCallbacks) {
nonzaCallback = nonzaCallbacks.get(key);
}
if (nonzaCallbacks == null) {
if (nonzaCallback == null) {
LOGGER.info("No nonza callback for " + key);
ParserUtils.forwardToEndTagOfDepth(parser, initialDepth);
return;
}
Nonza nonza = nonzaProvider.parse(parser, incomingStreamXmlEnvironment);
for (NonzaCallback nonzaCallback : nonzaCallbacks) {
nonzaCallback.onNonzaReceived(nonza);
}
nonzaCallback.onNonzaReceived(nonza);
}
protected void parseAndProcessStanza(XmlPullParser parser)
@ -2090,13 +2017,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
from = XmppStringUtils.completeJidFrom(localpart, to);
}
String id = getStreamId();
StreamOpen streamOpen = new StreamOpen(to, from, id, config.getXmlLang(), StreamOpen.StreamContentNamespace.client);
sendNonza(streamOpen);
XmlEnvironment.Builder xmlEnvironmentBuilder = XmlEnvironment.builder();
xmlEnvironmentBuilder.with(streamOpen);
outgoingStreamXmlEnvironment = xmlEnvironmentBuilder.build();
sendNonza(new StreamOpen(to, from, id, config.getXmlLang(), StreamOpen.StreamContentNamespace.client));
}
public static final class SmackTlsContext {

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2019 Florian Schmaus
* Copyright 2018 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,11 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.util;
package org.jivesoftware.smack;
// TODO: Replace with java.util.function.Consumer once Smack's minimum Android SDK level is 24 or higher.
public interface Consumer<T> {
import org.jivesoftware.smack.packet.Element;
void accept(T t);
public abstract class GenericElementListener<E extends Element> {
private final Class<? extends E> elementClass;
public GenericElementListener(Class<? extends E> elementClass) {
this.elementClass = elementClass;
}
public abstract void process(E element);
public final void processElement(Element element) {
E concreteEleement = elementClass.cast(element);
process(concreteEleement);
}
}

View file

@ -16,7 +16,6 @@
*/
package org.jivesoftware.smack;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@ -31,7 +30,7 @@ import org.jivesoftware.smack.util.XmppElementUtil;
public class NonzaCallback {
protected final AbstractXMPPConnection connection;
protected final Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners;
protected final Map<QName, GenericElementListener<? extends Nonza>> filterAndListeners;
private NonzaCallback(Builder builder) {
this.connection = builder.connection;
@ -39,18 +38,21 @@ public class NonzaCallback {
install();
}
void onNonzaReceived(Nonza nonza) throws IOException {
void onNonzaReceived(Nonza nonza) {
QName key = nonza.getQName();
ClassAndConsumer<? extends Nonza> classAndConsumer = filterAndListeners.get(key);
GenericElementListener<? extends Nonza> nonzaListener = filterAndListeners.get(key);
classAndConsumer.accept(nonza);
nonzaListener.processElement(nonza);
}
public void cancel() {
for (Map.Entry<QName, ClassAndConsumer<? extends Nonza>> entry : filterAndListeners.entrySet()) {
QName filterKey = entry.getKey();
synchronized (connection.nonzaCallbacksMap) {
connection.nonzaCallbacksMap.removeOne(filterKey, this);
synchronized (connection.nonzaCallbacks) {
for (Map.Entry<QName, GenericElementListener<? extends Nonza>> entry : filterAndListeners.entrySet()) {
QName filterKey = entry.getKey();
NonzaCallback installedCallback = connection.nonzaCallbacks.get(filterKey);
if (equals(installedCallback)) {
connection.nonzaCallbacks.remove(filterKey);
}
}
}
}
@ -60,9 +62,9 @@ public class NonzaCallback {
return;
}
for (QName key : filterAndListeners.keySet()) {
synchronized (connection.nonzaCallbacksMap) {
connection.nonzaCallbacksMap.put(key, this);
synchronized (connection.nonzaCallbacks) {
for (QName key : filterAndListeners.keySet()) {
connection.nonzaCallbacks.put(key, this);
}
}
}
@ -72,35 +74,31 @@ public class NonzaCallback {
private SN successNonza;
private FN failedNonza;
private NonzaResponseCallback(Class<SN> successNonzaClass, Class<FN> failedNonzaClass,
private NonzaResponseCallback(Class<? extends SN> successNonzaClass, Class<? extends FN> failedNonzaClass,
Builder builder) {
super(builder);
final QName successNonzaKey = XmppElementUtil.getQNameFor(successNonzaClass);
final QName failedNonzaKey = XmppElementUtil.getQNameFor(failedNonzaClass);
final NonzaListener<SN> successListener = new NonzaListener<SN>() {
final GenericElementListener<SN> successListener = new GenericElementListener<SN>(successNonzaClass) {
@Override
public void accept(SN successNonza) {
public void process(SN successNonza) {
NonzaResponseCallback.this.successNonza = successNonza;
notifyResponse();
}
};
final ClassAndConsumer<SN> successClassAndConsumer = new ClassAndConsumer<>(successNonzaClass,
successListener);
final NonzaListener<FN> failedListener = new NonzaListener<FN>() {
final GenericElementListener<FN> failedListener = new GenericElementListener<FN>(failedNonzaClass) {
@Override
public void accept(FN failedNonza) {
public void process(FN failedNonza) {
NonzaResponseCallback.this.failedNonza = failedNonza;
notifyResponse();
}
};
final ClassAndConsumer<FN> failedClassAndConsumer = new ClassAndConsumer<>(failedNonzaClass,
failedListener);
filterAndListeners.put(successNonzaKey, successClassAndConsumer);
filterAndListeners.put(failedNonzaKey, failedClassAndConsumer);
filterAndListeners.put(successNonzaKey, successListener);
filterAndListeners.put(failedNonzaKey, failedListener);
install();
}
@ -141,16 +139,15 @@ public class NonzaCallback {
public static final class Builder {
private final AbstractXMPPConnection connection;
private Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners = new HashMap<>();
private Map<QName, GenericElementListener<? extends Nonza>> filterAndListeners = new HashMap<>();
Builder(AbstractXMPPConnection connection) {
this.connection = connection;
}
public <N extends Nonza> Builder listenFor(Class<N> nonza, NonzaListener<N> nonzaListener) {
public <N extends Nonza> Builder listenFor(Class<? extends N> nonza, GenericElementListener<? extends N> nonzaListener) {
QName key = XmppElementUtil.getQNameFor(nonza);
ClassAndConsumer<N> classAndConsumer = new ClassAndConsumer<>(nonza, nonzaListener);
filterAndListeners.put(key, classAndConsumer);
filterAndListeners.put(key, nonzaListener);
return this;
}
@ -159,25 +156,6 @@ public class NonzaCallback {
}
}
public interface NonzaListener<N extends Nonza> {
void accept(N nonza) throws IOException;
}
private static final class ClassAndConsumer<N extends Nonza> {
private final Class<N> clazz;
private final NonzaListener<N> consumer;
private ClassAndConsumer(Class<N> clazz, NonzaListener<N> consumer) {
this.clazz = clazz;
this.consumer = consumer;
}
private void accept(Object object) throws IOException {
N nonza = clazz.cast(object);
consumer.accept(nonza);
}
}
static <SN extends Nonza, FN extends Nonza> SN sendAndWaitForResponse(NonzaCallback.Builder builder, Nonza nonza, Class<SN> successNonzaClass,
Class<FN> failedNonzaClass)
throws NoResponseException, NotConnectedException, InterruptedException, FailedNonzaException {

View file

@ -39,9 +39,8 @@ import org.jivesoftware.smack.packet.Mechanisms;
import org.jivesoftware.smack.sasl.SASLErrorException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslNonza.Success;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityBareJid;
@ -155,9 +154,20 @@ public final class SASLAuthentication {
private final ConnectionConfiguration configuration;
private SASLMechanism currentMechanism = null;
/**
* Boolean indicating if SASL negotiation has finished and was successful.
*/
private boolean authenticationSuccessful;
/**
* Either of type {@link SmackException} or {@link SASLErrorException}
*/
private Exception saslException;
SASLAuthentication(AbstractXMPPConnection connection, ConnectionConfiguration configuration) {
this.configuration = configuration;
this.connection = connection;
this.init();
}
/**
@ -181,26 +191,23 @@ public final class SASLAuthentication {
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws NoResponseException if there was no response from the remote entity.
*/
SASLMechanism authenticate(String username, String password, EntityBareJid authzid, SSLSession sslSession)
public SASLMechanism authenticate(String username, String password, EntityBareJid authzid, SSLSession sslSession)
throws XMPPErrorException, SASLErrorException, IOException,
InterruptedException, SmackSaslException, NotConnectedException, NoResponseException {
final SASLMechanism mechanism = selectMechanism(authzid);
currentMechanism = selectMechanism(authzid);
final CallbackHandler callbackHandler = configuration.getCallbackHandler();
final String host = connection.getHost();
final DomainBareJid xmppServiceDomain = connection.getXMPPServiceDomain();
synchronized (this) {
currentMechanism = mechanism;
if (callbackHandler != null) {
currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler, authzid, sslSession);
}
else {
currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid, sslSession);
}
final long deadline = System.currentTimeMillis() + connection.getReplyTimeout();
while (!mechanism.isFinished()) {
while (!authenticationSuccessful && saslException == null) {
final long now = System.currentTimeMillis();
if (now >= deadline) break;
// Wait until SASL negotiation finishes
@ -208,21 +215,35 @@ public final class SASLAuthentication {
}
}
mechanism.throwExceptionIfRequired();
if (saslException != null) {
if (saslException instanceof SmackSaslException) {
throw (SmackSaslException) saslException;
} else if (saslException instanceof SASLErrorException) {
throw (SASLErrorException) saslException;
} else if (saslException instanceof NotConnectedException) {
throw (NotConnectedException) saslException;
} else {
throw new IllegalStateException("Unexpected exception type" , saslException);
}
}
return mechanism;
if (!authenticationSuccessful) {
throw NoResponseException.newWith(connection, "successful SASL authentication");
}
return currentMechanism;
}
/**
* Wrapper for {@link #challengeReceived(String, boolean)}, with <code>finalChallenge</code> set
* to <code>false</code>.
*
* @param challenge the challenge Nonza.
* @param challenge a base64 encoded string representing the challenge.
* @throws SmackException if Smack detected an exceptional situation.
* @throws InterruptedException if the calling thread was interrupted.
*/
void challengeReceived(SaslNonza.Challenge challenge) throws SmackException, InterruptedException {
challengeReceived(challenge.getData(), false);
public void challengeReceived(String challenge) throws SmackException, InterruptedException {
challengeReceived(challenge, false);
}
/**
@ -237,12 +258,13 @@ public final class SASLAuthentication {
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
*/
private void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
SASLMechanism mechanism;
synchronized (this) {
mechanism = currentMechanism;
public void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
try {
currentMechanism.challengeReceived(challenge, finalChallenge);
} catch (InterruptedException | SmackSaslException | NotConnectedException e) {
authenticationFailed(e);
throw e;
}
mechanism.challengeReceived(challenge, finalChallenge);
}
/**
@ -251,10 +273,8 @@ public final class SASLAuthentication {
* @param success result of the authentication.
* @throws SmackException if Smack detected an exceptional situation.
* @throws InterruptedException if the calling thread was interrupted.
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws SmackSaslException if a SASL specific error occured.
*/
void authenticated(Success success) throws InterruptedException, SmackSaslException, NotConnectedException {
public void authenticated(Success success) throws SmackException, InterruptedException {
// RFC6120 6.3.10 "At the end of the authentication exchange, the SASL server (the XMPP
// "receiving entity") can include "additional data with success" if appropriate for the
// SASL mechanism in use. In XMPP, this is done by including the additional data as the XML
@ -263,11 +283,10 @@ public final class SASLAuthentication {
if (success.getData() != null) {
challengeReceived(success.getData(), true);
}
currentMechanism.checkIfSuccessfulOrThrow();
authenticationSuccessful = true;
// Wake up the thread that is waiting in the #authenticate method
synchronized (this) {
currentMechanism.afterFinalSaslChallenge();
notify();
}
}
@ -279,29 +298,30 @@ public final class SASLAuthentication {
* @param saslFailure the SASL failure as reported by the server
* @see <a href="https://tools.ietf.org/html/rfc6120#section-6.5">RFC6120 6.5</a>
*/
void authenticationFailed(SASLFailure saslFailure) {
SASLErrorException saslErrorException;
synchronized (this) {
saslErrorException = new SASLErrorException(currentMechanism.getName(), saslFailure);
}
authenticationFailed(saslErrorException);
public void authenticationFailed(SASLFailure saslFailure) {
authenticationFailed(new SASLErrorException(currentMechanism.getName(), saslFailure));
}
void authenticationFailed(Exception exception) {
private void authenticationFailed(Exception exception) {
saslException = exception;
// Wake up the thread that is waiting in the #authenticate method
synchronized (this) {
currentMechanism.setException(exception);
notify();
}
}
public boolean authenticationSuccessful() {
synchronized (this) {
if (currentMechanism == null) {
return false;
}
return currentMechanism.isAuthenticationSuccessful();
}
return authenticationSuccessful;
}
/**
* Initializes the internal state in order to be able to be reused. The authentication
* is used by the connection at the first login and then reused after the connection
* is disconnected and then reconnected.
*/
void init() {
authenticationSuccessful = false;
saslException = null;
}
String getNameOfLastUsedSaslMechansism() {

View file

@ -108,6 +108,30 @@ public final class SmackConfiguration {
return SmackInitialization.SMACK_VERSION;
}
/**
* Returns the number of milliseconds to wait for a response from
* the server. The default value is 5000 ms.
*
* @return the milliseconds to wait for a response from the server
* @deprecated use {@link #getDefaultReplyTimeout()} instead.
*/
@Deprecated
public static int getDefaultPacketReplyTimeout() {
return getDefaultReplyTimeout();
}
/**
* Sets the number of milliseconds to wait for a response from
* the server.
*
* @param timeout the milliseconds to wait for a response from the server
* @deprecated use {@link #setDefaultReplyTimeout(int)} instead.
*/
@Deprecated
public static void setDefaultPacketReplyTimeout(int timeout) {
setDefaultReplyTimeout(timeout);
}
/**
* Returns the number of milliseconds to wait for a response from
* the server. The default value is 5000 ms.

View file

@ -37,9 +37,6 @@ import org.jivesoftware.smack.packet.Message.Body;
import org.jivesoftware.smack.provider.BindIQProvider;
import org.jivesoftware.smack.provider.BodyElementProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.provider.SaslChallengeProvider;
import org.jivesoftware.smack.provider.SaslFailureProvider;
import org.jivesoftware.smack.provider.SaslSuccessProvider;
import org.jivesoftware.smack.provider.TlsFailureProvider;
import org.jivesoftware.smack.provider.TlsProceedProvider;
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
@ -129,9 +126,6 @@ public final class SmackInitialization {
ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());
ProviderManager.addExtensionProvider(Body.ELEMENT, Body.NAMESPACE, new BodyElementProvider());
ProviderManager.addNonzaProvider(SaslChallengeProvider.INSTANCE);
ProviderManager.addNonzaProvider(SaslSuccessProvider.INSTANCE);
ProviderManager.addNonzaProvider(SaslFailureProvider.INSTANCE);
ProviderManager.addNonzaProvider(TlsProceedProvider.INSTANCE);
ProviderManager.addNonzaProvider(TlsFailureProvider.INSTANCE);
ProviderManager.addNonzaProvider(CompressedProvider.INSTANCE);

View file

@ -53,6 +53,8 @@ import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.sasl.SASLErrorException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
@ -333,6 +335,19 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
parseFeatures(parser);
afterFeaturesReceived();
break;
// SASL related top level stream elements
case Challenge.ELEMENT:
// The server is challenging the SASL authentication made by the client
String challengeData = parser.nextText();
getSASLAuthentication().challengeReceived(challengeData);
break;
case Success.ELEMENT:
Success success = new Success(parser.nextText());
// The SASL authentication with the server was successful. The next step
// will be to bind the resource
getSASLAuthentication().authenticated(success);
sendStreamOpen();
break;
default:
parseAndProcessNonza(parser);
break;
@ -598,7 +613,7 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
prepareToWaitForFeaturesReceived();
LoginContext loginContext = walkStateGraphContext.loginContext;
SASLMechanism usedSaslMechanism = authenticate(loginContext.username, loginContext.password, config.getAuthzid(), getSSLSession());
SASLMechanism usedSaslMechanism = saslAuthentication.authenticate(loginContext.username, loginContext.password, config.getAuthzid(), getSSLSession());
// authenticate() will only return if the SASL authentication was successful, but we also need to wait for the next round of stream features.
waitForFeaturesReceived("server stream features after SASL authentication");

View file

@ -31,7 +31,6 @@ import java.util.Set;
import java.util.logging.Logger;
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.DisconnectedStateDescriptor;
import org.jivesoftware.smack.util.Consumer;
import org.jivesoftware.smack.util.MultiMap;
/**
@ -331,8 +330,7 @@ public class StateDescriptorGraph {
return res;
}
private static <E> void dfsVisit(GraphVertex<E> vertex, Consumer<GraphVertex<E>> dfsFinishedVertex,
DfsEdgeFound<E> dfsEdgeFound) {
private static <E> void dfsVisit(GraphVertex<E> vertex, DfsFinishedVertex<E> dfsFinishedVertex, DfsEdgeFound<E> dfsEdgeFound) {
vertex.color = GraphVertex.VertexColor.grey;
final int totalEdgeCount = vertex.getOutgoingEdges().size();
@ -351,12 +349,11 @@ public class StateDescriptorGraph {
vertex.color = GraphVertex.VertexColor.black;
if (dfsFinishedVertex != null) {
dfsFinishedVertex.accept(vertex);
dfsFinishedVertex.onVertexFinished(vertex);
}
}
private static <E> void dfs(Collection<GraphVertex<E>> vertexes, Consumer<GraphVertex<E>> dfsFinishedVertex,
DfsEdgeFound<E> dfsEdgeFound) {
private static <E> void dfs(Collection<GraphVertex<E>> vertexes, DfsFinishedVertex<E> dfsFinishedVertex, DfsEdgeFound<E> dfsEdgeFound) {
for (GraphVertex<E> vertex : vertexes) {
if (vertex.color == GraphVertex.VertexColor.white) {
dfsVisit(vertex, dfsFinishedVertex, dfsEdgeFound);
@ -410,6 +407,12 @@ public class StateDescriptorGraph {
dotOut.append("}\n");
}
// TODO: Replace with java.util.function.Consumer<GraphVertex<E>> once Smack's minimum Android SDK level is 24 or higher.
private interface DfsFinishedVertex<E> {
void onVertexFinished(GraphVertex<E> vertex);
}
// TODO: Replace with java.util.function.Consumer<GraphVertex<E>> once Smack's minimum Android SDK level is 24 or higher.
private interface DfsEdgeFound<E> {
void onEdgeFound(GraphVertex<E> from, GraphVertex<E> to, int edgeId, int totalEdgeCount);
}

View file

@ -163,12 +163,6 @@ public class XmlEnvironment {
return this;
}
public Builder with(StreamOpen streamOpen) {
withNamespace(streamOpen.getNamespace());
withLanguage(streamOpen.getLanguage());
return this;
}
public XmlEnvironment build() {
return new XmlEnvironment(this);
}

View file

@ -1,40 +0,0 @@
/**
*
* Copyright 2019 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.provider;
import java.io.IOException;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
public final class SaslChallengeProvider extends NonzaProvider<SaslNonza.Challenge> {
public static final SaslChallengeProvider INSTANCE = new SaslChallengeProvider();
private SaslChallengeProvider() {
}
@Override
public SaslNonza.Challenge parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws IOException, XmlPullParserException {
String data = parser.nextText();
return new SaslNonza.Challenge(data);
}
}

View file

@ -1,62 +0,0 @@
/**
*
* Copyright 2019 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.provider;
import java.io.IOException;
import java.util.Map;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
public final class SaslFailureProvider extends NonzaProvider<SASLFailure> {
public static final SaslFailureProvider INSTANCE = new SaslFailureProvider();
private SaslFailureProvider() {
}
@Override
public SASLFailure parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
String condition = null;
Map<String, String> descriptiveTexts = null;
outerloop: while (true) {
XmlPullParser.TagEvent eventType = parser.nextTag();
switch (eventType) {
case START_ELEMENT:
String name = parser.getName();
if (name.equals("text")) {
descriptiveTexts = PacketParserUtils.parseDescriptiveTexts(parser, descriptiveTexts);
}
else {
assert condition == null;
condition = parser.getName();
}
break;
case END_ELEMENT:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
return new SASLFailure(condition, descriptiveTexts);
}
}

View file

@ -1,40 +0,0 @@
/**
*
* Copyright 2019 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.provider;
import java.io.IOException;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
public final class SaslSuccessProvider extends NonzaProvider<SaslNonza.Success> {
public static final SaslSuccessProvider INSTANCE = new SaslSuccessProvider();
private SaslSuccessProvider() {
}
@Override
public SaslNonza.Success parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws IOException, XmlPullParserException {
String data = parser.nextText();
return new SaslNonza.Success(data);
}
}

View file

@ -20,7 +20,7 @@ import java.util.HashMap;
import java.util.Map;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
public class SASLErrorException extends XMPPException {

View file

@ -23,12 +23,11 @@ import javax.net.ssl.SSLSession;
import javax.security.auth.callback.CallbackHandler;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.SmackSaslException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
import org.jivesoftware.smack.sasl.packet.SaslNonza.Response;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.AuthMechanism;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.stringencoder.Base64;
@ -57,17 +56,6 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
public static final String GSSAPI = "GSSAPI";
public static final String PLAIN = "PLAIN";
/**
* Boolean indicating if SASL negotiation has finished and was successful.
*/
private boolean authenticationSuccessful;
/**
* Either of type {@link SmackSaslException},{@link SASLErrorException}, {@link NotConnectedException} or
* {@link InterruptedException}.
*/
private Exception exception;
protected XMPPConnection connection;
protected ConnectionConfiguration connectionConfiguration;
@ -287,18 +275,7 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
*/
public abstract int getPriority();
/**
* Check if the SASL mechanism was successful and if it was, then mark it so.
*
* @throws SmackSaslException in case of an SASL error.
*/
public final void afterFinalSaslChallenge() throws SmackSaslException {
checkIfSuccessfulOrThrow();
authenticationSuccessful = true;
}
protected abstract void checkIfSuccessfulOrThrow() throws SmackSaslException;
public abstract void checkIfSuccessfulOrThrow() throws SmackSaslException;
public SASLMechanism instanceForAuthentication(XMPPConnection connection, ConnectionConfiguration connectionConfiguration) {
SASLMechanism saslMechansim = newInstance();
@ -311,39 +288,6 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
return false;
}
public boolean isAuthenticationSuccessful() {
return authenticationSuccessful;
}
public boolean isFinished() {
return isAuthenticationSuccessful() || exception != null;
}
public void throwExceptionIfRequired() throws SmackSaslException, SASLErrorException, NotConnectedException,
InterruptedException, NoResponseException {
if (exception != null) {
if (exception instanceof SmackSaslException) {
throw (SmackSaslException) exception;
} else if (exception instanceof SASLErrorException) {
throw (SASLErrorException) exception;
} else if (exception instanceof NotConnectedException) {
throw (NotConnectedException) exception;
} else if (exception instanceof InterruptedException) {
throw (InterruptedException) exception;
} else {
throw new IllegalStateException("Unexpected exception type", exception);
}
}
if (!authenticationSuccessful) {
throw NoResponseException.newWith(connection, "successful SASL authentication");
}
}
public void setException(Exception exception) {
this.exception = exception;
}
protected abstract SASLMechanism newInstance();
protected static byte[] toBytes(String string) {

View file

@ -1,6 +1,6 @@
/**
*
* Copyright 2014-2019 Florian Schmaus
* Copyright 2014-2018 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,30 +18,21 @@ package org.jivesoftware.smack.sasl.packet;
import java.util.Map;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.AbstractError;
import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
public interface SaslNonza extends Nonza {
String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
@Override
default String getNamespace() {
return NAMESPACE;
}
public class SaslStreamElements {
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
/**
* Initiating SASL authentication by select a mechanism.
*/
class AuthMechanism implements SaslNonza {
public static class AuthMechanism implements Nonza {
public static final String ELEMENT = "auth";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final String mechanism;
private final String authenticationText;
@ -53,11 +44,11 @@ public interface SaslNonza extends Nonza {
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
xml.attribute("mechanism", mechanism).rightAngleBracket();
xml.escape(authenticationText);
xml.closeElement(this);
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).attribute("mechanism", mechanism).rightAngleBracket();
xml.optAppend(authenticationText);
xml.closeElement(ELEMENT);
return xml;
}
@ -69,6 +60,11 @@ public interface SaslNonza extends Nonza {
return authenticationText;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
@ -78,9 +74,8 @@ public interface SaslNonza extends Nonza {
/**
* A SASL challenge stream element.
*/
class Challenge implements SaslNonza {
public static class Challenge implements Nonza {
public static final String ELEMENT = "challenge";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final String data;
@ -88,15 +83,18 @@ public interface SaslNonza extends Nonza {
this.data = StringUtils.returnIfNotEmptyTrimmed(data);
}
public String getData() {
return data;
@Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder().halfOpenElement(ELEMENT).xmlnsAttribute(
NAMESPACE).rightAngleBracket();
xml.optAppend(data);
xml.closeElement(ELEMENT);
return xml;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
xml.optTextChild(data, this);
return xml;
public String getNamespace() {
return NAMESPACE;
}
@Override
@ -108,9 +106,8 @@ public interface SaslNonza extends Nonza {
/**
* A SASL response stream element.
*/
class Response implements SaslNonza {
public static class Response implements Nonza {
public static final String ELEMENT = "response";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final String authenticationText;
@ -123,9 +120,11 @@ public interface SaslNonza extends Nonza {
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
xml.optTextChild(authenticationText, this);
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
xml.optAppend(authenticationText);
xml.closeElement(ELEMENT);
return xml;
}
@ -133,6 +132,11 @@ public interface SaslNonza extends Nonza {
return authenticationText;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
@ -142,9 +146,8 @@ public interface SaslNonza extends Nonza {
/**
* A SASL success stream element.
*/
class Success implements SaslNonza {
public static class Success implements Nonza {
public static final String ELEMENT = "success";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final String data;
@ -168,12 +171,19 @@ public interface SaslNonza extends Nonza {
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
xml.optTextChild(data, this);
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
xml.optAppend(data);
xml.closeElement(ELEMENT);
return xml;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
@ -184,9 +194,8 @@ public interface SaslNonza extends Nonza {
* A SASL failure stream element, also called "SASL Error".
* @see <a href="http://xmpp.org/rfcs/rfc6120.html#sasl-errors">RFC 6120 6.5 SASL Errors</a>
*/
class SASLFailure extends AbstractError implements SaslNonza {
public static class SASLFailure extends AbstractError implements Nonza {
public static final String ELEMENT = "failure";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final SASLError saslError;
private final String saslErrorString;
@ -241,6 +250,11 @@ public interface SaslNonza extends Nonza {
return toXML().toString();
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;

View file

@ -51,9 +51,8 @@ public class CollectionUtil {
}
public static <T> ArrayList<T> newListWith(Collection<? extends T> collection) {
if (collection == null) {
return null;
}
return new ArrayList<>(collection);
ArrayList<T> arrayList = new ArrayList<>(collection.size());
arrayList.addAll(collection);
return arrayList;
}
}

View file

@ -49,6 +49,7 @@ import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
@ -685,6 +686,44 @@ public class PacketParserUtils {
return descriptiveTexts;
}
/**
* Parses SASL authentication error packets.
*
* @param parser the XML parser.
* @return a SASL Failure packet.
* @throws IOException if an I/O error occured.
* @throws XmlPullParserException if an error in the XML parser occured.
*/
public static SASLFailure parseSASLFailure(XmlPullParser parser) throws XmlPullParserException, IOException {
final int initialDepth = parser.getDepth();
String condition = null;
Map<String, String> descriptiveTexts = null;
outerloop: while (true) {
XmlPullParser.Event eventType = parser.next();
switch (eventType) {
case START_ELEMENT:
String name = parser.getName();
if (name.equals("text")) {
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
}
else {
assert condition == null;
condition = parser.getName();
}
break;
case END_ELEMENT:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
default:
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
break;
}
}
return new SASLFailure(condition, descriptiveTexts);
}
public static StreamError parseStreamError(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException {
return parseStreamError(parser, null);
}

View file

@ -18,10 +18,9 @@ package org.jivesoftware.smack.util;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Iterator;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.ExtensionElement;
@ -497,20 +496,17 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
return this;
}
public XmlStringBuilder optAppend(Element element) {
if (element != null) {
append(element.toXML(effectiveXmlEnvironment));
public XmlStringBuilder optAppend(CharSequence csq) {
if (csq != null) {
append(csq);
}
return this;
}
public XmlStringBuilder optTextChild(CharSequence sqc, NamedElement parentElement) {
if (sqc == null) {
return closeEmptyElement();
public XmlStringBuilder optAppend(Element element) {
if (element != null) {
append(element.toXML(effectiveXmlEnvironment));
}
rightAngleBracket();
escape(sqc);
closeElement(parentElement);
return this;
}
@ -610,46 +606,24 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
return toString().hashCode();
}
private static final class WrappedIoException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final IOException wrappedIoException;
private WrappedIoException(IOException wrappedIoException) {
this.wrappedIoException = wrappedIoException;
}
}
/**
* Write the contents of this <code>XmlStringBuilder</code> to a {@link Writer}. This will write
* the single parts one-by-one, avoiding allocation of a big continuous memory block holding the
* XmlStringBuilder contents.
*
* @param writer TODO javadoc me please
* @param enclosingXmlEnvironment the enclosing XML environment.
* @param enclosingNamespace the enclosing XML namespace.
* @throws IOException if an I/O error occured.
*/
public void write(Writer writer, XmlEnvironment enclosingXmlEnvironment) throws IOException {
try {
appendXmlTo(csq -> {
try {
writer.append(csq);
} catch (IOException e) {
throw new WrappedIoException(e);
}
}, enclosingXmlEnvironment);
} catch (WrappedIoException e) {
throw e.wrappedIoException;
}
public void write(Writer writer, String enclosingNamespace) throws IOException {
XmlEnvironment enclosingXmlEnvironment = XmlEnvironment.builder()
.withNamespace(enclosingNamespace)
.build();
appendXmlTo(writer, enclosingXmlEnvironment);
}
public List<CharSequence> toList(XmlEnvironment enclosingXmlEnvironment) {
List<CharSequence> res = new ArrayList<>(sb.getAsList().size());
appendXmlTo(csq -> res.add(csq), enclosingXmlEnvironment);
return res;
public Iterator<CharSequence> getCharSequenceIterator() {
return sb.getAsList().iterator();
}
@Override
@ -657,26 +631,29 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
// This is only the potential length, since the actual length depends on the given XmlEnvironment.
int potentialLength = length();
StringBuilder res = new StringBuilder(potentialLength);
appendXmlTo(csq -> res.append(csq), enclosingXmlEnvironment);
try {
appendXmlTo(res, enclosingXmlEnvironment);
} catch (IOException e) {
// Should never happen.
throw new AssertionError(e);
}
return res;
}
private void appendXmlTo(Consumer<CharSequence> charSequenceSink, XmlEnvironment enclosingXmlEnvironment) {
private void appendXmlTo(Appendable appendable, XmlEnvironment enclosingXmlEnvironment) throws IOException {
for (CharSequence csq : sb.getAsList()) {
if (csq instanceof XmlStringBuilder) {
((XmlStringBuilder) csq).appendXmlTo(charSequenceSink, enclosingXmlEnvironment);
((XmlStringBuilder) csq).appendXmlTo(appendable, enclosingXmlEnvironment);
}
else if (csq instanceof XmlNsAttribute) {
XmlNsAttribute xmlNsAttribute = (XmlNsAttribute) csq;
if (!xmlNsAttribute.value.equals(enclosingXmlEnvironment.getEffectiveNamespace())) {
charSequenceSink.accept(xmlNsAttribute);
appendable.append(xmlNsAttribute);
enclosingXmlEnvironment = new XmlEnvironment(xmlNsAttribute.value);
}
}
else {
charSequenceSink.accept(csq);
appendable.append(csq);
}
}
}

View file

@ -1,78 +0,0 @@
/**
*
* Copyright (C) 2007 Jive Software, 2019 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.provider;
import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
import java.io.IOException;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.xml.XmlPullParserException;
import com.jamesmurty.utils.XMLBuilder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
public class SaslProviderTest {
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void parseSASLFailureSimple(SmackTestUtil.XmlPullParserKind parserKind)
throws TransformerException, ParserConfigurationException, FactoryConfigurationError,
XmlPullParserException, IOException, SmackParsingException {
// @formatter:off
final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslNonza.NAMESPACE)
.e(SASLError.account_disabled.toString())
.asString();
// @formatter:on
SASLFailure saslFailure = SmackTestUtil.parse(saslFailureString, SaslFailureProvider.class, parserKind);
assertXmlSimilar(saslFailureString, saslFailure.toString());
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void parseSASLFailureExtended(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException, TransformerException,
ParserConfigurationException, FactoryConfigurationError {
// @formatter:off
final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslNonza.NAMESPACE)
.e(SASLError.account_disabled.toString())
.up()
.e("text").a("xml:lang", "en")
.t("Call 212-555-1212 for assistance.")
.up()
.e("text").a("xml:lang", "de")
.t("Bitte wenden sie sich an (04321) 123-4444")
.up()
.e("text")
.t("Wusel dusel")
.asString();
// @formatter:on
SASLFailure saslFailure = SmackTestUtil.parse(saslFailureString, SaslFailureProvider.class, parserKind);
assertXmlSimilar(saslFailureString, saslFailure.toXML(StreamOpen.CLIENT_NAMESPACE));
}
}

View file

@ -21,8 +21,8 @@ import static org.junit.Assert.assertEquals;
import org.jivesoftware.smack.DummyConnection;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
import org.jivesoftware.smack.sasl.packet.SaslNonza.Response;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.AuthMechanism;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.util.stringencoder.Base64;

View file

@ -125,19 +125,10 @@ public class SmackTestUtil {
return parser;
}
@SuppressWarnings("unchecked")
private static <E extends Element, P extends Provider<E>> P providerClassToProvider(Class<P> providerClass) {
P provider;
try {
provider = (P) providerClass.getDeclaredField("INSTANCE").get(null);
return provider;
} catch (NoSuchFieldException e) {
// Continue with the next approach.
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
throw new AssertionError(e);
}
// TODO: Consider adding a shortcut in case there is a static INSTANCE field holding an instance of the
// requested provider.
try {
provider = providerClass.getDeclaredConstructor().newInstance();
}

View file

@ -39,6 +39,9 @@ import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
@ -811,6 +814,41 @@ public class PacketParserUtilsTest {
assertXmlSimilar(stanza, result.toString());
}
@Test
public void parseSASLFailureSimple() throws FactoryConfigurationError, SAXException, IOException,
TransformerException, ParserConfigurationException, XmlPullParserException {
// @formatter:off
final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslStreamElements.NAMESPACE)
.e(SASLError.account_disabled.toString())
.asString();
// @formatter:on
XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT);
SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser);
assertXmlSimilar(saslFailureString, saslFailure.toString());
}
@Test
public void parseSASLFailureExtended() throws FactoryConfigurationError, TransformerException,
ParserConfigurationException, XmlPullParserException, IOException, SAXException {
// @formatter:off
final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslStreamElements.NAMESPACE)
.e(SASLError.account_disabled.toString())
.up()
.e("text").a("xml:lang", "en")
.t("Call 212-555-1212 for assistance.")
.up()
.e("text").a("xml:lang", "de")
.t("Bitte wenden sie sich an (04321) 123-4444")
.up()
.e("text")
.t("Wusel dusel")
.asString();
// @formatter:on
XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT);
SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser);
assertXmlSimilar(saslFailureString, saslFailure.toXML(StreamOpen.CLIENT_NAMESPACE));
}
@SuppressWarnings("ReferenceEquality")
private static String determineNonDefaultLanguage() {
String otherLanguage = "jp";

View file

@ -225,7 +225,9 @@ public abstract class AbstractHttpOverXmpp extends IQ {
@Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
xml.optTextChild(text, this);
xml.rightAngleBracket();
xml.optAppend(text);
xml.closeElement(this);
return xml;
}
@ -267,7 +269,9 @@ public abstract class AbstractHttpOverXmpp extends IQ {
@Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
xml.optTextChild(text, this);
xml.rightAngleBracket();
xml.optAppend(text);
xml.closeElement(this);
return xml;
}
@ -309,7 +313,9 @@ public abstract class AbstractHttpOverXmpp extends IQ {
@Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
xml.optTextChild(text, this);
xml.rightAngleBracket();
xml.optAppend(text);
xml.closeElement(this);
return xml;
}

View file

@ -46,7 +46,7 @@ public class QueryArchiveTest extends MamTest {
private static final String mamQueryResultExample = "<message to='hag66@shakespeare.lit/pda' from='coven@chat.shakespeare.lit' id='iasd207'>"
+ "<result xmlns='urn:xmpp:mam:1' queryid='g27' id='34482-21985-73620'>"
+ "<forwarded xmlns='urn:xmpp:forward:0'>"
+ "<delay xmlns='urn:xmpp:delay' stamp='2002-10-13T23:58:37.000+00:00'/>" + "<message "
+ "<delay xmlns='urn:xmpp:delay' stamp='2002-10-13T23:58:37.000+00:00'></delay>" + "<message "
+ "xmlns='jabber:client' from='coven@chat.shakespeare.lit/firstwitch' " + "id='162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2' "
+ "type='chat'>" + "<body>Thrice the brinded cat hath mew.</body>" + "</message>" + "</forwarded>"
+ "</result>" + "</message>";

View file

@ -106,7 +106,9 @@ public class DelayInformation implements ExtensionElement {
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
xml.attribute("stamp", XmppDateTime.formatXEP0082Date(stamp));
xml.optAttribute("from", from);
xml.optTextChild(reason, this);
xml.rightAngleBracket();
xml.optAppend(reason);
xml.closeElement(this);
return xml;
}

View file

@ -146,7 +146,7 @@ public class StreamInitiation extends IQ {
buf.rightAngleBracket();
// Add the file section if there is one.
buf.optElement(file);
buf.optAppend(file.toXML());
break;
case result:
buf.rightAngleBracket();

View file

@ -8,7 +8,6 @@ dependencies {
compile project(":smack-core")
compile project(":smack-resolver-javax")
compile project(":smack-sasl-javax")
implementation project(":smack-xmlparser-stax")
}
javadoc {

View file

@ -29,6 +29,11 @@ public class SASLGSSAPIMechanism extends SASLJavaXMechanism {
public static final String NAME = GSSAPI;
static {
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
System.setProperty("java.security.auth.login.config", "gss.conf");
}
@Override
public boolean authzidSupported() {
return true;

View file

@ -1,21 +1,12 @@
.PHONY := all clean
.PHONY := clean generate
GRADLE_QUITE_ARGS := --quiet --console plain
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG:.png=.dot)
GENERATED_FILES := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG) $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT)
all: $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG)
GENERATED_FILES := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
generate: $(GENERATED_FILES)
clean:
rm -f $(GENERATED_FILES)
%.png: %.dot
dot -Tpng -o $@ $^
$(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT): src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnection.java ../smack-core/src/main/java/org/jivesoftware/smack/fsm/AbstractXmppStateMachineConnection.java
# TODO: This also creates the dot file even if the command
# fails. It would be better if this was not the case.
gradle $(GRADLE_QUITE_ARGS) :smack-repl:printXmppNioTcpConnectionStateGraph > $@
src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png: src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnection.java ../smack-core/src/main/java/org/jivesoftware/smack/fsm/AbstractXmppStateMachineConnection.java
gradle $(GRADLE_QUITE_ARGS) :smack-repl:printXmppNioTcpConnectionStateGraph | dot -Tpng -o $@

View file

@ -1,2 +1 @@
/XmppNioTcpConnectionStateGraph.png
/XmppNioTcpConnectionStateGraph.dot

View file

@ -87,8 +87,12 @@ import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StartTls;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.sasl.packet.SaslNonza;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jivesoftware.smack.sm.SMUtils;
import org.jivesoftware.smack.sm.StreamManagementException;
import org.jivesoftware.smack.sm.StreamManagementException.StreamIdDoesNotMatchException;
@ -298,10 +302,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
}
});
// Re-init the reader and writer in case of SASL <success/>. This is done to reset the parser since a new stream
// is initiated.
buildNonzaCallback().listenFor(SaslNonza.Success.class, s -> resetParser()).install();
}
/**
@ -371,7 +371,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
SmackException, IOException, InterruptedException {
// Authenticate using SASL
SSLSession sslSession = secureSocket != null ? secureSocket.getSession() : null;
authenticate(username, password, config.getAuthzid(), sslSession);
saslAuthentication.authenticate(username, password, config.getAuthzid(), sslSession);
// Wait for stream features after the authentication.
// TODO: The name of this synchronization point "maybeCompressFeaturesReceived" is not perfect. It should be
@ -857,7 +857,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
tlsHandled.reportSuccess();
}
if (isSaslAuthenticated()) {
if (getSASLAuthentication().authenticationSuccessful()) {
// If we have received features after the SASL has been successfully completed, then we
// have also *maybe* received, as it is an optional feature, the compression feature
// from the server.
@ -865,17 +865,18 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
}
private void resetParser() throws IOException {
try {
packetReader.parser = SmackXmlParser.newXmlParser(reader);
} catch (XmlPullParserException e) {
throw new IOException(e);
}
}
private void openStreamAndResetParser() throws IOException, NotConnectedException, InterruptedException {
/**
* Resets the parser using the latest connection's reader. Resetting the parser is necessary
* when the plain connection has been secured or when a new opening stream element is going
* to be sent by the server.
*
* @throws SmackException if the parser could not be reset.
* @throws InterruptedException if the calling thread was interrupted.
* @throws XmlPullParserException if an error in the XML parser occured.
*/
void openStream() throws SmackException, InterruptedException, XmlPullParserException {
sendStreamOpen();
resetParser();
packetReader.parser = SmackXmlParser.newXmlParser(reader);
}
protected class PacketReader {
@ -920,7 +921,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
private void parsePackets() {
boolean initialStreamOpenSend = false;
try {
openStreamAndResetParser();
openStream();
initialStreamOpenSend = true;
XmlPullParser.Event eventType = parser.getEventType();
while (!done) {
@ -956,7 +957,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
// Secure the connection by negotiating TLS
proceedTLSReceived();
// Send a new opening stream to the server
openStreamAndResetParser();
openStream();
}
catch (Exception e) {
SmackException.SmackWrappedException smackException = new SmackException.SmackWrappedException(e);
@ -979,17 +980,35 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
compressSyncPoint.reportFailure(new SmackException.SmackMessageException(
"Could not establish compression"));
break;
default:
parseAndProcessNonza(parser);
case SaslStreamElements.NAMESPACE:
// SASL authentication has failed. The server may close the connection
// depending on the number of retries
final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser);
getSASLAuthentication().authenticationFailed(failure);
break;
}
break;
case Challenge.ELEMENT:
// The server is challenging the SASL authentication made by the client
String challengeData = parser.nextText();
getSASLAuthentication().challengeReceived(challengeData);
break;
case Success.ELEMENT:
Success success = new Success(parser.nextText());
// We now need to bind a resource for the connection
// Open a new stream and wait for the response
openStream();
// The SASL authentication with the server was successful. The next step
// will be to bind the resource
getSASLAuthentication().authenticated(success);
break;
case Compressed.ELEMENT:
// Server confirmed that it's possible to use stream compression. Start
// stream compression
// Initialize the reader and writer with the new compressed version
initReaderAndWriter();
// Send a new opening stream to the server
openStreamAndResetParser();
openStream();
// Notify that compression is being used
compressSyncPoint.reportSuccess();
break;
@ -1071,7 +1090,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
break;
default:
parseAndProcessNonza(parser);
LOGGER.warning("Unknown top level stream element: " + name);
break;
}
break;
@ -1328,9 +1347,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
}
maybeAddToUnacknowledgedStanzas(packet);
CharSequence elementXml = element.toXML(outgoingStreamXmlEnvironment);
CharSequence elementXml = element.toXML(StreamOpen.CLIENT_NAMESPACE);
if (elementXml instanceof XmlStringBuilder) {
((XmlStringBuilder) elementXml).write(writer, outgoingStreamXmlEnvironment);
((XmlStringBuilder) elementXml).write(writer, StreamOpen.CLIENT_NAMESPACE);
}
else {
writer.write(elementXml.toString());

View file

@ -509,7 +509,7 @@ public class XmppNioTcpConnection extends AbstractXmppNioConnection {
CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE);
if (nextCharSequence instanceof XmlStringBuilder) {
XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence;
outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator();
outgoingCharSequenceIterator = xmlStringBuilder.getCharSequenceIterator();
} else {
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
}

View file

@ -31,8 +31,7 @@ public class SmackXmlParser {
public static XmlPullParserFactory getXmlPullParserFactory() {
Iterator<XmlPullParserFactory> iterator = xmlPullParserFactoryServiceLoader.iterator();
if (!iterator.hasNext()) {
throw new IllegalStateException(
"No XmlPullParserFactory registered with Service Provider Interface (SPI). Is smack-xmlparser-xpp3 or smack-xmlparser-stax in classpath?");
throw new IllegalStateException("Could not load a XmlPullParserFactory via Service Provider Interface (SPI)");
}
return iterator.next();
}