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="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 name="RegexpSingleline">
|
||||
<property name="format" value="^\s+$"/>
|
||||
<property name="message" value="Line containing only whitespace character(s)"/>
|
||||
</module>
|
||||
<module name="FileTabCharacter"/>
|
||||
<module name="RegexpSingleline">
|
||||
<!--
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.jivesoftware.smack.AbstractXMPPConnection;
|
|||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.SmackException.ConnectionException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.SmackWrappedException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.XMPPException.StreamErrorException;
|
||||
|
@ -39,8 +40,6 @@ import org.jivesoftware.smack.packet.Nonza;
|
|||
import org.jivesoftware.smack.packet.Presence;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smack.packet.StanzaError;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||
import org.jivesoftware.smack.util.CloseableUtil;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
|
@ -218,7 +217,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
|||
protected void loginInternal(String username, String password, Resourcepart resource) throws XMPPException,
|
||||
SmackException, IOException, InterruptedException {
|
||||
// Authenticate using SASL
|
||||
saslAuthentication.authenticate(username, password, config.getAuthzid(), null);
|
||||
authenticate(username, password, config.getAuthzid(), null);
|
||||
|
||||
bindResourceAndEstablishSession(resource);
|
||||
|
||||
|
@ -395,6 +394,26 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
|||
readerConsumer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterSaslAuthenticationSuccess()
|
||||
throws NotConnectedException, InterruptedException, SmackWrappedException {
|
||||
// XMPP over BOSH is unusual when it comes to SASL authentication: Instead of sending a new stream open, it
|
||||
// requires a special XML element ot be send after successful SASL authentication.
|
||||
// See XEP-0206 § 5., especially the following is example 5 of XEP-0206.
|
||||
ComposableBody composeableBody = ComposableBody.builder().setNamespaceDefinition("xmpp",
|
||||
XMPPBOSHConnection.XMPP_BOSH_NS).setAttribute(
|
||||
BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart",
|
||||
"xmpp"), "true").setAttribute(
|
||||
BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), getXMPPServiceDomain().toString()).build();
|
||||
|
||||
try {
|
||||
send(composeableBody);
|
||||
} catch (BOSHException e) {
|
||||
// jbosh's exception API does not really match the one of Smack.
|
||||
throw new SmackException.SmackWrappedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener class which listen for a successfully established connection
|
||||
* and connection errors and notifies the BOSHConnection.
|
||||
|
@ -490,30 +509,9 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
|||
case Presence.ELEMENT:
|
||||
parseAndProcessStanza(parser);
|
||||
break;
|
||||
case "challenge":
|
||||
// The server is challenging the SASL authentication
|
||||
// made by the client
|
||||
final String challengeData = parser.nextText();
|
||||
getSASLAuthentication().challengeReceived(challengeData);
|
||||
break;
|
||||
case "success":
|
||||
send(ComposableBody.builder().setNamespaceDefinition("xmpp",
|
||||
XMPPBOSHConnection.XMPP_BOSH_NS).setAttribute(
|
||||
BodyQName.createWithPrefix(XMPPBOSHConnection.XMPP_BOSH_NS, "restart",
|
||||
"xmpp"), "true").setAttribute(
|
||||
BodyQName.create(XMPPBOSHConnection.BOSH_URI, "to"), getXMPPServiceDomain().toString()).build());
|
||||
Success success = new Success(parser.nextText());
|
||||
getSASLAuthentication().authenticated(success);
|
||||
break;
|
||||
case "features":
|
||||
parseFeatures(parser);
|
||||
break;
|
||||
case "failure":
|
||||
if ("urn:ietf:params:xml:ns:xmpp-sasl".equals(parser.getNamespace(null))) {
|
||||
final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser);
|
||||
getSASLAuthentication().authenticationFailed(failure);
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
// Some BOSH error isn't stream error.
|
||||
if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) {
|
||||
|
@ -522,6 +520,9 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
|||
StanzaError.Builder builder = PacketParserUtils.parseError(parser);
|
||||
throw new XMPPException.XMPPErrorException(null, builder.build());
|
||||
}
|
||||
default:
|
||||
parseAndProcessNonza(parser);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -60,6 +60,7 @@ import java.util.logging.Logger;
|
|||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.security.auth.callback.Callback;
|
||||
|
@ -78,6 +79,7 @@ import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
|||
import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException;
|
||||
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
|
||||
import org.jivesoftware.smack.SmackException.SmackSaslException;
|
||||
import org.jivesoftware.smack.SmackException.SmackWrappedException;
|
||||
import org.jivesoftware.smack.SmackFuture.InternalSmackFuture;
|
||||
import org.jivesoftware.smack.XMPPException.FailedNonzaException;
|
||||
|
@ -113,9 +115,14 @@ import org.jivesoftware.smack.parsing.SmackParsingException;
|
|||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||
import org.jivesoftware.smack.provider.NonzaProvider;
|
||||
import org.jivesoftware.smack.provider.ProviderManager;
|
||||
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
||||
import org.jivesoftware.smack.util.Async;
|
||||
import org.jivesoftware.smack.util.CollectionUtil;
|
||||
import org.jivesoftware.smack.util.DNSUtil;
|
||||
import org.jivesoftware.smack.util.MultiMap;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jivesoftware.smack.util.ParserUtils;
|
||||
|
@ -127,6 +134,7 @@ import org.jivesoftware.smack.xml.XmlPullParser;
|
|||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||
|
||||
import org.jxmpp.jid.DomainBareJid;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.EntityFullJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
|
@ -235,7 +243,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
|
||||
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();
|
||||
|
||||
|
@ -306,7 +316,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
/**
|
||||
* 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
|
||||
|
@ -400,6 +410,26 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
protected AbstractXMPPConnection(ConnectionConfiguration configuration) {
|
||||
saslAuthentication = new SASLAuthentication(this, configuration);
|
||||
config = configuration;
|
||||
|
||||
// Install the SASL Nonza callbacks.
|
||||
buildNonzaCallback()
|
||||
.listenFor(SaslNonza.Challenge.class, c -> {
|
||||
try {
|
||||
saslAuthentication.challengeReceived(c);
|
||||
} catch (SmackException | InterruptedException e) {
|
||||
saslAuthentication.authenticationFailed(e);
|
||||
}
|
||||
})
|
||||
.listenFor(SaslNonza.Success.class, s -> {
|
||||
try {
|
||||
saslAuthentication.authenticated(s);
|
||||
} catch (SmackSaslException | NotConnectedException | InterruptedException e) {
|
||||
saslAuthentication.authenticationFailed(e);
|
||||
}
|
||||
})
|
||||
.listenFor(SaslNonza.SASLFailure.class, f -> saslAuthentication.authenticationFailed(f))
|
||||
.install();
|
||||
|
||||
SmackDebuggerFactory debuggerFactory = configuration.getDebuggerFactory();
|
||||
if (debuggerFactory != null) {
|
||||
debugger = debuggerFactory.create(this);
|
||||
|
@ -498,7 +528,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
|
||||
// Reset the connection state
|
||||
initState();
|
||||
saslAuthentication.init();
|
||||
streamId = null;
|
||||
|
||||
try {
|
||||
|
@ -809,14 +838,50 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the SASLAuthentication manager that is responsible for authenticating with
|
||||
* the server.
|
||||
* Authenticate a connection.
|
||||
*
|
||||
* @return the SASLAuthentication manager that is responsible for authenticating with
|
||||
* the server.
|
||||
* @param username the username that is authenticating with the server.
|
||||
* @param password the password to send to the server.
|
||||
* @param authzid the authorization identifier (typically null).
|
||||
* @param sslSession the optional SSL/TLS session (if one was established)
|
||||
* @return the used SASLMechanism.
|
||||
* @throws XMPPErrorException if there was an XMPP error returned.
|
||||
* @throws SASLErrorException if a SASL protocol error was returned.
|
||||
* @throws IOException if an I/O error occured.
|
||||
* @throws InterruptedException if the calling thread was interrupted.
|
||||
* @throws SmackSaslException if a SASL specific error occured.
|
||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||
* @throws NoResponseException if there was no response from the remote entity.
|
||||
* @throws SmackWrappedException in case of an exception.
|
||||
* @see SASLAuthentication#authenticate(String, String, EntityBareJid, SSLSession)
|
||||
*/
|
||||
protected SASLAuthentication getSASLAuthentication() {
|
||||
return saslAuthentication;
|
||||
protected final SASLMechanism authenticate(String username, String password, EntityBareJid authzid,
|
||||
SSLSession sslSession) throws XMPPErrorException, SASLErrorException, SmackSaslException,
|
||||
NotConnectedException, NoResponseException, IOException, InterruptedException, SmackWrappedException {
|
||||
SASLMechanism saslMechanism = saslAuthentication.authenticate(username, password, authzid, sslSession);
|
||||
afterSaslAuthenticationSuccess();
|
||||
return saslMechanism;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for subclasses right after successful SASL authentication. RFC 6120 § 6.4.6. specifies a that the initiating
|
||||
* entity, needs to initiate a new stream in this case. But some transports, like BOSH, requires a special handling.
|
||||
* <p>
|
||||
* Note that we can not reset XMPPTCPConnection's parser here, because this method is invoked by the thread calling
|
||||
* {@link #login()}, but the parser reset has to be done within the reader thread.
|
||||
* </p>
|
||||
*
|
||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||
* @throws InterruptedException if the calling thread was interrupted.
|
||||
* @throws SmackWrappedException in case of an exception.
|
||||
*/
|
||||
protected void afterSaslAuthenticationSuccess()
|
||||
throws NotConnectedException, InterruptedException, SmackWrappedException {
|
||||
sendStreamOpen();
|
||||
}
|
||||
|
||||
protected final boolean isSaslAuthenticated() {
|
||||
return saslAuthentication.authenticationSuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1224,6 +1289,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
}
|
||||
|
||||
protected final void parseAndProcessNonza(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
|
||||
ParserUtils.assertAtStartTag(parser);
|
||||
|
||||
final int initialDepth = parser.getDepth();
|
||||
final String element = parser.getName();
|
||||
final String namespace = parser.getNamespace();
|
||||
final QName key = new QName(namespace, element);
|
||||
|
@ -1231,21 +1299,26 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
NonzaProvider<? extends Nonza> nonzaProvider = ProviderManager.getNonzaProvider(key);
|
||||
if (nonzaProvider == null) {
|
||||
LOGGER.severe("Unknown nonza: " + key);
|
||||
ParserUtils.forwardToEndTagOfDepth(parser, initialDepth);
|
||||
return;
|
||||
}
|
||||
|
||||
NonzaCallback nonzaCallback;
|
||||
synchronized (nonzaCallbacks) {
|
||||
nonzaCallback = nonzaCallbacks.get(key);
|
||||
List<NonzaCallback> nonzaCallbacks;
|
||||
synchronized (nonzaCallbacksMap) {
|
||||
nonzaCallbacks = nonzaCallbacksMap.getAll(key);
|
||||
nonzaCallbacks = CollectionUtil.newListWith(nonzaCallbacks);
|
||||
}
|
||||
if (nonzaCallback == null) {
|
||||
if (nonzaCallbacks == null) {
|
||||
LOGGER.info("No nonza callback for " + key);
|
||||
ParserUtils.forwardToEndTagOfDepth(parser, initialDepth);
|
||||
return;
|
||||
}
|
||||
|
||||
Nonza nonza = nonzaProvider.parse(parser, incomingStreamXmlEnvironment);
|
||||
|
||||
nonzaCallback.onNonzaReceived(nonza);
|
||||
for (NonzaCallback nonzaCallback : nonzaCallbacks) {
|
||||
nonzaCallback.onNonzaReceived(nonza);
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseAndProcessStanza(XmlPullParser parser)
|
||||
|
@ -2017,7 +2090,13 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
|||
from = XmppStringUtils.completeJidFrom(localpart, to);
|
||||
}
|
||||
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 {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.jivesoftware.smack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -30,7 +31,7 @@ import org.jivesoftware.smack.util.XmppElementUtil;
|
|||
public class NonzaCallback {
|
||||
|
||||
protected final AbstractXMPPConnection connection;
|
||||
protected final Map<QName, GenericElementListener<? extends Nonza>> filterAndListeners;
|
||||
protected final Map<QName, ClassAndConsumer<? extends Nonza>> filterAndListeners;
|
||||
|
||||
private NonzaCallback(Builder builder) {
|
||||
this.connection = builder.connection;
|
||||
|
@ -38,21 +39,18 @@ public class NonzaCallback {
|
|||
install();
|
||||
}
|
||||
|
||||
void onNonzaReceived(Nonza nonza) {
|
||||
void onNonzaReceived(Nonza nonza) throws IOException {
|
||||
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() {
|
||||
synchronized (connection.nonzaCallbacks) {
|
||||
for (Map.Entry<QName, GenericElementListener<? extends Nonza>> entry : filterAndListeners.entrySet()) {
|
||||
QName filterKey = entry.getKey();
|
||||
NonzaCallback installedCallback = connection.nonzaCallbacks.get(filterKey);
|
||||
if (equals(installedCallback)) {
|
||||
connection.nonzaCallbacks.remove(filterKey);
|
||||
}
|
||||
for (Map.Entry<QName, ClassAndConsumer<? extends Nonza>> entry : filterAndListeners.entrySet()) {
|
||||
QName filterKey = entry.getKey();
|
||||
synchronized (connection.nonzaCallbacksMap) {
|
||||
connection.nonzaCallbacksMap.removeOne(filterKey, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,9 +60,9 @@ public class NonzaCallback {
|
|||
return;
|
||||
}
|
||||
|
||||
synchronized (connection.nonzaCallbacks) {
|
||||
for (QName key : filterAndListeners.keySet()) {
|
||||
connection.nonzaCallbacks.put(key, this);
|
||||
for (QName key : filterAndListeners.keySet()) {
|
||||
synchronized (connection.nonzaCallbacksMap) {
|
||||
connection.nonzaCallbacksMap.put(key, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,31 +72,35 @@ public class NonzaCallback {
|
|||
private SN successNonza;
|
||||
private FN failedNonza;
|
||||
|
||||
private NonzaResponseCallback(Class<? extends SN> successNonzaClass, Class<? extends FN> failedNonzaClass,
|
||||
private NonzaResponseCallback(Class<SN> successNonzaClass, Class<FN> failedNonzaClass,
|
||||
Builder builder) {
|
||||
super(builder);
|
||||
|
||||
final QName successNonzaKey = XmppElementUtil.getQNameFor(successNonzaClass);
|
||||
final QName failedNonzaKey = XmppElementUtil.getQNameFor(failedNonzaClass);
|
||||
|
||||
final GenericElementListener<SN> successListener = new GenericElementListener<SN>(successNonzaClass) {
|
||||
final NonzaListener<SN> successListener = new NonzaListener<SN>() {
|
||||
@Override
|
||||
public void process(SN successNonza) {
|
||||
public void accept(SN successNonza) {
|
||||
NonzaResponseCallback.this.successNonza = successNonza;
|
||||
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
|
||||
public void process(FN failedNonza) {
|
||||
public void accept(FN failedNonza) {
|
||||
NonzaResponseCallback.this.failedNonza = failedNonza;
|
||||
notifyResponse();
|
||||
}
|
||||
};
|
||||
final ClassAndConsumer<FN> failedClassAndConsumer = new ClassAndConsumer<>(failedNonzaClass,
|
||||
failedListener);
|
||||
|
||||
filterAndListeners.put(successNonzaKey, successListener);
|
||||
filterAndListeners.put(failedNonzaKey, failedListener);
|
||||
filterAndListeners.put(successNonzaKey, successClassAndConsumer);
|
||||
filterAndListeners.put(failedNonzaKey, failedClassAndConsumer);
|
||||
|
||||
install();
|
||||
}
|
||||
|
@ -139,15 +141,16 @@ public class NonzaCallback {
|
|||
public static final class Builder {
|
||||
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) {
|
||||
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);
|
||||
filterAndListeners.put(key, nonzaListener);
|
||||
ClassAndConsumer<N> classAndConsumer = new ClassAndConsumer<>(nonza, nonzaListener);
|
||||
filterAndListeners.put(key, classAndConsumer);
|
||||
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,
|
||||
Class<FN> failedNonzaClass)
|
||||
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.SASLMechanism;
|
||||
import org.jivesoftware.smack.sasl.core.ScramSha1PlusMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.Success;
|
||||
|
||||
import org.jxmpp.jid.DomainBareJid;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
|
@ -154,20 +155,9 @@ public final class SASLAuthentication {
|
|||
private final ConnectionConfiguration configuration;
|
||||
private SASLMechanism currentMechanism = null;
|
||||
|
||||
/**
|
||||
* Boolean indicating if SASL negotiation has finished and was successful.
|
||||
*/
|
||||
private boolean authenticationSuccessful;
|
||||
|
||||
/**
|
||||
* Either of type {@link SmackException} or {@link SASLErrorException}
|
||||
*/
|
||||
private Exception saslException;
|
||||
|
||||
SASLAuthentication(AbstractXMPPConnection connection, ConnectionConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
this.connection = connection;
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,23 +181,26 @@ public final class SASLAuthentication {
|
|||
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||
* @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,
|
||||
InterruptedException, SmackSaslException, NotConnectedException, NoResponseException {
|
||||
currentMechanism = selectMechanism(authzid);
|
||||
final SASLMechanism mechanism = selectMechanism(authzid);
|
||||
final CallbackHandler callbackHandler = configuration.getCallbackHandler();
|
||||
final String host = connection.getHost();
|
||||
final DomainBareJid xmppServiceDomain = connection.getXMPPServiceDomain();
|
||||
|
||||
synchronized (this) {
|
||||
currentMechanism = mechanism;
|
||||
|
||||
if (callbackHandler != null) {
|
||||
currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler, authzid, sslSession);
|
||||
}
|
||||
else {
|
||||
currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid, sslSession);
|
||||
}
|
||||
|
||||
final long deadline = System.currentTimeMillis() + connection.getReplyTimeout();
|
||||
while (!authenticationSuccessful && saslException == null) {
|
||||
while (!mechanism.isFinished()) {
|
||||
final long now = System.currentTimeMillis();
|
||||
if (now >= deadline) break;
|
||||
// Wait until SASL negotiation finishes
|
||||
|
@ -215,35 +208,21 @@ public final class SASLAuthentication {
|
|||
}
|
||||
}
|
||||
|
||||
if (saslException != null) {
|
||||
if (saslException instanceof SmackSaslException) {
|
||||
throw (SmackSaslException) saslException;
|
||||
} else if (saslException instanceof SASLErrorException) {
|
||||
throw (SASLErrorException) saslException;
|
||||
} else if (saslException instanceof NotConnectedException) {
|
||||
throw (NotConnectedException) saslException;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected exception type" , saslException);
|
||||
}
|
||||
}
|
||||
mechanism.throwExceptionIfRequired();
|
||||
|
||||
if (!authenticationSuccessful) {
|
||||
throw NoResponseException.newWith(connection, "successful SASL authentication");
|
||||
}
|
||||
|
||||
return currentMechanism;
|
||||
return mechanism;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@link #challengeReceived(String, boolean)}, with <code>finalChallenge</code> set
|
||||
* 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 InterruptedException if the calling thread was interrupted.
|
||||
*/
|
||||
public void challengeReceived(String challenge) throws SmackException, InterruptedException {
|
||||
challengeReceived(challenge, false);
|
||||
void challengeReceived(SaslNonza.Challenge challenge) throws SmackException, InterruptedException {
|
||||
challengeReceived(challenge.getData(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,13 +237,12 @@ public final class SASLAuthentication {
|
|||
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||
* @throws InterruptedException if the calling thread was interrupted.
|
||||
*/
|
||||
public void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
|
||||
try {
|
||||
currentMechanism.challengeReceived(challenge, finalChallenge);
|
||||
} catch (InterruptedException | SmackSaslException | NotConnectedException e) {
|
||||
authenticationFailed(e);
|
||||
throw e;
|
||||
private void challengeReceived(String challenge, boolean finalChallenge) throws SmackSaslException, NotConnectedException, InterruptedException {
|
||||
SASLMechanism mechanism;
|
||||
synchronized (this) {
|
||||
mechanism = currentMechanism;
|
||||
}
|
||||
mechanism.challengeReceived(challenge, finalChallenge);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,8 +251,10 @@ public final class SASLAuthentication {
|
|||
* @param success result of the authentication.
|
||||
* @throws SmackException if Smack detected an exceptional situation.
|
||||
* @throws InterruptedException if the calling thread was interrupted.
|
||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||
* @throws SmackSaslException if a SASL specific error occured.
|
||||
*/
|
||||
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
|
||||
// "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
|
||||
|
@ -283,10 +263,11 @@ public final class SASLAuthentication {
|
|||
if (success.getData() != null) {
|
||||
challengeReceived(success.getData(), true);
|
||||
}
|
||||
currentMechanism.checkIfSuccessfulOrThrow();
|
||||
authenticationSuccessful = true;
|
||||
|
||||
// Wake up the thread that is waiting in the #authenticate method
|
||||
synchronized (this) {
|
||||
currentMechanism.afterFinalSaslChallenge();
|
||||
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
@ -298,30 +279,29 @@ public final class SASLAuthentication {
|
|||
* @param saslFailure the SASL failure as reported by the server
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6120#section-6.5">RFC6120 6.5</a>
|
||||
*/
|
||||
public void authenticationFailed(SASLFailure saslFailure) {
|
||||
authenticationFailed(new SASLErrorException(currentMechanism.getName(), saslFailure));
|
||||
void authenticationFailed(SASLFailure saslFailure) {
|
||||
SASLErrorException saslErrorException;
|
||||
synchronized (this) {
|
||||
saslErrorException = new SASLErrorException(currentMechanism.getName(), saslFailure);
|
||||
}
|
||||
authenticationFailed(saslErrorException);
|
||||
}
|
||||
|
||||
private void authenticationFailed(Exception exception) {
|
||||
saslException = exception;
|
||||
void authenticationFailed(Exception exception) {
|
||||
// Wake up the thread that is waiting in the #authenticate method
|
||||
synchronized (this) {
|
||||
currentMechanism.setException(exception);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean authenticationSuccessful() {
|
||||
return authenticationSuccessful;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the internal state in order to be able to be reused. The authentication
|
||||
* is used by the connection at the first login and then reused after the connection
|
||||
* is disconnected and then reconnected.
|
||||
*/
|
||||
void init() {
|
||||
authenticationSuccessful = false;
|
||||
saslException = null;
|
||||
synchronized (this) {
|
||||
if (currentMechanism == null) {
|
||||
return false;
|
||||
}
|
||||
return currentMechanism.isAuthenticationSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
String getNameOfLastUsedSaslMechansism() {
|
||||
|
|
|
@ -108,30 +108,6 @@ public final class SmackConfiguration {
|
|||
return SmackInitialization.SMACK_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds to wait for a response from
|
||||
* the server. The default value is 5000 ms.
|
||||
*
|
||||
* @return the milliseconds to wait for a response from the server
|
||||
* @deprecated use {@link #getDefaultReplyTimeout()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static int getDefaultPacketReplyTimeout() {
|
||||
return getDefaultReplyTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of milliseconds to wait for a response from
|
||||
* the server.
|
||||
*
|
||||
* @param timeout the milliseconds to wait for a response from the server
|
||||
* @deprecated use {@link #setDefaultReplyTimeout(int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setDefaultPacketReplyTimeout(int timeout) {
|
||||
setDefaultReplyTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds to wait for a response from
|
||||
* the server. The default value is 5000 ms.
|
||||
|
|
|
@ -37,6 +37,9 @@ import org.jivesoftware.smack.packet.Message.Body;
|
|||
import org.jivesoftware.smack.provider.BindIQProvider;
|
||||
import org.jivesoftware.smack.provider.BodyElementProvider;
|
||||
import org.jivesoftware.smack.provider.ProviderManager;
|
||||
import org.jivesoftware.smack.provider.SaslChallengeProvider;
|
||||
import org.jivesoftware.smack.provider.SaslFailureProvider;
|
||||
import org.jivesoftware.smack.provider.SaslSuccessProvider;
|
||||
import org.jivesoftware.smack.provider.TlsFailureProvider;
|
||||
import org.jivesoftware.smack.provider.TlsProceedProvider;
|
||||
import org.jivesoftware.smack.sasl.core.SASLAnonymous;
|
||||
|
@ -126,6 +129,9 @@ public final class SmackInitialization {
|
|||
ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider());
|
||||
ProviderManager.addExtensionProvider(Body.ELEMENT, Body.NAMESPACE, new BodyElementProvider());
|
||||
|
||||
ProviderManager.addNonzaProvider(SaslChallengeProvider.INSTANCE);
|
||||
ProviderManager.addNonzaProvider(SaslSuccessProvider.INSTANCE);
|
||||
ProviderManager.addNonzaProvider(SaslFailureProvider.INSTANCE);
|
||||
ProviderManager.addNonzaProvider(TlsProceedProvider.INSTANCE);
|
||||
ProviderManager.addNonzaProvider(TlsFailureProvider.INSTANCE);
|
||||
ProviderManager.addNonzaProvider(CompressedProvider.INSTANCE);
|
||||
|
|
|
@ -53,8 +53,6 @@ import org.jivesoftware.smack.packet.StreamError;
|
|||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||
import org.jivesoftware.smack.sasl.SASLErrorException;
|
||||
import org.jivesoftware.smack.sasl.SASLMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
|
@ -335,19 +333,6 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
|
|||
parseFeatures(parser);
|
||||
afterFeaturesReceived();
|
||||
break;
|
||||
// SASL related top level stream elements
|
||||
case Challenge.ELEMENT:
|
||||
// The server is challenging the SASL authentication made by the client
|
||||
String challengeData = parser.nextText();
|
||||
getSASLAuthentication().challengeReceived(challengeData);
|
||||
break;
|
||||
case Success.ELEMENT:
|
||||
Success success = new Success(parser.nextText());
|
||||
// The SASL authentication with the server was successful. The next step
|
||||
// will be to bind the resource
|
||||
getSASLAuthentication().authenticated(success);
|
||||
sendStreamOpen();
|
||||
break;
|
||||
default:
|
||||
parseAndProcessNonza(parser);
|
||||
break;
|
||||
|
@ -613,7 +598,7 @@ public abstract class AbstractXmppStateMachineConnection extends AbstractXMPPCon
|
|||
prepareToWaitForFeaturesReceived();
|
||||
|
||||
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.
|
||||
|
||||
waitForFeaturesReceived("server stream features after SASL authentication");
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Set;
|
|||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.fsm.AbstractXmppStateMachineConnection.DisconnectedStateDescriptor;
|
||||
import org.jivesoftware.smack.util.Consumer;
|
||||
import org.jivesoftware.smack.util.MultiMap;
|
||||
|
||||
/**
|
||||
|
@ -330,7 +331,8 @@ public class StateDescriptorGraph {
|
|||
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;
|
||||
|
||||
final int totalEdgeCount = vertex.getOutgoingEdges().size();
|
||||
|
@ -349,11 +351,12 @@ public class StateDescriptorGraph {
|
|||
|
||||
vertex.color = GraphVertex.VertexColor.black;
|
||||
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) {
|
||||
if (vertex.color == GraphVertex.VertexColor.white) {
|
||||
dfsVisit(vertex, dfsFinishedVertex, dfsEdgeFound);
|
||||
|
@ -407,12 +410,6 @@ public class StateDescriptorGraph {
|
|||
dotOut.append("}\n");
|
||||
}
|
||||
|
||||
// TODO: Replace with java.util.function.Consumer<GraphVertex<E>> once Smack's minimum Android SDK level is 24 or higher.
|
||||
private interface DfsFinishedVertex<E> {
|
||||
void onVertexFinished(GraphVertex<E> vertex);
|
||||
}
|
||||
|
||||
// TODO: Replace with java.util.function.Consumer<GraphVertex<E>> once Smack's minimum Android SDK level is 24 or higher.
|
||||
private interface DfsEdgeFound<E> {
|
||||
void onEdgeFound(GraphVertex<E> from, GraphVertex<E> to, int edgeId, int totalEdgeCount);
|
||||
}
|
||||
|
|
|
@ -163,6 +163,12 @@ public class XmlEnvironment {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder with(StreamOpen streamOpen) {
|
||||
withNamespace(streamOpen.getNamespace());
|
||||
withLanguage(streamOpen.getLanguage());
|
||||
return this;
|
||||
}
|
||||
|
||||
public XmlEnvironment build() {
|
||||
return new XmlEnvironment(this);
|
||||
}
|
||||
|
|
|
@ -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 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 {
|
||||
|
||||
|
|
|
@ -23,11 +23,12 @@ import javax.net.ssl.SSLSession;
|
|||
import javax.security.auth.callback.CallbackHandler;
|
||||
|
||||
import org.jivesoftware.smack.ConnectionConfiguration;
|
||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.SmackException.SmackSaslException;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.AuthMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.Response;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
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 PLAIN = "PLAIN";
|
||||
|
||||
/**
|
||||
* Boolean indicating if SASL negotiation has finished and was successful.
|
||||
*/
|
||||
private boolean authenticationSuccessful;
|
||||
|
||||
/**
|
||||
* Either of type {@link SmackSaslException},{@link SASLErrorException}, {@link NotConnectedException} or
|
||||
* {@link InterruptedException}.
|
||||
*/
|
||||
private Exception exception;
|
||||
|
||||
protected XMPPConnection connection;
|
||||
|
||||
protected ConnectionConfiguration connectionConfiguration;
|
||||
|
@ -275,7 +287,18 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
|||
*/
|
||||
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) {
|
||||
SASLMechanism saslMechansim = newInstance();
|
||||
|
@ -288,6 +311,39 @@ public abstract class SASLMechanism implements Comparable<SASLMechanism> {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isAuthenticationSuccessful() {
|
||||
return authenticationSuccessful;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return isAuthenticationSuccessful() || exception != null;
|
||||
}
|
||||
|
||||
public void throwExceptionIfRequired() throws SmackSaslException, SASLErrorException, NotConnectedException,
|
||||
InterruptedException, NoResponseException {
|
||||
if (exception != null) {
|
||||
if (exception instanceof SmackSaslException) {
|
||||
throw (SmackSaslException) exception;
|
||||
} else if (exception instanceof SASLErrorException) {
|
||||
throw (SASLErrorException) exception;
|
||||
} else if (exception instanceof NotConnectedException) {
|
||||
throw (NotConnectedException) exception;
|
||||
} else if (exception instanceof InterruptedException) {
|
||||
throw (InterruptedException) exception;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected exception type", exception);
|
||||
}
|
||||
}
|
||||
|
||||
if (!authenticationSuccessful) {
|
||||
throw NoResponseException.newWith(connection, "successful SASL authentication");
|
||||
}
|
||||
}
|
||||
|
||||
public void setException(Exception exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
protected abstract SASLMechanism newInstance();
|
||||
|
||||
protected static byte[] toBytes(String string) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2014-2018 Florian Schmaus
|
||||
* Copyright 2014-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.
|
||||
|
@ -18,21 +18,30 @@ package org.jivesoftware.smack.sasl.packet;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.jivesoftware.smack.packet.AbstractError;
|
||||
import org.jivesoftware.smack.packet.Nonza;
|
||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||
import org.jivesoftware.smack.sasl.SASLError;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
||||
public class SaslStreamElements {
|
||||
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||
public interface SaslNonza extends Nonza {
|
||||
String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||
|
||||
@Override
|
||||
default String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 QName QNAME = new QName(NAMESPACE, ELEMENT);
|
||||
|
||||
private final String mechanism;
|
||||
private final String authenticationText;
|
||||
|
@ -44,11 +53,11 @@ public class SaslStreamElements {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder();
|
||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).attribute("mechanism", mechanism).rightAngleBracket();
|
||||
xml.optAppend(authenticationText);
|
||||
xml.closeElement(ELEMENT);
|
||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||
xml.attribute("mechanism", mechanism).rightAngleBracket();
|
||||
xml.escape(authenticationText);
|
||||
xml.closeElement(this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
@ -60,11 +69,6 @@ public class SaslStreamElements {
|
|||
return authenticationText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
|
@ -74,8 +78,9 @@ public class SaslStreamElements {
|
|||
/**
|
||||
* A SASL challenge stream element.
|
||||
*/
|
||||
public static class Challenge implements Nonza {
|
||||
class Challenge implements SaslNonza {
|
||||
public static final String ELEMENT = "challenge";
|
||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
||||
|
||||
private final String data;
|
||||
|
||||
|
@ -83,18 +88,15 @@ public class SaslStreamElements {
|
|||
this.data = StringUtils.returnIfNotEmptyTrimmed(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder().halfOpenElement(ELEMENT).xmlnsAttribute(
|
||||
NAMESPACE).rightAngleBracket();
|
||||
xml.optAppend(data);
|
||||
xml.closeElement(ELEMENT);
|
||||
return xml;
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||
xml.optTextChild(data, this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,8 +108,9 @@ public class SaslStreamElements {
|
|||
/**
|
||||
* A SASL response stream element.
|
||||
*/
|
||||
public static class Response implements Nonza {
|
||||
class Response implements SaslNonza {
|
||||
public static final String ELEMENT = "response";
|
||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
||||
|
||||
private final String authenticationText;
|
||||
|
||||
|
@ -120,11 +123,9 @@ public class SaslStreamElements {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder();
|
||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
||||
xml.optAppend(authenticationText);
|
||||
xml.closeElement(ELEMENT);
|
||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||
xml.optTextChild(authenticationText, this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
@ -132,11 +133,6 @@ public class SaslStreamElements {
|
|||
return authenticationText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
|
@ -146,8 +142,9 @@ public class SaslStreamElements {
|
|||
/**
|
||||
* A SASL success stream element.
|
||||
*/
|
||||
public static class Success implements Nonza {
|
||||
class Success implements SaslNonza {
|
||||
public static final String ELEMENT = "success";
|
||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
||||
|
||||
private final String data;
|
||||
|
||||
|
@ -171,19 +168,12 @@ public class SaslStreamElements {
|
|||
}
|
||||
|
||||
@Override
|
||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder();
|
||||
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
|
||||
xml.optAppend(data);
|
||||
xml.closeElement(ELEMENT);
|
||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||
xml.optTextChild(data, this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
||||
|
@ -194,8 +184,9 @@ public class SaslStreamElements {
|
|||
* 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>
|
||||
*/
|
||||
public static class SASLFailure extends AbstractError implements Nonza {
|
||||
class SASLFailure extends AbstractError implements SaslNonza {
|
||||
public static final String ELEMENT = "failure";
|
||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
||||
|
||||
private final SASLError saslError;
|
||||
private final String saslErrorString;
|
||||
|
@ -250,11 +241,6 @@ public class SaslStreamElements {
|
|||
return toXML().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return ELEMENT;
|
|
@ -51,8 +51,9 @@ public class CollectionUtil {
|
|||
}
|
||||
|
||||
public static <T> ArrayList<T> newListWith(Collection<? extends T> collection) {
|
||||
ArrayList<T> arrayList = new ArrayList<>(collection.size());
|
||||
arrayList.addAll(collection);
|
||||
return arrayList;
|
||||
if (collection == null) {
|
||||
return null;
|
||||
}
|
||||
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");
|
||||
* 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
|
||||
* 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> {
|
||||
|
||||
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);
|
||||
}
|
||||
void accept(T t);
|
||||
|
||||
}
|
|
@ -49,7 +49,6 @@ import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
|
|||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||
import org.jivesoftware.smack.provider.IQProvider;
|
||||
import org.jivesoftware.smack.provider.ProviderManager;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
import org.jivesoftware.smack.xml.SmackXmlParser;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||
|
@ -686,44 +685,6 @@ public class PacketParserUtils {
|
|||
return descriptiveTexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses SASL authentication error packets.
|
||||
*
|
||||
* @param parser the XML parser.
|
||||
* @return a SASL Failure packet.
|
||||
* @throws IOException if an I/O error occured.
|
||||
* @throws XmlPullParserException if an error in the XML parser occured.
|
||||
*/
|
||||
public static SASLFailure parseSASLFailure(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
final int initialDepth = parser.getDepth();
|
||||
String condition = null;
|
||||
Map<String, String> descriptiveTexts = null;
|
||||
outerloop: while (true) {
|
||||
XmlPullParser.Event eventType = parser.next();
|
||||
switch (eventType) {
|
||||
case START_ELEMENT:
|
||||
String name = parser.getName();
|
||||
if (name.equals("text")) {
|
||||
descriptiveTexts = parseDescriptiveTexts(parser, descriptiveTexts);
|
||||
}
|
||||
else {
|
||||
assert condition == null;
|
||||
condition = parser.getName();
|
||||
}
|
||||
break;
|
||||
case END_ELEMENT:
|
||||
if (parser.getDepth() == initialDepth) {
|
||||
break outerloop;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new SASLFailure(condition, descriptiveTexts);
|
||||
}
|
||||
|
||||
public static StreamError parseStreamError(XmlPullParser parser) throws XmlPullParserException, IOException, SmackParsingException {
|
||||
return parseStreamError(parser, null);
|
||||
}
|
||||
|
|
|
@ -18,9 +18,10 @@ package org.jivesoftware.smack.util;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.packet.Element;
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
|
@ -496,13 +497,6 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
|||
return this;
|
||||
}
|
||||
|
||||
public XmlStringBuilder optAppend(CharSequence csq) {
|
||||
if (csq != null) {
|
||||
append(csq);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public XmlStringBuilder optAppend(Element element) {
|
||||
if (element != null) {
|
||||
append(element.toXML(effectiveXmlEnvironment));
|
||||
|
@ -510,6 +504,16 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
|||
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) {
|
||||
assert xsb != null;
|
||||
sb.append(xsb.sb);
|
||||
|
@ -606,24 +610,46 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
|||
return toString().hashCode();
|
||||
}
|
||||
|
||||
private static final class WrappedIoException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final IOException wrappedIoException;
|
||||
|
||||
private WrappedIoException(IOException wrappedIoException) {
|
||||
this.wrappedIoException = wrappedIoException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of this <code>XmlStringBuilder</code> to a {@link Writer}. This will write
|
||||
* the single parts one-by-one, avoiding allocation of a big continuous memory block holding the
|
||||
* XmlStringBuilder contents.
|
||||
*
|
||||
* @param writer TODO javadoc me please
|
||||
* @param enclosingNamespace the enclosing XML namespace.
|
||||
* @param enclosingXmlEnvironment the enclosing XML environment.
|
||||
* @throws IOException if an I/O error occured.
|
||||
*/
|
||||
public void write(Writer writer, String enclosingNamespace) throws IOException {
|
||||
XmlEnvironment enclosingXmlEnvironment = XmlEnvironment.builder()
|
||||
.withNamespace(enclosingNamespace)
|
||||
.build();
|
||||
appendXmlTo(writer, enclosingXmlEnvironment);
|
||||
public void write(Writer writer, XmlEnvironment enclosingXmlEnvironment) throws IOException {
|
||||
try {
|
||||
appendXmlTo(csq -> {
|
||||
try {
|
||||
writer.append(csq);
|
||||
} catch (IOException e) {
|
||||
throw new WrappedIoException(e);
|
||||
}
|
||||
}, enclosingXmlEnvironment);
|
||||
} catch (WrappedIoException e) {
|
||||
throw e.wrappedIoException;
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator<CharSequence> getCharSequenceIterator() {
|
||||
return sb.getAsList().iterator();
|
||||
public List<CharSequence> toList(XmlEnvironment enclosingXmlEnvironment) {
|
||||
List<CharSequence> res = new ArrayList<>(sb.getAsList().size());
|
||||
|
||||
appendXmlTo(csq -> res.add(csq), enclosingXmlEnvironment);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@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.
|
||||
int potentialLength = length();
|
||||
StringBuilder res = new StringBuilder(potentialLength);
|
||||
try {
|
||||
appendXmlTo(res, enclosingXmlEnvironment);
|
||||
} catch (IOException e) {
|
||||
// Should never happen.
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
appendXmlTo(csq -> res.append(csq), enclosingXmlEnvironment);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private void appendXmlTo(Appendable appendable, XmlEnvironment enclosingXmlEnvironment) throws IOException {
|
||||
private void appendXmlTo(Consumer<CharSequence> charSequenceSink, XmlEnvironment enclosingXmlEnvironment) {
|
||||
for (CharSequence csq : sb.getAsList()) {
|
||||
if (csq instanceof XmlStringBuilder) {
|
||||
((XmlStringBuilder) csq).appendXmlTo(appendable, enclosingXmlEnvironment);
|
||||
((XmlStringBuilder) csq).appendXmlTo(charSequenceSink, enclosingXmlEnvironment);
|
||||
}
|
||||
else if (csq instanceof XmlNsAttribute) {
|
||||
XmlNsAttribute xmlNsAttribute = (XmlNsAttribute) csq;
|
||||
if (!xmlNsAttribute.value.equals(enclosingXmlEnvironment.getEffectiveNamespace())) {
|
||||
appendable.append(xmlNsAttribute);
|
||||
charSequenceSink.accept(xmlNsAttribute);
|
||||
enclosingXmlEnvironment = new XmlEnvironment(xmlNsAttribute.value);
|
||||
}
|
||||
}
|
||||
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.SmackException;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.AuthMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Response;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.AuthMechanism;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza.Response;
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||
|
||||
|
|
|
@ -125,10 +125,19 @@ public class SmackTestUtil {
|
|||
return parser;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Element, P extends Provider<E>> P providerClassToProvider(Class<P> providerClass) {
|
||||
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 {
|
||||
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.StanzaError;
|
||||
import org.jivesoftware.smack.packet.StreamOpen;
|
||||
import org.jivesoftware.smack.sasl.SASLError;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
import org.jivesoftware.smack.test.util.SmackTestUtil;
|
||||
import org.jivesoftware.smack.test.util.TestUtils;
|
||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||
|
@ -814,41 +811,6 @@ public class PacketParserUtilsTest {
|
|||
assertXmlSimilar(stanza, result.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseSASLFailureSimple() throws FactoryConfigurationError, SAXException, IOException,
|
||||
TransformerException, ParserConfigurationException, XmlPullParserException {
|
||||
// @formatter:off
|
||||
final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslStreamElements.NAMESPACE)
|
||||
.e(SASLError.account_disabled.toString())
|
||||
.asString();
|
||||
// @formatter:on
|
||||
XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT);
|
||||
SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser);
|
||||
assertXmlSimilar(saslFailureString, saslFailure.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseSASLFailureExtended() throws FactoryConfigurationError, TransformerException,
|
||||
ParserConfigurationException, XmlPullParserException, IOException, SAXException {
|
||||
// @formatter:off
|
||||
final String saslFailureString = XMLBuilder.create(SASLFailure.ELEMENT, SaslStreamElements.NAMESPACE)
|
||||
.e(SASLError.account_disabled.toString())
|
||||
.up()
|
||||
.e("text").a("xml:lang", "en")
|
||||
.t("Call 212-555-1212 for assistance.")
|
||||
.up()
|
||||
.e("text").a("xml:lang", "de")
|
||||
.t("Bitte wenden sie sich an (04321) 123-4444")
|
||||
.up()
|
||||
.e("text")
|
||||
.t("Wusel dusel")
|
||||
.asString();
|
||||
// @formatter:on
|
||||
XmlPullParser parser = TestUtils.getParser(saslFailureString, SASLFailure.ELEMENT);
|
||||
SASLFailure saslFailure = PacketParserUtils.parseSASLFailure(parser);
|
||||
assertXmlSimilar(saslFailureString, saslFailure.toXML(StreamOpen.CLIENT_NAMESPACE));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ReferenceEquality")
|
||||
private static String determineNonDefaultLanguage() {
|
||||
String otherLanguage = "jp";
|
||||
|
|
|
@ -225,9 +225,7 @@ public abstract class AbstractHttpOverXmpp extends IQ {
|
|||
@Override
|
||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||
xml.rightAngleBracket();
|
||||
xml.optAppend(text);
|
||||
xml.closeElement(this);
|
||||
xml.optTextChild(text, this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
@ -269,9 +267,7 @@ public abstract class AbstractHttpOverXmpp extends IQ {
|
|||
@Override
|
||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||
xml.rightAngleBracket();
|
||||
xml.optAppend(text);
|
||||
xml.closeElement(this);
|
||||
xml.optTextChild(text, this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
@ -313,9 +309,7 @@ public abstract class AbstractHttpOverXmpp extends IQ {
|
|||
@Override
|
||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||
xml.rightAngleBracket();
|
||||
xml.optAppend(text);
|
||||
xml.closeElement(this);
|
||||
xml.optTextChild(text, this);
|
||||
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'>"
|
||||
+ "<result xmlns='urn:xmpp:mam:1' queryid='g27' id='34482-21985-73620'>"
|
||||
+ "<forwarded xmlns='urn:xmpp:forward:0'>"
|
||||
+ "<delay xmlns='urn:xmpp:delay' stamp='2002-10-13T23:58:37.000+00:00'></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' "
|
||||
+ "type='chat'>" + "<body>Thrice the brinded cat hath mew.</body>" + "</message>" + "</forwarded>"
|
||||
+ "</result>" + "</message>";
|
||||
|
|
|
@ -106,9 +106,7 @@ public class DelayInformation implements ExtensionElement {
|
|||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||
xml.attribute("stamp", XmppDateTime.formatXEP0082Date(stamp));
|
||||
xml.optAttribute("from", from);
|
||||
xml.rightAngleBracket();
|
||||
xml.optAppend(reason);
|
||||
xml.closeElement(this);
|
||||
xml.optTextChild(reason, this);
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ public class StreamInitiation extends IQ {
|
|||
buf.rightAngleBracket();
|
||||
|
||||
// Add the file section if there is one.
|
||||
buf.optAppend(file.toXML());
|
||||
buf.optElement(file);
|
||||
break;
|
||||
case result:
|
||||
buf.rightAngleBracket();
|
||||
|
|
|
@ -8,6 +8,7 @@ dependencies {
|
|||
compile project(":smack-core")
|
||||
compile project(":smack-resolver-javax")
|
||||
compile project(":smack-sasl-javax")
|
||||
implementation project(":smack-xmlparser-stax")
|
||||
}
|
||||
|
||||
javadoc {
|
||||
|
|
|
@ -29,11 +29,6 @@ public class SASLGSSAPIMechanism extends SASLJavaXMechanism {
|
|||
|
||||
public static final String NAME = GSSAPI;
|
||||
|
||||
static {
|
||||
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
|
||||
System.setProperty("java.security.auth.login.config", "gss.conf");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authzidSupported() {
|
||||
return true;
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
.PHONY := clean generate
|
||||
.PHONY := all clean
|
||||
|
||||
GRADLE_QUITE_ARGS := --quiet --console plain
|
||||
|
||||
GENERATED_FILES := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
|
||||
generate: $(GENERATED_FILES)
|
||||
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG := src/javadoc/org/jivesoftware/smack/tcp/doc-files/XmppNioTcpConnectionStateGraph.png
|
||||
XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG:.png=.dot)
|
||||
|
||||
GENERATED_FILES := $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG) $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT)
|
||||
|
||||
all: $(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_PNG)
|
||||
|
||||
clean:
|
||||
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
|
||||
gradle $(GRADLE_QUITE_ARGS) :smack-repl:printXmppNioTcpConnectionStateGraph | dot -Tpng -o $@
|
||||
%.png: %.dot
|
||||
dot -Tpng -o $@ $^
|
||||
|
||||
$(XMPP_NIO_TCP_CONNECTION_STATE_GRAPH_DOT): src/main/java/org/jivesoftware/smack/tcp/XmppNioTcpConnection.java ../smack-core/src/main/java/org/jivesoftware/smack/fsm/AbstractXmppStateMachineConnection.java
|
||||
# TODO: This also creates the dot file even if the command
|
||||
# fails. It would be better if this was not the case.
|
||||
gradle $(GRADLE_QUITE_ARGS) :smack-repl:printXmppNioTcpConnectionStateGraph > $@
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
/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.StartTls;
|
||||
import org.jivesoftware.smack.packet.StreamError;
|
||||
import org.jivesoftware.smack.packet.StreamOpen;
|
||||
import org.jivesoftware.smack.proxy.ProxyInfo;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
|
||||
import org.jivesoftware.smack.sasl.packet.SaslNonza;
|
||||
import org.jivesoftware.smack.sm.SMUtils;
|
||||
import org.jivesoftware.smack.sm.StreamManagementException;
|
||||
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 {
|
||||
// Authenticate using SASL
|
||||
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.
|
||||
// TODO: The name of this synchronization point "maybeCompressFeaturesReceived" is not perfect. It should be
|
||||
|
@ -857,7 +857,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
tlsHandled.reportSuccess();
|
||||
}
|
||||
|
||||
if (getSASLAuthentication().authenticationSuccessful()) {
|
||||
if (isSaslAuthenticated()) {
|
||||
// If we have received features after the SASL has been successfully completed, then we
|
||||
// have also *maybe* received, as it is an optional feature, the compression feature
|
||||
// from the server.
|
||||
|
@ -865,18 +865,17 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the parser using the latest connection's reader. Resetting the parser is necessary
|
||||
* when the plain connection has been secured or when a new opening stream element is going
|
||||
* to be sent by the server.
|
||||
*
|
||||
* @throws SmackException if the parser could not be reset.
|
||||
* @throws InterruptedException if the calling thread was interrupted.
|
||||
* @throws XmlPullParserException if an error in the XML parser occured.
|
||||
*/
|
||||
void openStream() throws SmackException, InterruptedException, XmlPullParserException {
|
||||
private void resetParser() throws IOException {
|
||||
try {
|
||||
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
||||
} catch (XmlPullParserException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void openStreamAndResetParser() throws IOException, NotConnectedException, InterruptedException {
|
||||
sendStreamOpen();
|
||||
packetReader.parser = SmackXmlParser.newXmlParser(reader);
|
||||
resetParser();
|
||||
}
|
||||
|
||||
protected class PacketReader {
|
||||
|
@ -921,7 +920,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
private void parsePackets() {
|
||||
boolean initialStreamOpenSend = false;
|
||||
try {
|
||||
openStream();
|
||||
openStreamAndResetParser();
|
||||
initialStreamOpenSend = true;
|
||||
XmlPullParser.Event eventType = parser.getEventType();
|
||||
while (!done) {
|
||||
|
@ -957,7 +956,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
// Secure the connection by negotiating TLS
|
||||
proceedTLSReceived();
|
||||
// Send a new opening stream to the server
|
||||
openStream();
|
||||
openStreamAndResetParser();
|
||||
}
|
||||
catch (Exception e) {
|
||||
SmackException.SmackWrappedException smackException = new SmackException.SmackWrappedException(e);
|
||||
|
@ -980,35 +979,17 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
compressSyncPoint.reportFailure(new SmackException.SmackMessageException(
|
||||
"Could not establish compression"));
|
||||
break;
|
||||
case SaslStreamElements.NAMESPACE:
|
||||
// SASL authentication has failed. The server may close the connection
|
||||
// depending on the number of retries
|
||||
final SASLFailure failure = PacketParserUtils.parseSASLFailure(parser);
|
||||
getSASLAuthentication().authenticationFailed(failure);
|
||||
break;
|
||||
default:
|
||||
parseAndProcessNonza(parser);
|
||||
}
|
||||
break;
|
||||
case Challenge.ELEMENT:
|
||||
// The server is challenging the SASL authentication made by the client
|
||||
String challengeData = parser.nextText();
|
||||
getSASLAuthentication().challengeReceived(challengeData);
|
||||
break;
|
||||
case Success.ELEMENT:
|
||||
Success success = new Success(parser.nextText());
|
||||
// We now need to bind a resource for the connection
|
||||
// Open a new stream and wait for the response
|
||||
openStream();
|
||||
// The SASL authentication with the server was successful. The next step
|
||||
// will be to bind the resource
|
||||
getSASLAuthentication().authenticated(success);
|
||||
break;
|
||||
case Compressed.ELEMENT:
|
||||
// Server confirmed that it's possible to use stream compression. Start
|
||||
// stream compression
|
||||
// Initialize the reader and writer with the new compressed version
|
||||
initReaderAndWriter();
|
||||
// Send a new opening stream to the server
|
||||
openStream();
|
||||
openStreamAndResetParser();
|
||||
// Notify that compression is being used
|
||||
compressSyncPoint.reportSuccess();
|
||||
break;
|
||||
|
@ -1090,7 +1071,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
LOGGER.warning("Unknown top level stream element: " + name);
|
||||
parseAndProcessNonza(parser);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -1347,9 +1328,9 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
|||
}
|
||||
maybeAddToUnacknowledgedStanzas(packet);
|
||||
|
||||
CharSequence elementXml = element.toXML(StreamOpen.CLIENT_NAMESPACE);
|
||||
CharSequence elementXml = element.toXML(outgoingStreamXmlEnvironment);
|
||||
if (elementXml instanceof XmlStringBuilder) {
|
||||
((XmlStringBuilder) elementXml).write(writer, StreamOpen.CLIENT_NAMESPACE);
|
||||
((XmlStringBuilder) elementXml).write(writer, outgoingStreamXmlEnvironment);
|
||||
}
|
||||
else {
|
||||
writer.write(elementXml.toString());
|
||||
|
|
|
@ -509,7 +509,7 @@ public class XmppNioTcpConnection extends AbstractXmppNioConnection {
|
|||
CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE);
|
||||
if (nextCharSequence instanceof XmlStringBuilder) {
|
||||
XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence;
|
||||
outgoingCharSequenceIterator = xmlStringBuilder.getCharSequenceIterator();
|
||||
outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator();
|
||||
} else {
|
||||
outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator();
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ public class SmackXmlParser {
|
|||
public static XmlPullParserFactory getXmlPullParserFactory() {
|
||||
Iterator<XmlPullParserFactory> iterator = xmlPullParserFactoryServiceLoader.iterator();
|
||||
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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue