From aad66a782c7a0776e54af56f6dfd272f90f9770c Mon Sep 17 00:00:00 2001 From: ramabit Date: Fri, 22 Jul 2016 15:49:08 -0300 Subject: [PATCH 1/5] Token-based reconnection implementation --- documentation/extensions/index.md | 2 + .../smack/bosh/XMPPBOSHConnection.java | 43 ++++-- .../smack/AbstractXMPPConnection.java | 78 ++++++++-- .../smack/SASLAuthentication.java | 108 +++++++++++--- .../smack/SmackInitialization.java | 6 + .../jivesoftware/smack/XMPPConnection.java | 18 ++- .../smack/sasl/SASLMechanism.java | 22 ++- .../smack/sasl/core/SASLXOAUTHMechanism.java | 71 +++++++++ .../jivesoftware/smack/tbr/TBRManager.java | 98 +++++++++++++ .../org/jivesoftware/smack/tbr/TBRTokens.java | 54 +++++++ .../smack/tbr/element/TBRGetTokensIQ.java | 52 +++++++ .../smack/tbr/element/TBRTokensIQ.java | 83 +++++++++++ .../smack/tbr/element/package-info.java | 24 ++++ .../jivesoftware/smack/tbr/package-info.java | 24 ++++ .../smack/tbr/provider/TBRTokensProvider.java | 62 ++++++++ .../smack/tbr/provider/package-info.java | 24 ++++ .../jivesoftware/smack/DummyConnection.java | 5 + .../smack/tbr/TBRGetTokensIQTest.java | 61 ++++++++ .../smack/tcp/XMPPTCPConnection.java | 136 ++++++++++-------- 19 files changed, 866 insertions(+), 105 deletions(-) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRTokens.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRTokensIQ.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/element/package-info.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/package-info.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/TBRTokensProvider.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/package-info.java create mode 100644 smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index 28c556c143..18bd0eaebb 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -15,6 +15,8 @@ Currently supported XEPs of Smack (all subprojects) | Name | XEP | Description | |---------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------| | Nonzas | [XEP-0360](http://xmpp.org/extensions/xep-0360.html) | Defines the term "Nonza", describing every top level stream element that is not a Stanza. | +| Token-based reconnection | [XEP-xxxx](http://www.xmpp.org/extensions/inbox/token-reconnection.html) | Defines a token-based session authentication mechanism. | + Currently supported XEPs of smack-tcp ------------------------------------- diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java index cbc8a26541..282f0cebe7 100644 --- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java +++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java @@ -25,19 +25,31 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.igniterealtime.jbosh.AbstractBody; +import org.igniterealtime.jbosh.BOSHClient; +import org.igniterealtime.jbosh.BOSHClientConfig; +import org.igniterealtime.jbosh.BOSHClientConnEvent; +import org.igniterealtime.jbosh.BOSHClientConnListener; +import org.igniterealtime.jbosh.BOSHClientRequestListener; +import org.igniterealtime.jbosh.BOSHClientResponseListener; +import org.igniterealtime.jbosh.BOSHException; +import org.igniterealtime.jbosh.BOSHMessageEvent; +import org.igniterealtime.jbosh.BodyQName; +import org.igniterealtime.jbosh.ComposableBody; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.ConnectionException; -import org.jivesoftware.smack.XMPPException.StreamErrorException; +import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.XMPPException.StreamErrorException; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jivesoftware.smack.util.PacketParserUtils; @@ -45,17 +57,6 @@ import org.jxmpp.jid.parts.Resourcepart; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserFactory; -import org.igniterealtime.jbosh.AbstractBody; -import org.igniterealtime.jbosh.BOSHClient; -import org.igniterealtime.jbosh.BOSHClientConfig; -import org.igniterealtime.jbosh.BOSHClientConnEvent; -import org.igniterealtime.jbosh.BOSHClientConnListener; -import org.igniterealtime.jbosh.BOSHClientRequestListener; -import org.igniterealtime.jbosh.BOSHClientResponseListener; -import org.igniterealtime.jbosh.BOSHException; -import org.igniterealtime.jbosh.BOSHMessageEvent; -import org.igniterealtime.jbosh.BodyQName; -import org.igniterealtime.jbosh.ComposableBody; /** * Creates a connection to an XMPP server via HTTP binding. @@ -221,6 +222,20 @@ protected void loginInternal(String username, String password, Resourcepart reso // Authenticate using SASL saslAuthentication.authenticate(username, password, config.getAuthzid()); + afterAuth(resource); + } + + @Override + protected void loginInternal(String token, Resourcepart resource) + throws XMPPException, SmackException, IOException, InterruptedException { + // Authenticate using SASL + saslAuthentication.authenticate(token); + + afterAuth(resource); + } + + private void afterAuth(Resourcepart resource) + throws XMPPErrorException, SmackException, InterruptedException, NotConnectedException { bindResourceAndEstablishSession(resource); afterSuccessfulLogin(false); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java index 2f535f5638..295f360a61 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -58,15 +58,15 @@ import org.jivesoftware.smack.iqrequest.IQRequestHandler; import org.jivesoftware.smack.packet.Bind; import org.jivesoftware.smack.packet.ErrorIQ; +import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Mechanisms; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Stanza; -import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Session; +import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StartTls; -import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.StreamError; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.parsing.ParsingExceptionCallback; @@ -286,6 +286,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { private final Map setIqRequestHandler = new HashMap<>(); private final Map getIqRequestHandler = new HashMap<>(); + /** + * Indicates the last refresh token received via the X-OAUTH mechanism. + */ + protected String refreshTokenXOAUTH; + /** * Create a new XMPPConnection to an XMPP server. * @@ -384,13 +389,20 @@ public synchronized AbstractXMPPConnection connect() throws SmackException, IOEx */ protected abstract void connectInternal() throws SmackException, IOException, XMPPException, InterruptedException; - private String usedUsername, usedPassword; + private String usedUsername, usedPassword, usedToken; /** * The resourcepart used for this connection. May not be the resulting resourcepart if it's null or overridden by the XMPP service. */ private Resourcepart usedResource; + /** + * Method to avoid reconnection using token. + */ + public void avoidTokenReconnection() { + usedToken = null; + } + /** * Logs in to the server using the strongest SASL mechanism supported by * the server. If more than the connection's default stanza(/packet) timeout elapses in each step of the @@ -420,7 +432,12 @@ public synchronized void login() throws XMPPException, SmackException, IOExcepti CharSequence username = usedUsername != null ? usedUsername : config.getUsername(); String password = usedPassword != null ? usedPassword : config.getPassword(); Resourcepart resource = usedResource != null ? usedResource : config.getResource(); - login(username, password, resource); + + if (usedToken != null) { + login(usedToken, resource); + } else { + login(username, password, resource); + } } /** @@ -466,9 +483,35 @@ public synchronized void login(CharSequence username, String password, Resourcep loginInternal(usedUsername, usedPassword, usedResource); } + /** + * Login with the given token. Calling this method Smack will try to login + * using the X-OAUTH mechanism. + * + * @param token + * @param resource + * @throws XMPPException + * @throws SmackException + * @throws IOException + * @throws InterruptedException + */ + public synchronized void login(String token, Resourcepart resource) + throws XMPPException, SmackException, IOException, InterruptedException { + + StringUtils.requireNotNullOrEmpty(token, "Token must not be null or empty"); + + throwNotConnectedExceptionIfAppropriate("Did you call connect() before login()?"); + throwAlreadyLoggedInExceptionIfAppropriate(); + usedToken = token; + usedResource = resource; + loginInternal(token, resource); + } + protected abstract void loginInternal(String username, String password, Resourcepart resource) throws XMPPException, SmackException, IOException, InterruptedException; + protected abstract void loginInternal(String token, Resourcepart resource) + throws XMPPException, SmackException, IOException, InterruptedException; + @Override public final boolean isConnected() { return connected; @@ -555,15 +598,34 @@ protected void afterSuccessfulLogin(final boolean resumed) throws NotConnectedEx } } + /** + * Get the X-OAUTH last refresh token. + * + * @return the X-OAUTH last refresh token + */ + @Override + public String getXOAUTHLastRefreshToken() { + return refreshTokenXOAUTH; + } + + /** + * Set the X-OAUTH last refresh token. + */ + @Override + public void setXOAUTHLastRefreshToken(String token) { + refreshTokenXOAUTH = token; + } + @Override public final boolean isAnonymous() { return isAuthenticated() && SASLAnonymous.NAME.equals(getUsedSaslMechansism()); } /** - * Get the name of the SASL mechanism that was used to authenticate this connection. This returns the name of - * mechanism which was used the last time this conneciton was authenticated, and will return null if - * this connection was not authenticated before. + * Get the name of the SASL mechanism that was used to authenticate this + * connection. This returns the name of mechanism which was used the last + * time this connection was authenticated, and will return null + * if this connection was not authenticated before. * * @return the name of the used SASL mechanism. * @since 4.2 diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java index faf90a2916..fe231aa4e7 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SASLAuthentication.java @@ -17,18 +17,6 @@ package org.jivesoftware.smack; -import org.jivesoftware.smack.SmackException.NoResponseException; -import org.jivesoftware.smack.XMPPException.XMPPErrorException; -import org.jivesoftware.smack.packet.Mechanisms; -import org.jivesoftware.smack.sasl.SASLErrorException; -import org.jivesoftware.smack.sasl.SASLMechanism; -import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; -import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; -import org.jxmpp.jid.DomainBareJid; -import org.jxmpp.jid.EntityBareJid; - -import javax.security.auth.callback.CallbackHandler; - import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -40,6 +28,18 @@ import java.util.Set; import java.util.logging.Logger; +import javax.security.auth.callback.CallbackHandler; + +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.packet.Mechanisms; +import org.jivesoftware.smack.sasl.SASLErrorException; +import org.jivesoftware.smack.sasl.SASLMechanism; +import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; +import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; +import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.EntityBareJid; + /** *

This class is responsible authenticating the user using SASL, binding the resource * to the connection and establishing a session with the server.

@@ -63,6 +63,8 @@ public final class SASLAuthentication { private static final List REGISTERED_MECHANISMS = new ArrayList(); + private static SASLMechanism XOAUTH_REGISTERED_MECHANISM; + private static final Set BLACKLISTED_MECHANISMS = new HashSet(); /** @@ -77,6 +79,15 @@ public static void registerSASLMechanism(SASLMechanism mechanism) { } } + /** + * Register a new X-OAUTH SASL mechanism. + * + * @param mechanism + */ + public static void registerXOAUTHSASLMechanism(SASLMechanism mechanism) { + XOAUTH_REGISTERED_MECHANISM = mechanism; + } + /** * Returns the registered SASLMechanism sorted by the level of preference. * @@ -203,13 +214,53 @@ public void authenticate(String username, String password, EntityBareJid authzid } } - if (saslException != null){ + afterAuthenticate(); + } + + /** + * Authenticate with a given token. + * + * @param token + * @throws XMPPErrorException + * @throws SASLErrorException + * @throws IOException + * @throws SmackException + * @throws InterruptedException + */ + public void authenticate(String token) + throws XMPPErrorException, SASLErrorException, IOException, SmackException, InterruptedException { + currentMechanism = selectXOAUTHMechanism(); + + if (currentMechanism == null) { + return; + } + + final CallbackHandler callbackHandler = configuration.getCallbackHandler(); + + synchronized (this) { + currentMechanism.authenticate(token, callbackHandler); + + final long deadline = System.currentTimeMillis() + connection.getPacketReplyTimeout(); + while (!authenticationSuccessful && saslException == null) { + final long now = System.currentTimeMillis(); + if (now > deadline) + break; + // Wait until SASL negotiation finishes + wait(deadline - now); + } + } + + afterAuthenticate(); + } + + private void afterAuthenticate() throws SmackException, SASLErrorException, NoResponseException { + if (saslException != null) { if (saslException instanceof SmackException) { throw (SmackException) saslException; } else if (saslException instanceof SASLErrorException) { throw (SASLErrorException) saslException; } else { - throw new IllegalStateException("Unexpected exception type" , saslException); + throw new IllegalStateException("Unexpected exception type", saslException); } } @@ -219,12 +270,13 @@ public void authenticate(String username, String password, EntityBareJid authzid } /** - * Wrapper for {@link #challengeReceived(String, boolean)}, with finalChallenge set - * to false. + * Wrapper for {@link #challengeReceived(String, boolean)}, with + * finalChallenge set to false. * - * @param challenge a base64 encoded string representing the challenge. + * @param challenge + * a base64 encoded string representing the challenge. * @throws SmackException - * @throws InterruptedException + * @throws InterruptedException */ public void challengeReceived(String challenge) throws SmackException, InterruptedException { challengeReceived(challenge, false); @@ -314,6 +366,24 @@ String getNameOfLastUsedSaslMechansism() { return lastUsedMech.getName(); } + private SASLMechanism selectXOAUTHMechanism() throws SmackException { + SASLMechanism mechanism = XOAUTH_REGISTERED_MECHANISM; + + final List serverMechanisms = getServerMechanisms(); + if (serverMechanisms.isEmpty()) { + LOGGER.warning("Server did not report any SASL mechanisms"); + } + + String mechanismName = mechanism.getName(); + + if (serverMechanisms.contains(mechanismName)) { + return mechanism.instanceForAuthentication(connection); + } else { + return null; + } + + } + private SASLMechanism selectMechanism(EntityBareJid authzid) throws SmackException { Iterator it = REGISTERED_MECHANISMS.iterator(); final List serverMechanisms = getServerMechanisms(); @@ -345,6 +415,8 @@ private SASLMechanism selectMechanism(EntityBareJid authzid) throws SmackExcepti } synchronized (BLACKLISTED_MECHANISMS) { + List mechanisms = REGISTERED_MECHANISMS; + mechanisms.add(XOAUTH_REGISTERED_MECHANISM); // @formatter:off throw new SmackException( "No supported and enabled SASL Mechanism provided by server. " + diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java index a674c36297..90402797fb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackInitialization.java @@ -33,8 +33,11 @@ import org.jivesoftware.smack.provider.BindIQProvider; import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smack.sasl.core.SASLAnonymous; +import org.jivesoftware.smack.sasl.core.SASLXOAUTHMechanism; import org.jivesoftware.smack.sasl.core.SASLXOauth2Mechanism; import org.jivesoftware.smack.sasl.core.SCRAMSHA1Mechanism; +import org.jivesoftware.smack.tbr.element.TBRTokensIQ; +import org.jivesoftware.smack.tbr.provider.TBRTokensProvider; import org.jivesoftware.smack.util.FileUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -140,7 +143,10 @@ public final class SmackInitialization { SASLAuthentication.registerSASLMechanism(new SASLXOauth2Mechanism()); SASLAuthentication.registerSASLMechanism(new SASLAnonymous()); + SASLAuthentication.registerXOAUTHSASLMechanism(new SASLXOAUTHMechanism()); + ProviderManager.addIQProvider(Bind.ELEMENT, Bind.NAMESPACE, new BindIQProvider()); + ProviderManager.addIQProvider(TBRTokensIQ.ELEMENT, TBRTokensIQ.NAMESPACE, new TBRTokensProvider()); SmackConfiguration.smackInitialized = true; } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java index 6acc76240d..99ac3cfb93 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java @@ -22,10 +22,10 @@ import org.jivesoftware.smack.filter.IQReplyFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.iqrequest.IQRequestHandler; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Nonza; +import org.jivesoftware.smack.packet.Stanza; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityFullJid; @@ -625,4 +625,18 @@ public void sendIqWithResponseCallback(IQ iqRequest, final StanzaListener callba */ public long getLastStanzaReceived(); + /** + * Set the X-OAUTH last refresh token. + * + * @param token + */ + public void setXOAUTHLastRefreshToken(String token); + + /** + * Get the X-OAUTH last refresh token. + * + * @return the X-OAUTH last refresh token + */ + public String getXOAUTHLastRefreshToken(); + } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java index 198985e493..3a94612318 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java @@ -16,6 +16,8 @@ */ package org.jivesoftware.smack.sasl; +import javax.security.auth.callback.CallbackHandler; + import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; @@ -27,8 +29,6 @@ import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; -import javax.security.auth.callback.CallbackHandler; - /** * Base class for SASL mechanisms. Subclasses must implement these methods: *
    @@ -73,6 +73,7 @@ public abstract class SASLMechanism implements Comparable { public static final String EXTERNAL = "EXTERNAL"; public static final String GSSAPI = "GSSAPI"; public static final String PLAIN = "PLAIN"; + public static final String XOAUTH = "X-OAUTH"; // TODO Remove once Smack's min Android API is 9, where java.text.Normalizer is available private static StringTransformer saslPrepTransformer; @@ -181,6 +182,19 @@ public final void authenticate(String username, String host, DomainBareJid servi authenticate(); } + /** + * Authenticate with token. + * + * @param token + * @param cbh + * @throws SmackException + * @throws InterruptedException + */ + public final void authenticate(String token, CallbackHandler cbh) throws SmackException, InterruptedException { + authenticateInternal(cbh); + authenticateXOAUTH(token); + } + /** * @throws SmackException */ @@ -229,6 +243,10 @@ private final void authenticate() throws SmackException, NotConnectedException, connection.sendNonza(new AuthMechanism(getName(), authenticationText)); } + private void authenticateXOAUTH(String token) throws NotConnectedException, InterruptedException { + connection.sendNonza(new AuthMechanism(getName(), token)); + } + /** * Should return the initial response of the SASL mechanism. The returned byte array will be * send base64 encoded to the server. SASL mechanism are free to return null or an diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java new file mode 100644 index 0000000000..91f2513c35 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java @@ -0,0 +1,71 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.sasl.core; + +import javax.security.auth.callback.CallbackHandler; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.sasl.SASLMechanism; +import org.jivesoftware.smack.util.stringencoder.Base64; + +public class SASLXOAUTHMechanism extends SASLMechanism { + + public static final String NAME = XOAUTH; + + public String getName() { + return NAME; + } + + @Override + public boolean authzidSupported() { + return true; + } + + @Override + public int getPriority() { + return 600; + } + + @Override + public SASLXOAUTHMechanism newInstance() { + return new SASLXOAUTHMechanism(); + } + + @Override + protected void authenticateInternal(CallbackHandler cbh) throws SmackException { + // Nothing to do here + } + + @Override + protected byte[] getAuthenticationText() throws SmackException { + // Nothing to do here + return null; + } + + @Override + public void checkIfSuccessfulOrThrow() throws SmackException { + // No check performed + } + + @Override + protected byte[] evaluateChallenge(byte[] challenge) throws SmackException { + String refreshToken = Base64.encodeToString(challenge); + connection.setXOAUTHLastRefreshToken(refreshToken); + return null; + } + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java new file mode 100644 index 0000000000..09e1ab2df2 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java @@ -0,0 +1,98 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.tbr; + +import java.util.Map; +import java.util.WeakHashMap; + +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.SmackException.NoResponseException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPConnectionRegistry; +import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.filter.IQReplyFilter; +import org.jivesoftware.smack.filter.StanzaFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.tbr.element.TBRGetTokensIQ; +import org.jivesoftware.smack.tbr.element.TBRTokensIQ; + +/** + * Token-based reconnection manager class. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +public final class TBRManager extends Manager { + + public static final String AUTH_NAMESPACE = "erlang-solutions.com:xmpp:token-auth:0"; + + static { + XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(XMPPConnection connection) { + getInstanceFor(connection); + } + }); + } + + private static final Map INSTANCES = new WeakHashMap<>(); + + /** + * Get the singleton instance of TBRManager. + * + * @param connection + * @return the instance of TBRManager + */ + public static synchronized TBRManager getInstanceFor(XMPPConnection connection) { + TBRManager tbrManager = INSTANCES.get(connection); + + if (tbrManager == null) { + tbrManager = new TBRManager(connection); + INSTANCES.put(connection, tbrManager); + } + + return tbrManager; + } + + private TBRManager(XMPPConnection connection) { + super(connection); + } + + /** + * Get tokens. + * + * @return the tokens + * @throws NoResponseException + * @throws XMPPErrorException + * @throws NotConnectedException + * @throws InterruptedException + */ + public TBRTokens getTokens() + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + TBRGetTokensIQ getTokensIQ = new TBRGetTokensIQ(connection().getUser().asBareJid()); + StanzaFilter responseFilter = new IQReplyFilter(getTokensIQ, connection()); + + IQ responseIq = connection().createPacketCollectorAndSend(responseFilter, getTokensIQ).nextResultOrThrow(); + TBRTokensIQ tokensIQ = (TBRTokensIQ) responseIq; + + return new TBRTokens(tokensIQ.getAccessToken(), tokensIQ.getRefreshToken()); + } + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRTokens.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRTokens.java new file mode 100644 index 0000000000..a29e39103f --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRTokens.java @@ -0,0 +1,54 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.tbr; + +/** + * Token-based reconnection token model class. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +public class TBRTokens { + + private String accessToken; + private String refreshToken; + + public TBRTokens(String accessToken, String refreshToken) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + + /** + * Get the access token. + * + * @return the access token + */ + public String getAccessToken() { + return accessToken; + } + + /** + * Get the refresh token. + * + * @return the refresh token + */ + public String getRefreshToken() { + return refreshToken; + } + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java new file mode 100644 index 0000000000..3aebcd6464 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java @@ -0,0 +1,52 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.tbr.element; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.tbr.TBRManager; +import org.jxmpp.jid.Jid; + +/** + * TBR get tokens IQ class. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +public class TBRGetTokensIQ extends IQ { + + public static final String NAMESPACE = TBRManager.AUTH_NAMESPACE; + public static final String ELEMENT = QUERY_ELEMENT; + + /** + * TBR get tokens IQ constructor. + * + * @param to + */ + public TBRGetTokensIQ(Jid to) { + super(ELEMENT, NAMESPACE); + this.setType(Type.get); + this.setTo(to); + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + xml.rightAngleBracket(); + return xml; + } + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRTokensIQ.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRTokensIQ.java new file mode 100644 index 0000000000..cbc303a9e9 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRTokensIQ.java @@ -0,0 +1,83 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.tbr.element; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.tbr.TBRManager; + +/** + * TBR tokens IQ class. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +public class TBRTokensIQ extends IQ { + + public static final String NAMESPACE = TBRManager.AUTH_NAMESPACE; + public static final String ELEMENT = "items"; + + private final String accessToken; + private final String refreshToken; + + /** + * TBR tokens IQ constructor. + * + * @param accessToken + * @param refreshToken + */ + public TBRTokensIQ(String accessToken, String refreshToken) { + super(ELEMENT, NAMESPACE); + this.setType(Type.result); + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + + /** + * Get the access token. + * + * @return the access token + */ + public String getAccessToken() { + return accessToken; + } + + /** + * Get the refresh token. + * + * @return the refresh token + */ + public String getRefreshToken() { + return refreshToken; + } + + @Override + protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { + xml.rightAngleBracket(); + + if (accessToken != null) { + xml.element("access_token", accessToken); + } + + if (refreshToken != null) { + xml.element("refresh_token", refreshToken); + } + + return xml; + } + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/package-info.java new file mode 100644 index 0000000000..5e16761b1f --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/package-info.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ +/** + * Token-based reconnection elements. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +package org.jivesoftware.smack.tbr.element; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/package-info.java new file mode 100644 index 0000000000..d5f4fce41b --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/package-info.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ +/** + * Token-based reconnection classes and interfaces. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +package org.jivesoftware.smack.tbr; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/TBRTokensProvider.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/TBRTokensProvider.java new file mode 100644 index 0000000000..1b3e5bdd7c --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/TBRTokensProvider.java @@ -0,0 +1,62 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.tbr.provider; + +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.tbr.element.TBRTokensIQ; +import org.xmlpull.v1.XmlPullParser; + +/** + * TBR tokens provider class. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +public class TBRTokensProvider extends IQProvider { + + @Override + public TBRTokensIQ parse(XmlPullParser parser, int initialDepth) throws Exception { + String accessToken = null; + String refreshToken = null; + + outerloop: while (true) { + int eventType = parser.next(); + + if (eventType == XmlPullParser.START_TAG) { + + if (parser.getName().equals("access_token")) { + accessToken = parser.nextText(); + } + + if (parser.getName().equals("refresh_token")) { + refreshToken = parser.nextText(); + } + + } else if (eventType == XmlPullParser.END_TAG) { + + if (parser.getDepth() == initialDepth) { + break outerloop; + } + + } + } + + return new TBRTokensIQ(accessToken, refreshToken); + } + +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/package-info.java new file mode 100644 index 0000000000..3a523af9a1 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/provider/package-info.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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. + */ +/** + * Token-based reconnection providers. + * + * @author Fernando Ramirez + * @see + * XEP-xxxx: Token-based reconnection + */ +package org.jivesoftware.smack.tbr.provider; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java index eb1e50647e..c6626f39ff 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java @@ -231,4 +231,9 @@ protected Builder getThis() { } } } + + @Override + protected void loginInternal(String token, Resourcepart resource) throws XMPPException, SmackException, IOException, InterruptedException { + // TODO Auto-generated method stub + } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java b/smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java new file mode 100644 index 0000000000..9969a9c68f --- /dev/null +++ b/smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java @@ -0,0 +1,61 @@ +/** + * + * Copyright 2016 Fernando Ramirez + * + * 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.tbr; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.tbr.element.TBRGetTokensIQ; +import org.jivesoftware.smack.tbr.element.TBRTokensIQ; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.junit.Assert; +import org.junit.Test; +import org.jxmpp.jid.impl.JidCreate; + +public class TBRGetTokensIQTest { + + String getTokensIQExample = "" + + "" + ""; + + String tokensResponseExample = "" + + "" + + "YWNjZXNzAGFsaWNlQHdvbmRlcmxhbmQuY29tL01pY2hhbC1QaW90cm93c2tpcy1NYWNCb29rLVBybwA2MzYyMTg4Mzc2NAA4M2QwNzNiZjBkOGJlYzVjZmNkODgyY2ZlMzkyZWM5NGIzZjA4ODNlNDI4ZjQzYjc5MGYxOWViM2I2ZWJlNDc0ODc3MDkxZTIyN2RhOGMwYTk2ZTc5ODBhNjM5NjE1Zjk=" + + "cmVmcmVzaABhbGljZUB3b25kZXJsYW5kLmNvbS9NaWNoYWwtUGlvdHJvd3NraXMtTWFjQm9vay1Qcm8ANjM2MjMwMDYxODQAMQAwZGQxOGJjODhkMGQ0N2MzNTBkYzAwYjcxZjMyZDVmOWIwOTljMmI1ODU5MmNhN2QxZGFmNWFkNGM0NDQ2ZGU2MWYxYzdhNTJjNDUyMGI5YmIxNGIxNTMwMTE4YTM1NTc=" + + "" + ""; + + @Test + public void checkGetTokensIQ() throws Exception { + TBRGetTokensIQ tbrGetTokensIQ = new TBRGetTokensIQ(JidCreate.from("alice@wonderland.com")); + tbrGetTokensIQ.setStanzaId("123"); + + Assert.assertEquals(getTokensIQExample, tbrGetTokensIQ.toXML().toString()); + } + + @Test + public void checkTokensResponseParse() throws Exception { + IQ iq = (IQ) PacketParserUtils.parseStanza(tokensResponseExample); + TBRTokensIQ tokensIQ = (TBRTokensIQ) iq; + tokensIQ.setStanzaId("123"); + + Assert.assertEquals( + "YWNjZXNzAGFsaWNlQHdvbmRlcmxhbmQuY29tL01pY2hhbC1QaW90cm93c2tpcy1NYWNCb29rLVBybwA2MzYyMTg4Mzc2NAA4M2QwNzNiZjBkOGJlYzVjZmNkODgyY2ZlMzkyZWM5NGIzZjA4ODNlNDI4ZjQzYjc5MGYxOWViM2I2ZWJlNDc0ODc3MDkxZTIyN2RhOGMwYTk2ZTc5ODBhNjM5NjE1Zjk=", + tokensIQ.getAccessToken()); + Assert.assertEquals( + "cmVmcmVzaABhbGljZUB3b25kZXJsYW5kLmNvbS9NaWNoYWwtUGlvdHJvd3NraXMtTWFjQm9vay1Qcm8ANjM2MjMwMDYxODQAMQAwZGQxOGJjODhkMGQ0N2MzNTBkYzAwYjcxZjMyZDVmOWIwOTljMmI1ODU5MmNhN2QxZGFmNWFkNGM0NDQ2ZGU2MWYxYzdhNTJjNDUyMGI5YmIxNGIxNTMwMTE4YTM1NTc=", + tokensIQ.getRefreshToken()); + Assert.assertEquals(tokensResponseExample, tokensIQ.toXML().toString()); + } + +} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index d1db146cb5..c40a11c582 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -16,37 +16,92 @@ */ package org.jivesoftware.smack.tcp; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.lang.reflect.Constructor; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.SocketFactory; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; + import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; -import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.AlreadyConnectedException; import org.jivesoftware.smack.SmackException.AlreadyLoggedInException; +import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; -import org.jivesoftware.smack.SmackException.ConnectionException; import org.jivesoftware.smack.SmackException.SecurityRequiredByClientException; import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException; import org.jivesoftware.smack.SmackException.SecurityRequiredException; +import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.SynchronizationPoint; -import org.jivesoftware.smack.XMPPException.StreamErrorException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.XMPPException.StreamErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.compress.packet.Compress; import org.jivesoftware.smack.compress.packet.Compressed; import org.jivesoftware.smack.compression.XMPPInputOutputStream; import org.jivesoftware.smack.filter.StanzaFilter; -import org.jivesoftware.smack.compress.packet.Compress; import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.StreamOpen; -import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StartTls; +import org.jivesoftware.smack.packet.StreamOpen; +import org.jivesoftware.smack.packet.XMPPError; +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; @@ -67,9 +122,6 @@ import org.jivesoftware.smack.sm.packet.StreamManagement.StreamManagementFeature; import org.jivesoftware.smack.sm.predicates.Predicate; import org.jivesoftware.smack.sm.provider.ParseStreamManagement; -import org.jivesoftware.smack.packet.Nonza; -import org.jivesoftware.smack.packet.XMPPError; -import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown; import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.PacketParserUtils; @@ -84,58 +136,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import javax.net.SocketFactory; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.PasswordCallback; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.lang.reflect.Constructor; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.Provider; -import java.security.Security; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import java.util.logging.Logger; - /** * Creates a socket connection to an XMPP server. This is the default connection * to an XMPP server and is specified in the XMPP Core (RFC 6120). @@ -380,6 +380,11 @@ protected synchronized void loginInternal(String username, String password, Reso // Authenticate using SASL saslAuthentication.authenticate(username, password, config.getAuthzid()); + afterAuth(resource); + } + + private void afterAuth(Resourcepart resource) throws NotConnectedException, NoResponseException, SmackException, + InterruptedException, XMPPErrorException, XMPPException { // If compression is enabled then request the server to use stream compression. XEP-170 // recommends to perform stream compression before resource binding. maybeEnableCompression(); @@ -438,6 +443,15 @@ protected synchronized void loginInternal(String username, String password, Reso afterSuccessfulLogin(false); } + @Override + protected void loginInternal(String token, Resourcepart resource) + throws XMPPException, SmackException, IOException, InterruptedException { + // Authenticate using SASL + saslAuthentication.authenticate(token); + + afterAuth(resource); + } + @Override public boolean isSecureConnection() { return usingTLS; From c93b455f95c9df560ee525bf97873f3d586cf2c4 Mon Sep 17 00:00:00 2001 From: ramabit Date: Wed, 27 Jul 2016 16:31:31 -0300 Subject: [PATCH 2/5] fixes --- .../java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java | 2 +- .../java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java index 3aebcd6464..6ccf124dbf 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/element/TBRGetTokensIQ.java @@ -45,7 +45,7 @@ public TBRGetTokensIQ(Jid to) { @Override protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { - xml.rightAngleBracket(); + xml.setEmptyElement(); return xml; } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java b/smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java index 9969a9c68f..b37e737259 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/tbr/TBRGetTokensIQTest.java @@ -27,7 +27,7 @@ public class TBRGetTokensIQTest { String getTokensIQExample = "" - + "" + ""; + + "" + ""; String tokensResponseExample = "" + "" From dd05cf7581cf6123bff0f3eef27d4eca1dabd30d Mon Sep 17 00:00:00 2001 From: ramabit Date: Fri, 29 Jul 2016 12:44:19 -0300 Subject: [PATCH 3/5] refactoring --- .../src/main/java/org/jivesoftware/smack/tbr/TBRManager.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java b/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java index 09e1ab2df2..1921e71a46 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/tbr/TBRManager.java @@ -26,8 +26,6 @@ import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPException.XMPPErrorException; -import org.jivesoftware.smack.filter.IQReplyFilter; -import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.tbr.element.TBRGetTokensIQ; import org.jivesoftware.smack.tbr.element.TBRTokensIQ; @@ -87,9 +85,8 @@ private TBRManager(XMPPConnection connection) { public TBRTokens getTokens() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { TBRGetTokensIQ getTokensIQ = new TBRGetTokensIQ(connection().getUser().asBareJid()); - StanzaFilter responseFilter = new IQReplyFilter(getTokensIQ, connection()); - IQ responseIq = connection().createPacketCollectorAndSend(responseFilter, getTokensIQ).nextResultOrThrow(); + IQ responseIq = connection().createPacketCollectorAndSend(getTokensIQ).nextResultOrThrow(); TBRTokensIQ tokensIQ = (TBRTokensIQ) responseIq; return new TBRTokens(tokensIQ.getAccessToken(), tokensIQ.getRefreshToken()); From 0d9ede2ae1cc59a754775bcc81f717b6b555a2fb Mon Sep 17 00:00:00 2001 From: ramabit Date: Fri, 29 Jul 2016 12:57:58 -0300 Subject: [PATCH 4/5] adding documentation --- documentation/extensions/index.md | 2 +- documentation/extensions/tokenreconnection.md | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 documentation/extensions/tokenreconnection.md diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index 18bd0eaebb..794a978705 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -15,7 +15,7 @@ Currently supported XEPs of Smack (all subprojects) | Name | XEP | Description | |---------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------| | Nonzas | [XEP-0360](http://xmpp.org/extensions/xep-0360.html) | Defines the term "Nonza", describing every top level stream element that is not a Stanza. | -| Token-based reconnection | [XEP-xxxx](http://www.xmpp.org/extensions/inbox/token-reconnection.html) | Defines a token-based session authentication mechanism. | +| [Token-based reconnection](tokenreconnection.md) | [XEP-xxxx](http://www.xmpp.org/extensions/inbox/token-reconnection.html) | Defines a token-based session authentication mechanism. | Currently supported XEPs of smack-tcp diff --git a/documentation/extensions/tokenreconnection.md b/documentation/extensions/tokenreconnection.md new file mode 100644 index 0000000000..ccfa9c3617 --- /dev/null +++ b/documentation/extensions/tokenreconnection.md @@ -0,0 +1,50 @@ +Token-based reconnection +======================== + +Allows to manage communications blocking. + + * Login with token + * Get tokens + * Get last refresh token from "success" nonza + * Avoid reconnection using token + + +**XEP related:** [XEP-xxxx](http://www.xmpp.org/extensions/inbox/token-reconnection.html) + + +Login with token +---------------- + +``` +xmppConnection.login(token, resourcepart); +``` +*token* is a `String` + +*resourcepart* is a `Resourcepart` + + +Get tokens +---------- + +``` +TBRManager tbrManager = TBRManager.getInstanceFor(xmppConnection); +TBRTokens tbrTokens = tbrManager.getTokens(); +String accessToken = tbrTokens.getAccessToken(); +String refreshToken = tbrTokens.getRefreshToken(); +``` + + +Get last refresh token from "success" nonza +------------------------------------------- + +``` +String refreshToken = xmppConnection.getXOAUTHLastRefreshToken(); +``` + + +Avoid reconnection using token +------------------------------ + +``` +xmppConnection.avoidTokenReconnection(); +``` From bd51b8225128e976e9b34a04e0094520d63d31f8 Mon Sep 17 00:00:00 2001 From: Fernando Ramirez Date: Tue, 11 Apr 2017 11:33:52 -0300 Subject: [PATCH 5/5] small fix --- .../org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java | 1 + 1 file changed, 1 insertion(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java index 91f2513c35..60720ad30b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/SASLXOAUTHMechanism.java @@ -26,6 +26,7 @@ public class SASLXOAUTHMechanism extends SASLMechanism { public static final String NAME = XOAUTH; + @Override public String getName() { return NAME; }