mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-22 20:12:07 +01:00
Compare commits
16 commits
b83dacc60a
...
f0300dc906
Author | SHA1 | Date | |
---|---|---|---|
|
f0300dc906 | ||
|
35cf094386 | ||
|
eeb6c52f7e | ||
|
c3247ef006 | ||
|
cf48f12565 | ||
|
3ee4dd7b3a | ||
|
1d03cfdc79 | ||
|
93aaf6d8d7 | ||
|
bf538129c2 | ||
|
5b1d2664af | ||
|
be0830fc8f | ||
|
28ef30b4b3 | ||
|
b1a5509927 | ||
|
bd4b91fc26 | ||
|
002d060584 | ||
|
6d7b2b70e8 |
35 changed files with 662 additions and 428 deletions
|
@ -35,10 +35,6 @@
|
||||||
<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,6 +29,7 @@ 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;
|
||||||
|
@ -39,8 +40,6 @@ 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;
|
||||||
|
@ -218,7 +217,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
|
||||||
saslAuthentication.authenticate(username, password, config.getAuthzid(), null);
|
authenticate(username, password, config.getAuthzid(), null);
|
||||||
|
|
||||||
bindResourceAndEstablishSession(resource);
|
bindResourceAndEstablishSession(resource);
|
||||||
|
|
||||||
|
@ -395,6 +394,26 @@ 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.
|
||||||
|
@ -490,30 +509,9 @@ 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))) {
|
||||||
|
@ -522,6 +520,9 @@ 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,6 +60,7 @@ 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;
|
||||||
|
@ -78,6 +79,7 @@ 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;
|
||||||
|
@ -113,9 +115,14 @@ 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;
|
||||||
|
@ -127,6 +134,7 @@ 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;
|
||||||
|
@ -235,7 +243,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
|
|
||||||
private XmlEnvironment incomingStreamXmlEnvironment;
|
private XmlEnvironment incomingStreamXmlEnvironment;
|
||||||
|
|
||||||
final Map<QName, NonzaCallback> nonzaCallbacks = new HashMap<>();
|
protected XmlEnvironment outgoingStreamXmlEnvironment;
|
||||||
|
|
||||||
|
final MultiMap<QName, NonzaCallback> nonzaCallbacksMap = new MultiMap<>();
|
||||||
|
|
||||||
protected final Lock connectionLock = new ReentrantLock();
|
protected final Lock connectionLock = new ReentrantLock();
|
||||||
|
|
||||||
|
@ -306,7 +316,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.
|
||||||
*/
|
*/
|
||||||
protected final SASLAuthentication saslAuthentication;
|
private 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
|
||||||
|
@ -400,6 +410,26 @@ 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);
|
||||||
|
@ -498,7 +528,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
|
|
||||||
// Reset the connection state
|
// Reset the connection state
|
||||||
initState();
|
initState();
|
||||||
saslAuthentication.init();
|
|
||||||
streamId = null;
|
streamId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -809,14 +838,50 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the SASLAuthentication manager that is responsible for authenticating with
|
* Authenticate a connection.
|
||||||
* the server.
|
|
||||||
*
|
*
|
||||||
* @return the SASLAuthentication manager that is responsible for authenticating with
|
* @param username the username that is authenticating with the server.
|
||||||
* the server.
|
* @param password the password to send to the server.
|
||||||
|
* @param authzid the authorization identifier (typically null).
|
||||||
|
* @param sslSession the optional SSL/TLS session (if one was established)
|
||||||
|
* @return the used SASLMechanism.
|
||||||
|
* @throws XMPPErrorException if there was an XMPP error returned.
|
||||||
|
* @throws SASLErrorException if a SASL protocol error was returned.
|
||||||
|
* @throws IOException if an I/O error occured.
|
||||||
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
|
* @throws SmackSaslException if a SASL specific error occured.
|
||||||
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
|
* @throws NoResponseException if there was no response from the remote entity.
|
||||||
|
* @throws SmackWrappedException in case of an exception.
|
||||||
|
* @see SASLAuthentication#authenticate(String, String, EntityBareJid, SSLSession)
|
||||||
*/
|
*/
|
||||||
protected SASLAuthentication getSASLAuthentication() {
|
protected final SASLMechanism authenticate(String username, String password, EntityBareJid authzid,
|
||||||
return saslAuthentication;
|
SSLSession sslSession) throws XMPPErrorException, SASLErrorException, SmackSaslException,
|
||||||
|
NotConnectedException, NoResponseException, IOException, InterruptedException, SmackWrappedException {
|
||||||
|
SASLMechanism saslMechanism = saslAuthentication.authenticate(username, password, authzid, sslSession);
|
||||||
|
afterSaslAuthenticationSuccess();
|
||||||
|
return saslMechanism;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for subclasses right after successful SASL authentication. RFC 6120 § 6.4.6. specifies a that the initiating
|
||||||
|
* entity, needs to initiate a new stream in this case. But some transports, like BOSH, requires a special handling.
|
||||||
|
* <p>
|
||||||
|
* Note that we can not reset XMPPTCPConnection's parser here, because this method is invoked by the thread calling
|
||||||
|
* {@link #login()}, but the parser reset has to be done within the reader thread.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
|
* @throws SmackWrappedException in case of an exception.
|
||||||
|
*/
|
||||||
|
protected void afterSaslAuthenticationSuccess()
|
||||||
|
throws NotConnectedException, InterruptedException, SmackWrappedException {
|
||||||
|
sendStreamOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isSaslAuthenticated() {
|
||||||
|
return saslAuthentication.authenticationSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1224,6 +1289,9 @@ 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);
|
||||||
|
@ -1231,21 +1299,26 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
NonzaCallback nonzaCallback;
|
List<NonzaCallback> nonzaCallbacks;
|
||||||
synchronized (nonzaCallbacks) {
|
synchronized (nonzaCallbacksMap) {
|
||||||
nonzaCallback = nonzaCallbacks.get(key);
|
nonzaCallbacks = nonzaCallbacksMap.getAll(key);
|
||||||
|
nonzaCallbacks = CollectionUtil.newListWith(nonzaCallbacks);
|
||||||
}
|
}
|
||||||
if (nonzaCallback == null) {
|
if (nonzaCallbacks == 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);
|
||||||
|
|
||||||
nonzaCallback.onNonzaReceived(nonza);
|
for (NonzaCallback nonzaCallback : nonzaCallbacks) {
|
||||||
|
nonzaCallback.onNonzaReceived(nonza);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void parseAndProcessStanza(XmlPullParser parser)
|
protected void parseAndProcessStanza(XmlPullParser parser)
|
||||||
|
@ -2017,7 +2090,13 @@ 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 {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|
||||||
|
@ -30,7 +31,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, GenericElementListener<? extends Nonza>> filterAndListeners;
|
protected final Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners;
|
||||||
|
|
||||||
private NonzaCallback(Builder builder) {
|
private NonzaCallback(Builder builder) {
|
||||||
this.connection = builder.connection;
|
this.connection = builder.connection;
|
||||||
|
@ -38,21 +39,18 @@ public class NonzaCallback {
|
||||||
install();
|
install();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNonzaReceived(Nonza nonza) {
|
void onNonzaReceived(Nonza nonza) throws IOException {
|
||||||
QName key = nonza.getQName();
|
QName key = nonza.getQName();
|
||||||
GenericElementListener<? extends Nonza> nonzaListener = filterAndListeners.get(key);
|
ClassAndConsumer<? extends Nonza> classAndConsumer = filterAndListeners.get(key);
|
||||||
|
|
||||||
nonzaListener.processElement(nonza);
|
classAndConsumer.accept(nonza);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
synchronized (connection.nonzaCallbacks) {
|
for (Map.Entry<QName, ClassAndConsumer<? extends Nonza>> entry : filterAndListeners.entrySet()) {
|
||||||
for (Map.Entry<QName, GenericElementListener<? extends Nonza>> entry : filterAndListeners.entrySet()) {
|
QName filterKey = entry.getKey();
|
||||||
QName filterKey = entry.getKey();
|
synchronized (connection.nonzaCallbacksMap) {
|
||||||
NonzaCallback installedCallback = connection.nonzaCallbacks.get(filterKey);
|
connection.nonzaCallbacksMap.removeOne(filterKey, this);
|
||||||
if (equals(installedCallback)) {
|
|
||||||
connection.nonzaCallbacks.remove(filterKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,9 +60,9 @@ public class NonzaCallback {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (connection.nonzaCallbacks) {
|
for (QName key : filterAndListeners.keySet()) {
|
||||||
for (QName key : filterAndListeners.keySet()) {
|
synchronized (connection.nonzaCallbacksMap) {
|
||||||
connection.nonzaCallbacks.put(key, this);
|
connection.nonzaCallbacksMap.put(key, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,31 +72,35 @@ public class NonzaCallback {
|
||||||
private SN successNonza;
|
private SN successNonza;
|
||||||
private FN failedNonza;
|
private FN failedNonza;
|
||||||
|
|
||||||
private NonzaResponseCallback(Class<? extends SN> successNonzaClass, Class<? extends FN> failedNonzaClass,
|
private NonzaResponseCallback(Class<SN> successNonzaClass, Class<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 GenericElementListener<SN> successListener = new GenericElementListener<SN>(successNonzaClass) {
|
final NonzaListener<SN> successListener = new NonzaListener<SN>() {
|
||||||
@Override
|
@Override
|
||||||
public void process(SN successNonza) {
|
public void accept(SN successNonza) {
|
||||||
NonzaResponseCallback.this.successNonza = successNonza;
|
NonzaResponseCallback.this.successNonza = successNonza;
|
||||||
notifyResponse();
|
notifyResponse();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
final ClassAndConsumer<SN> successClassAndConsumer = new ClassAndConsumer<>(successNonzaClass,
|
||||||
|
successListener);
|
||||||
|
|
||||||
final GenericElementListener<FN> failedListener = new GenericElementListener<FN>(failedNonzaClass) {
|
final NonzaListener<FN> failedListener = new NonzaListener<FN>() {
|
||||||
@Override
|
@Override
|
||||||
public void process(FN failedNonza) {
|
public void accept(FN failedNonza) {
|
||||||
NonzaResponseCallback.this.failedNonza = failedNonza;
|
NonzaResponseCallback.this.failedNonza = failedNonza;
|
||||||
notifyResponse();
|
notifyResponse();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
final ClassAndConsumer<FN> failedClassAndConsumer = new ClassAndConsumer<>(failedNonzaClass,
|
||||||
|
failedListener);
|
||||||
|
|
||||||
filterAndListeners.put(successNonzaKey, successListener);
|
filterAndListeners.put(successNonzaKey, successClassAndConsumer);
|
||||||
filterAndListeners.put(failedNonzaKey, failedListener);
|
filterAndListeners.put(failedNonzaKey, failedClassAndConsumer);
|
||||||
|
|
||||||
install();
|
install();
|
||||||
}
|
}
|
||||||
|
@ -139,15 +141,16 @@ public class NonzaCallback {
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private final AbstractXMPPConnection connection;
|
private final AbstractXMPPConnection connection;
|
||||||
|
|
||||||
private Map<QName, GenericElementListener<? extends Nonza>> filterAndListeners = new HashMap<>();
|
private Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners = new HashMap<>();
|
||||||
|
|
||||||
Builder(AbstractXMPPConnection connection) {
|
Builder(AbstractXMPPConnection connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <N extends Nonza> Builder listenFor(Class<? extends N> nonza, GenericElementListener<? extends N> nonzaListener) {
|
public <N extends Nonza> Builder listenFor(Class<N> nonza, NonzaListener<N> nonzaListener) {
|
||||||
QName key = XmppElementUtil.getQNameFor(nonza);
|
QName key = XmppElementUtil.getQNameFor(nonza);
|
||||||
filterAndListeners.put(key, nonzaListener);
|
ClassAndConsumer<N> classAndConsumer = new ClassAndConsumer<>(nonza, nonzaListener);
|
||||||
|
filterAndListeners.put(key, classAndConsumer);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +159,25 @@ 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,8 +39,9 @@ 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.SaslStreamElements.SASLFailure;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
|
||||||
|
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;
|
||||||
|
@ -154,20 +155,9 @@ 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,23 +181,26 @@ 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.
|
||||||
*/
|
*/
|
||||||
public SASLMechanism authenticate(String username, String password, EntityBareJid authzid, SSLSession sslSession)
|
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 {
|
||||||
currentMechanism = selectMechanism(authzid);
|
final SASLMechanism mechanism = 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 (!authenticationSuccessful && saslException == null) {
|
while (!mechanism.isFinished()) {
|
||||||
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
|
||||||
|
@ -215,35 +208,21 @@ public final class SASLAuthentication {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saslException != null) {
|
mechanism.throwExceptionIfRequired();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!authenticationSuccessful) {
|
return mechanism;
|
||||||
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 a base64 encoded string representing the challenge.
|
* @param challenge the challenge Nonza.
|
||||||
* @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.
|
||||||
*/
|
*/
|
||||||
public void challengeReceived(String challenge) throws SmackException, InterruptedException {
|
void challengeReceived(SaslNonza.Challenge challenge) throws SmackException, InterruptedException {
|
||||||
challengeReceived(challenge, false);
|
challengeReceived(challenge.getData(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -258,13 +237,12 @@ 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.
|
||||||
*/
|
*/
|
||||||
public void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
|
private void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
|
||||||
try {
|
SASLMechanism mechanism;
|
||||||
currentMechanism.challengeReceived(challenge, finalChallenge);
|
synchronized (this) {
|
||||||
} catch (InterruptedException | SmackSaslException | NotConnectedException e) {
|
mechanism = currentMechanism;
|
||||||
authenticationFailed(e);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
mechanism.challengeReceived(challenge, finalChallenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -273,8 +251,10 @@ 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.
|
||||||
*/
|
*/
|
||||||
public void authenticated(Success success) throws SmackException, InterruptedException {
|
void authenticated(Success success) throws InterruptedException, SmackSaslException, NotConnectedException {
|
||||||
// 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
|
||||||
|
@ -283,10 +263,11 @@ 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,30 +279,29 @@ 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>
|
||||||
*/
|
*/
|
||||||
public void authenticationFailed(SASLFailure saslFailure) {
|
void authenticationFailed(SASLFailure saslFailure) {
|
||||||
authenticationFailed(new SASLErrorException(currentMechanism.getName(), saslFailure));
|
SASLErrorException saslErrorException;
|
||||||
|
synchronized (this) {
|
||||||
|
saslErrorException = new SASLErrorException(currentMechanism.getName(), saslFailure);
|
||||||
|
}
|
||||||
|
authenticationFailed(saslErrorException);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticationFailed(Exception exception) {
|
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() {
|
||||||
return authenticationSuccessful;
|
synchronized (this) {
|
||||||
}
|
if (currentMechanism == null) {
|
||||||
|
return false;
|
||||||
/**
|
}
|
||||||
* Initializes the internal state in order to be able to be reused. The authentication
|
return currentMechanism.isAuthenticationSuccessful();
|
||||||
* 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,30 +108,6 @@ 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,6 +37,9 @@ 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;
|
||||||
|
@ -126,6 +129,9 @@ 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,8 +53,6 @@ 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;
|
||||||
|
@ -335,19 +333,6 @@ 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;
|
||||||
|
@ -613,7 +598,7 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
|
||||||
prepareToWaitForFeaturesReceived();
|
prepareToWaitForFeaturesReceived();
|
||||||
|
|
||||||
LoginContext loginContext = walkStateGraphContext.loginContext;
|
LoginContext loginContext = walkStateGraphContext.loginContext;
|
||||||
SASLMechanism usedSaslMechanism = saslAuthentication.authenticate(loginContext.username, loginContext.password, config.getAuthzid(), getSSLSession());
|
SASLMechanism usedSaslMechanism = 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,6 +31,7 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -330,7 +331,8 @@ public class StateDescriptorGraph {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <E> void dfsVisit(GraphVertex<E> vertex, DfsFinishedVertex<E> dfsFinishedVertex, DfsEdgeFound<E> dfsEdgeFound) {
|
private static <E> void dfsVisit(GraphVertex<E> vertex, Consumer<GraphVertex<E>> dfsFinishedVertex,
|
||||||
|
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();
|
||||||
|
@ -349,11 +351,12 @@ public class StateDescriptorGraph {
|
||||||
|
|
||||||
vertex.color = GraphVertex.VertexColor.black;
|
vertex.color = GraphVertex.VertexColor.black;
|
||||||
if (dfsFinishedVertex != null) {
|
if (dfsFinishedVertex != null) {
|
||||||
dfsFinishedVertex.onVertexFinished(vertex);
|
dfsFinishedVertex.accept(vertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <E> void dfs(Collection<GraphVertex<E>> vertexes, DfsFinishedVertex<E> dfsFinishedVertex, DfsEdgeFound<E> dfsEdgeFound) {
|
private static <E> void dfs(Collection<GraphVertex<E>> vertexes, Consumer<GraphVertex<E>> dfsFinishedVertex,
|
||||||
|
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);
|
||||||
|
@ -407,12 +410,6 @@ 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,6 +163,12 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.SaslStreamElements.SASLFailure;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
|
||||||
|
|
||||||
public class SASLErrorException extends XMPPException {
|
public class SASLErrorException extends XMPPException {
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,12 @@ 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.SaslStreamElements.AuthMechanism;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza.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;
|
||||||
|
|
||||||
|
@ -56,6 +57,17 @@ 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;
|
||||||
|
@ -275,7 +287,18 @@ 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();
|
||||||
|
@ -288,6 +311,39 @@ 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-2018 Florian Schmaus
|
* Copyright 2014-2019 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,21 +18,30 @@ 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 class SaslStreamElements {
|
public interface SaslNonza extends Nonza {
|
||||||
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
|
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.
|
||||||
*/
|
*/
|
||||||
public static class AuthMechanism implements Nonza {
|
class AuthMechanism implements SaslNonza {
|
||||||
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;
|
||||||
|
@ -44,11 +53,11 @@ public class SaslStreamElements {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder();
|
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).attribute("mechanism", mechanism).rightAngleBracket();
|
xml.attribute("mechanism", mechanism).rightAngleBracket();
|
||||||
xml.optAppend(authenticationText);
|
xml.escape(authenticationText);
|
||||||
xml.closeElement(ELEMENT);
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,11 +69,6 @@ public class SaslStreamElements {
|
||||||
return authenticationText;
|
return authenticationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNamespace() {
|
|
||||||
return NAMESPACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getElementName() {
|
public String getElementName() {
|
||||||
return ELEMENT;
|
return ELEMENT;
|
||||||
|
@ -74,8 +78,9 @@ public class SaslStreamElements {
|
||||||
/**
|
/**
|
||||||
* A SASL challenge stream element.
|
* A SASL challenge stream element.
|
||||||
*/
|
*/
|
||||||
public static class Challenge implements Nonza {
|
class Challenge implements SaslNonza {
|
||||||
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;
|
||||||
|
|
||||||
|
@ -83,18 +88,15 @@ public class SaslStreamElements {
|
||||||
this.data = StringUtils.returnIfNotEmptyTrimmed(data);
|
this.data = StringUtils.returnIfNotEmptyTrimmed(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getData() {
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
return data;
|
||||||
XmlStringBuilder xml = new XmlStringBuilder().halfOpenElement(ELEMENT).xmlnsAttribute(
|
|
||||||
NAMESPACE).rightAngleBracket();
|
|
||||||
xml.optAppend(data);
|
|
||||||
xml.closeElement(ELEMENT);
|
|
||||||
return xml;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
return NAMESPACE;
|
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||||
|
xml.optTextChild(data, this);
|
||||||
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -106,8 +108,9 @@ public class SaslStreamElements {
|
||||||
/**
|
/**
|
||||||
* A SASL response stream element.
|
* A SASL response stream element.
|
||||||
*/
|
*/
|
||||||
public static class Response implements Nonza {
|
class Response implements SaslNonza {
|
||||||
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;
|
||||||
|
|
||||||
|
@ -120,11 +123,9 @@ public class SaslStreamElements {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder();
|
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
xml.optTextChild(authenticationText, this);
|
||||||
xml.optAppend(authenticationText);
|
|
||||||
xml.closeElement(ELEMENT);
|
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,11 +133,6 @@ public class SaslStreamElements {
|
||||||
return authenticationText;
|
return authenticationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNamespace() {
|
|
||||||
return NAMESPACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getElementName() {
|
public String getElementName() {
|
||||||
return ELEMENT;
|
return ELEMENT;
|
||||||
|
@ -146,8 +142,9 @@ public class SaslStreamElements {
|
||||||
/**
|
/**
|
||||||
* A SASL success stream element.
|
* A SASL success stream element.
|
||||||
*/
|
*/
|
||||||
public static class Success implements Nonza {
|
class Success implements SaslNonza {
|
||||||
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;
|
||||||
|
|
||||||
|
@ -171,19 +168,12 @@ public class SaslStreamElements {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder();
|
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
xml.optTextChild(data, this);
|
||||||
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;
|
||||||
|
@ -194,8 +184,9 @@ public class SaslStreamElements {
|
||||||
* 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>
|
||||||
*/
|
*/
|
||||||
public static class SASLFailure extends AbstractError implements Nonza {
|
class SASLFailure extends AbstractError implements SaslNonza {
|
||||||
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;
|
||||||
|
@ -250,11 +241,6 @@ public class SaslStreamElements {
|
||||||
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,8 +51,9 @@ public class CollectionUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ArrayList<T> newListWith(Collection<? extends T> collection) {
|
public static <T> ArrayList<T> newListWith(Collection<? extends T> collection) {
|
||||||
ArrayList<T> arrayList = new ArrayList<>(collection.size());
|
if (collection == null) {
|
||||||
arrayList.addAll(collection);
|
return null;
|
||||||
return arrayList;
|
}
|
||||||
|
return new ArrayList<>(collection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2018 Florian Schmaus
|
* Copyright 2019 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,23 +14,11 @@
|
||||||
* 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;
|
package org.jivesoftware.smack.util;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Element;
|
// TODO: Replace with java.util.function.Consumer once Smack's minimum Android SDK level is 24 or higher.
|
||||||
|
public interface Consumer<T> {
|
||||||
|
|
||||||
public abstract class GenericElementListener<E extends Element> {
|
void accept(T t);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -49,7 +49,6 @@ 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;
|
||||||
|
@ -686,44 +685,6 @@ 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,9 +18,10 @@ 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.Iterator;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Element;
|
import org.jivesoftware.smack.packet.Element;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
@ -496,13 +497,6 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmlStringBuilder optAppend(CharSequence csq) {
|
|
||||||
if (csq != null) {
|
|
||||||
append(csq);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public XmlStringBuilder optAppend(Element element) {
|
public XmlStringBuilder optAppend(Element element) {
|
||||||
if (element != null) {
|
if (element != null) {
|
||||||
append(element.toXML(effectiveXmlEnvironment));
|
append(element.toXML(effectiveXmlEnvironment));
|
||||||
|
@ -510,6 +504,16 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder optTextChild(CharSequence sqc, NamedElement parentElement) {
|
||||||
|
if (sqc == null) {
|
||||||
|
return closeEmptyElement();
|
||||||
|
}
|
||||||
|
rightAngleBracket();
|
||||||
|
escape(sqc);
|
||||||
|
closeElement(parentElement);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public XmlStringBuilder append(XmlStringBuilder xsb) {
|
public XmlStringBuilder append(XmlStringBuilder xsb) {
|
||||||
assert xsb != null;
|
assert xsb != null;
|
||||||
sb.append(xsb.sb);
|
sb.append(xsb.sb);
|
||||||
|
@ -606,24 +610,46 @@ 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 enclosingNamespace the enclosing XML namespace.
|
* @param enclosingXmlEnvironment the enclosing XML environment.
|
||||||
* @throws IOException if an I/O error occured.
|
* @throws IOException if an I/O error occured.
|
||||||
*/
|
*/
|
||||||
public void write(Writer writer, String enclosingNamespace) throws IOException {
|
public void write(Writer writer, XmlEnvironment enclosingXmlEnvironment) throws IOException {
|
||||||
XmlEnvironment enclosingXmlEnvironment = XmlEnvironment.builder()
|
try {
|
||||||
.withNamespace(enclosingNamespace)
|
appendXmlTo(csq -> {
|
||||||
.build();
|
try {
|
||||||
appendXmlTo(writer, enclosingXmlEnvironment);
|
writer.append(csq);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new WrappedIoException(e);
|
||||||
|
}
|
||||||
|
}, enclosingXmlEnvironment);
|
||||||
|
} catch (WrappedIoException e) {
|
||||||
|
throw e.wrappedIoException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<CharSequence> getCharSequenceIterator() {
|
public List<CharSequence> toList(XmlEnvironment enclosingXmlEnvironment) {
|
||||||
return sb.getAsList().iterator();
|
List<CharSequence> res = new ArrayList<>(sb.getAsList().size());
|
||||||
|
|
||||||
|
appendXmlTo(csq -> res.add(csq), enclosingXmlEnvironment);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -631,29 +657,26 @@ 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(res, enclosingXmlEnvironment);
|
appendXmlTo(csq -> res.append(csq), enclosingXmlEnvironment);
|
||||||
} catch (IOException e) {
|
|
||||||
// Should never happen.
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendXmlTo(Appendable appendable, XmlEnvironment enclosingXmlEnvironment) throws IOException {
|
private void appendXmlTo(Consumer<CharSequence> charSequenceSink, XmlEnvironment enclosingXmlEnvironment) {
|
||||||
for (CharSequence csq : sb.getAsList()) {
|
for (CharSequence csq : sb.getAsList()) {
|
||||||
if (csq instanceof XmlStringBuilder) {
|
if (csq instanceof XmlStringBuilder) {
|
||||||
((XmlStringBuilder) csq).appendXmlTo(appendable, enclosingXmlEnvironment);
|
((XmlStringBuilder) csq).appendXmlTo(charSequenceSink, 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())) {
|
||||||
appendable.append(xmlNsAttribute);
|
charSequenceSink.accept(xmlNsAttribute);
|
||||||
enclosingXmlEnvironment = new XmlEnvironment(xmlNsAttribute.value);
|
enclosingXmlEnvironment = new XmlEnvironment(xmlNsAttribute.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
appendable.append(csq);
|
charSequenceSink.accept(csq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.SaslStreamElements.AuthMechanism;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
|
||||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza.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,10 +125,19 @@ 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
|
|
||||||
// requested provider.
|
try {
|
||||||
|
provider = (P) providerClass.getDeclaredField("INSTANCE").get(null);
|
||||||
|
return provider;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// Continue with the next approach.
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
provider = providerClass.getDeclaredConstructor().newInstance();
|
provider = providerClass.getDeclaredConstructor().newInstance();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,6 @@ 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;
|
||||||
|
@ -814,41 +811,6 @@ 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,9 +225,7 @@ 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.rightAngleBracket();
|
xml.optTextChild(text, this);
|
||||||
xml.optAppend(text);
|
|
||||||
xml.closeElement(this);
|
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,9 +267,7 @@ 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.rightAngleBracket();
|
xml.optTextChild(text, this);
|
||||||
xml.optAppend(text);
|
|
||||||
xml.closeElement(this);
|
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,9 +309,7 @@ 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.rightAngleBracket();
|
xml.optTextChild(text, this);
|
||||||
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'></delay>" + "<message "
|
+ "<delay xmlns='urn:xmpp:delay' stamp='2002-10-13T23:58:37.000+00:00'/>" + "<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,9 +106,7 @@ 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.rightAngleBracket();
|
xml.optTextChild(reason, this);
|
||||||
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.optAppend(file.toXML());
|
buf.optElement(file);
|
||||||
break;
|
break;
|
||||||
case result:
|
case result:
|
||||||
buf.rightAngleBracket();
|
buf.rightAngleBracket();
|
||||||
|
|
|
@ -8,6 +8,7 @@ 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,11 +29,6 @@ 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,12 +1,21 @@
|
||||||
.PHONY := clean generate
|
.PHONY := all clean
|
||||||
|
|
||||||
GRADLE_QUITE_ARGS := --quiet --console plain
|
GRADLE_QUITE_ARGS := --quiet --console plain
|
||||||
|
|
||||||
GENERATED_FILES := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
|
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
|
||||||
generate: $(GENERATED_FILES)
|
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG:.png=.dot)
|
||||||
|
|
||||||
|
GENERATED_FILES := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG) $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT)
|
||||||
|
|
||||||
|
all: $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(GENERATED_FILES)
|
rm -f $(GENERATED_FILES)
|
||||||
|
|
||||||
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
|
%.png: %.dot
|
||||||
gradle $(GRADLE_QUITE_ARGS) :smack-repl:printXmppNioTcpConnectionStateGraph | dot -Tpng -o $@
|
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 +1,2 @@
|
||||||
/XmppNioTcpConnectionStateGraph.png
|
/XmppNioTcpConnectionStateGraph.png
|
||||||
|
/XmppNioTcpConnectionStateGraph.dot
|
||||||
|
|
|
@ -87,12 +87,8 @@ 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.SaslStreamElements;
|
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
||||||
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;
|
||||||
|
@ -302,6 +298,10 @@ 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;
|
||||||
saslAuthentication.authenticate(username, password, config.getAuthzid(), sslSession);
|
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 (getSASLAuthentication().authenticationSuccessful()) {
|
if (isSaslAuthenticated()) {
|
||||||
// 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,18 +865,17 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void resetParser() throws IOException {
|
||||||
* Resets the parser using the latest connection's reader. Resetting the parser is necessary
|
try {
|
||||||
* when the plain connection has been secured or when a new opening stream element is going
|
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
||||||
* to be sent by the server.
|
} catch (XmlPullParserException e) {
|
||||||
*
|
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();
|
||||||
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
resetParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class PacketReader {
|
protected class PacketReader {
|
||||||
|
@ -921,7 +920,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
private void parsePackets() {
|
private void parsePackets() {
|
||||||
boolean initialStreamOpenSend = false;
|
boolean initialStreamOpenSend = false;
|
||||||
try {
|
try {
|
||||||
openStream();
|
openStreamAndResetParser();
|
||||||
initialStreamOpenSend = true;
|
initialStreamOpenSend = true;
|
||||||
XmlPullParser.Event eventType = parser.getEventType();
|
XmlPullParser.Event eventType = parser.getEventType();
|
||||||
while (!done) {
|
while (!done) {
|
||||||
|
@ -957,7 +956,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
|
||||||
openStream();
|
openStreamAndResetParser();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
SmackException.SmackWrappedException smackException = new SmackException.SmackWrappedException(e);
|
SmackException.SmackWrappedException smackException = new SmackException.SmackWrappedException(e);
|
||||||
|
@ -980,35 +979,17 @@ 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;
|
||||||
case SaslStreamElements.NAMESPACE:
|
default:
|
||||||
// SASL authentication has failed. The server may close the connection
|
parseAndProcessNonza(parser);
|
||||||
// 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
|
||||||
openStream();
|
openStreamAndResetParser();
|
||||||
// Notify that compression is being used
|
// Notify that compression is being used
|
||||||
compressSyncPoint.reportSuccess();
|
compressSyncPoint.reportSuccess();
|
||||||
break;
|
break;
|
||||||
|
@ -1090,7 +1071,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGGER.warning("Unknown top level stream element: " + name);
|
parseAndProcessNonza(parser);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1347,9 +1328,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
maybeAddToUnacknowledgedStanzas(packet);
|
maybeAddToUnacknowledgedStanzas(packet);
|
||||||
|
|
||||||
CharSequence elementXml = element.toXML(StreamOpen.CLIENT_NAMESPACE);
|
CharSequence elementXml = element.toXML(outgoingStreamXmlEnvironment);
|
||||||
if (elementXml instanceof XmlStringBuilder) {
|
if (elementXml instanceof XmlStringBuilder) {
|
||||||
((XmlStringBuilder) elementXml).write(writer, StreamOpen.CLIENT_NAMESPACE);
|
((XmlStringBuilder) elementXml).write(writer, outgoingStreamXmlEnvironment);
|
||||||
}
|
}
|
||||||
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.getCharSequenceIterator();
|
outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator();
|
||||||
} else {
|
} else {
|
||||||
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
|
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ 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("Could not load a XmlPullParserFactory via Service Provider Interface (SPI)");
|
throw new IllegalStateException(
|
||||||
|
"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