mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-22 03:52:06 +01:00
Move SASL logic into AbstractXMPPConnection
Besides the way the transport handles the stream after SASL <success/>, the SASL logic is independend from the underlying transport (BOSH, TCP, …). Hence move it up into AbstractXMPPConnection. This also has the benefit that we can make some more methods private or package-private. Also introduce XmlStringBuilder.optTextChild(), which causes some associated changes.
This commit is contained in:
parent
c3247ef006
commit
eeb6c52f7e
23 changed files with 490 additions and 329 deletions
|
@ -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;
|
||||||
|
@ -237,7 +245,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
|
|
||||||
protected XmlEnvironment outgoingStreamXmlEnvironment;
|
protected XmlEnvironment outgoingStreamXmlEnvironment;
|
||||||
|
|
||||||
final Map<QName, NonzaCallback> nonzaCallbacks = new HashMap<>();
|
final MultiMap<QName, NonzaCallback> nonzaCallbacksMap = new MultiMap<>();
|
||||||
|
|
||||||
protected final Lock connectionLock = new ReentrantLock();
|
protected final Lock connectionLock = new ReentrantLock();
|
||||||
|
|
||||||
|
@ -308,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
|
||||||
|
@ -402,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);
|
||||||
|
@ -810,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1225,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);
|
||||||
|
@ -1232,22 +1299,27 @@ 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);
|
||||||
|
|
||||||
|
for (NonzaCallback nonzaCallback : nonzaCallbacks) {
|
||||||
nonzaCallback.onNonzaReceived(nonza);
|
nonzaCallback.onNonzaReceived(nonza);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void parseAndProcessStanza(XmlPullParser parser)
|
protected void parseAndProcessStanza(XmlPullParser parser)
|
||||||
throws XmlPullParserException, IOException, InterruptedException {
|
throws XmlPullParserException, IOException, InterruptedException {
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2018 Florian Schmaus
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Element;
|
|
||||||
|
|
||||||
public abstract class GenericElementListener<E extends Element> {
|
|
||||||
|
|
||||||
private final Class<? extends E> elementClass;
|
|
||||||
|
|
||||||
public GenericElementListener(Class<? extends E> elementClass) {
|
|
||||||
this.elementClass = elementClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void process(E element);
|
|
||||||
|
|
||||||
public final void processElement(Element element) {
|
|
||||||
E concreteEleement = elementClass.cast(element);
|
|
||||||
process(concreteEleement);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -16,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();
|
||||||
NonzaCallback installedCallback = connection.nonzaCallbacks.get(filterKey);
|
synchronized (connection.nonzaCallbacksMap) {
|
||||||
if (equals(installedCallback)) {
|
connection.nonzaCallbacksMap.removeOne(filterKey, this);
|
||||||
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()) {
|
||||||
connection.nonzaCallbacks.put(key, this);
|
synchronized (connection.nonzaCallbacksMap) {
|
||||||
|
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;
|
||||||
|
@ -180,7 +181,7 @@ 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 {
|
||||||
final SASLMechanism mechanism = selectMechanism(authzid);
|
final SASLMechanism mechanism = selectMechanism(authzid);
|
||||||
|
@ -216,12 +217,12 @@ public final class SASLAuthentication {
|
||||||
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,15 +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;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
currentMechanism.challengeReceived(challenge, finalChallenge);
|
mechanism = currentMechanism;
|
||||||
}
|
|
||||||
} catch (InterruptedException | SmackSaslException | NotConnectedException e) {
|
|
||||||
authenticationFailed(e);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
mechanism.challengeReceived(challenge, finalChallenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -253,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
|
||||||
|
@ -279,11 +279,15 @@ 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) {
|
||||||
// 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);
|
currentMechanism.setException(exception);
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ 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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -497,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));
|
||||||
|
@ -511,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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,10 +88,7 @@ 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.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;
|
||||||
|
@ -301,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,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
|
||||||
|
@ -856,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.
|
||||||
|
@ -864,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
|
|
||||||
* to be sent by the server.
|
|
||||||
*
|
|
||||||
* @throws SmackException if the parser could not be reset.
|
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
|
||||||
* @throws XmlPullParserException if an error in the XML parser occured.
|
|
||||||
*/
|
|
||||||
void openStream() throws SmackException, InterruptedException, XmlPullParserException {
|
|
||||||
sendStreamOpen();
|
|
||||||
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openStreamAndResetParser() throws IOException, NotConnectedException, InterruptedException {
|
||||||
|
sendStreamOpen();
|
||||||
|
resetParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class PacketReader {
|
protected class PacketReader {
|
||||||
|
@ -920,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) {
|
||||||
|
@ -956,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);
|
||||||
|
@ -979,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;
|
||||||
|
@ -1089,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;
|
||||||
|
|
Loading…
Reference in a new issue