mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 04:22:05 +01:00
Compare commits
No commits in common. "f0300dc906089564d57135f791c4e4900cfe224d" and "b83dacc60a3a23ea0b432079f7e70ffd455720f3" have entirely different histories.
f0300dc906
...
b83dacc60a
35 changed files with 427 additions and 661 deletions
|
@ -35,6 +35,10 @@
|
||||||
<property name="format" value="\.append\("(.|\\.)"\)"/>
|
<property name="format" value="\.append\("(.|\\.)"\)"/>
|
||||||
<property name="message" value="Don't use StringBuilder.append(String) when you can use StringBuilder.append(char). Solution: Replace double quotes of append's argument with single quotes."/>
|
<property name="message" value="Don't use StringBuilder.append(String) when you can use StringBuilder.append(char). Solution: Replace double quotes of append's argument with single quotes."/>
|
||||||
</module>
|
</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="FileTabCharacter"/>
|
||||||
<module name="RegexpSingleline">
|
<module name="RegexpSingleline">
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.SmackException.SmackWrappedException;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.XMPPException.StreamErrorException;
|
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.Presence;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.StanzaError;
|
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.CloseableUtil;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
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,
|
protected void loginInternal(String username, String password, Resourcepart resource) throws XMPPException,
|
||||||
SmackException, IOException, InterruptedException {
|
SmackException, IOException, InterruptedException {
|
||||||
// Authenticate using SASL
|
// Authenticate using SASL
|
||||||
authenticate(username, password, config.getAuthzid(), null);
|
saslAuthentication.authenticate(username, password, config.getAuthzid(), null);
|
||||||
|
|
||||||
bindResourceAndEstablishSession(resource);
|
bindResourceAndEstablishSession(resource);
|
||||||
|
|
||||||
|
@ -394,26 +395,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
||||||
readerConsumer.start();
|
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
|
* A listener class which listen for a successfully established connection
|
||||||
* and connection errors and notifies the BOSHConnection.
|
* and connection errors and notifies the BOSHConnection.
|
||||||
|
@ -509,9 +490,30 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
||||||
case Presence.ELEMENT:
|
case Presence.ELEMENT:
|
||||||
parseAndProcessStanza(parser);
|
parseAndProcessStanza(parser);
|
||||||
break;
|
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":
|
case "features":
|
||||||
parseFeatures(parser);
|
parseFeatures(parser);
|
||||||
break;
|
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":
|
case "error":
|
||||||
// Some BOSH error isn't stream error.
|
// Some BOSH error isn't stream error.
|
||||||
if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) {
|
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);
|
StanzaError.Builder builder = PacketParserUtils.parseError(parser);
|
||||||
throw new XMPPException.XMPPErrorException(null, builder.build());
|
throw new XMPPException.XMPPErrorException(null, builder.build());
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
parseAndProcessNonza(parser);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -60,7 +60,6 @@ import java.util.logging.Logger;
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSession;
|
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
import javax.security.auth.callback.Callback;
|
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.ResourceBindingNotOfferedException;
|
||||||
import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException;
|
import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException;
|
||||||
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
|
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
|
||||||
import org.jivesoftware.smack.SmackException.SmackSaslException;
|
|
||||||
import org.jivesoftware.smack.SmackException.SmackWrappedException;
|
import org.jivesoftware.smack.SmackException.SmackWrappedException;
|
||||||
import org.jivesoftware.smack.SmackFuture.InternalSmackFuture;
|
import org.jivesoftware.smack.SmackFuture.InternalSmackFuture;
|
||||||
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
|
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.ExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.NonzaProvider;
|
import org.jivesoftware.smack.provider.NonzaProvider;
|
||||||
import org.jivesoftware.smack.provider.ProviderManager;
|
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.core.SASLAnonymous;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
|
||||||
import org.jivesoftware.smack.util.Async;
|
import org.jivesoftware.smack.util.Async;
|
||||||
import org.jivesoftware.smack.util.CollectionUtil;
|
|
||||||
import org.jivesoftware.smack.util.DNSUtil;
|
import org.jivesoftware.smack.util.DNSUtil;
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
|
@ -134,7 +127,6 @@ import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
import org.jxmpp.jid.DomainBareJid;
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
|
||||||
import org.jxmpp.jid.EntityFullJid;
|
import org.jxmpp.jid.EntityFullJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
@ -243,9 +235,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
|
|
||||||
private XmlEnvironment incomingStreamXmlEnvironment;
|
private XmlEnvironment incomingStreamXmlEnvironment;
|
||||||
|
|
||||||
protected XmlEnvironment outgoingStreamXmlEnvironment;
|
final Map<QName, NonzaCallback> nonzaCallbacks = new HashMap<>();
|
||||||
|
|
||||||
final MultiMap<QName, NonzaCallback> nonzaCallbacksMap = new MultiMap<>();
|
|
||||||
|
|
||||||
protected final Lock connectionLock = new ReentrantLock();
|
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.
|
* 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
|
* 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) {
|
protected AbstractXMPPConnection(ConnectionConfiguration configuration) {
|
||||||
saslAuthentication = new SASLAuthentication(this, configuration);
|
saslAuthentication = new SASLAuthentication(this, configuration);
|
||||||
config = 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();
|
SmackDebuggerFactory debuggerFactory = configuration.getDebuggerFactory();
|
||||||
if (debuggerFactory != null) {
|
if (debuggerFactory != null) {
|
||||||
debugger = debuggerFactory.create(this);
|
debugger = debuggerFactory.create(this);
|
||||||
|
@ -528,6 +498,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
|
|
||||||
// Reset the connection state
|
// Reset the connection state
|
||||||
initState();
|
initState();
|
||||||
|
saslAuthentication.init();
|
||||||
streamId = null;
|
streamId = null;
|
||||||
|
|
||||||
try {
|
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.
|
* @return the SASLAuthentication manager that is responsible for authenticating with
|
||||||
* @param password the password to send to the server.
|
* 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)
|
|
||||||
*/
|
*/
|
||||||
protected final SASLMechanism authenticate(String username, String password, EntityBareJid authzid,
|
protected SASLAuthentication getSASLAuthentication() {
|
||||||
SSLSession sslSession) throws XMPPErrorException, SASLErrorException, SmackSaslException,
|
return saslAuthentication;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1289,9 +1224,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void parseAndProcessNonza(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
|
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 element = parser.getName();
|
||||||
final String namespace = parser.getNamespace();
|
final String namespace = parser.getNamespace();
|
||||||
final QName key = new QName(namespace, element);
|
final QName key = new QName(namespace, element);
|
||||||
|
@ -1299,26 +1231,21 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
NonzaProvider<? extends Nonza> nonzaProvider = ProviderManager.getNonzaProvider(key);
|
NonzaProvider<? extends Nonza> nonzaProvider = ProviderManager.getNonzaProvider(key);
|
||||||
if (nonzaProvider == null) {
|
if (nonzaProvider == null) {
|
||||||
LOGGER.severe("Unknown nonza: " + key);
|
LOGGER.severe("Unknown nonza: " + key);
|
||||||
ParserUtils.forwardToEndTagOfDepth(parser, initialDepth);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<NonzaCallback> nonzaCallbacks;
|
NonzaCallback nonzaCallback;
|
||||||
synchronized (nonzaCallbacksMap) {
|
synchronized (nonzaCallbacks) {
|
||||||
nonzaCallbacks = nonzaCallbacksMap.getAll(key);
|
nonzaCallback = nonzaCallbacks.get(key);
|
||||||
nonzaCallbacks = CollectionUtil.newListWith(nonzaCallbacks);
|
|
||||||
}
|
}
|
||||||
if (nonzaCallbacks == null) {
|
if (nonzaCallback == null) {
|
||||||
LOGGER.info("No nonza callback for " + key);
|
LOGGER.info("No nonza callback for " + key);
|
||||||
ParserUtils.forwardToEndTagOfDepth(parser, initialDepth);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Nonza nonza = nonzaProvider.parse(parser, incomingStreamXmlEnvironment);
|
Nonza nonza = nonzaProvider.parse(parser, incomingStreamXmlEnvironment);
|
||||||
|
|
||||||
for (NonzaCallback nonzaCallback : nonzaCallbacks) {
|
nonzaCallback.onNonzaReceived(nonza);
|
||||||
nonzaCallback.onNonzaReceived(nonza);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void parseAndProcessStanza(XmlPullParser parser)
|
protected void parseAndProcessStanza(XmlPullParser parser)
|
||||||
|
@ -2090,13 +2017,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
from = XmppStringUtils.completeJidFrom(localpart, to);
|
from = XmppStringUtils.completeJidFrom(localpart, to);
|
||||||
}
|
}
|
||||||
String id = getStreamId();
|
String id = getStreamId();
|
||||||
|
sendNonza(new StreamOpen(to, from, id, config.getXmlLang(), StreamOpen.StreamContentNamespace.client));
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class SmackTlsContext {
|
public static final class SmackTlsContext {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2019 Florian Schmaus
|
* Copyright 2018 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.
|
||||||
|
@ -14,11 +14,23 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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.
|
import org.jivesoftware.smack.packet.Element;
|
||||||
public interface Consumer<T> {
|
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack;
|
package org.jivesoftware.smack;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ import org.jivesoftware.smack.util.XmppElementUtil;
|
||||||
public class NonzaCallback {
|
public class NonzaCallback {
|
||||||
|
|
||||||
protected final AbstractXMPPConnection connection;
|
protected final AbstractXMPPConnection connection;
|
||||||
protected final Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners;
|
protected final Map<QName, GenericElementListener<? extends Nonza>> filterAndListeners;
|
||||||
|
|
||||||
private NonzaCallback(Builder builder) {
|
private NonzaCallback(Builder builder) {
|
||||||
this.connection = builder.connection;
|
this.connection = builder.connection;
|
||||||
|
@ -39,18 +38,21 @@ public class NonzaCallback {
|
||||||
install();
|
install();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNonzaReceived(Nonza nonza) throws IOException {
|
void onNonzaReceived(Nonza nonza) {
|
||||||
QName key = nonza.getQName();
|
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() {
|
public void cancel() {
|
||||||
for (Map.Entry<QName, ClassAndConsumer<? extends Nonza>> entry : filterAndListeners.entrySet()) {
|
synchronized (connection.nonzaCallbacks) {
|
||||||
QName filterKey = entry.getKey();
|
for (Map.Entry<QName, GenericElementListener<? extends Nonza>> entry : filterAndListeners.entrySet()) {
|
||||||
synchronized (connection.nonzaCallbacksMap) {
|
QName filterKey = entry.getKey();
|
||||||
connection.nonzaCallbacksMap.removeOne(filterKey, this);
|
NonzaCallback installedCallback = connection.nonzaCallbacks.get(filterKey);
|
||||||
|
if (equals(installedCallback)) {
|
||||||
|
connection.nonzaCallbacks.remove(filterKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,9 +62,9 @@ public class NonzaCallback {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QName key : filterAndListeners.keySet()) {
|
synchronized (connection.nonzaCallbacks) {
|
||||||
synchronized (connection.nonzaCallbacksMap) {
|
for (QName key : filterAndListeners.keySet()) {
|
||||||
connection.nonzaCallbacksMap.put(key, this);
|
connection.nonzaCallbacks.put(key, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,35 +74,31 @@ public class NonzaCallback {
|
||||||
private SN successNonza;
|
private SN successNonza;
|
||||||
private FN failedNonza;
|
private FN failedNonza;
|
||||||
|
|
||||||
private NonzaResponseCallback(Class<SN> successNonzaClass, Class<FN> failedNonzaClass,
|
private NonzaResponseCallback(Class<? extends SN> successNonzaClass, Class<? extends FN> failedNonzaClass,
|
||||||
Builder builder) {
|
Builder builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
|
|
||||||
final QName successNonzaKey = XmppElementUtil.getQNameFor(successNonzaClass);
|
final QName successNonzaKey = XmppElementUtil.getQNameFor(successNonzaClass);
|
||||||
final QName failedNonzaKey = XmppElementUtil.getQNameFor(failedNonzaClass);
|
final QName failedNonzaKey = XmppElementUtil.getQNameFor(failedNonzaClass);
|
||||||
|
|
||||||
final NonzaListener<SN> successListener = new NonzaListener<SN>() {
|
final GenericElementListener<SN> successListener = new GenericElementListener<SN>(successNonzaClass) {
|
||||||
@Override
|
@Override
|
||||||
public void accept(SN successNonza) {
|
public void process(SN successNonza) {
|
||||||
NonzaResponseCallback.this.successNonza = successNonza;
|
NonzaResponseCallback.this.successNonza = successNonza;
|
||||||
notifyResponse();
|
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
|
@Override
|
||||||
public void accept(FN failedNonza) {
|
public void process(FN failedNonza) {
|
||||||
NonzaResponseCallback.this.failedNonza = failedNonza;
|
NonzaResponseCallback.this.failedNonza = failedNonza;
|
||||||
notifyResponse();
|
notifyResponse();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
final ClassAndConsumer<FN> failedClassAndConsumer = new ClassAndConsumer<>(failedNonzaClass,
|
|
||||||
failedListener);
|
|
||||||
|
|
||||||
filterAndListeners.put(successNonzaKey, successClassAndConsumer);
|
filterAndListeners.put(successNonzaKey, successListener);
|
||||||
filterAndListeners.put(failedNonzaKey, failedClassAndConsumer);
|
filterAndListeners.put(failedNonzaKey, failedListener);
|
||||||
|
|
||||||
install();
|
install();
|
||||||
}
|
}
|
||||||
|
@ -141,16 +139,15 @@ public class NonzaCallback {
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private final AbstractXMPPConnection connection;
|
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) {
|
Builder(AbstractXMPPConnection connection) {
|
||||||
this.connection = 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);
|
QName key = XmppElementUtil.getQNameFor(nonza);
|
||||||
ClassAndConsumer<N> classAndConsumer = new ClassAndConsumer<>(nonza, nonzaListener);
|
filterAndListeners.put(key, nonzaListener);
|
||||||
filterAndListeners.put(key, classAndConsumer);
|
|
||||||
return this;
|
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,
|
static <SN extends Nonza, FN extends Nonza> SN sendAndWaitForResponse(NonzaCallback.Builder builder, Nonza nonza, Class<SN> successNonzaClass,
|
||||||
Class<FN> failedNonzaClass)
|
Class<FN> failedNonzaClass)
|
||||||
throws NoResponseException, NotConnectedException, InterruptedException, FailedNonzaException {
|
throws NoResponseException, NotConnectedException, InterruptedException, FailedNonzaException {
|
||||||
|
|
|
@ -39,9 +39,8 @@ import org.jivesoftware.smack.packet.Mechanisms;
|
||||||
import org.jivesoftware.smack.sasl.SASLErrorException;
|
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||||
import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
|
import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.Success;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.DomainBareJid;
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
@ -155,9 +154,20 @@ public final class SASLAuthentication {
|
||||||
private final ConnectionConfiguration configuration;
|
private final ConnectionConfiguration configuration;
|
||||||
private SASLMechanism currentMechanism = null;
|
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) {
|
SASLAuthentication(AbstractXMPPConnection connection, ConnectionConfiguration configuration) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,26 +191,23 @@ public final class SASLAuthentication {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws NoResponseException if there was no response from the remote entity.
|
* @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,
|
throws XMPPErrorException, SASLErrorException, IOException,
|
||||||
InterruptedException, SmackSaslException, NotConnectedException, NoResponseException {
|
InterruptedException, SmackSaslException, NotConnectedException, NoResponseException {
|
||||||
final SASLMechanism mechanism = selectMechanism(authzid);
|
currentMechanism = selectMechanism(authzid);
|
||||||
final CallbackHandler callbackHandler = configuration.getCallbackHandler();
|
final CallbackHandler callbackHandler = configuration.getCallbackHandler();
|
||||||
final String host = connection.getHost();
|
final String host = connection.getHost();
|
||||||
final DomainBareJid xmppServiceDomain = connection.getXMPPServiceDomain();
|
final DomainBareJid xmppServiceDomain = connection.getXMPPServiceDomain();
|
||||||
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
currentMechanism = mechanism;
|
|
||||||
|
|
||||||
if (callbackHandler != null) {
|
if (callbackHandler != null) {
|
||||||
currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler, authzid, sslSession);
|
currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler, authzid, sslSession);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid, sslSession);
|
currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid, sslSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
final long deadline = System.currentTimeMillis() + connection.getReplyTimeout();
|
final long deadline = System.currentTimeMillis() + connection.getReplyTimeout();
|
||||||
while (!mechanism.isFinished()) {
|
while (!authenticationSuccessful && saslException == null) {
|
||||||
final long now = System.currentTimeMillis();
|
final long now = System.currentTimeMillis();
|
||||||
if (now >= deadline) break;
|
if (now >= deadline) break;
|
||||||
// Wait until SASL negotiation finishes
|
// 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
|
* Wrapper for {@link #challengeReceived(String, boolean)}, with <code>finalChallenge</code> set
|
||||||
* to <code>false</code>.
|
* 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 SmackException if Smack detected an exceptional situation.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
void challengeReceived(SaslNonza.Challenge challenge) throws SmackException, InterruptedException {
|
public void challengeReceived(String challenge) throws SmackException, InterruptedException {
|
||||||
challengeReceived(challenge.getData(), false);
|
challengeReceived(challenge, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,12 +258,13 @@ public final class SASLAuthentication {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
private void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
|
public void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
|
||||||
SASLMechanism mechanism;
|
try {
|
||||||
synchronized (this) {
|
currentMechanism.challengeReceived(challenge, finalChallenge);
|
||||||
mechanism = currentMechanism;
|
} 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.
|
* @param success result of the authentication.
|
||||||
* @throws SmackException if Smack detected an exceptional situation.
|
* @throws SmackException if Smack detected an exceptional situation.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @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
|
// 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
|
// "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
|
// 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) {
|
if (success.getData() != null) {
|
||||||
challengeReceived(success.getData(), true);
|
challengeReceived(success.getData(), true);
|
||||||
}
|
}
|
||||||
|
currentMechanism.checkIfSuccessfulOrThrow();
|
||||||
|
authenticationSuccessful = true;
|
||||||
// Wake up the thread that is waiting in the #authenticate method
|
// Wake up the thread that is waiting in the #authenticate method
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
currentMechanism.afterFinalSaslChallenge();
|
|
||||||
|
|
||||||
notify();
|
notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,29 +298,30 @@ public final class SASLAuthentication {
|
||||||
* @param saslFailure the SASL failure as reported by the server
|
* @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>
|
* @see <a href="https://tools.ietf.org/html/rfc6120#section-6.5">RFC6120 6.5</a>
|
||||||
*/
|
*/
|
||||||
void authenticationFailed(SASLFailure saslFailure) {
|
public void authenticationFailed(SASLFailure saslFailure) {
|
||||||
SASLErrorException saslErrorException;
|
authenticationFailed(new SASLErrorException(currentMechanism.getName(), saslFailure));
|
||||||
synchronized (this) {
|
|
||||||
saslErrorException = new SASLErrorException(currentMechanism.getName(), saslFailure);
|
|
||||||
}
|
|
||||||
authenticationFailed(saslErrorException);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void authenticationFailed(Exception exception) {
|
private void authenticationFailed(Exception exception) {
|
||||||
|
saslException = exception;
|
||||||
// Wake up the thread that is waiting in the #authenticate method
|
// Wake up the thread that is waiting in the #authenticate method
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
currentMechanism.setException(exception);
|
|
||||||
notify();
|
notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean authenticationSuccessful() {
|
public boolean authenticationSuccessful() {
|
||||||
synchronized (this) {
|
return authenticationSuccessful;
|
||||||
if (currentMechanism == null) {
|
}
|
||||||
return false;
|
|
||||||
}
|
/**
|
||||||
return currentMechanism.isAuthenticationSuccessful();
|
* 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() {
|
String getNameOfLastUsedSaslMechansism() {
|
||||||
|
|
|
@ -108,6 +108,30 @@ public final class SmackConfiguration {
|
||||||
return SmackInitialization.SMACK_VERSION;
|
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
|
* Returns the number of milliseconds to wait for a response from
|
||||||
* the server. The default value is 5000 ms.
|
* the server. The default value is 5000 ms.
|
||||||
|
|
|
@ -37,9 +37,6 @@ import org.jivesoftware.smack.packet.Message.Body;
|
||||||
import org.jivesoftware.smack.provider.BindIQProvider;
|
import org.jivesoftware.smack.provider.BindIQProvider;
|
||||||
import org.jivesoftware.smack.provider.BodyElementProvider;
|
import org.jivesoftware.smack.provider.BodyElementProvider;
|
||||||
import org.jivesoftware.smack.provider.ProviderManager;
|
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.TlsFailureProvider;
|
||||||
import org.jivesoftware.smack.provider.TlsProceedProvider;
|
import org.jivesoftware.smack.provider.TlsProceedProvider;
|
||||||
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
||||||
|
@ -129,9 +126,6 @@ public final class SmackInitialization {
|
||||||
ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());
|
ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());
|
||||||
ProviderManager.addExtensionProvider(Body.ELEMENT, Body.NAMESPACE, new BodyElementProvider());
|
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(TlsProceedProvider.INSTANCE);
|
||||||
ProviderManager.addNonzaProvider(TlsFailureProvider.INSTANCE);
|
ProviderManager.addNonzaProvider(TlsFailureProvider.INSTANCE);
|
||||||
ProviderManager.addNonzaProvider(CompressedProvider.INSTANCE);
|
ProviderManager.addNonzaProvider(CompressedProvider.INSTANCE);
|
||||||
|
|
|
@ -53,6 +53,8 @@ import org.jivesoftware.smack.packet.StreamError;
|
||||||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
import org.jivesoftware.smack.sasl.SASLErrorException;
|
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
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.Objects;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
|
@ -333,6 +335,19 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
|
||||||
parseFeatures(parser);
|
parseFeatures(parser);
|
||||||
afterFeaturesReceived();
|
afterFeaturesReceived();
|
||||||
break;
|
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:
|
default:
|
||||||
parseAndProcessNonza(parser);
|
parseAndProcessNonza(parser);
|
||||||
break;
|
break;
|
||||||
|
@ -598,7 +613,7 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
|
||||||
prepareToWaitForFeaturesReceived();
|
prepareToWaitForFeaturesReceived();
|
||||||
|
|
||||||
LoginContext loginContext = walkStateGraphContext.loginContext;
|
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.
|
// 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");
|
waitForFeaturesReceived("server stream features after SASL authentication");
|
||||||
|
|
|
@ -31,7 +31,6 @@ import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.DisconnectedStateDescriptor;
|
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.DisconnectedStateDescriptor;
|
||||||
import org.jivesoftware.smack.util.Consumer;
|
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
import org.jivesoftware.smack.util.MultiMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -331,8 +330,7 @@ public class StateDescriptorGraph {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <E> void dfsVisit(GraphVertex<E> vertex, Consumer<GraphVertex<E>> dfsFinishedVertex,
|
private static <E> void dfsVisit(GraphVertex<E> vertex, DfsFinishedVertex<E> dfsFinishedVertex, DfsEdgeFound<E> dfsEdgeFound) {
|
||||||
DfsEdgeFound<E> dfsEdgeFound) {
|
|
||||||
vertex.color = GraphVertex.VertexColor.grey;
|
vertex.color = GraphVertex.VertexColor.grey;
|
||||||
|
|
||||||
final int totalEdgeCount = vertex.getOutgoingEdges().size();
|
final int totalEdgeCount = vertex.getOutgoingEdges().size();
|
||||||
|
@ -351,12 +349,11 @@ public class StateDescriptorGraph {
|
||||||
|
|
||||||
vertex.color = GraphVertex.VertexColor.black;
|
vertex.color = GraphVertex.VertexColor.black;
|
||||||
if (dfsFinishedVertex != null) {
|
if (dfsFinishedVertex != null) {
|
||||||
dfsFinishedVertex.accept(vertex);
|
dfsFinishedVertex.onVertexFinished(vertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <E> void dfs(Collection<GraphVertex<E>> vertexes, Consumer<GraphVertex<E>> dfsFinishedVertex,
|
private static <E> void dfs(Collection<GraphVertex<E>> vertexes, DfsFinishedVertex<E> dfsFinishedVertex, DfsEdgeFound<E> dfsEdgeFound) {
|
||||||
DfsEdgeFound<E> dfsEdgeFound) {
|
|
||||||
for (GraphVertex<E> vertex : vertexes) {
|
for (GraphVertex<E> vertex : vertexes) {
|
||||||
if (vertex.color == GraphVertex.VertexColor.white) {
|
if (vertex.color == GraphVertex.VertexColor.white) {
|
||||||
dfsVisit(vertex, dfsFinishedVertex, dfsEdgeFound);
|
dfsVisit(vertex, dfsFinishedVertex, dfsEdgeFound);
|
||||||
|
@ -410,6 +407,12 @@ public class StateDescriptorGraph {
|
||||||
dotOut.append("}\n");
|
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> {
|
private interface DfsEdgeFound<E> {
|
||||||
void onEdgeFound(GraphVertex<E> from, GraphVertex<E> to, int edgeId, int totalEdgeCount);
|
void onEdgeFound(GraphVertex<E> from, GraphVertex<E> to, int edgeId, int totalEdgeCount);
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,12 +163,6 @@ public class XmlEnvironment {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder with(StreamOpen streamOpen) {
|
|
||||||
withNamespace(streamOpen.getNamespace());
|
|
||||||
withLanguage(streamOpen.getLanguage());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public XmlEnvironment build() {
|
public XmlEnvironment build() {
|
||||||
return new XmlEnvironment(this);
|
return new XmlEnvironment(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -20,7 +20,7 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPException;
|
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 {
|
public class SASLErrorException extends XMPPException {
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,11 @@ import javax.net.ssl.SSLSession;
|
||||||
import javax.security.auth.callback.CallbackHandler;
|
import javax.security.auth.callback.CallbackHandler;
|
||||||
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.SmackException.SmackSaslException;
|
import org.jivesoftware.smack.SmackException.SmackSaslException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.AuthMechanism;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.Response;
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
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 GSSAPI = "GSSAPI";
|
||||||
public static final String PLAIN = "PLAIN";
|
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 XMPPConnection connection;
|
||||||
|
|
||||||
protected ConnectionConfiguration connectionConfiguration;
|
protected ConnectionConfiguration connectionConfiguration;
|
||||||
|
@ -287,18 +275,7 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
||||||
*/
|
*/
|
||||||
public abstract int getPriority();
|
public abstract int getPriority();
|
||||||
|
|
||||||
/**
|
public abstract void checkIfSuccessfulOrThrow() throws SmackSaslException;
|
||||||
* 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 SASLMechanism instanceForAuthentication(XMPPConnection connection, ConnectionConfiguration connectionConfiguration) {
|
public SASLMechanism instanceForAuthentication(XMPPConnection connection, ConnectionConfiguration connectionConfiguration) {
|
||||||
SASLMechanism saslMechansim = newInstance();
|
SASLMechanism saslMechansim = newInstance();
|
||||||
|
@ -311,39 +288,6 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
||||||
return false;
|
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 abstract SASLMechanism newInstance();
|
||||||
|
|
||||||
protected static byte[] toBytes(String string) {
|
protected static byte[] toBytes(String string) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2014-2019 Florian Schmaus
|
* Copyright 2014-2018 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.
|
||||||
|
@ -18,30 +18,21 @@ package org.jivesoftware.smack.sasl.packet;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.xml.namespace.QName;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.AbstractError;
|
import org.jivesoftware.smack.packet.AbstractError;
|
||||||
import org.jivesoftware.smack.packet.Nonza;
|
import org.jivesoftware.smack.packet.Nonza;
|
||||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
|
||||||
import org.jivesoftware.smack.sasl.SASLError;
|
import org.jivesoftware.smack.sasl.SASLError;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
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;
|
||||||
|
|
||||||
public interface SaslNonza extends Nonza {
|
public class SaslStreamElements {
|
||||||
String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
|
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||||
|
|
||||||
@Override
|
|
||||||
default String getNamespace() {
|
|
||||||
return NAMESPACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiating SASL authentication by select a mechanism.
|
* 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 String ELEMENT = "auth";
|
||||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
|
||||||
|
|
||||||
private final String mechanism;
|
private final String mechanism;
|
||||||
private final String authenticationText;
|
private final String authenticationText;
|
||||||
|
@ -53,11 +44,11 @@ public interface SaslNonza extends Nonza {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
XmlStringBuilder xml = new XmlStringBuilder();
|
||||||
xml.attribute("mechanism", mechanism).rightAngleBracket();
|
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).attribute("mechanism", mechanism).rightAngleBracket();
|
||||||
xml.escape(authenticationText);
|
xml.optAppend(authenticationText);
|
||||||
xml.closeElement(this);
|
xml.closeElement(ELEMENT);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +60,11 @@ public interface SaslNonza extends Nonza {
|
||||||
return authenticationText;
|
return authenticationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getElementName() {
|
public String getElementName() {
|
||||||
return ELEMENT;
|
return ELEMENT;
|
||||||
|
@ -78,9 +74,8 @@ public interface SaslNonza extends Nonza {
|
||||||
/**
|
/**
|
||||||
* A SASL challenge stream element.
|
* A SASL challenge stream element.
|
||||||
*/
|
*/
|
||||||
class Challenge implements SaslNonza {
|
public static class Challenge implements Nonza {
|
||||||
public static final String ELEMENT = "challenge";
|
public static final String ELEMENT = "challenge";
|
||||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
|
||||||
|
|
||||||
private final String data;
|
private final String data;
|
||||||
|
|
||||||
|
@ -88,15 +83,18 @@ public interface SaslNonza extends Nonza {
|
||||||
this.data = StringUtils.returnIfNotEmptyTrimmed(data);
|
this.data = StringUtils.returnIfNotEmptyTrimmed(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getData() {
|
@Override
|
||||||
return data;
|
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
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
public String getNamespace() {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
return NAMESPACE;
|
||||||
xml.optTextChild(data, this);
|
|
||||||
return xml;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -108,9 +106,8 @@ public interface SaslNonza extends Nonza {
|
||||||
/**
|
/**
|
||||||
* A SASL response stream element.
|
* A SASL response stream element.
|
||||||
*/
|
*/
|
||||||
class Response implements SaslNonza {
|
public static class Response implements Nonza {
|
||||||
public static final String ELEMENT = "response";
|
public static final String ELEMENT = "response";
|
||||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
|
||||||
|
|
||||||
private final String authenticationText;
|
private final String authenticationText;
|
||||||
|
|
||||||
|
@ -123,9 +120,11 @@ public interface SaslNonza extends Nonza {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
XmlStringBuilder xml = new XmlStringBuilder();
|
||||||
xml.optTextChild(authenticationText, this);
|
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
||||||
|
xml.optAppend(authenticationText);
|
||||||
|
xml.closeElement(ELEMENT);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +132,11 @@ public interface SaslNonza extends Nonza {
|
||||||
return authenticationText;
|
return authenticationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getElementName() {
|
public String getElementName() {
|
||||||
return ELEMENT;
|
return ELEMENT;
|
||||||
|
@ -142,9 +146,8 @@ public interface SaslNonza extends Nonza {
|
||||||
/**
|
/**
|
||||||
* A SASL success stream element.
|
* A SASL success stream element.
|
||||||
*/
|
*/
|
||||||
class Success implements SaslNonza {
|
public static class Success implements Nonza {
|
||||||
public static final String ELEMENT = "success";
|
public static final String ELEMENT = "success";
|
||||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
|
||||||
|
|
||||||
private final String data;
|
private final String data;
|
||||||
|
|
||||||
|
@ -168,12 +171,19 @@ public interface SaslNonza extends Nonza {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
XmlStringBuilder xml = new XmlStringBuilder();
|
||||||
xml.optTextChild(data, this);
|
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
||||||
|
xml.optAppend(data);
|
||||||
|
xml.closeElement(ELEMENT);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getElementName() {
|
public String getElementName() {
|
||||||
return ELEMENT;
|
return ELEMENT;
|
||||||
|
@ -184,9 +194,8 @@ public interface SaslNonza extends Nonza {
|
||||||
* A SASL failure stream element, also called "SASL Error".
|
* 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>
|
* @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 String ELEMENT = "failure";
|
||||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
|
||||||
|
|
||||||
private final SASLError saslError;
|
private final SASLError saslError;
|
||||||
private final String saslErrorString;
|
private final String saslErrorString;
|
||||||
|
@ -241,6 +250,11 @@ public interface SaslNonza extends Nonza {
|
||||||
return toXML().toString();
|
return toXML().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getElementName() {
|
public String getElementName() {
|
||||||
return ELEMENT;
|
return ELEMENT;
|
|
@ -51,9 +51,8 @@ public class CollectionUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ArrayList<T> newListWith(Collection<? extends T> collection) {
|
public static <T> ArrayList<T> newListWith(Collection<? extends T> collection) {
|
||||||
if (collection == null) {
|
ArrayList<T> arrayList = new ArrayList<>(collection.size());
|
||||||
return null;
|
arrayList.addAll(collection);
|
||||||
}
|
return arrayList;
|
||||||
return new ArrayList<>(collection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.IQProvider;
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
import org.jivesoftware.smack.provider.ProviderManager;
|
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.SmackXmlParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
@ -685,6 +686,44 @@ public class PacketParserUtils {
|
||||||
return descriptiveTexts;
|
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 {
|
public static StreamError parseStreamError(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
return parseStreamError(parser, null);
|
return parseStreamError(parser, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,9 @@ package org.jivesoftware.smack.util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Element;
|
import org.jivesoftware.smack.packet.Element;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
@ -497,20 +496,17 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmlStringBuilder optAppend(Element element) {
|
public XmlStringBuilder optAppend(CharSequence csq) {
|
||||||
if (element != null) {
|
if (csq != null) {
|
||||||
append(element.toXML(effectiveXmlEnvironment));
|
append(csq);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmlStringBuilder optTextChild(CharSequence sqc, NamedElement parentElement) {
|
public XmlStringBuilder optAppend(Element element) {
|
||||||
if (sqc == null) {
|
if (element != null) {
|
||||||
return closeEmptyElement();
|
append(element.toXML(effectiveXmlEnvironment));
|
||||||
}
|
}
|
||||||
rightAngleBracket();
|
|
||||||
escape(sqc);
|
|
||||||
closeElement(parentElement);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,46 +606,24 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
return toString().hashCode();
|
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
|
* 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
|
* the single parts one-by-one, avoiding allocation of a big continuous memory block holding the
|
||||||
* XmlStringBuilder contents.
|
* XmlStringBuilder contents.
|
||||||
*
|
*
|
||||||
* @param writer TODO javadoc me please
|
* @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.
|
* @throws IOException if an I/O error occured.
|
||||||
*/
|
*/
|
||||||
public void write(Writer writer, XmlEnvironment enclosingXmlEnvironment) throws IOException {
|
public void write(Writer writer, String enclosingNamespace) throws IOException {
|
||||||
try {
|
XmlEnvironment enclosingXmlEnvironment = XmlEnvironment.builder()
|
||||||
appendXmlTo(csq -> {
|
.withNamespace(enclosingNamespace)
|
||||||
try {
|
.build();
|
||||||
writer.append(csq);
|
appendXmlTo(writer, enclosingXmlEnvironment);
|
||||||
} catch (IOException e) {
|
|
||||||
throw new WrappedIoException(e);
|
|
||||||
}
|
|
||||||
}, enclosingXmlEnvironment);
|
|
||||||
} catch (WrappedIoException e) {
|
|
||||||
throw e.wrappedIoException;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CharSequence> toList(XmlEnvironment enclosingXmlEnvironment) {
|
public Iterator<CharSequence> getCharSequenceIterator() {
|
||||||
List<CharSequence> res = new ArrayList<>(sb.getAsList().size());
|
return sb.getAsList().iterator();
|
||||||
|
|
||||||
appendXmlTo(csq -> res.add(csq), enclosingXmlEnvironment);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.
|
// This is only the potential length, since the actual length depends on the given XmlEnvironment.
|
||||||
int potentialLength = length();
|
int potentialLength = length();
|
||||||
StringBuilder res = new StringBuilder(potentialLength);
|
StringBuilder res = new StringBuilder(potentialLength);
|
||||||
|
try {
|
||||||
appendXmlTo(csq -> res.append(csq), enclosingXmlEnvironment);
|
appendXmlTo(res, enclosingXmlEnvironment);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Should never happen.
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendXmlTo(Consumer<CharSequence> charSequenceSink, XmlEnvironment enclosingXmlEnvironment) {
|
private void appendXmlTo(Appendable appendable, XmlEnvironment enclosingXmlEnvironment) throws IOException {
|
||||||
for (CharSequence csq : sb.getAsList()) {
|
for (CharSequence csq : sb.getAsList()) {
|
||||||
if (csq instanceof XmlStringBuilder) {
|
if (csq instanceof XmlStringBuilder) {
|
||||||
((XmlStringBuilder) csq).appendXmlTo(charSequenceSink, enclosingXmlEnvironment);
|
((XmlStringBuilder) csq).appendXmlTo(appendable, enclosingXmlEnvironment);
|
||||||
}
|
}
|
||||||
else if (csq instanceof XmlNsAttribute) {
|
else if (csq instanceof XmlNsAttribute) {
|
||||||
XmlNsAttribute xmlNsAttribute = (XmlNsAttribute) csq;
|
XmlNsAttribute xmlNsAttribute = (XmlNsAttribute) csq;
|
||||||
if (!xmlNsAttribute.value.equals(enclosingXmlEnvironment.getEffectiveNamespace())) {
|
if (!xmlNsAttribute.value.equals(enclosingXmlEnvironment.getEffectiveNamespace())) {
|
||||||
charSequenceSink.accept(xmlNsAttribute);
|
appendable.append(xmlNsAttribute);
|
||||||
enclosingXmlEnvironment = new XmlEnvironment(xmlNsAttribute.value);
|
enclosingXmlEnvironment = new XmlEnvironment(xmlNsAttribute.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
charSequenceSink.accept(csq);
|
appendable.append(csq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -21,8 +21,8 @@ import static org.junit.Assert.assertEquals;
|
||||||
import org.jivesoftware.smack.DummyConnection;
|
import org.jivesoftware.smack.DummyConnection;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.AuthMechanism;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.Response;
|
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||||
|
|
||||||
|
|
|
@ -125,19 +125,10 @@ public class SmackTestUtil {
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <E extends Element, P extends Provider<E>> P providerClassToProvider(Class<P> providerClass) {
|
private static <E extends Element, P extends Provider<E>> P providerClassToProvider(Class<P> providerClass) {
|
||||||
P provider;
|
P provider;
|
||||||
|
// TODO: Consider adding a shortcut in case there is a static INSTANCE field holding an instance of the
|
||||||
try {
|
// requested provider.
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
provider = providerClass.getDeclaredConstructor().newInstance();
|
provider = providerClass.getDeclaredConstructor().newInstance();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,9 @@ import org.jivesoftware.smack.packet.Presence;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.StanzaError;
|
import org.jivesoftware.smack.packet.StanzaError;
|
||||||
import org.jivesoftware.smack.packet.StreamOpen;
|
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.SmackTestUtil;
|
||||||
import org.jivesoftware.smack.test.util.TestUtils;
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
|
@ -811,6 +814,41 @@ public class PacketParserUtilsTest {
|
||||||
assertXmlSimilar(stanza, result.toString());
|
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")
|
@SuppressWarnings("ReferenceEquality")
|
||||||
private static String determineNonDefaultLanguage() {
|
private static String determineNonDefaultLanguage() {
|
||||||
String otherLanguage = "jp";
|
String otherLanguage = "jp";
|
||||||
|
|
|
@ -225,7 +225,9 @@ public abstract class AbstractHttpOverXmpp extends IQ {
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||||
xml.optTextChild(text, this);
|
xml.rightAngleBracket();
|
||||||
|
xml.optAppend(text);
|
||||||
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +269,9 @@ public abstract class AbstractHttpOverXmpp extends IQ {
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||||
xml.optTextChild(text, this);
|
xml.rightAngleBracket();
|
||||||
|
xml.optAppend(text);
|
||||||
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +313,9 @@ public abstract class AbstractHttpOverXmpp extends IQ {
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||||
xml.optTextChild(text, this);
|
xml.rightAngleBracket();
|
||||||
|
xml.optAppend(text);
|
||||||
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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'>"
|
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'>"
|
+ "<result xmlns='urn:xmpp:mam:1' queryid='g27' id='34482-21985-73620'>"
|
||||||
+ "<forwarded xmlns='urn:xmpp:forward:0'>"
|
+ "<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' "
|
+ "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>"
|
+ "type='chat'>" + "<body>Thrice the brinded cat hath mew.</body>" + "</message>" + "</forwarded>"
|
||||||
+ "</result>" + "</message>";
|
+ "</result>" + "</message>";
|
||||||
|
|
|
@ -106,7 +106,9 @@ public class DelayInformation implements ExtensionElement {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||||
xml.attribute("stamp", XmppDateTime.formatXEP0082Date(stamp));
|
xml.attribute("stamp", XmppDateTime.formatXEP0082Date(stamp));
|
||||||
xml.optAttribute("from", from);
|
xml.optAttribute("from", from);
|
||||||
xml.optTextChild(reason, this);
|
xml.rightAngleBracket();
|
||||||
|
xml.optAppend(reason);
|
||||||
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ public class StreamInitiation extends IQ {
|
||||||
buf.rightAngleBracket();
|
buf.rightAngleBracket();
|
||||||
|
|
||||||
// Add the file section if there is one.
|
// Add the file section if there is one.
|
||||||
buf.optElement(file);
|
buf.optAppend(file.toXML());
|
||||||
break;
|
break;
|
||||||
case result:
|
case result:
|
||||||
buf.rightAngleBracket();
|
buf.rightAngleBracket();
|
||||||
|
|
|
@ -8,7 +8,6 @@ dependencies {
|
||||||
compile project(":smack-core")
|
compile project(":smack-core")
|
||||||
compile project(":smack-resolver-javax")
|
compile project(":smack-resolver-javax")
|
||||||
compile project(":smack-sasl-javax")
|
compile project(":smack-sasl-javax")
|
||||||
implementation project(":smack-xmlparser-stax")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
javadoc {
|
javadoc {
|
||||||
|
|
|
@ -29,6 +29,11 @@ public class SASLGSSAPIMechanism extends SASLJavaXMechanism {
|
||||||
|
|
||||||
public static final String NAME = GSSAPI;
|
public static final String NAME = GSSAPI;
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
|
||||||
|
System.setProperty("java.security.auth.login.config", "gss.conf");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean authzidSupported() {
|
public boolean authzidSupported() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,21 +1,12 @@
|
||||||
.PHONY := all clean
|
.PHONY := clean generate
|
||||||
|
|
||||||
GRADLE_QUITE_ARGS := --quiet --console plain
|
GRADLE_QUITE_ARGS := --quiet --console plain
|
||||||
|
|
||||||
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
|
GENERATED_FILES := 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)
|
generate: $(GENERATED_FILES)
|
||||||
|
|
||||||
GENERATED_FILES := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG) $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT)
|
|
||||||
|
|
||||||
all: $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(GENERATED_FILES)
|
rm -f $(GENERATED_FILES)
|
||||||
|
|
||||||
%.png: %.dot
|
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
|
||||||
dot -Tpng -o $@ $^
|
gradle $(GRADLE_QUITE_ARGS) :smack-repl:printXmppNioTcpConnectionStateGraph | 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 > $@
|
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
/XmppNioTcpConnectionStateGraph.png
|
/XmppNioTcpConnectionStateGraph.png
|
||||||
/XmppNioTcpConnectionStateGraph.dot
|
|
||||||
|
|
|
@ -87,8 +87,12 @@ import org.jivesoftware.smack.packet.Presence;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.StartTls;
|
import org.jivesoftware.smack.packet.StartTls;
|
||||||
import org.jivesoftware.smack.packet.StreamError;
|
import org.jivesoftware.smack.packet.StreamError;
|
||||||
|
import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
import org.jivesoftware.smack.proxy.ProxyInfo;
|
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.SMUtils;
|
||||||
import org.jivesoftware.smack.sm.StreamManagementException;
|
import org.jivesoftware.smack.sm.StreamManagementException;
|
||||||
import org.jivesoftware.smack.sm.StreamManagementException.StreamIdDoesNotMatchException;
|
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 {
|
SmackException, IOException, InterruptedException {
|
||||||
// Authenticate using SASL
|
// Authenticate using SASL
|
||||||
SSLSession sslSession = secureSocket != null ? secureSocket.getSession() : null;
|
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.
|
// Wait for stream features after the authentication.
|
||||||
// TODO: The name of this synchronization point "maybeCompressFeaturesReceived" is not perfect. It should be
|
// 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();
|
tlsHandled.reportSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSaslAuthenticated()) {
|
if (getSASLAuthentication().authenticationSuccessful()) {
|
||||||
// If we have received features after the SASL has been successfully completed, then we
|
// 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
|
// have also *maybe* received, as it is an optional feature, the compression feature
|
||||||
// from the server.
|
// from the server.
|
||||||
|
@ -865,17 +865,18 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetParser() throws IOException {
|
/**
|
||||||
try {
|
* Resets the parser using the latest connection's reader. Resetting the parser is necessary
|
||||||
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
* when the plain connection has been secured or when a new opening stream element is going
|
||||||
} catch (XmlPullParserException e) {
|
* to be sent by the server.
|
||||||
throw new IOException(e);
|
*
|
||||||
}
|
* @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.
|
||||||
private void openStreamAndResetParser() throws IOException, NotConnectedException, InterruptedException {
|
*/
|
||||||
|
void openStream() throws SmackException, InterruptedException, XmlPullParserException {
|
||||||
sendStreamOpen();
|
sendStreamOpen();
|
||||||
resetParser();
|
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class PacketReader {
|
protected class PacketReader {
|
||||||
|
@ -920,7 +921,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
private void parsePackets() {
|
private void parsePackets() {
|
||||||
boolean initialStreamOpenSend = false;
|
boolean initialStreamOpenSend = false;
|
||||||
try {
|
try {
|
||||||
openStreamAndResetParser();
|
openStream();
|
||||||
initialStreamOpenSend = true;
|
initialStreamOpenSend = true;
|
||||||
XmlPullParser.Event eventType = parser.getEventType();
|
XmlPullParser.Event eventType = parser.getEventType();
|
||||||
while (!done) {
|
while (!done) {
|
||||||
|
@ -956,7 +957,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
// Secure the connection by negotiating TLS
|
// Secure the connection by negotiating TLS
|
||||||
proceedTLSReceived();
|
proceedTLSReceived();
|
||||||
// Send a new opening stream to the server
|
// Send a new opening stream to the server
|
||||||
openStreamAndResetParser();
|
openStream();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
SmackException.SmackWrappedException smackException = new SmackException.SmackWrappedException(e);
|
SmackException.SmackWrappedException smackException = new SmackException.SmackWrappedException(e);
|
||||||
|
@ -979,17 +980,35 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
compressSyncPoint.reportFailure(new SmackException.SmackMessageException(
|
compressSyncPoint.reportFailure(new SmackException.SmackMessageException(
|
||||||
"Could not establish compression"));
|
"Could not establish compression"));
|
||||||
break;
|
break;
|
||||||
default:
|
case SaslStreamElements.NAMESPACE:
|
||||||
parseAndProcessNonza(parser);
|
// 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;
|
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:
|
case Compressed.ELEMENT:
|
||||||
// Server confirmed that it's possible to use stream compression. Start
|
// Server confirmed that it's possible to use stream compression. Start
|
||||||
// stream compression
|
// stream compression
|
||||||
// Initialize the reader and writer with the new compressed version
|
// Initialize the reader and writer with the new compressed version
|
||||||
initReaderAndWriter();
|
initReaderAndWriter();
|
||||||
// Send a new opening stream to the server
|
// Send a new opening stream to the server
|
||||||
openStreamAndResetParser();
|
openStream();
|
||||||
// Notify that compression is being used
|
// Notify that compression is being used
|
||||||
compressSyncPoint.reportSuccess();
|
compressSyncPoint.reportSuccess();
|
||||||
break;
|
break;
|
||||||
|
@ -1071,7 +1090,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
parseAndProcessNonza(parser);
|
LOGGER.warning("Unknown top level stream element: " + name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1328,9 +1347,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
maybeAddToUnacknowledgedStanzas(packet);
|
maybeAddToUnacknowledgedStanzas(packet);
|
||||||
|
|
||||||
CharSequence elementXml = element.toXML(outgoingStreamXmlEnvironment);
|
CharSequence elementXml = element.toXML(StreamOpen.CLIENT_NAMESPACE);
|
||||||
if (elementXml instanceof XmlStringBuilder) {
|
if (elementXml instanceof XmlStringBuilder) {
|
||||||
((XmlStringBuilder) elementXml).write(writer, outgoingStreamXmlEnvironment);
|
((XmlStringBuilder) elementXml).write(writer, StreamOpen.CLIENT_NAMESPACE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
writer.write(elementXml.toString());
|
writer.write(elementXml.toString());
|
||||||
|
|
|
@ -509,7 +509,7 @@ public class XmppNioTcpConnection extends AbstractXmppNioConnection {
|
||||||
CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE);
|
CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE);
|
||||||
if (nextCharSequence instanceof XmlStringBuilder) {
|
if (nextCharSequence instanceof XmlStringBuilder) {
|
||||||
XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence;
|
XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence;
|
||||||
outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator();
|
outgoingCharSequenceIterator = xmlStringBuilder.getCharSequenceIterator();
|
||||||
} else {
|
} else {
|
||||||
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
|
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,7 @@ public class SmackXmlParser {
|
||||||
public static XmlPullParserFactory getXmlPullParserFactory() {
|
public static XmlPullParserFactory getXmlPullParserFactory() {
|
||||||
Iterator<XmlPullParserFactory> iterator = xmlPullParserFactoryServiceLoader.iterator();
|
Iterator<XmlPullParserFactory> iterator = xmlPullParserFactoryServiceLoader.iterator();
|
||||||
if (!iterator.hasNext()) {
|
if (!iterator.hasNext()) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException("Could not load a XmlPullParserFactory via Service Provider Interface (SPI)");
|
||||||
"No XmlPullParserFactory registered with Service Provider Interface (SPI). Is smack-xmlparser-xpp3 or smack-xmlparser-stax in classpath?");
|
|
||||||
}
|
}
|
||||||
return iterator.next();
|
return iterator.next();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue