diff --git a/.travis.yml b/.travis.yml index 94cc1fb74..10f7b90f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,16 +35,7 @@ install: gradle assemble --stacktrace # functional. Which hasn't always be the case in the past, see # 90cbcaebc7a89f4f771f733a33ac9f389df85be2 # Also run javadocAll to ensure it works. -script: - - | - JAVAC_MAJOR_VERSION=$(javac -version | sed -E 's/javac ([[:digit:]]+).*/\1/') - GRADLE_TASKS=() - GRADLE_TASKS+=(check) - GRADLE_TASKS+=(publishToMavenLocal) - if [[ ${JAVAC_MAJOR_VERSION} -ge 11 ]]; then - GRADLE_TASKS+=(javadocAll) - fi - gradle ${GRADLE_TASKS[@]} --stacktrace +script: gradle check publishToMavenLocal javadocAll --stacktrace after_success: - JAVAC_VERSION=$((javac -version) 2>&1) diff --git a/build.gradle b/build.gradle index 96dcab393..de43fba80 100644 --- a/build.gradle +++ b/build.gradle @@ -122,10 +122,10 @@ allprojects { // See also: // - https://issues.apache.org/jira/browse/MNG-6232 // - https://issues.igniterealtime.org/browse/SMACK-858 - jxmppVersion = '0.7.0-alpha6' - miniDnsVersion = '0.4.0-alpha5' + jxmppVersion = '0.7.0-alpha5' + miniDnsVersion = '0.4.0-alpha3' smackMinAndroidSdk = 19 - junitVersion = '5.6.2' + junitVersion = '5.6.0' commonsIoVersion = '2.6' bouncyCastleVersion = '1.65' @@ -283,7 +283,7 @@ tasks.withType(Javadoc) { testFixturesApi "org.mockito:mockito-core:3.3.3" testImplementation 'com.jamesmurty.utils:java-xmlbuilder:1.2' - errorprone 'com.google.errorprone:error_prone_core:2.3.4' + errorprone 'com.google.errorprone:error_prone_core:2.3.3' errorproneJavac('com.google.errorprone:javac:9+181-r4173-1') } @@ -409,7 +409,7 @@ subprojects { apply plugin: 'org.kordamp.gradle.clirr' checkstyle { - toolVersion = '8.27' + toolVersion = '8.22' } task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' @@ -441,7 +441,7 @@ subprojects { packaging = 'jar' inceptionYear = '2003' url = 'http://www.igniterealtime.org/projects/smack/' - description = project.description + description project.description issueManagement { system = 'JIRA' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ce6b4200d..c62f00a71 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -104,7 +104,13 @@ - + + + + + + + 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 136976a09..87f6f091e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -16,9 +16,24 @@ */ package org.jivesoftware.smack; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.Reader; import java.io.Writer; +import java.lang.reflect.Constructor; +import java.nio.charset.StandardCharsets; +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.SecureRandom; +import java.security.Security; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -41,9 +56,18 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; 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; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; import javax.xml.namespace.QName; +import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.SmackConfiguration.UnknownIqRequestReplyMode; import org.jivesoftware.smack.SmackException.AlreadyConnectedException; @@ -68,7 +92,6 @@ import org.jivesoftware.smack.debugger.SmackDebuggerFactory; import org.jivesoftware.smack.filter.IQReplyFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaIdFilter; -import org.jivesoftware.smack.internal.SmackTlsContext; import org.jivesoftware.smack.iqrequest.IQRequestHandler; import org.jivesoftware.smack.packet.Bind; import org.jivesoftware.smack.packet.ErrorIQ; @@ -105,12 +128,16 @@ import org.jivesoftware.smack.sasl.packet.SaslNonza; import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.Consumer; +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; import org.jivesoftware.smack.util.Predicate; import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.TLSUtils; +import org.jivesoftware.smack.util.dns.SmackDaneProvider; +import org.jivesoftware.smack.util.dns.SmackDaneVerifier; import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; @@ -345,6 +372,17 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { protected final AsyncButOrdered inOrderListeners = new AsyncButOrdered<>(); + /** + * An executor which uses {@link #asyncGoLimited(Runnable)} to limit the number of asynchronously processed runnables + * per connection. + */ + private final Executor limitedExcutor = new Executor() { + @Override + public void execute(Runnable runnable) { + asyncGoLimited(runnable); + } + }; + /** * The used host to establish the connection to */ @@ -1485,7 +1523,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { executorService = ASYNC_BUT_ORDERED.asExecutorFor(this); break; case async: - executorService = this::asyncGoLimited; + executorService = limitedExcutor; break; } final IQRequestHandler finalIqRequestHandler = iqRequestHandler; @@ -2124,7 +2162,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } protected static ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit) { - return SMACK_REACTOR.schedule(runnable, delay, unit, ScheduledAction.Kind.NonBlocking); + return SMACK_REACTOR.schedule(runnable, delay, unit); } protected void onStreamOpen(XmlPullParser parser) { @@ -2175,7 +2213,148 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { outgoingStreamXmlEnvironment = xmlEnvironmentBuilder.build(); } - protected final SmackTlsContext getSmackTlsContext() { - return config.smackTlsContext; + public static final class SmackTlsContext { + public final SSLContext sslContext; + public final SmackDaneVerifier daneVerifier; + + private SmackTlsContext(SSLContext sslContext, SmackDaneVerifier daneVerifier) { + assert sslContext != null; + this.sslContext = sslContext; + this.daneVerifier = daneVerifier; + } + } + + protected final SmackTlsContext getSmackTlsContext() throws KeyManagementException, NoSuchAlgorithmException, + CertificateException, IOException, UnrecoverableKeyException, KeyStoreException, NoSuchProviderException { + SmackDaneVerifier daneVerifier = null; + + if (config.getDnssecMode() == DnssecMode.needsDnssecAndDane) { + SmackDaneProvider daneProvider = DNSUtil.getDaneProvider(); + if (daneProvider == null) { + throw new UnsupportedOperationException("DANE enabled but no SmackDaneProvider configured"); + } + daneVerifier = daneProvider.newInstance(); + if (daneVerifier == null) { + throw new IllegalStateException("DANE requested but DANE provider did not return a DANE verifier"); + } + } + + SSLContext context = this.config.getCustomSSLContext(); + KeyStore ks = null; + PasswordCallback pcb = null; + + if (context == null) { + final String keyStoreType = config.getKeystoreType(); + final CallbackHandler callbackHandler = config.getCallbackHandler(); + final String keystorePath = config.getKeystorePath(); + if ("PKCS11".equals(keyStoreType)) { + try { + Constructor c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class); + String pkcs11Config = "name = SmartCard\nlibrary = " + config.getPKCS11Library(); + ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8)); + Provider p = (Provider) c.newInstance(config); + Security.addProvider(p); + ks = KeyStore.getInstance("PKCS11", p); + pcb = new PasswordCallback("PKCS11 Password: ", false); + callbackHandler.handle(new Callback[] {pcb}); + ks.load(null, pcb.getPassword()); + } + catch (Exception e) { + LOGGER.log(Level.WARNING, "Exception", e); + ks = null; + } + } + else if ("Apple".equals(keyStoreType)) { + ks = KeyStore.getInstance("KeychainStore", "Apple"); + ks.load(null, null); + // pcb = new PasswordCallback("Apple Keychain",false); + // pcb.setPassword(null); + } + else if (keyStoreType != null) { + ks = KeyStore.getInstance(keyStoreType); + if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) { + try { + pcb = new PasswordCallback("Keystore Password: ", false); + callbackHandler.handle(new Callback[] { pcb }); + ks.load(new FileInputStream(keystorePath), pcb.getPassword()); + } + catch (Exception e) { + LOGGER.log(Level.WARNING, "Exception", e); + ks = null; + } + } else { + InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); + try { + // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous + // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702 + char[] password = "changeit".toCharArray(); + try { + ks.load(stream, password); + } finally { + stream.close(); + } + } catch (IOException e) { + LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e); + + ks = KeyStore.getInstance("jks"); + // Open the stream again, so that we read it from the beginning. + stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); + try { + ks.load(stream, null); + } finally { + stream.close(); + } + } + } + } + + KeyManager[] kms = null; + + if (ks != null) { + String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); + KeyManagerFactory kmf = null; + try { + kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm); + } + catch (NoSuchAlgorithmException e) { + LOGGER.log(Level.FINE, "Could get the default KeyManagerFactory for the '" + + keyManagerFactoryAlgorithm + "' algorithm", e); + } + if (kmf != null) { + try { + if (pcb == null) { + kmf.init(ks, null); + } + else { + kmf.init(ks, pcb.getPassword()); + pcb.clearPassword(); + } + kms = kmf.getKeyManagers(); + } + catch (NullPointerException npe) { + LOGGER.log(Level.WARNING, "NullPointerException", npe); + } + } + } + + // If the user didn't specify a SSLContext, use the default one + context = SSLContext.getInstance("TLS"); + + final SecureRandom secureRandom = new java.security.SecureRandom(); + X509TrustManager trustManager = config.getCustomX509TrustManager(); + if (trustManager == null) { + trustManager = TLSUtils.getDefaultX509TrustManager(ks); + } + + if (daneVerifier != null) { + // User requested DANE verification. + daneVerifier.init(context, kms, trustManager, secureRandom); + } else { + TrustManager[] customTrustManagers = new TrustManager[] { trustManager }; + context.init(kms, customTrustManagers, secureRandom); + } + } + + return new SmackTlsContext(context, daneVerifier); } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java index e6009754b..3d59269c7 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -17,24 +17,9 @@ package org.jivesoftware.smack; -import java.io.ByteArrayInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; -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.Arrays; import java.util.Collection; import java.util.Collections; @@ -46,34 +31,20 @@ 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.TrustManager; import javax.net.ssl.X509TrustManager; -import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.debugger.SmackDebuggerFactory; -import org.jivesoftware.smack.internal.SmackTlsContext; import org.jivesoftware.smack.packet.id.StandardStanzaIdSource; import org.jivesoftware.smack.packet.id.StanzaIdSource; import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory; import org.jivesoftware.smack.proxy.ProxyInfo; import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.sasl.core.SASLAnonymous; -import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smack.util.CollectionUtil; -import org.jivesoftware.smack.util.DNSUtil; import org.jivesoftware.smack.util.Objects; -import org.jivesoftware.smack.util.SslContextFactory; import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smack.util.TLSUtils; -import org.jivesoftware.smack.util.dns.SmackDaneProvider; -import org.jivesoftware.smack.util.dns.SmackDaneVerifier; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; @@ -130,7 +101,12 @@ public abstract class ConnectionConfiguration { protected final InetAddress hostAddress; protected final DnsName host; - protected final UInt16 port; + protected final int port; + + private final String keystorePath; + private final String keystoreType; + private final String pkcs11Library; + private final SSLContext customSSLContext; /** * Used to get information from the user @@ -161,10 +137,10 @@ public abstract class ConnectionConfiguration { private final SecurityMode securityMode; - final SmackTlsContext smackTlsContext; - private final DnssecMode dnssecMode; + private final X509TrustManager customX509TrustManager; + /** * */ @@ -189,17 +165,6 @@ public abstract class ConnectionConfiguration { private final StanzaIdSourceFactory stanzaIdSourceFactory; protected ConnectionConfiguration(Builder builder) { - try { - smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory, - builder.customX509TrustManager, builder.keystoreType, builder.keystorePath, - builder.callbackHandler, builder.pkcs11Library); - } catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException - | KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException - | SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException | UnsupportedCallbackException e) { - throw new IllegalArgumentException(e); - } - authzid = builder.authzid; username = builder.username; password = builder.password; @@ -236,7 +201,13 @@ public abstract class ConnectionConfiguration { dnssecMode = builder.dnssecMode; + customX509TrustManager = builder.customX509TrustManager; + securityMode = builder.securityMode; + keystoreType = builder.keystoreType; + keystorePath = builder.keystorePath; + pkcs11Library = builder.pkcs11Library; + customSSLContext = builder.customSSLContext; enabledSSLProtocols = builder.enabledSSLProtocols; enabledSSLCiphers = builder.enabledSSLCiphers; hostnameVerifier = builder.hostnameVerifier; @@ -251,115 +222,11 @@ public abstract class ConnectionConfiguration { // If the enabledSaslmechanisms are set, then they must not be empty assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty(); - } - private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory, - X509TrustManager trustManager, String keystoreType, String keystorePath, - CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException, - CertificateException, IOException, KeyStoreException, NoSuchProviderException, - UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException, - NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, - IllegalAccessException, IllegalArgumentException, InvocationTargetException { - final SSLContext context; - if (sslContextFactory != null) { - context = sslContextFactory.createSslContext(); - } else { - // If the user didn't specify a SslContextFactory, use the default one - context = SSLContext.getInstance("TLS"); + if (dnssecMode != DnssecMode.disabled && customSSLContext != null) { + throw new IllegalStateException("You can not use a custom SSL context with DNSSEC enabled"); } - KeyStore ks = null; - PasswordCallback pcb = null; - KeyManager[] kms = null; - - if ("PKCS11".equals(keystoreType)) { - Constructor c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class); - String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library; - ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8)); - Provider p = (Provider) c.newInstance(config); - Security.addProvider(p); - ks = KeyStore.getInstance("PKCS11", p); - pcb = new PasswordCallback("PKCS11 Password: ", false); - callbackHandler.handle(new Callback[] { pcb }); - ks.load(null, pcb.getPassword()); - } else if ("Apple".equals(keystoreType)) { - ks = KeyStore.getInstance("KeychainStore", "Apple"); - ks.load(null, null); - // pcb = new PasswordCallback("Apple Keychain",false); - // pcb.setPassword(null); - } else if (keystoreType != null) { - ks = KeyStore.getInstance(keystoreType); - if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) { - pcb = new PasswordCallback("Keystore Password: ", false); - callbackHandler.handle(new Callback[] { pcb }); - ks.load(new FileInputStream(keystorePath), pcb.getPassword()); - } else { - InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); - try { - // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous - // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702 - char[] password = "changeit".toCharArray(); - try { - ks.load(stream, password); - } finally { - CloseableUtil.maybeClose(stream); - } - } catch (IOException e) { - LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e); - - ks = KeyStore.getInstance("jks"); - // Open the stream again, so that we read it from the beginning. - stream = TLSUtils.getDefaultTruststoreStreamIfPossible(); - try { - ks.load(stream, null); - } finally { - CloseableUtil.maybeClose(stream); - } - } - } - } - - if (ks != null) { - String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); - KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm); - if (kmf != null) { - if (pcb == null) { - kmf.init(ks, null); - } else { - kmf.init(ks, pcb.getPassword()); - pcb.clearPassword(); - } - kms = kmf.getKeyManagers(); - } - } - - SmackDaneVerifier daneVerifier = null; - if (dnssecMode == DnssecMode.needsDnssecAndDane) { - SmackDaneProvider daneProvider = DNSUtil.getDaneProvider(); - if (daneProvider == null) { - throw new UnsupportedOperationException("DANE enabled but no SmackDaneProvider configured"); - } - daneVerifier = daneProvider.newInstance(); - if (daneVerifier == null) { - throw new IllegalStateException("DANE requested but DANE provider did not return a DANE verifier"); - } - - // User requested DANE verification. - daneVerifier.init(context, kms, trustManager, null); - } else { - final TrustManager[] trustManagers; - if (trustManager != null) { - trustManagers = new TrustManager[] { trustManager }; - } else { - // Ensure trustManagers is null in case there was no explicit trust manager provided, so that the - // default one is used. - trustManagers = null; - } - - context.init(kms, trustManagers, null); - } - - return new SmackTlsContext(context, daneVerifier); } public DnsName getHost() { @@ -370,7 +237,7 @@ public abstract class ConnectionConfiguration { return hostAddress; } - public UInt16 getPort() { + public int getPort() { return port; } @@ -419,6 +286,50 @@ public abstract class ConnectionConfiguration { return dnssecMode; } + public X509TrustManager getCustomX509TrustManager() { + return customX509TrustManager; + } + + /** + * Retuns the path to the keystore file. The key store file contains the + * certificates that may be used to authenticate the client to the server, + * in the event the server requests or requires it. + * + * @return the path to the keystore file. + */ + public String getKeystorePath() { + return keystorePath; + } + + /** + * Returns the keystore type, or null if it's not set. + * + * @return the keystore type. + */ + public String getKeystoreType() { + return keystoreType; + } + + /** + * Returns the PKCS11 library file location, needed when the + * Keystore type is PKCS11. + * + * @return the path to the PKCS11 library file + */ + public String getPKCS11Library() { + return pkcs11Library; + } + + /** + * Gets the custom SSLContext previously set with {@link ConnectionConfiguration.Builder#setCustomSSLContext(SSLContext)} for + * SSL sockets. This is null by default. + * + * @return the custom SSLContext or null. + */ + public SSLContext getCustomSSLContext() { + return this.customSSLContext; + } + /** * Return the enabled SSL/TLS protocols. * @@ -688,12 +599,12 @@ public abstract class ConnectionConfiguration { * @param the resulting connection configuration type parameter. */ public abstract static class Builder, C extends ConnectionConfiguration> { - private SecurityMode securityMode = SecurityMode.required; + private SecurityMode securityMode = SecurityMode.ifpossible; private DnssecMode dnssecMode = DnssecMode.disabled; - private String keystorePath; - private String keystoreType; + private String keystorePath = System.getProperty("javax.net.ssl.keyStore"); + private String keystoreType = KeyStore.getDefaultType(); private String pkcs11Library = "pkcs11.config"; - private SslContextFactory sslContextFactory; + private SSLContext customSSLContext; private String[] enabledSSLProtocols; private String[] enabledSSLCiphers; private HostnameVerifier hostnameVerifier; @@ -710,7 +621,7 @@ public abstract class ConnectionConfiguration { private DomainBareJid xmppServiceDomain; private InetAddress hostAddress; private DnsName host; - private UInt16 port = UInt16.from(5222); + private int port = 5222; private boolean allowEmptyOrNullUsername = false; private boolean saslMechanismsSealed; private Set enabledSaslMechanisms; @@ -926,12 +837,7 @@ public abstract class ConnectionConfiguration { throw new IllegalArgumentException( "Port must be a 16-bit unsigned integer (i.e. between 0-65535. Port was: " + port); } - UInt16 portUint16 = UInt16.from(port); - return setPort(portUint16); - } - - public B setPort(UInt16 port) { - this.port = Objects.requireNonNull(port); + this.port = port; return getThis(); } @@ -1017,28 +923,9 @@ public abstract class ConnectionConfiguration { * * @param context the custom SSLContext for new sockets. * @return a reference to this builder. - * @deprecated use {@link #setSslContextFactory(SslContextFactory)} instead}. */ - // TODO: Remove in Smack 4.5. - @Deprecated public B setCustomSSLContext(SSLContext context) { - return setSslContextFactory(() -> { - return context; - }); - } - - /** - * Sets a custom SSLContext for creating SSL sockets. - *

- * For more information on how to create a SSLContext see Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager - * - * @param sslContextFactory the custom SSLContext for new sockets. - * @return a reference to this builder. - */ - public B setSslContextFactory(SslContextFactory sslContextFactory) { - this.sslContextFactory = Objects.requireNonNull(sslContextFactory, "The provided SslContextFactory must not be null"); + this.customSSLContext = Objects.requireNonNull(context, "The SSLContext must not be null"); return getThis(); } @@ -1279,4 +1166,5 @@ public abstract class ConnectionConfiguration { protected abstract B getThis(); } + } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/Manager.java b/smack-core/src/main/java/org/jivesoftware/smack/Manager.java index a306bc3af..d3c4beec1 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/Manager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/Manager.java @@ -54,14 +54,6 @@ public abstract class Manager { } protected static final ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit) { - return schedule(runnable, delay, unit, ScheduledAction.Kind.NonBlocking); - } - - protected static final ScheduledAction scheduleBlocking(Runnable runnable, long delay, TimeUnit unit) { - return schedule(runnable, delay, unit, ScheduledAction.Kind.Blocking); - } - - protected static final ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit, ScheduledAction.Kind scheduledActionKind) { - return AbstractXMPPConnection.SMACK_REACTOR.schedule(runnable, delay, unit, scheduledActionKind); + return AbstractXMPPConnection.SMACK_REACTOR.schedule(runnable, delay, unit); } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java b/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java index da0377fab..5b5f0b238 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java @@ -20,34 +20,20 @@ import java.util.Date; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; -import org.jivesoftware.smack.util.Async; - public class ScheduledAction implements Delayed { - enum Kind { - NonBlocking, - Blocking, - } - - private final Runnable action; + final Runnable action; final Date releaseTime; final SmackReactor smackReactor; - final Kind kind; - ScheduledAction(Runnable action, Date releaseTime, SmackReactor smackReactor, Kind kind) { + ScheduledAction(Runnable action, Date releaseTime, SmackReactor smackReactor) { this.action = action; this.releaseTime = releaseTime; this.smackReactor = smackReactor; - this.kind = kind; } - /** - * Cancels this scheduled action. - * - * @return true if the scheduled action was still pending and got removed, false otherwise. - */ - public boolean cancel() { - return smackReactor.cancel(this); + public void cancel() { + smackReactor.cancel(this); } public boolean isDue() { @@ -77,15 +63,4 @@ public class ScheduledAction implements Delayed { long delayInMillis = getTimeToDueMillis(); return unit.convert(delayInMillis, TimeUnit.MILLISECONDS); } - - void run() { - switch (kind) { - case NonBlocking: - action.run(); - break; - case Blocking: - Async.go(() -> action.run()); - break; - } - } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java index eaff9ca3d..76d028204 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java @@ -145,21 +145,15 @@ public class SmackReactor { } } - ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit, ScheduledAction.Kind scheduledActionKind) { + ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit) { long releaseTimeEpoch = System.currentTimeMillis() + unit.toMillis(delay); Date releaseTimeDate = new Date(releaseTimeEpoch); - ScheduledAction scheduledAction = new ScheduledAction(runnable, releaseTimeDate, this, scheduledActionKind); + ScheduledAction scheduledAction = new ScheduledAction(runnable, releaseTimeDate, this); scheduledActions.add(scheduledAction); selector.wakeup(); return scheduledAction; } - /** - * Cancels the scheduled action. - * - * @param scheduledAction the scheduled action to cancel. - * @return true if the scheduled action was still pending and got removed, false otherwise. - */ boolean cancel(ScheduledAction scheduledAction) { return scheduledActions.remove(scheduledAction); } @@ -206,7 +200,7 @@ public class SmackReactor { } if (dueScheduledAction != null) { - dueScheduledAction.run(); + dueScheduledAction.action.run(); return; } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java index 061d25e7a..9694b47b8 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/ModularXmppClientToServerConnection.java @@ -17,6 +17,11 @@ package org.jivesoftware.smack.c2s; import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Collection; @@ -60,8 +65,6 @@ import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex; import org.jivesoftware.smack.fsm.StateMachineException; import org.jivesoftware.smack.fsm.StateTransitionResult; import org.jivesoftware.smack.fsm.StateTransitionResult.AttemptResult; -import org.jivesoftware.smack.internal.AbstractStats; -import org.jivesoftware.smack.internal.SmackTlsContext; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Nonza; @@ -75,7 +78,6 @@ import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.sasl.SASLErrorException; import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown; -import org.jivesoftware.smack.util.ExtendedAppendable; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.xml.XmlPullParser; @@ -182,7 +184,9 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } @Override - public SmackTlsContext getSmackTlsContext() { + public SmackTlsContext getSmackTlsContext() + throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException, + UnrecoverableKeyException, KeyStoreException, NoSuchProviderException { return ModularXmppClientToServerConnection.this.getSmackTlsContext(); } @@ -1065,7 +1069,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne return new Stats(transportsStats, filterStats); } - public static final class Stats extends AbstractStats { + public static final class Stats { public final Map, XmppClientToServerTransport.Stats> transportsStats; public final Map filtersStats; @@ -1075,8 +1079,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne this.filtersStats = Collections.unmodifiableMap(filtersStats); } - @Override - public void appendStatsTo(ExtendedAppendable appendable) throws IOException { + public void appendStatsTo(Appendable appendable) throws IOException { StringUtils.appendHeading(appendable, "Connection stats", '#').append('\n'); for (Map.Entry, XmppClientToServerTransport.Stats> entry : transportsStats.entrySet()) { @@ -1096,5 +1099,16 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + try { + appendStatsTo(sb); + } catch (IOException e) { + // Should never happen. + throw new AssertionError(e); + } + return sb.toString(); + } } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java index b08676914..57452f270 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/c2s/internal/ModularXmppClientToServerConnectionInternal.java @@ -16,12 +16,20 @@ */ package org.jivesoftware.smack.c2s.internal; +import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.ListIterator; import java.util.Queue; +import org.jivesoftware.smack.AbstractXMPPConnection.SmackTlsContext; import org.jivesoftware.smack.SmackException.ConnectionUnexpectedTerminatedException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; @@ -33,7 +41,6 @@ import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection; import org.jivesoftware.smack.c2s.XmppClientToServerTransport; import org.jivesoftware.smack.debugger.SmackDebugger; import org.jivesoftware.smack.fsm.ConnectionStateEvent; -import org.jivesoftware.smack.internal.SmackTlsContext; import org.jivesoftware.smack.packet.Nonza; import org.jivesoftware.smack.packet.TopLevelStreamElement; import org.jivesoftware.smack.packet.XmlEnvironment; @@ -100,7 +107,9 @@ public abstract class ModularXmppClientToServerConnectionInternal { public abstract void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException, ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException; - public abstract SmackTlsContext getSmackTlsContext(); + public abstract SmackTlsContext getSmackTlsContext() + throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException, + UnrecoverableKeyException, KeyStoreException, NoSuchProviderException; public abstract SN sendAndWaitForResponse(Nonza nonza, Class successNonzaClass, Class failedNonzaClass) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/compression/XMPPInputOutputStream.java b/smack-core/src/main/java/org/jivesoftware/smack/compression/XMPPInputOutputStream.java index 5f133a640..837028c64 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/compression/XMPPInputOutputStream.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/compression/XMPPInputOutputStream.java @@ -1,6 +1,6 @@ /** * - * Copyright 2013-2020 Florian Schmaus + * Copyright 2013-2018 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,11 +20,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import org.jivesoftware.smack.util.Objects; - public abstract class XMPPInputOutputStream { - protected static FlushMethod flushMethod = FlushMethod.SYNC_FLUSH; + protected static FlushMethod flushMethod; /** * Set the used flushed method when compressing data. The default is full flush which may not @@ -35,7 +33,7 @@ public abstract class XMPPInputOutputStream { * @param flushMethod TODO javadoc me please */ public static void setFlushMethod(FlushMethod flushMethod) { - XMPPInputOutputStream.flushMethod = Objects.requireNonNull(flushMethod); + XMPPInputOutputStream.flushMethod = flushMethod; } public static FlushMethod getFlushMethod() { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaFilter.java b/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaFilter.java index 147d0c9f1..f37d08b11 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaFilter.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/filter/StanzaFilter.java @@ -66,13 +66,4 @@ public interface StanzaFilter extends Predicate { default boolean test(Stanza stanza) { return accept(stanza); } - - default Predicate asPredicate(Class stanzaClass) { - return s -> { - if (!stanzaClass.isAssignableFrom(s.getClass())) { - return false; - } - return accept(s); - }; - } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/internal/AbstractStats.java b/smack-core/src/main/java/org/jivesoftware/smack/internal/AbstractStats.java deleted file mode 100644 index 1cddd964c..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/internal/AbstractStats.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * - * Copyright 2020 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.internal; - -import java.io.IOException; - -import org.jivesoftware.smack.util.ExtendedAppendable; - -public abstract class AbstractStats { - - public final void appendStatsTo(Appendable appendable) throws IOException { - appendStatsTo(new ExtendedAppendable(appendable)); - } - - public abstract void appendStatsTo(ExtendedAppendable appendable) throws IOException; - - private transient String toStringCache; - - @Override - public final String toString() { - if (toStringCache != null) { - return toStringCache; - } - - StringBuilder sb = new StringBuilder(); - try { - appendStatsTo(sb); - } catch (IOException e) { - // Should never happen. - throw new AssertionError(e); - } - - toStringCache = sb.toString(); - - return toStringCache; - } - -} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/internal/SmackTlsContext.java b/smack-core/src/main/java/org/jivesoftware/smack/internal/SmackTlsContext.java deleted file mode 100644 index b0425b162..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/internal/SmackTlsContext.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * Copyright 2020 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.internal; - -import javax.net.ssl.SSLContext; - -import org.jivesoftware.smack.util.dns.SmackDaneVerifier; - -public final class SmackTlsContext { - public final SSLContext sslContext; - public final SmackDaneVerifier daneVerifier; - - public SmackTlsContext(SSLContext sslContext, SmackDaneVerifier daneVerifier) { - assert sslContext != null; - this.sslContext = sslContext; - this.daneVerifier = daneVerifier; - } -} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/internal/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/internal/package-info.java deleted file mode 100644 index 6311fbe06..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/internal/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - * Copyright 2020 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. - */ - -/** - * Smack internal classes and interfaces. - */ -package org.jivesoftware.smack.internal; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractIqBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractIqBuilder.java index 5eac12435..d975db0ef 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractIqBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractIqBuilder.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019-2020 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. @@ -23,10 +23,6 @@ public abstract class AbstractIqBuilder> extend protected IQ.Type type = IQ.Type.get; - AbstractIqBuilder(IQ other, String stanzaId) { - super(other, stanzaId); - } - AbstractIqBuilder(AbstractIqBuilder other) { super(other); type = other.type; diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java index 644f077ea..2e24f53b2 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/IqBuilder.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019-2020 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. @@ -22,10 +22,6 @@ import org.jivesoftware.smack.util.Objects; public abstract class IqBuilder, I extends IQ> extends AbstractIqBuilder { - protected IqBuilder(IQ other, String stanzaId) { - super(other, stanzaId); - } - protected IqBuilder(AbstractIqBuilder other) { super(other); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java index 0f819b429..9a1d1a43d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/Presence.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2020 Florian Schmaus. + * Copyright 2003-2007 Jive Software. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,10 @@ package org.jivesoftware.smack.packet; -import java.util.List; import java.util.Locale; +import javax.net.SocketFactory; + import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.TypedCloneable; @@ -28,7 +29,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.jid.Jid; /** - * Represents XMPP presence stanzas. Every presence stanza has a type, which is one of + * Represents XMPP presence packets. Every presence stanza has a type, which is one of * the following values: *

    *
  • {@link Presence.Type#available available} -- (Default) indicates the user is available to @@ -54,7 +55,7 @@ import org.jxmpp.jid.Jid; * {@link Mode#dnd dnd} (do not disturb). *

* - * Presence stanzas are used for two purposes. First, to notify the server of + * Presence packets are used for two purposes. First, to notify the server of * the user's current presence status. Second, they are used to subscribe and * unsubscribe users from the roster. * @@ -82,7 +83,7 @@ public final class Presence extends MessageOrPresence * Creates a new presence update. Status, priority, and mode are left un-set. * * @param type the type. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. + * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -98,7 +99,7 @@ public final class Presence extends MessageOrPresence * @param to the recipient. * @param type the type. * @since 4.2 - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. + * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -114,7 +115,7 @@ public final class Presence extends MessageOrPresence * @param status a text message describing the presence update. * @param priority the priority of this presence update. * @param mode the mode type for this presence update. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. + * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -190,7 +191,7 @@ public final class Presence extends MessageOrPresence * Sets the type of the presence packet. * * @param type the type of the presence packet. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. + * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -208,7 +209,7 @@ public final class Presence extends MessageOrPresence * describing a user's presence (i.e., "gone to lunch"). * * @param status the status message. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. + * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -235,7 +236,7 @@ public final class Presence extends MessageOrPresence * @param priority the priority of the presence. * @throws IllegalArgumentException if the priority is outside the valid range. * @see RFC 6121 § 4.7.2.3. Priority Element - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. + * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -264,7 +265,7 @@ public final class Presence extends MessageOrPresence * to be the same thing as {@link Presence.Mode#available}. * * @param mode the mode. - * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. + * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -308,16 +309,6 @@ public final class Presence extends MessageOrPresence if (type != Type.available) { buf.attribute("type", type); } - - List extensions = getExtensions(); - if (status == null - && priority == null - && (mode == null || mode == Mode.available) - && extensions.isEmpty() - && getError() == null) { - return buf.closeEmptyElement(); - } - buf.rightAngleBracket(); buf.optElement("status", status); @@ -326,7 +317,7 @@ public final class Presence extends MessageOrPresence buf.element("show", mode); } - buf.append(extensions); + buf.append(getExtensions()); // Add the error sub-packet, if there is one. appendErrorIfExists(buf); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/ExtendedAppendable.java b/smack-core/src/main/java/org/jivesoftware/smack/util/ExtendedAppendable.java deleted file mode 100644 index 4cf97622c..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/ExtendedAppendable.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * - * Copyright 2020 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.util; - -import java.io.IOException; - -public class ExtendedAppendable implements Appendable { - - private final Appendable appendable; - - public ExtendedAppendable(Appendable appendable) { - this.appendable = appendable; - } - - @Override - public ExtendedAppendable append(CharSequence csq) throws IOException { - appendable.append(csq); - return this; - } - - @Override - public ExtendedAppendable append(CharSequence csq, int start, int end) throws IOException { - appendable.append(csq, start, end); - return this; - } - - @Override - public ExtendedAppendable append(char c) throws IOException { - appendable.append(c); - return this; - } - - public ExtendedAppendable append(boolean b) throws IOException { - appendable.append(String.valueOf(b)); - return this; - } - - public ExtendedAppendable append(int i) throws IOException { - appendable.append(String.valueOf(i)); - return this; - } -} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java index 98635309d..831ccf0ad 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/NumberUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2015-2020 Florian Schmaus + * Copyright © 2015-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. @@ -33,15 +33,15 @@ public class NumberUtil { /** * Checks if the given long is within the range of an unsigned 32-bit integer, the XML type "xs:unsignedInt". * - * @param value the long to check. + * @param value TODO javadoc me please * @return the input value. */ public static long requireUInt32(long value) { if (value < 0) { - throw new IllegalArgumentException("unsigned 32-bit integers can't be negative: " + value); + throw new IllegalArgumentException("unsigned 32-bit integers can't be negative"); } if (value > ((1L << 32) - 1)) { - throw new IllegalArgumentException("unsigned 32-bit integers can't be greater than 2^32 - 1: " + value); + throw new IllegalArgumentException("unsigned 32-bit integers can't be greater than 2^32 - 1"); } return value; } @@ -49,15 +49,15 @@ public class NumberUtil { /** * Checks if the given int is within the range of an unsigned 16-bit integer, the XML type "xs:unsignedShort". * - * @param value the int to check. + * @param value TODO javadoc me please * @return the input value. */ public static int requireUShort16(int value) { if (value < 0) { - throw new IllegalArgumentException("unsigned 16-bit integers can't be negative: " + value); + throw new IllegalArgumentException("unsigned 16-bit integers can't be negative"); } if (value > ((1 << 16) - 1)) { - throw new IllegalArgumentException("unsigned 16-bit integers can't be greater than 2^16 - 1: " + value); + throw new IllegalArgumentException("unsigned 16-bit integers can't be greater than 2^16 - 1"); } return value; } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/SslContextFactory.java b/smack-core/src/main/java/org/jivesoftware/smack/util/SslContextFactory.java deleted file mode 100644 index a3c308b9f..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/SslContextFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * - * Copyright 2020 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.util; - -import javax.net.ssl.SSLContext; - -public interface SslContextFactory { - - SSLContext createSslContext(); - -} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java index c1af8ba61..60c644320 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/TLSUtils.java @@ -22,8 +22,12 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; @@ -34,9 +38,13 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import org.jivesoftware.smack.ConnectionConfiguration; @@ -124,13 +132,25 @@ public class TLSUtils { * * @param builder a connection configuration builder. * @param Type of the ConnectionConfiguration builder. + * @throws NoSuchAlgorithmException if no such algorithm is available. + * @throws KeyManagementException if there was a key mangement error. * @return the given builder. */ - public static > B acceptAllCertificates(B builder) { - builder.setCustomX509TrustManager(new AcceptAllTrustManager()); + public static > B acceptAllCertificates(B builder) throws NoSuchAlgorithmException, KeyManagementException { + SSLContext context = SSLContext.getInstance(TLS); + context.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom()); + builder.setCustomSSLContext(context); return builder; } + private static final HostnameVerifier DOES_NOT_VERIFY_VERIFIER = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + // This verifier doesn't verify the hostname, it always returns true. + return true; + } + }; + /** * Disable the hostname verification of TLS certificates. *

@@ -144,9 +164,7 @@ public class TLSUtils { * @return the given builder. */ public static > B disableHostnameVerificationForTlsCertificates(B builder) { - builder.setHostnameVerifier((hostname, session) -> { - return true; - }); + builder.setHostnameVerifier(DOES_NOT_VERIFY_VERIFIER); return builder; } @@ -256,6 +274,24 @@ public class TLSUtils { } } + public static X509TrustManager getDefaultX509TrustManager(KeyStore keyStore) { + String defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory trustManagerFactory; + try { + trustManagerFactory = TrustManagerFactory.getInstance(defaultAlgorithm); + trustManagerFactory.init(keyStore); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + throw new AssertionError(e); + } + + for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { + if (trustManager instanceof X509TrustManager) { + return (X509TrustManager) trustManager; + } + } + throw new AssertionError("No trust manager for the default algorithm " + defaultAlgorithm + " found"); + } + private static final File DEFAULT_TRUSTSTORE_PATH; static { diff --git a/smack-core/src/test/java/org/jivesoftware/smack/provider/ProviderManagerTest.java b/smack-core/src/test/java/org/jivesoftware/smack/provider/ProviderManagerTest.java index 1c005322d..5210e6344 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/provider/ProviderManagerTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/provider/ProviderManagerTest.java @@ -29,7 +29,6 @@ public class ProviderManagerTest { /** * This test should be run in a clean (e.g. forked) VM - * @throws Exception if exception. */ @Test public void shouldInitializeSmackTest() throws Exception { diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java b/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java index 6a7fe8e54..eef18918f 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/util/DnsUtilTest.java @@ -25,7 +25,6 @@ import org.junit.Test; public class DnsUtilTest { - @SuppressWarnings("UnnecessaryAnonymousClass") private static final SmackDaneProvider DNS_UTIL_TEST_DANE_PROVIDER = new SmackDaneProvider() { @Override public SmackDaneVerifier newInstance() { diff --git a/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java b/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java index d0864842b..135fd79d0 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java @@ -150,7 +150,6 @@ public class DummyConnection extends AbstractXMPPConnection { * Returns the first stanza that's sent through {@link #sendStanza(Stanza)} * and that has not been returned by earlier calls to this method. * - * @param

the top level stream element class. * @return a sent packet. */ public

P getSentPacket() { @@ -163,8 +162,6 @@ public class DummyConnection extends AbstractXMPPConnection { * method will block for up to the specified number of seconds if no packets * have been sent yet. * - * @param wait how long to wait in seconds. - * @param

the top level stream element class. * @return a sent packet. */ @SuppressWarnings("unchecked") @@ -225,7 +222,6 @@ public class DummyConnection extends AbstractXMPPConnection { ConnectionConfiguration.Builder { private Builder() { - setSecurityMode(SecurityMode.disabled); } @Override diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java index 2b7220ba1..960922d07 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/carbons/packet/CarbonExtension.java @@ -18,7 +18,6 @@ package org.jivesoftware.smackx.carbons.packet; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.MessageBuilder; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smackx.forward.packet.Forwarded; @@ -155,25 +154,12 @@ public class CarbonExtension implements ExtensionElement { return "<" + ELEMENT + " xmlns='" + NAMESPACE + "'/>"; } - /** - * Marks a message "private", so that it will not be carbon-copied, by adding private packet - * extension to the message. - * - * @param messageBuilder the message to add the private extension to - */ - public static void addTo(MessageBuilder messageBuilder) { - messageBuilder.addExtension(INSTANCE); - } - /** * Marks a message "private", so that it will not be carbon-copied, by adding private packet * extension to the message. * * @param message the message to add the private extension to - * @deprecated use {@link #addTo(MessageBuilder)} instead. */ - // TODO: Remove in Smack 4.6 - @Deprecated public static void addTo(Message message) { message.addExtension(INSTANCE); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationManager.java index e0859dd26..da58e6ae9 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationManager.java @@ -25,6 +25,7 @@ import org.jivesoftware.smack.AsyncButOrdered; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPException; @@ -67,20 +68,23 @@ public final class FallbackIndicationManager extends Manager { private final StanzaFilter fallbackIndicationElementFilter = new AndFilter(StanzaTypeFilter.MESSAGE, new StanzaExtensionFilter(FallbackIndicationElement.ELEMENT, FallbackIndicationElement.NAMESPACE)); - private void fallbackIndicationElementListener(Stanza packet) { - Message message = (Message) packet; - FallbackIndicationElement indicator = FallbackIndicationElement.fromMessage(message); - String body = message.getBody(); - asyncButOrdered.performAsyncButOrdered(message.getFrom().asBareJid(), () -> { - for (FallbackIndicationListener l : listeners) { - l.onFallbackIndicationReceived(message, indicator, body); - } - }); - } + private final StanzaListener fallbackIndicationElementListener = new StanzaListener() { + @Override + public void processStanza(Stanza packet) { + Message message = (Message) packet; + FallbackIndicationElement indicator = FallbackIndicationElement.fromMessage(message); + String body = message.getBody(); + asyncButOrdered.performAsyncButOrdered(message.getFrom().asBareJid(), () -> { + for (FallbackIndicationListener l : listeners) { + l.onFallbackIndicationReceived(message, indicator, body); + } + }); + } + }; private FallbackIndicationManager(XMPPConnection connection) { super(connection); - connection.addAsyncStanzaListener(this::fallbackIndicationElementListener, fallbackIndicationElementFilter); + connection.addAsyncStanzaListener(fallbackIndicationElementListener, fallbackIndicationElementFilter); ServiceDiscoveryManager.getInstanceFor(connection).addFeature(FallbackIndicationElement.NAMESPACE); } @@ -135,7 +139,7 @@ public final class FallbackIndicationManager extends Manager { * @param fallbackMessageBody fallback message body * @return builder with set body and added fallback element */ - public static MessageBuilder addFallbackIndicationWithBody(MessageBuilder messageBuilder, String fallbackMessageBody) { + public MessageBuilder addFallbackIndicationWithBody(MessageBuilder messageBuilder, String fallbackMessageBody) { return addFallbackIndication(messageBuilder).setBody(fallbackMessageBody); } @@ -145,7 +149,7 @@ public final class FallbackIndicationManager extends Manager { * @param messageBuilder message builder * @return message builder with added fallback element */ - public static MessageBuilder addFallbackIndication(MessageBuilder messageBuilder) { + public MessageBuilder addFallbackIndication(MessageBuilder messageBuilder) { return messageBuilder.addExtension(new FallbackIndicationElement()); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java index 23379a4cc..299466bbb 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java @@ -37,6 +37,7 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; +import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.Manager; @@ -306,9 +307,7 @@ public final class HttpFileUploadManager extends Manager { public URL uploadFile(InputStream inputStream, String fileName, long fileSize, UploadProgressListener listener) throws XMPPErrorException, InterruptedException, SmackException, IOException { Objects.requireNonNull(inputStream, "Input Stream cannot be null"); Objects.requireNonNull(fileName, "Filename Stream cannot be null"); - if (fileSize < 0) { - throw new IllegalArgumentException("File size cannot be negative"); - } + Objects.requireNonNull(fileSize, "Filesize Stream cannot be null"); final Slot slot = requestSlot(fileName, fileSize, "application/octet-stream"); upload(inputStream, fileSize, slot, listener); return slot.getGetUrl(); @@ -325,7 +324,8 @@ public final class HttpFileUploadManager extends Manager { * supported by the service. * @throws InterruptedException if the calling thread was interrupted. * @throws XMPPException.XMPPErrorException if there was an XMPP error returned. - * @throws SmackException if smack exception. + * @throws SmackException.NotConnectedException if the XMPP connection is not connected. + * @throws SmackException.NoResponseException if there was no response from the remote entity. */ public Slot requestSlot(String filename, long fileSize) throws InterruptedException, XMPPException.XMPPErrorException, SmackException { @@ -348,7 +348,7 @@ public final class HttpFileUploadManager extends Manager { * @throws SmackException.NotConnectedException if the XMPP connection is not connected. * @throws InterruptedException if the calling thread was interrupted. * @throws XMPPException.XMPPErrorException if there was an XMPP error returned. - * @throws SmackException if smack exception. + * @throws SmackException.NoResponseException if there was no response from the remote entity. */ public Slot requestSlot(String filename, long fileSize, String contentType) throws SmackException, InterruptedException, XMPPException.XMPPErrorException { @@ -427,6 +427,11 @@ public final class HttpFileUploadManager extends Manager { this.tlsSocketFactory = tlsContext.getSocketFactory(); } + public void useTlsSettingsFrom(ConnectionConfiguration connectionConfiguration) { + SSLContext sslContext = connectionConfiguration.getCustomSSLContext(); + setTlsContext(sslContext); + } + private void upload(InputStream iStream, long fileSize, Slot slot, UploadProgressListener listener) throws IOException { final URL putUrl = slot.getPutUrl(); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java index 9fcff7b9c..034245a5a 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamElements.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2016-2020 Florian Schmaus and Fernando Ramirez + * Copyright © 2016 Florian Schmaus and Fernando Ramirez * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ import org.jxmpp.jid.Jid; */ public class MamElements { - public static final String NAMESPACE = "urn:xmpp:mam:2"; + public static final String NAMESPACE = "urn:xmpp:mam:1"; /** * MAM result extension class. diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/message_retraction/MessageRetractionManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/message_retraction/MessageRetractionManager.java index 7fcac31de..e5d211161 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/message_retraction/MessageRetractionManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/message_retraction/MessageRetractionManager.java @@ -102,7 +102,7 @@ public final class MessageRetractionManager extends Manager { * @param retractedMessageId {@link OriginIdElement OriginID} of the message that the user wants to retract * @param carrierMessageBuilder message used to transmit the message retraction to the recipient */ - public static void addRetractionElementToMessage(OriginIdElement retractedMessageId, MessageBuilder carrierMessageBuilder) { + public void addRetractionElementToMessage(OriginIdElement retractedMessageId, MessageBuilder carrierMessageBuilder) { FasteningElement fasteningElement = FasteningElement.builder() .setOriginId(retractedMessageId) .addWrappedPayload(new RetractElement()) diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java index 35242eba9..d8911aa3e 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018 Paul Schaub, 2020 Florian Schmaus + * Copyright 2018 Paul Schaub * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,10 @@ import org.jivesoftware.smack.filter.NotFilter; import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.ToTypeFilter; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.MessageBuilder; +import org.jivesoftware.smack.util.Consumer; +import org.jivesoftware.smack.util.Predicate; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.sid.element.OriginIdElement; @@ -58,8 +62,14 @@ public final class StableUniqueStanzaIdManager extends Manager { // Filter that filters for messages with an origin id private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE); + // Listener for outgoing stanzas that adds origin-ids to outgoing stanzas. + private static final Consumer ADD_ORIGIN_ID_INTERCEPTOR = mb -> OriginIdElement.addOriginId(mb); + // We need a filter for outgoing messages that do not carry an origin-id already. private static final StanzaFilter ADD_ORIGIN_ID_FILTER = new AndFilter(OUTGOING_FILTER, new NotFilter(ORIGIN_ID_FILTER)); + private static final Predicate ADD_ORIGIN_ID_PREDICATE = m -> { + return ADD_ORIGIN_ID_FILTER.accept(m); + }; static { XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { @@ -103,7 +113,7 @@ public final class StableUniqueStanzaIdManager extends Manager { * Start appending origin-id elements to outgoing stanzas and add the feature to disco. */ public synchronized void enable() { - connection().addMessageInterceptor(OriginIdElement::addTo, ADD_ORIGIN_ID_FILTER::accept); + connection().addMessageInterceptor(ADD_ORIGIN_ID_INTERCEPTOR, ADD_ORIGIN_ID_PREDICATE); ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE); } @@ -112,7 +122,7 @@ public final class StableUniqueStanzaIdManager extends Manager { */ public synchronized void disable() { ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE); - connection().removeMessageInterceptor(OriginIdElement::addTo); + connection().removeMessageInterceptor(ADD_ORIGIN_ID_INTERCEPTOR); } /** diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java index 1a448b516..b157b40a0 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/element/OriginIdElement.java @@ -39,7 +39,7 @@ public class OriginIdElement extends StableAndUniqueIdElement { * * @param message message. * @return the added origin-id element. - * @deprecated use {@link #addTo(MessageBuilder)} instead. + * @deprecated use {@link #addOriginId(MessageBuilder)} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -57,7 +57,7 @@ public class OriginIdElement extends StableAndUniqueIdElement { * @param messageBuilder the message builder to add an origin ID to. * @return the added origin-id element. */ - public static OriginIdElement addTo(MessageBuilder messageBuilder) { + public static OriginIdElement addOriginId(MessageBuilder messageBuilder) { OriginIdElement originId = new OriginIdElement(); messageBuilder.addExtension(originId); // TODO: Find solution to have both the originIds stanzaId and a nice to look at incremental stanzaID. diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers index 8e6d3df51..a6791cbb7 100644 --- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers +++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers @@ -17,22 +17,22 @@ prefs - urn:xmpp:mam:2 + urn:xmpp:mam:1 org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider query - urn:xmpp:mam:2 + urn:xmpp:mam:1 org.jivesoftware.smackx.mam.provider.MamQueryIQProvider fin - urn:xmpp:mam:2 + urn:xmpp:mam:1 org.jivesoftware.smackx.mam.provider.MamFinIQProvider result - urn:xmpp:mam:2 + urn:xmpp:mam:1 org.jivesoftware.smackx.mam.provider.MamResultProvider diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationTest.java index 7695ac47d..342e783b5 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/fallback_indication/FallbackIndicationTest.java @@ -20,7 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.StanzaBuilder; +import org.jivesoftware.smack.packet.MessageBuilder; import org.jivesoftware.smackx.fallback_indication.element.FallbackIndicationElement; @@ -30,11 +30,11 @@ public class FallbackIndicationTest { @Test public void testFallbackIndicationElementFromMessageTest() { - Message messageWithoutFallback = StanzaBuilder.buildMessage() + Message messageWithoutFallback = MessageBuilder.buildMessage() .build(); assertNull(FallbackIndicationElement.fromMessage(messageWithoutFallback)); - Message messageWithFallback = StanzaBuilder.buildMessage() + Message messageWithFallback = MessageBuilder.buildMessage() .addExtension(new FallbackIndicationElement()) .build(); assertNotNull(FallbackIndicationElement.fromMessage(messageWithFallback)); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java index cbb1b5c1d..f0cd5df68 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamFinProviderTest.java @@ -34,7 +34,7 @@ import org.junit.jupiter.api.Test; public class MamFinProviderTest extends MamTest { - static final String exmapleMamFinXml = "" + static final String exmapleMamFinXml = "" + "" + "10" + "09af3-cc343-b409f" + "" + ""; @@ -56,7 +56,7 @@ public class MamFinProviderTest extends MamTest { public void checkQueryLimitedResults() throws Exception { // @formatter:off final String IQ_LIMITED_RESULTS_EXAMPLE = "" - + "" + + "" + "" + "23452-4534-1" + "390-2342-22" + "16" diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java index cd5d4c0ca..1c0ff85f8 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamPrefIQProviderTest.java @@ -33,19 +33,19 @@ import org.jxmpp.jid.Jid; public class MamPrefIQProviderTest extends MamTest { - private static final String exampleMamPrefsIQ1 = "" + "" + private static final String exampleMamPrefsIQ1 = "" + "" + "" + "romeo@montague.lit" + "" + "" + "montague@montague.lit" + "" + "" + ""; - private static final String exampleMamPrefsIQ2 = "" + "" + private static final String exampleMamPrefsIQ2 = "" + "" + "" + "romeo@montague.lit" + "montague@montague.lit" + "" + "" + "" + "" + ""; - private static final String exampleMamPrefsIQ3 = "" + "" + "" + private static final String exampleMamPrefsIQ3 = "" + "" + "" + ""; private static final String exampleMamPrefsResultIQ = "" - + "" + "" + "romeo@montague.lit" + + "" + "" + "romeo@montague.lit" + "" + "" + "sarasa@montague.lit" + "montague@montague.lit" + "" + "" + ""; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java index eeac45f5e..c9f3be99e 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamQueryIQProviderTest.java @@ -35,18 +35,18 @@ import org.junit.jupiter.api.Test; public class MamQueryIQProviderTest { - private static final String exampleMamQueryIQ1 = "" + "" + private static final String exampleMamQueryIQ1 = "" + "" + "" + "" - + "urn:xmpp:mam:2" + "" + + "urn:xmpp:mam:1" + "" + "" + "Where arth thou, my Juliet?" + "" + "" + "{http://jabber.org/protocol/mood}mood/lonely" + "" + "" + "" + ""; - private static final String exampleMamQueryIQ2 = "" + "" + private static final String exampleMamQueryIQ2 = "" + "" + "" + "" - + "urn:xmpp:mam:2" + "" + "" + + "urn:xmpp:mam:1" + "" + "" + "" + "" + "" + "" + "" + "" + ""; @@ -80,7 +80,7 @@ public class MamQueryIQProviderTest { assertEquals(dataForm2.getType(), DataForm.Type.form); List fields2 = dataForm2.getFields(); - assertEquals(fields2.get(0).getValues().get(0).toString(), "urn:xmpp:mam:2"); + assertEquals(fields2.get(0).getValues().get(0).toString(), "urn:xmpp:mam:1"); assertTrue(fields2.get(0).getValues().size() == 1); assertEquals(fields2.get(1).getType(), FormField.Type.jid_single); assertEquals(fields2.get(2).getType(), FormField.Type.text_single); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java index f7a0a28b4..7fbcd7c7e 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamResultProviderTest.java @@ -34,7 +34,7 @@ import org.junit.jupiter.api.Test; public class MamResultProviderTest { - private static final String exampleMamResultXml = "" + private static final String exampleMamResultXml = "" + "" + "" + "" @@ -42,7 +42,7 @@ public class MamResultProviderTest { + "" + "" + ""; private static final String exampleResultMessage = "" - + "" + + "" + "" + "" + "" + "Hail to thee" + "" + "" + "" + ""; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java index 9a3a4e61a..1f1450efd 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/PagingTest.java @@ -29,9 +29,9 @@ import org.junit.jupiter.api.Test; public class PagingTest extends MamTest { - private static final String pagingStanza = "" + "" + private static final String pagingStanza = "" + "" + "" + "" - + "urn:xmpp:mam:2" + "" + "" + "" + + "urn:xmpp:mam:1" + "" + "" + "" + "10" + "" + "" + ""; @Test @@ -46,7 +46,7 @@ public class PagingTest extends MamTest { mamQueryIQ.addExtension(rsmSet); assertEquals(mamQueryIQ.getDataForm(), dataForm); - assertEquals(mamQueryIQ.getDataForm().getFields().get(0).getValues().get(0).toString(), "urn:xmpp:mam:2"); + assertEquals(mamQueryIQ.getDataForm().getFields().get(0).getValues().get(0).toString(), "urn:xmpp:mam:1"); assertEquals(pagingStanza, mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString()); } diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java index 1603fb99f..8fe38eeec 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/QueryArchiveTest.java @@ -40,12 +40,12 @@ import org.jxmpp.jid.impl.JidCreate; public class QueryArchiveTest extends MamTest { - private static final String mamSimpleQueryIQ = "" + "" + private static final String mamSimpleQueryIQ = "" + "" + "" + "" + "" + MamElements.NAMESPACE + "" + "" + "" + "" + ""; private static final String mamQueryResultExample = "" - + "" + + "" + "" + "" + "" + private static final String resultsLimitStanza = "" + "" + "" + "" + "" + MamElements.NAMESPACE + "" + "" + "" + "" + "10" + "" + "" + ""; diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java index 7a36752da..8228fccfb 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/message_fastening/MessageFasteningElementsTest.java @@ -29,7 +29,6 @@ import java.util.Arrays; import org.jivesoftware.smack.packet.MessageBuilder; import org.jivesoftware.smack.packet.StandardExtensionElement; -import org.jivesoftware.smack.packet.StanzaBuilder; import org.jivesoftware.smack.packet.StanzaFactory; import org.jivesoftware.smack.packet.id.StandardStanzaIdSource; import org.jivesoftware.smack.parsing.SmackParsingException; @@ -167,10 +166,10 @@ public class MessageFasteningElementsTest { @Test public void hasFasteningElementTest() { - MessageBuilder messageBuilderWithFasteningElement = StanzaBuilder.buildMessage() + MessageBuilder messageBuilderWithFasteningElement = MessageBuilder.buildMessage() .setBody("Hi!") .addExtension(FasteningElement.builder().setOriginId("origin-id-1").build()); - MessageBuilder messageBuilderWithoutFasteningElement = StanzaBuilder.buildMessage() + MessageBuilder messageBuilderWithoutFasteningElement = MessageBuilder.buildMessage() .setBody("Ho!"); assertTrue(FasteningElement.hasFasteningElement(messageBuilderWithFasteningElement)); diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java index a95e5caac..c9c4367a8 100644 --- a/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdTest.java @@ -78,7 +78,7 @@ public class StableUniqueStanzaIdTest extends SmackTestSuite { assertFalse(OriginIdElement.hasOriginId(message)); assertFalse(StanzaIdElement.hasStanzaId(message)); - OriginIdElement.addTo(messageBuilder); + OriginIdElement.addOriginId(messageBuilder); message = messageBuilder.build(); assertTrue(OriginIdElement.hasOriginId(message)); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index c63471bcb..d7ee5b45a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java @@ -33,6 +33,8 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionListener; @@ -49,11 +51,13 @@ import org.jivesoftware.smack.filter.PresenceTypeFilter; import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; +import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.PresenceBuilder; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.roster.AbstractPresenceEventListener; import org.jivesoftware.smack.roster.Roster; +import org.jivesoftware.smack.util.Consumer; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.stringencoder.Base64; @@ -83,6 +87,7 @@ import org.jxmpp.util.cache.LruCache; * @see XEP-0115: Entity Capabilities */ public final class EntityCapsManager extends Manager { + private static final Logger LOGGER = Logger.getLogger(EntityCapsManager.class.getName()); public static final String NAMESPACE = CapsExtension.NAMESPACE; public static final String ELEMENT = CapsExtension.ELEMENT; @@ -302,6 +307,7 @@ public final class EntityCapsManager extends Manager { private boolean entityCapsEnabled; private CapsVersionAndHash currentCapsVersion; + private volatile Presence presenceSend; /** * The entity node String used by this EntityCapsManager instance. @@ -311,11 +317,11 @@ public final class EntityCapsManager extends Manager { // Intercept presence packages and add caps data when intended. // XEP-0115 specifies that a client SHOULD include entity capabilities // with every presence notification it sends. - private void addCapsExtension(PresenceBuilder presenceBuilder) { + private final Consumer presenceInterceptor = presenceBuilder -> { CapsVersionAndHash capsVersionAndHash = getCapsVersionAndHash(); CapsExtension caps = new CapsExtension(entityNode, capsVersionAndHash.version, capsVersionAndHash.hash); presenceBuilder.overrideExtension(caps); - } + }; private EntityCapsManager(XMPPConnection connection) { super(connection); @@ -336,6 +342,11 @@ public final class EntityCapsManager extends Manager { // feature, so we try to process it after we are connected and // once after we are authenticated. processCapsStreamFeatureIfAvailable(connection); + + // Reset presenceSend when the connection was not resumed + if (!resumed) { + presenceSend = null; + } } private void processCapsStreamFeatureIfAvailable(XMPPConnection connection) { CapsExtension capsExtension = connection.getFeature( @@ -348,6 +359,9 @@ public final class EntityCapsManager extends Manager { } }); + // This calculates the local entity caps version + updateLocalEntityCaps(); + if (autoEnableEntityCaps) enableEntityCaps(); @@ -373,16 +387,26 @@ public final class EntityCapsManager extends Manager { } }); + connection.addStanzaSendingListener(new StanzaListener() { + @Override + public void processStanza(Stanza packet) { + presenceSend = (Presence) packet; + } + }, PresenceTypeFilter.OUTGOING_PRESENCE_BROADCAST); + + + enableEntityCaps(); + // It's important to do this as last action. Since it changes the // behavior of the SDM in some ways sdm.addEntityCapabilitiesChangedListener(new EntityCapabilitiesChangedListener() { @Override - public void onEntityCapabilitiesChanged(DiscoverInfo synthesizedDiscoveryInfo) { + public void onEntityCapailitiesChanged() { if (!entityCapsEnabled()) { return; } - updateLocalEntityCaps(synthesizedDiscoveryInfo); + updateLocalEntityCaps(); } }); } @@ -401,12 +425,13 @@ public final class EntityCapsManager extends Manager { } public synchronized void enableEntityCaps() { - connection().addPresenceInterceptor(this::addCapsExtension, p -> { + connection().addPresenceInterceptor(presenceInterceptor, p -> { return PresenceTypeFilter.AVAILABLE.accept(p); }); // Add Entity Capabilities (XEP-0115) feature node. sdm.addFeature(NAMESPACE); + updateLocalEntityCaps(); entityCapsEnabled = true; } @@ -414,13 +439,18 @@ public final class EntityCapsManager extends Manager { entityCapsEnabled = false; sdm.removeFeature(NAMESPACE); - connection().removePresenceInterceptor(this::addCapsExtension); + connection().removePresenceInterceptor(presenceInterceptor); } public boolean entityCapsEnabled() { return entityCapsEnabled; } + public void setEntityNode(String entityNode) { + this.entityNode = entityNode; + updateLocalEntityCaps(); + } + /** * Remove a record telling what entity caps node a user has. * @@ -492,10 +522,13 @@ public final class EntityCapsManager extends Manager { * presence is send to inform others about your new Entity Caps node string. * */ - private void updateLocalEntityCaps(DiscoverInfo synthesizedDiscoveryInfo) { + private void updateLocalEntityCaps() { XMPPConnection connection = connection(); - DiscoverInfoBuilder discoverInfoBuilder = synthesizedDiscoveryInfo.asBuilder("synthesized-disco-info-result"); + DiscoverInfoBuilder discoverInfoBuilder = DiscoverInfo.builder("synthetized-disco-info-response") + .ofType(IQ.Type.result); + sdm.addDiscoverInfoTo(discoverInfoBuilder); + // getLocalNodeVer() will return a result only after currentCapsVersion is set. Therefore // set it first and then call getLocalNodeVer() currentCapsVersion = generateVerificationString(discoverInfoBuilder); @@ -531,6 +564,20 @@ public final class EntityCapsManager extends Manager { return packetExtensions; } }); + + // Re-send the last sent presence, and let the stanza interceptor + // add a node to it. + // See http://xmpp.org/extensions/xep-0115.html#advertise + // We only send a presence packet if there was already one send + // to respect ConnectionConfiguration.isSendPresence() + if (connection != null && connection.isAuthenticated() && presenceSend != null) { + try { + connection.sendStanza(presenceSend.cloneWithNewId()); + } + catch (InterruptedException | NotConnectedException e) { + LOGGER.log(Level.WARNING, "Could could not update presence with caps info", e); + } + } } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java index c2627dd5b..0f4614181 100755 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/commands/AdHocCommandManager.java @@ -517,7 +517,7 @@ public final class AdHocCommandManager extends Manager { private boolean sessionSweeperScheduled; - private void sessionSweeper() { + private final Runnable sessionSweeper = () -> { final long currentTime = System.currentTimeMillis(); synchronized (this) { for (Iterator> it = executingCommands.entrySet().iterator(); it.hasNext();) { @@ -553,7 +553,7 @@ public final class AdHocCommandManager extends Manager { } sessionSweeperScheduled = true; - schedule(this::sessionSweeper, 10, TimeUnit.SECONDS); + schedule(sessionSweeper, 10, TimeUnit.SECONDS); } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/EntityCapabilitiesChangedListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/EntityCapabilitiesChangedListener.java index bb790813f..e075117f2 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/EntityCapabilitiesChangedListener.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/EntityCapabilitiesChangedListener.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018-2020 Florian Schmaus. + * Copyright 2018 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,8 @@ */ package org.jivesoftware.smackx.disco; -import org.jivesoftware.smackx.disco.packet.DiscoverInfo; - public interface EntityCapabilitiesChangedListener { - void onEntityCapabilitiesChanged(DiscoverInfo synthesizedDiscoveryInfo); + void onEntityCapailitiesChanged(); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index 61103d2ee..84b529aad 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -16,7 +16,6 @@ */ package org.jivesoftware.smackx.disco; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -29,30 +28,20 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; import org.jivesoftware.smack.ConnectionCreationListener; -import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.Manager; -import org.jivesoftware.smack.ScheduledAction; 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.PresenceTypeFilter; -import org.jivesoftware.smack.internal.AbstractStats; import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.util.CollectionUtil; -import org.jivesoftware.smack.util.ExtendedAppendable; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; @@ -82,8 +71,6 @@ import org.jxmpp.util.cache.ExpirationCache; */ public final class ServiceDiscoveryManager extends Manager { - private static final Logger LOGGER = Logger.getLogger(ServiceDiscoveryManager.class.getName()); - private static final String DEFAULT_IDENTITY_NAME = "Smack"; private static final String DEFAULT_IDENTITY_CATEGORY = "client"; private static final String DEFAULT_IDENTITY_TYPE = "pc"; @@ -104,8 +91,6 @@ public final class ServiceDiscoveryManager extends Manager { private List extendedInfos = new ArrayList<>(2); private final Map nodeInformationProviders = new ConcurrentHashMap<>(); - private volatile Presence presenceSend; - // Create a new ServiceDiscoveryManager on every established connection static { XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { @@ -205,18 +190,6 @@ public final class ServiceDiscoveryManager extends Manager { return response; } }); - - connection.addConnectionListener(new ConnectionListener() { - @Override - public void authenticated(XMPPConnection connection, boolean resumed) { - // Reset presenceSend when the connection was not resumed - if (!resumed) { - presenceSend = null; - } - } - }); - connection.addStanzaSendingListener(p -> presenceSend = (Presence) p, - PresenceTypeFilter.OUTGOING_PRESENCE_BROADCAST); } /** @@ -921,55 +894,13 @@ public final class ServiceDiscoveryManager extends Manager { return entityCapabilitiesChangedListeners.add(entityCapabilitiesChangedListener); } - private static final int RENEW_ENTITY_CAPS_DELAY_MILLIS = 25; - - private ScheduledAction renewEntityCapsScheduledAction; - - private final AtomicInteger renewEntityCapsPerformed = new AtomicInteger(); - private int renewEntityCapsRequested = 0; - private int scheduledRenewEntityCapsAvoided = 0; - /** * Notify the {@link EntityCapabilitiesChangedListener} about changed capabilities. */ - private synchronized void renewEntityCapsVersion() { - renewEntityCapsRequested++; - if (renewEntityCapsScheduledAction != null) { - boolean canceled = renewEntityCapsScheduledAction.cancel(); - if (canceled) { - scheduledRenewEntityCapsAvoided++; - } + private void renewEntityCapsVersion() { + for (EntityCapabilitiesChangedListener entityCapabilitiesChangedListener : entityCapabilitiesChangedListeners) { + entityCapabilitiesChangedListener.onEntityCapailitiesChanged(); } - - final XMPPConnection connection = connection(); - - renewEntityCapsScheduledAction = scheduleBlocking(() -> { - renewEntityCapsPerformed.incrementAndGet(); - - DiscoverInfoBuilder discoverInfoBuilder = DiscoverInfo.builder("synthetized-disco-info-response") - .ofType(IQ.Type.result); - addDiscoverInfoTo(discoverInfoBuilder); - DiscoverInfo synthesizedDiscoveryInfo = discoverInfoBuilder.build(); - - for (EntityCapabilitiesChangedListener entityCapabilitiesChangedListener : entityCapabilitiesChangedListeners) { - entityCapabilitiesChangedListener.onEntityCapabilitiesChanged(synthesizedDiscoveryInfo); - } - - // Re-send the last sent presence, and let the stanza interceptor - // add a node to it. - // See http://xmpp.org/extensions/xep-0115.html#advertise - // We only send a presence packet if there was already one send - // to respect ConnectionConfiguration.isSendPresence() - final Presence presenceSend = this.presenceSend; - if (connection.isAuthenticated() && presenceSend != null) { - try { - connection.sendStanza(presenceSend.cloneWithNewId()); - } - catch (InterruptedException | NotConnectedException e) { - LOGGER.log(Level.WARNING, "Could could not update presence with caps info", e); - } - } - }, RENEW_ENTITY_CAPS_DELAY_MILLIS, TimeUnit.MILLISECONDS); } public static void addDiscoInfoLookupShortcutMechanism(DiscoInfoLookupShortcutMechanism discoInfoLookupShortcutMechanism) { @@ -984,30 +915,4 @@ public final class ServiceDiscoveryManager extends Manager { discoInfoLookupShortcutMechanisms.remove(discoInfoLookupShortcutMechanism); } } - - public synchronized Stats getStats() { - return new Stats(this); - } - - public static final class Stats extends AbstractStats { - - public final int renewEntityCapsRequested; - public final int renewEntityCapsPerformed; - public final int scheduledRenewEntityCapsAvoided; - - private Stats(ServiceDiscoveryManager serviceDiscoveryManager) { - renewEntityCapsRequested = serviceDiscoveryManager.renewEntityCapsRequested; - renewEntityCapsPerformed = serviceDiscoveryManager.renewEntityCapsPerformed.get(); - scheduledRenewEntityCapsAvoided = serviceDiscoveryManager.scheduledRenewEntityCapsAvoided; - } - - @Override - public void appendStatsTo(ExtendedAppendable appendable) throws IOException { - StringUtils.appendHeading(appendable, "ServiceDiscoveryManager stats", '#').append('\n'); - appendable.append("renew-entitycaps-requested: ").append(renewEntityCapsRequested).append('\n'); - appendable.append("renew-entitycaps-performed: ").append(renewEntityCapsPerformed).append('\n'); - appendable.append("scheduled-renew-entitycaps-avoided: ").append(scheduledRenewEntityCapsAvoided).append('\n'); - } - - } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java index 7de48f1be..796e51857 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java @@ -299,8 +299,8 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView, TypedCloneable return containsDuplicateFeatures; } - public DiscoverInfoBuilder asBuilder(String stanzaId) { - return new DiscoverInfoBuilder(this, stanzaId); + public DiscoverInfoBuilder asBuilder() { + return new DiscoverInfoBuilder(this); } // TODO: Deprecate in favor of asBuilder(). diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfoBuilder.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfoBuilder.java index b8e179c70..66f965de4 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfoBuilder.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfoBuilder.java @@ -1,6 +1,6 @@ /** * - * Copyright 2019-2020 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. @@ -48,8 +48,8 @@ public class DiscoverInfoBuilder extends IqBuilder AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER = mb -> DeliveryReceiptRequest.addTo(mb); + /** * Enables automatic requests of delivery receipts for outgoing messages of * {@link org.jivesoftware.smack.packet.Message.Type#normal}, {@link org.jivesoftware.smack.packet.Message.Type#chat} or {@link org.jivesoftware.smack.packet.Message.Type#headline}, and @@ -280,7 +284,7 @@ public final class DeliveryReceiptManager extends Manager { * @see #dontAutoAddDeliveryReceiptRequests() */ public void autoAddDeliveryReceiptRequests() { - connection().addMessageInterceptor(DeliveryReceiptRequest::addTo, m -> { + connection().addMessageInterceptor(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER, m -> { return MESSAGES_TO_REQUEST_RECEIPTS_FOR.accept(m); }); } @@ -292,7 +296,7 @@ public final class DeliveryReceiptManager extends Manager { * @see #autoAddDeliveryReceiptRequests() */ public void dontAutoAddDeliveryReceiptRequests() { - connection().removeMessageInterceptor(DeliveryReceiptRequest::addTo); + connection().removeMessageInterceptor(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER); } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoManager.java index b6c05922e..11af9b810 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/softwareinfo/SoftwareInfoManager.java @@ -16,14 +16,18 @@ */ package org.jivesoftware.smackx.softwareinfo; +import java.io.IOException; import java.util.Map; import java.util.WeakHashMap; import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.SmackException.FeatureNotSupportedException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException.XMPPErrorException; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.softwareinfo.form.SoftwareInfoForm; @@ -40,11 +44,11 @@ import org.jxmpp.jid.Jid; */ public final class SoftwareInfoManager extends Manager { + private static final String FEATURE = "http://jabber.org/protocol/disco"; private static final Map INSTANCES = new WeakHashMap<>(); - private final ServiceDiscoveryManager serviceDiscoveryManager; - public static synchronized SoftwareInfoManager getInstanceFor (XMPPConnection connection) { + public static synchronized SoftwareInfoManager getInstanceFor (XMPPConnection connection) throws IOException, XmlPullParserException, SmackParsingException { SoftwareInfoManager manager = INSTANCES.get(connection); if (manager == null) { manager = new SoftwareInfoManager(connection); @@ -53,11 +57,25 @@ public final class SoftwareInfoManager extends Manager { return manager; } - private SoftwareInfoManager(XMPPConnection connection) { + private SoftwareInfoManager(XMPPConnection connection) throws IOException, XmlPullParserException, SmackParsingException { super(connection); serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); } + /** + * Returns true if the feature is supported by the Jid. + *
+ * @param jid Jid to be checked for support + * @return boolean + * @throws NoResponseException if there was no response from the remote entity + * @throws XMPPErrorException if there was an XMPP error returned + * @throws NotConnectedException if the XMPP connection is not connected + * @throws InterruptedException if the calling thread was interrupted + */ + public boolean isSupported(Jid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + return serviceDiscoveryManager.supportsFeatures(jid, FEATURE); + } + /** * Publishes the provided {@link SoftwareInfoForm} as an extended info. *
@@ -68,16 +86,20 @@ public final class SoftwareInfoManager extends Manager { } /** - * Get Software Information from the provided XMPP address. Returns null in case the queried entity does not announce that information. - * + * Get SoftwareInfoForm from Jid provided. + *
* @param jid jid to get software information from - * @return {@link SoftwareInfoForm} Form containing software information or null. + * @return {@link SoftwareInfoForm} Form containing software information * @throws NoResponseException if there was no response from the remote entity * @throws XMPPErrorException if there was an XMPP error returned * @throws NotConnectedException if the XMPP connection is not connected * @throws InterruptedException if the calling thread was interrupted + * @throws FeatureNotSupportedException if the feature is not supported */ - public SoftwareInfoForm fromJid(Jid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + public SoftwareInfoForm fromJid(Jid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, FeatureNotSupportedException { + if (!isSupported(jid)) { + throw new FeatureNotSupportedException(SoftwareInfoForm.FORM_TYPE, jid); + } DiscoverInfo discoverInfo = serviceDiscoveryManager.discoverInfo(jid); DataForm dataForm = DataForm.from(discoverInfo, SoftwareInfoForm.FORM_TYPE); if (dataForm == null) { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java index a564f5331..36daa66af 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/usertune/element/UserTuneElement.java @@ -186,8 +186,8 @@ public final class UserTuneElement implements ExtensionElement { /** * Artist is an optional element in UserTuneElement. - * @param artist the artist. - * @return a reference to this builder. + * @param artist. + * @return builder. */ public Builder setArtist(String artist) { this.artist = artist; @@ -196,8 +196,8 @@ public final class UserTuneElement implements ExtensionElement { /** * Length is an optional element in UserTuneElement. - * @param length the length. - * @return a reference to this builder. + * @param length. + * @return builder. */ public Builder setLength(int length) { return setLength(UInt16.from(length)); @@ -205,8 +205,8 @@ public final class UserTuneElement implements ExtensionElement { /** * Length is an optional element in UserTuneElement. - * @param length the length. - * @return a reference to this builder. + * @param length. + * @return builder. */ public Builder setLength(UInt16 length) { this.length = length; @@ -215,8 +215,8 @@ public final class UserTuneElement implements ExtensionElement { /** * Rating is an optional element in UserTuneElement. - * @param rating the rating. - * @return a reference to this builder. + * @param rating. + * @return builder. */ public Builder setRating(int rating) { this.rating = rating; @@ -225,8 +225,8 @@ public final class UserTuneElement implements ExtensionElement { /** * Source is an optional element in UserTuneElement. - * @param source the source. - * @return a reference to this builder. + * @param source. + * @return builder. */ public Builder setSource(String source) { this.source = source; @@ -235,8 +235,8 @@ public final class UserTuneElement implements ExtensionElement { /** * Title is an optional element in UserTuneElement. - * @param title the title. - * @return a reference to this builder. + * @param title. + * @return builder. */ public Builder setTitle(String title) { this.title = title; @@ -245,8 +245,8 @@ public final class UserTuneElement implements ExtensionElement { /** * Track is an optional element in UserTuneElement. - * @param track the track. - * @return a reference to this builder. + * @param track. + * @return builder. */ public Builder setTrack(String track) { this.track = track; @@ -255,8 +255,8 @@ public final class UserTuneElement implements ExtensionElement { /** * URI is an optional element in UserTuneElement. - * @param uri the URI. - * @return a reference to this builder. + * @param uri. + * @return builder. */ public Builder setUri(URI uri) { this.uri = uri; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLManager.java index 695af1124..de2def875 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xhtmlim/XHTMLManager.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.packet.Message; -import org.jivesoftware.smack.packet.MessageBuilder; -import org.jivesoftware.smack.packet.MessageView; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension; @@ -59,7 +57,7 @@ public class XHTMLManager { * @param message an XHTML message * @return an Iterator for the bodies in the message or null if none. */ - public static List getBodies(MessageView message) { + public static List getBodies(Message message) { XHTMLExtension xhtmlExtension = XHTMLExtension.from(message); if (xhtmlExtension != null) return xhtmlExtension.getBodies(); @@ -67,32 +65,12 @@ public class XHTMLManager { return null; } - /** - * Adds an XHTML body to the message. - * - * @param messageBuilder the message that will receive the XHTML body - * @param xhtmlText the string to add as an XHTML body to the message - */ - public static void addBody(MessageBuilder messageBuilder, XHTMLText xhtmlText) { - XHTMLExtension xhtmlExtension = XHTMLExtension.from(messageBuilder); - if (xhtmlExtension == null) { - // Create an XHTMLExtension and add it to the message - xhtmlExtension = new XHTMLExtension(); - messageBuilder.addExtension(xhtmlExtension); - } - // Add the required bodies to the message - xhtmlExtension.addBody(xhtmlText.toXML()); - } - /** * Adds an XHTML body to the message. * * @param message the message that will receive the XHTML body * @param xhtmlText the string to add as an XHTML body to the message - * @deprecated use {@link #addBody(MessageBuilder, XHTMLText)} instead. */ - // TODO: Remove in Smack 4.6 - @Deprecated public static void addBody(Message message, XHTMLText xhtmlText) { XHTMLExtension xhtmlExtension = XHTMLExtension.from(message); if (xhtmlExtension == null) { diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java index b72c35b69..99dd32d22 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java @@ -86,7 +86,7 @@ public class Socks5ProxyTest { * When inserting new network addresses to the proxy the order should remain in the order they * were inserted. * - * @throws UnknownHostException if unknown host. + * @throws UnknownHostException */ @Test public void shouldPreserveAddressOrderOnInsertions() throws UnknownHostException { @@ -114,7 +114,7 @@ public class Socks5ProxyTest { * When replacing network addresses of the proxy the order should remain in the order if the * given list. * - * @throws UnknownHostException if unknown host. + * @throws UnknownHostException */ @Test public void shouldPreserveAddressOrderOnReplace() throws UnknownHostException { diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/DataValidationHelperTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/DataValidationHelperTest.java index 42f08f499..a12effdb6 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/DataValidationHelperTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/DataValidationHelperTest.java @@ -44,8 +44,9 @@ public class DataValidationHelperTest { () -> element.checkConsistency(field)); assertEquals("Field type 'jid-single' is not consistent with validation method 'basic'.", vce.getMessage()); - assertThrows(IllegalArgumentException.class, + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> new ListRange(-1L, 1L)); + assertEquals("unsigned 32-bit integers can't be negative", iae.getMessage()); element.setListRange(new ListRange(10L, 100L)); vce = assertThrows(ValidationConsistencyException.class, () -> element.checkConsistency(field)); diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStore.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStore.java index a78a44c28..dec89bc68 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStore.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStore.java @@ -1,6 +1,6 @@ /** * - * Copyright 2013-2015 the original author or authors, 2020 Florian Schmaus + * Copyright 2013-2015 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.jivesoftware.smack.roster.rosterstore; import java.io.File; +import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; @@ -55,10 +56,15 @@ public final class DirectoryRosterStore implements RosterStore { private static final String STORE_ID = "DEFAULT_ROSTER_STORE"; private static final Logger LOGGER = Logger.getLogger(DirectoryRosterStore.class.getName()); - private static boolean rosterDirFilter(File file) { - String name = file.getName(); - return name.startsWith(ENTRY_PREFIX); - } + private static final FileFilter rosterDirFilter = new FileFilter() { + + @Override + public boolean accept(File file) { + String name = file.getName(); + return name.startsWith(ENTRY_PREFIX); + } + + }; /** * @param baseDir TODO javadoc me please @@ -116,7 +122,7 @@ public final class DirectoryRosterStore implements RosterStore { public List getEntries() { List entries = new ArrayList<>(); - for (File file : fileDir.listFiles(DirectoryRosterStore::rosterDirFilter)) { + for (File file : fileDir.listFiles(rosterDirFilter)) { Item entry = readEntry(file); if (entry == null) { // Roster directory store corrupt. Abort and signal this by returning null. @@ -162,7 +168,7 @@ public final class DirectoryRosterStore implements RosterStore { @Override public boolean resetEntries(Collection items, String version) { - for (File file : fileDir.listFiles(DirectoryRosterStore::rosterDirFilter)) { + for (File file : fileDir.listFiles(rosterDirFilter)) { file.delete(); } for (Item item : items) { diff --git a/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterTest.java b/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterTest.java index adb75cb09..8bd98748c 100644 --- a/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterTest.java +++ b/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterTest.java @@ -95,8 +95,6 @@ public class RosterTest extends InitSmackIm { * Test a simple roster initialization according to the example in * RFC3921: Retrieving One's Roster on Login. - * - * @throws Exception in case of an exception. */ @Test public void testSimpleRosterInitialization() throws Exception { @@ -133,8 +131,6 @@ public class RosterTest extends InitSmackIm { * Test adding a roster item according to the example in * RFC3921: Adding a Roster Item. - * - * @throws Throwable in case a throwable is thrown. */ @Test public void testAddRosterItem() throws Throwable { @@ -207,8 +203,6 @@ public class RosterTest extends InitSmackIm { * Test updating a roster item according to the example in * RFC3921: Updating a Roster Item. - * - * @throws Throwable in case a throwable is thrown. */ @Test public void testUpdateRosterItem() throws Throwable { @@ -285,7 +279,6 @@ public class RosterTest extends InitSmackIm { * Test deleting a roster item according to the example in * RFC3921: Deleting a Roster Item. - * @throws Throwable if throwable is thrown. */ @Test public void testDeleteRosterItem() throws Throwable { @@ -334,7 +327,6 @@ public class RosterTest extends InitSmackIm { * Test a simple roster push according to the example in * RFC3921bis-03: Roster Push. - * @throws Throwable in case a throwable is thrown. */ @Test public void testSimpleRosterPush() throws Throwable { @@ -406,7 +398,6 @@ public class RosterTest extends InitSmackIm { * Test if adding an user with an empty group is equivalent with providing * no group. * - * @throws Throwable in case a throwable is thrown. * @see SMACK-294 */ @Test(timeout = 5000) @@ -475,7 +466,6 @@ public class RosterTest extends InitSmackIm { * Test processing a roster push with an empty group is equivalent with providing * no group. * - * @throws Throwable in case a throwable is thrown. * @see SMACK-294 */ @Test diff --git a/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterVersioningTest.java b/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterVersioningTest.java index aa2a51687..91be648ed 100644 --- a/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterVersioningTest.java +++ b/smack-im/src/test/java/org/jivesoftware/smack/roster/RosterVersioningTest.java @@ -97,8 +97,6 @@ public class RosterVersioningTest { * by all entries of the roster store. * @throws SmackException if Smack detected an exceptional situation. * @throws XMPPException if an XMPP protocol error was received. - * @throws InterruptedException if interrupted. - * @throws IOException if IO exception. */ @Test(timeout = 300000) public void testEqualVersionStored() throws InterruptedException, IOException, XMPPException, SmackException { @@ -174,7 +172,6 @@ public class RosterVersioningTest { /** * Test roster versioning with roster pushes. - * @throws Throwable in case a throwable is thrown. */ @SuppressWarnings("UndefinedEquals") @Test(timeout = 5000) diff --git a/smack-im/src/test/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStoreTest.java b/smack-im/src/test/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStoreTest.java index 9dc9ba3c7..bf116b874 100644 --- a/smack-im/src/test/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStoreTest.java +++ b/smack-im/src/test/java/org/jivesoftware/smack/roster/rosterstore/DirectoryRosterStoreTest.java @@ -57,7 +57,6 @@ public class DirectoryRosterStoreTest { /** * Tests that opening an uninitialized directory fails. - * @throws IOException if IO exception. */ @Test public void testStoreUninitialized() throws IOException { @@ -67,7 +66,6 @@ public class DirectoryRosterStoreTest { /** * Tests that an initialized directory is empty. - * @throws IOException if IO exception. */ @Test public void testStoreInitializedEmpty() throws IOException { @@ -82,7 +80,6 @@ public class DirectoryRosterStoreTest { /** * Tests adding and removing entries. - * @throws IOException if IO exception. */ @Test public void testStoreAddRemove() throws IOException { diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java index 2b94b2190..2bdb340bd 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java @@ -84,9 +84,9 @@ public abstract class AbstractSmackIntTest { protected HttpURLConnection getHttpUrlConnectionFor(URL url) throws IOException { HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - if (sinttestConfiguration.sslContextFactory != null && urlConnection instanceof HttpsURLConnection) { + if (sinttestConfiguration.tlsContext != null && urlConnection instanceof HttpsURLConnection) { HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection; - httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.sslContextFactory.createSslContext().getSocketFactory()); + httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.tlsContext.getSocketFactory()); } return urlConnection; } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java index 593d5b847..befb94c08 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java @@ -20,15 +20,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.filter.AndFilter; -import org.jivesoftware.smack.filter.FromMatchesFilter; -import org.jivesoftware.smack.filter.PresenceTypeFilter; -import org.jivesoftware.smack.packet.Stanza; -import org.jivesoftware.smack.util.Async.ThrowingRunnable; - -import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest { @@ -66,48 +58,4 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest connectionsLocal.add(conThree); this.connections = Collections.unmodifiableList(connectionsLocal); } - - /** - * Perform action and wait until conA observes a presence form conB. - *

- * This method is usually used so that 'action' performs an operation that changes one entities - * features/nodes/capabilities, and we want to check that another connection is able to observe this change, and use - * that new "thing" that was added to the connection. - *

- *

- * Note that this method is a workaround at best and not reliable. Because it is not guaranteed that any XEP-0030 - * related manager, e.g. EntityCapsManager, already processed the presence when this method returns. - *

- * TODO: Come up with a better solution. - * - * @param conA the connection to observe the presence on. - * @param conB the connection sending the presence - * @param action the action to perform. - * @throws Exception in case of an exception. - */ - protected void performActionAndWaitForPresence(XMPPConnection conA, XMPPConnection conB, ThrowingRunnable action) - throws Exception { - final SimpleResultSyncPoint presenceReceivedSyncPoint = new SimpleResultSyncPoint(); - final StanzaListener presenceListener = new StanzaListener() { - @Override - public void processStanza(Stanza packet) { - presenceReceivedSyncPoint.signal(); - } - }; - - // Add a stanzaListener to listen for incoming presence - conA.addAsyncStanzaListener(presenceListener, new AndFilter( - PresenceTypeFilter.AVAILABLE, - FromMatchesFilter.create(conB.getUser()) - )); - - action.runOrThrow(); - - try { - // wait for the dummy feature to get sent via presence - presenceReceivedSyncPoint.waitForResult(timeout); - } finally { - conA.removeAsyncStanzaListener(presenceListener); - } - } } diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java index 9f5c32633..258fb3c1f 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java @@ -36,7 +36,6 @@ import org.jivesoftware.smack.debugger.ConsoleDebugger; import org.jivesoftware.smack.util.Function; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.ParserUtils; -import org.jivesoftware.smack.util.SslContextFactory; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.debugger.EnhancedDebugger; @@ -73,7 +72,7 @@ public final class Configuration { public final String serviceTlsPin; - public final SslContextFactory sslContextFactory; + public final SSLContext tlsContext; public final SecurityMode securityMode; @@ -122,10 +121,9 @@ public final class Configuration { "'service' must be set. Either via 'properties' files or via system property 'sinttest.service'."); serviceTlsPin = builder.serviceTlsPin; if (serviceTlsPin != null) { - SSLContext sslContext = Java7Pinning.forPin(serviceTlsPin); - sslContextFactory = () -> sslContext; + tlsContext = Java7Pinning.forPin(serviceTlsPin); } else { - sslContextFactory = null; + tlsContext = null; } securityMode = builder.securityMode; if (builder.replyTimeout > 0) { @@ -170,8 +168,8 @@ public final class Configuration { this.testPackages = builder.testPackages; this.configurationApplier = b -> { - if (sslContextFactory != null) { - b.setSslContextFactory(sslContextFactory); + if (tlsContext != null) { + b.setCustomSSLContext(tlsContext); } b.setSecurityMode(securityMode); b.setXmppDomain(service); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java index 4c373a9ce..0a0110882 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java @@ -31,7 +31,6 @@ import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotLoggedInException; import org.jivesoftware.smack.StanzaListener; -import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.filter.AndFilter; @@ -41,7 +40,6 @@ import org.jivesoftware.smack.filter.PresenceTypeFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.roster.RosterUtil; -import org.jivesoftware.smack.util.Async.ThrowingRunnable; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; @@ -51,6 +49,7 @@ import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.annotations.AfterClass; import org.igniterealtime.smack.inttest.annotations.BeforeClass; import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint; public class EntityCapsTest extends AbstractSmackIntegrationTest { @@ -125,7 +124,11 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { /** * Test if entity caps actually prevent a disco info request and reply. * - * @throws Exception if exception. + * @throws XMPPException if an XMPP protocol error was received. + * @throws InterruptedException if the calling thread was interrupted. + * @throws NotConnectedException if the XMPP connection is not connected. + * @throws NoResponseException if there was no response from the remote entity. + * */ @SmackIntegrationTest public void testPreventDiscoInfo() throws Exception { @@ -140,7 +143,26 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { }, new AndFilter(new StanzaTypeFilter(DiscoverInfo.class), IQTypeFilter.GET)); - addFeatureAndWaitForPresence(conOne, conTwo, dummyFeature); + final SimpleResultSyncPoint presenceReceivedSyncPoint = new SimpleResultSyncPoint(); + final StanzaListener presenceListener = new StanzaListener() { + @Override + public void processStanza(Stanza packet) { + presenceReceivedSyncPoint.signal(); + } + }; + + // Add a stanzaListener to listen for incoming presence + conOne.addAsyncStanzaListener(presenceListener, PresenceTypeFilter.AVAILABLE); + + // add a bogus feature so that con1 ver won't match con0's + sdmTwo.addFeature(dummyFeature); + + try { + // wait for the dummy feature to get sent via presence + presenceReceivedSyncPoint.waitForResult(timeout); + } finally { + conOne.removeAsyncStanzaListener(presenceListener); + } dropCapsCache(); // discover that @@ -159,10 +181,10 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { } @SmackIntegrationTest - public void testCapsChanged() throws Exception { + public void testCapsChanged() { final String dummyFeature = getNewDummyFeature(); String nodeVerBefore = EntityCapsManager.getNodeVersionByJid(conTwo.getUser()); - addFeatureAndWaitForPresence(conOne, conTwo, dummyFeature); + sdmTwo.addFeature(dummyFeature); String nodeVerAfter = EntityCapsManager.getNodeVersionByJid(conTwo.getUser()); assertFalse(nodeVerBefore.equals(nodeVerAfter)); @@ -207,24 +229,4 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { private static void dropCapsCache() { EntityCapsManager.CAPS_CACHE.clear(); } - - /** - * Adds 'feature' to conB and waits until conA observes a presence form conB. - * - * @param conA the connection to observe the presence on. - * @param conB the connection to add the feature to. - * @param feature the feature to add. - * @throws Exception in case of an exception. - */ - private void addFeatureAndWaitForPresence(XMPPConnection conA, XMPPConnection conB, String feature) - throws Exception { - final ServiceDiscoveryManager sdmB = ServiceDiscoveryManager.getInstanceFor(conB); - ThrowingRunnable action = new ThrowingRunnable() { - @Override - public void runOrThrow() throws Exception { - sdmB.addFeature(feature); - } - }; - performActionAndWaitForPresence(conA, conB, action); - } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java index 11e1340f4..6b153b3a2 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/chatstate/ChatStateIntegrationTest.java @@ -21,6 +21,7 @@ import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smackx.chatstates.ChatState; +import org.jivesoftware.smackx.chatstates.ChatStateListener; import org.jivesoftware.smackx.chatstates.ChatStateManager; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; @@ -33,19 +34,25 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { // Listener for composing chat state private final SimpleResultSyncPoint composingSyncPoint = new SimpleResultSyncPoint(); - private void composingListener(Chat chat, ChatState state, Message message) { - if (state.equals(ChatState.composing)) { - composingSyncPoint.signal(); + private final ChatStateListener composingListener = new ChatStateListener() { + @Override + public void stateChanged(Chat chat, ChatState state, Message message) { + if (state.equals(ChatState.composing)) { + composingSyncPoint.signal(); + } } - } + }; // Listener for active chat state private final SimpleResultSyncPoint activeSyncPoint = new SimpleResultSyncPoint(); - private void activeListener(Chat chat, ChatState state, Message message) { - if (state.equals(ChatState.active)) { - activeSyncPoint.signal(); + private final ChatStateListener activeListener = new ChatStateListener() { + @Override + public void stateChanged(Chat chat, ChatState state, Message message) { + if (state.equals(ChatState.active)) { + activeSyncPoint.signal(); + } } - } + }; public ChatStateIntegrationTest(SmackIntegrationTestEnvironment environment) { @@ -58,8 +65,8 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { ChatStateManager manTwo = ChatStateManager.getInstance(conTwo); // Add chatState listeners. - manTwo.addChatStateListener(this::composingListener); - manTwo.addChatStateListener(this::activeListener); + manTwo.addChatStateListener(composingListener); + manTwo.addChatStateListener(activeListener); Chat chatOne = ChatManager.getInstanceFor(conOne) .chatWith(conTwo.getUser().asEntityBareJid()); @@ -79,7 +86,7 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { @AfterClass public void cleanup() { ChatStateManager manTwo = ChatStateManager.getInstance(conTwo); - manTwo.removeChatStateListener(this::composingListener); - manTwo.removeChatStateListener(this::activeListener); + manTwo.removeChatStateListener(composingListener); + manTwo.removeChatStateListener(activeListener); } } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java index 243bfec36..7736c0657 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java @@ -57,9 +57,7 @@ public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest + " does not accept files of size " + FILE_SIZE + ". It only accepts files with a maximum size of " + uploadService.getMaxFileSize()); } - if (environment.configuration.sslContextFactory != null) { - hfumOne.setTlsContext(environment.configuration.sslContextFactory.createSslContext()); - } + hfumOne.setTlsContext(environment.configuration.tlsContext); } @SmackIntegrationTest diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java index f289c96be..e2e37fae5 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/softwareInfo/SoftwareInfoIntegrationTest.java @@ -16,14 +16,13 @@ */ package org.jivesoftware.smackx.softwareInfo; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import org.jivesoftware.smack.parsing.SmackParsingException; -import org.jivesoftware.smack.util.Async.ThrowingRunnable; import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.mediaelement.element.MediaElement; import org.jivesoftware.smackx.softwareinfo.SoftwareInfoManager; @@ -55,14 +54,9 @@ public class SoftwareInfoIntegrationTest extends AbstractSmackIntegrationTest { @SmackIntegrationTest public void test() throws Exception { SoftwareInfoForm softwareInfoSent = createSoftwareInfoForm(); - performActionAndWaitForPresence(conTwo, conOne, new ThrowingRunnable() { - @Override - public void runOrThrow() throws Exception { - sim1.publishSoftwareInformationForm(softwareInfoSent); - } - }); + sim1.publishSoftwareInformationForm(softwareInfoSent); SoftwareInfoForm softwareInfoFormReceived = sim2.fromJid(conOne.getUser()); - assertEquals(softwareInfoSent, softwareInfoFormReceived); + assertTrue(softwareInfoFormReceived.equals(softwareInfoSent)); } private static SoftwareInfoForm createSoftwareInfoForm() throws URISyntaxException { diff --git a/smack-omemo-signal/src/test/java/org/jivesoftware/smackx/omemo/SignalOmemoStoreTest.java b/smack-omemo-signal/src/test/java/org/jivesoftware/smackx/omemo/SignalOmemoStoreTest.java index a4a9bedea..37b493a26 100644 --- a/smack-omemo-signal/src/test/java/org/jivesoftware/smackx/omemo/SignalOmemoStoreTest.java +++ b/smack-omemo-signal/src/test/java/org/jivesoftware/smackx/omemo/SignalOmemoStoreTest.java @@ -60,7 +60,7 @@ public class SignalOmemoStoreTest extends OmemoStoreTest omemoMessageListeners = new HashSet<>(); private final HashSet omemoMucMessageListeners = new HashSet<>(); - private final PepManager pepManager; - private OmemoTrustCallback trustCallback; private BareJid ownJid; @@ -115,7 +120,6 @@ public final class OmemoManager extends Manager { super(connection); service = OmemoService.getInstance(); - pepManager = PepManager.getInstanceFor(connection); this.deviceId = deviceId; @@ -134,6 +138,9 @@ public final class OmemoManager extends Manager { // StanzaListeners resumeStanzaAndPEPListeners(); + + // Announce OMEMO support + ServiceDiscoveryManager.getInstanceFor(connection).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); } /** @@ -244,6 +251,7 @@ public final class OmemoManager extends Manager { } getOmemoService().init(new LoggedInOmemoManager(this)); + ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); } /** @@ -885,25 +893,27 @@ public final class OmemoManager extends Manager { * after {@link #stopStanzaAndPEPListeners()} was called. */ public void resumeStanzaAndPEPListeners() { + PepManager pepManager = PepManager.getInstanceFor(connection()); CarbonManager carbonManager = CarbonManager.getInstanceFor(connection()); // Remove listeners to avoid them getting added twice - connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener); - carbonManager.removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); + connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener); + carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); + pepManager.removePepListener(deviceListUpdateListener); // Add listeners - pepManager.addPepEventListener(OmemoConstants.PEP_NODE_DEVICE_LIST, OmemoDeviceListElement.class, pepOmemoDeviceListEventListener); - connection().addAsyncStanzaListener(this::internalOmemoMessageStanzaListener, OmemoManager::isOmemoMessage); - carbonManager.addCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); + pepManager.addPepListener(deviceListUpdateListener); + connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter); + carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); } /** * Remove active stanza listeners needed for OMEMO. */ public void stopStanzaAndPEPListeners() { - pepManager.removePepEventListener(pepOmemoDeviceListEventListener); - connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener); - CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); + PepManager.getInstanceFor(connection()).removePepListener(deviceListUpdateListener); + connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener); + CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); } /** @@ -951,87 +961,127 @@ public final class OmemoManager extends Manager { /** * StanzaListener that listens for incoming Stanzas which contain OMEMO elements. */ - private void internalOmemoMessageStanzaListener(final Stanza packet) { - Async.go(new Runnable() { - @Override - public void run() { - try { - getOmemoService().onOmemoMessageStanzaReceived(packet, - new LoggedInOmemoManager(OmemoManager.this)); - } catch (SmackException.NotLoggedInException | IOException e) { - LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e); - } - } - }); - } + private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() { - /** - * CarbonCopyListener that listens for incoming carbon copies which contain OMEMO elements. - */ - private void internalOmemoCarbonCopyListener(final CarbonExtension.Direction direction, - final Message carbonCopy, - final Message wrappingMessage) { - Async.go(new Runnable() { - @Override - public void run() { - if (isOmemoMessage(carbonCopy)) { - try { - getOmemoService().onOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage, - new LoggedInOmemoManager(OmemoManager.this)); - } catch (SmackException.NotLoggedInException | IOException e) { - LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e); - } - } - } - }); - } - - @SuppressWarnings("UnnecessaryLambda") - private final PepEventListener pepOmemoDeviceListEventListener = - (from, receivedDeviceList, id, message) -> { - // Device List - OmemoCachedDeviceList deviceList; - try { - getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, - receivedDeviceList); - - if (!from.asBareJid().equals(getOwnJid())) { - return; - } - - deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice()); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, - "IOException while processing OMEMO PEP device updates. Message: " + message, - e); - return; - } - final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList); - - if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) { - LOGGER.log(Level.FINE, "Republish deviceList due to changes:" + - " Received: " + Arrays.toString(receivedDeviceList.copyDeviceIds().toArray()) + - " Published: " + Arrays.toString(newDeviceList.copyDeviceIds().toArray())); + @Override + public void processStanza(final Stanza packet) { Async.go(new Runnable() { @Override public void run() { try { - OmemoService.publishDeviceList(connection(), newDeviceList); - } catch (InterruptedException | XMPPException.XMPPErrorException | - SmackException.NotConnectedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException e) { - LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e); + getOmemoService().onOmemoMessageStanzaReceived(packet, + new LoggedInOmemoManager(OmemoManager.this)); + } catch (SmackException.NotLoggedInException | IOException e) { + LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e); } } }); } }; + /** + * CarbonCopyListener that listens for incoming carbon copies which contain OMEMO elements. + */ + private final CarbonCopyReceivedListener internalOmemoCarbonCopyListener = new CarbonCopyReceivedListener() { + @Override + public void onCarbonCopyReceived(final CarbonExtension.Direction direction, + final Message carbonCopy, + final Message wrappingMessage) { + Async.go(new Runnable() { + @Override + public void run() { + if (omemoMessageStanzaFilter.accept(carbonCopy)) { + try { + getOmemoService().onOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage, + new LoggedInOmemoManager(OmemoManager.this)); + } catch (SmackException.NotLoggedInException | IOException e) { + LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e); + } + } + } + }); + } + }; + + /** + * PEPListener that listens for OMEMO deviceList updates. + */ + private final PepListener deviceListUpdateListener = new PepListener() { + @Override + public void eventReceived(EntityBareJid from, EventElement event, Message message) { + + // Unknown sender, no more work to do. + if (from == null) { + // TODO: This DOES happen for some reason. Figure out when... + return; + } + + for (ExtensionElement items : event.getExtensions()) { + if (!(items instanceof ItemsExtension)) { + continue; + } + + for (ExtensionElement item : ((ItemsExtension) items).getExtensions()) { + if (!(item instanceof PayloadItem)) { + continue; + } + + PayloadItem payloadItem = (PayloadItem) item; + + if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement)) { + continue; + } + + // Device List + OmemoCachedDeviceList deviceList; + OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload(); + try { + getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, + receivedDeviceList); + + if (!from.asBareJid().equals(getOwnJid())) { + continue; + } + + deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice()); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, + "IOException while processing OMEMO PEP device updates. Message: " + message, + e); + continue; + } + final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList); + + if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) { + LOGGER.log(Level.FINE, "Republish deviceList due to changes:" + + " Received: " + Arrays.toString(receivedDeviceList.copyDeviceIds().toArray()) + + " Published: " + Arrays.toString(newDeviceList.copyDeviceIds().toArray())); + Async.go(new Runnable() { + @Override + public void run() { + try { + OmemoService.publishDeviceList(connection(), newDeviceList); + } catch (InterruptedException | XMPPException.XMPPErrorException | + SmackException.NotConnectedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException e) { + LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e); + } + } + }); + } + } + } + } + }; + /** * StanzaFilter that filters messages containing a OMEMO element. */ - private static boolean isOmemoMessage(Stanza stanza) { - return stanza instanceof Message && OmemoManager.stanzaContainsOmemoElement(stanza); - } + private final StanzaFilter omemoMessageStanzaFilter = new StanzaFilter() { + @Override + public boolean accept(Stanza stanza) { + return stanza instanceof Message && OmemoManager.stanzaContainsOmemoElement(stanza); + } + }; /** * Guard class which ensures that the wrapped OmemoManager knows its BareJid. diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java index 352853450..f91dc199f 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java @@ -31,6 +31,7 @@ public final class OmemoConstants { // PubSub Node names public static final String PEP_NODE_DEVICE_LIST = OMEMO_NAMESPACE_V_AXOLOTL + ".devicelist"; + public static final String PEP_NODE_DEVICE_LIST_NOTIFY = PEP_NODE_DEVICE_LIST + "+notify"; public static final String PEP_NODE_BUNDLES = OMEMO_NAMESPACE_V_AXOLOTL + ".bundles"; /** diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java index a3a8a7c1b..d16296a05 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2020 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.chat2.Chat; import org.jivesoftware.smack.chat2.ChatManager; +import org.jivesoftware.smack.chat2.IncomingChatMessageListener; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.stringencoder.Base64; @@ -68,10 +69,12 @@ import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore; import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore; import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil; import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper; -import org.jivesoftware.smackx.pep.PepEventListener; import org.jivesoftware.smackx.pep.PepListener; import org.jivesoftware.smackx.pep.PepManager; +import org.jivesoftware.smackx.pubsub.EventElement; +import org.jivesoftware.smackx.pubsub.ItemsExtension; import org.jivesoftware.smackx.pubsub.LeafNode; +import org.jivesoftware.smackx.pubsub.PayloadItem; import org.jivesoftware.smackx.pubsub.PubSubException; import org.jivesoftware.smackx.pubsub.PubSubFeature; @@ -170,9 +173,6 @@ public final class OpenPgpManager extends Manager { private final Set signElementReceivedListeners = new HashSet<>(); private final Set cryptElementReceivedListeners = new HashSet<>(); - @SuppressWarnings("UnnecessaryLambda") - private final PepEventListener pepPublicKeyListElementListener = (from, listElement, id, message) -> processPublicKeysListElement(from, listElement);; - /** * Private constructor to avoid instantiation without putting the object into {@code INSTANCES}. * @@ -180,7 +180,7 @@ public final class OpenPgpManager extends Manager { */ private OpenPgpManager(XMPPConnection connection) { super(connection); - ChatManager.getInstanceFor(connection).addIncomingListener(this::incomingChatMessageListener); + ChatManager.getInstanceFor(connection).addIncomingListener(incomingOpenPgpMessageListener); pepManager = PepManager.getInstanceFor(connection); } @@ -279,7 +279,7 @@ public final class OpenPgpManager extends Manager { publishPublicKey(pepManager, pubkeyElement, primaryFingerprint); // Subscribe to public key changes - pepManager.addPepEventListener(PEP_NODE_PUBLIC_KEYS, PublicKeysListElement.class, pepPublicKeyListElementListener); + PepManager.getInstanceFor(connection()).addPepListener(metadataListener); ServiceDiscoveryManager.getInstanceFor(connection()) .addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY); } @@ -381,7 +381,7 @@ public final class OpenPgpManager extends Manager { * Remove the metadata listener. This method is mainly used in tests. */ public void stopMetadataListener() { - pepManager.removePepEventListener(pepPublicKeyListElementListener); + PepManager.getInstanceFor(connection()).removePepListener(metadataListener); } /** @@ -497,6 +497,31 @@ public final class OpenPgpManager extends Manager { Private stuff. */ + /** + * {@link PepListener} that listens for changes to the OX public keys metadata node. + * + * @see XEP-0373 §4.4 + */ + private final PepListener metadataListener = new PepListener() { + @Override + public void eventReceived(final EntityBareJid from, final EventElement event, final Message message) { + if (PEP_NODE_PUBLIC_KEYS.equals(event.getEvent().getNode())) { + final BareJid contact = from.asBareJid(); + LOGGER.log(Level.INFO, "Received OpenPGP metadata update from " + contact); + Async.go(new Runnable() { + @Override + public void run() { + ItemsExtension items = (ItemsExtension) event.getExtensions().get(0); + PayloadItem payload = (PayloadItem) items.getItems().get(0); + PublicKeysListElement listElement = (PublicKeysListElement) payload.getPayload(); + + processPublicKeysListElement(from, listElement); + } + }, "ProcessOXMetadata"); + } + } + }; + private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) { OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible()); try { @@ -523,60 +548,62 @@ public final class OpenPgpManager extends Manager { return provider.decryptAndOrVerify(element, getOpenPgpSelf(), sender); } - private void incomingChatMessageListener(final EntityBareJid from, final Message message, Chat chat) { - Async.go(new Runnable() { - @Override - public void run() { - OpenPgpElement element = message.getExtension(OpenPgpElement.class); - if (element == null) { - // Message does not contain an OpenPgpElement -> discard - return; - } + private final IncomingChatMessageListener incomingOpenPgpMessageListener = + new IncomingChatMessageListener() { + @Override + public void newIncomingMessage(final EntityBareJid from, final Message message, Chat chat) { + Async.go(new Runnable() { + @Override + public void run() { + OpenPgpElement element = message.getExtension(OpenPgpElement.class); + if (element == null) { + // Message does not contain an OpenPgpElement -> discard + return; + } - OpenPgpContact contact = getOpenPgpContact(from); + OpenPgpContact contact = getOpenPgpContact(from); - OpenPgpMessage decrypted = null; - OpenPgpContentElement contentElement = null; - try { - decrypted = decryptOpenPgpElement(element, contact); - contentElement = decrypted.getOpenPgpContentElement(); - } catch (PGPException e) { - LOGGER.log(Level.WARNING, "Could not decrypt incoming OpenPGP encrypted message", e); - } catch (XmlPullParserException | IOException e) { - LOGGER.log(Level.WARNING, "Invalid XML content of incoming OpenPGP encrypted message", e); - } catch (SmackException.NotLoggedInException e) { - LOGGER.log(Level.WARNING, "Cannot determine our JID, since we are not logged in.", e); - } + OpenPgpMessage decrypted = null; + OpenPgpContentElement contentElement = null; + try { + decrypted = decryptOpenPgpElement(element, contact); + contentElement = decrypted.getOpenPgpContentElement(); + } catch (PGPException e) { + LOGGER.log(Level.WARNING, "Could not decrypt incoming OpenPGP encrypted message", e); + } catch (XmlPullParserException | IOException e) { + LOGGER.log(Level.WARNING, "Invalid XML content of incoming OpenPGP encrypted message", e); + } catch (SmackException.NotLoggedInException e) { + LOGGER.log(Level.WARNING, "Cannot determine our JID, since we are not logged in.", e); + } - if (contentElement instanceof SigncryptElement) { - for (SigncryptElementReceivedListener l : signcryptElementReceivedListeners) { - l.signcryptElementReceived(contact, message, (SigncryptElement) contentElement, - decrypted.getMetadata()); - } - return; - } + if (contentElement instanceof SigncryptElement) { + for (SigncryptElementReceivedListener l : signcryptElementReceivedListeners) { + l.signcryptElementReceived(contact, message, (SigncryptElement) contentElement, decrypted.getMetadata()); + } + return; + } - if (contentElement instanceof SignElement) { - for (SignElementReceivedListener l : signElementReceivedListeners) { - l.signElementReceived(contact, message, (SignElement) contentElement, decrypted.getMetadata()); - } - return; - } + if (contentElement instanceof SignElement) { + for (SignElementReceivedListener l : signElementReceivedListeners) { + l.signElementReceived(contact, message, (SignElement) contentElement, decrypted.getMetadata()); + } + return; + } - if (contentElement instanceof CryptElement) { - for (CryptElementReceivedListener l : cryptElementReceivedListeners) { - l.cryptElementReceived(contact, message, (CryptElement) contentElement, - decrypted.getMetadata()); - } - return; - } + if (contentElement instanceof CryptElement) { + for (CryptElementReceivedListener l : cryptElementReceivedListeners) { + l.cryptElementReceived(contact, message, (CryptElement) contentElement, decrypted.getMetadata()); + } + return; + } - else { - throw new AssertionError("Invalid element received: " + contentElement.getClass().getName()); + else { + throw new AssertionError("Invalid element received: " + contentElement.getClass().getName()); + } + } + }); } - } - }); - } + }; /** * Create a {@link PubkeyElement} which contains the OpenPGP public key of {@code owner} which belongs to diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java index 4383a02b3..1ccdabe0f 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java @@ -44,6 +44,7 @@ import org.jivesoftware.smackx.ox.crypto.OpenPgpElementAndMetadata; import org.jivesoftware.smackx.ox.element.OpenPgpContentElement; import org.jivesoftware.smackx.ox.element.OpenPgpElement; import org.jivesoftware.smackx.ox.element.SigncryptElement; +import org.jivesoftware.smackx.ox.listener.SigncryptElementReceivedListener; import org.bouncycastle.openpgp.PGPException; import org.jxmpp.jid.BareJid; @@ -126,7 +127,7 @@ public final class OXInstantMessagingManager extends Manager { private OXInstantMessagingManager(final XMPPConnection connection) { super(connection); openPgpManager = OpenPgpManager.getInstanceFor(connection); - openPgpManager.registerSigncryptReceivedListener(this::signcryptElementReceivedListener); + openPgpManager.registerSigncryptReceivedListener(signcryptElementReceivedListener); announceSupportForOxInstantMessaging(); } @@ -357,9 +358,12 @@ public final class OXInstantMessagingManager extends Manager { message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging."); } - private void signcryptElementReceivedListener(OpenPgpContact contact, Message originalMessage, SigncryptElement signcryptElement, OpenPgpMetadata metadata) { - for (OxMessageListener listener : oxMessageListeners) { - listener.newIncomingOxMessage(contact, originalMessage, signcryptElement, metadata); + private final SigncryptElementReceivedListener signcryptElementReceivedListener = new SigncryptElementReceivedListener() { + @Override + public void signcryptElementReceived(OpenPgpContact contact, Message originalMessage, SigncryptElement signcryptElement, OpenPgpMetadata metadata) { + for (OxMessageListener listener : oxMessageListeners) { + listener.newIncomingOxMessage(contact, originalMessage, signcryptElement, metadata); + } } - } + }; } diff --git a/smack-repl/build.gradle b/smack-repl/build.gradle index 2ba95bcc4..3176e1985 100644 --- a/smack-repl/build.gradle +++ b/smack-repl/build.gradle @@ -2,9 +2,6 @@ plugins { id "com.github.alisiikh.scalastyle_2.12" version "2.0.2" } -description = """\ -A REPL (Read-Eval-Print-Loop) for Smack, or, in other words, a CLI (Command Line Interface) for Smack.""" - apply plugin: 'scala' apply plugin: 'com.github.alisiikh.scalastyle_2.12' diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/Nio.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/Nio.java index af4b939d6..d9d766447 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/Nio.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/Nio.java @@ -42,8 +42,6 @@ import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.sm.StreamManagementModuleDescriptor; import org.jivesoftware.smack.tcp.XmppTcpTransportModuleDescriptor; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; - import org.jxmpp.util.XmppDateTime; public class Nio { @@ -130,10 +128,9 @@ public class Nio { connection.disconnect(); ModularXmppClientToServerConnection.Stats connectionStats = connection.getStats(); - ServiceDiscoveryManager.Stats serviceDiscoveryManagerStats = ServiceDiscoveryManager.getInstanceFor(connection).getStats(); // CHECKSTYLE:OFF - System.out.println("NIO successfully finished, yeah!\n" + connectionStats + '\n' + serviceDiscoveryManagerStats); + System.out.println("NIO successfully finished, yeah!\n" + connectionStats); // CHECKSTYLE:ON } diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/TlsTest.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/TlsTest.java index 08bd25b6d..557f1812d 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/TlsTest.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/TlsTest.java @@ -89,7 +89,7 @@ public class TlsTest { if (StringUtils.isNotEmpty(tlsPin)) { SSLContext sslContext = Java7Pinning.forPin(tlsPin); - builder.setSslContextFactory(() -> sslContext); + builder.setCustomSSLContext(sslContext); } 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 ea34b9518..f71e3f196 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 @@ -81,7 +81,6 @@ import org.jivesoftware.smack.compress.packet.Compressed; import org.jivesoftware.smack.compression.XMPPInputOutputStream; import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.filter.StanzaFilter; -import org.jivesoftware.smack.internal.SmackTlsContext; import org.jivesoftware.smack.packet.Element; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java index 74286fdd9..ead3a6438 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java @@ -21,9 +21,13 @@ import java.net.InetSocketAddress; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; -import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Collection; @@ -45,6 +49,7 @@ import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; +import org.jivesoftware.smack.AbstractXMPPConnection.SmackTlsContext; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.ConnectionException; @@ -56,6 +61,7 @@ import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException; import org.jivesoftware.smack.SmackException.SmackWrappedException; import org.jivesoftware.smack.SmackFuture; import org.jivesoftware.smack.SmackFuture.InternalSmackFuture; +import org.jivesoftware.smack.SmackReactor.ChannelSelectedCallback; import org.jivesoftware.smack.SmackReactor.SelectionKeyAttachment; import org.jivesoftware.smack.XMPPException.FailedNonzaException; import org.jivesoftware.smack.XmppInputOutputFilter; @@ -69,7 +75,6 @@ import org.jivesoftware.smack.debugger.SmackDebugger; import org.jivesoftware.smack.fsm.State; import org.jivesoftware.smack.fsm.StateDescriptor; import org.jivesoftware.smack.fsm.StateTransitionResult; -import org.jivesoftware.smack.internal.SmackTlsContext; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StartTls; import org.jivesoftware.smack.packet.StreamOpen; @@ -275,292 +280,294 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM } }; - private void onChannelSelected(SelectableChannel selectedChannel, SelectionKey selectedSelectionKey) { - assert selectionKey == null || selectionKey == selectedSelectionKey; - SocketChannel selectedSocketChannel = (SocketChannel) selectedChannel; - // We are *always* interested in OP_READ. - int newInterestedOps = SelectionKey.OP_READ; - boolean newPendingOutputFilterData = false; + private final ChannelSelectedCallback channelSelectedCallback = + (selectedChannel, selectedSelectionKey) -> { + assert selectionKey == null || selectionKey == selectedSelectionKey; + SocketChannel selectedSocketChannel = (SocketChannel) selectedChannel; + // We are *always* interested in OP_READ. + int newInterestedOps = SelectionKey.OP_READ; + boolean newPendingOutputFilterData = false; - if (!channelSelectedCallbackLock.tryLock()) { - rejectedChannelSelectedCallbacks.incrementAndGet(); - return; - } + if (!channelSelectedCallbackLock.tryLock()) { + rejectedChannelSelectedCallbacks.incrementAndGet(); + return; + } - handledChannelSelectedCallbacks++; + handledChannelSelectedCallbacks++; - long callbackBytesRead = 0; - long callbackBytesWritten = 0; + long callbackBytesRead = 0; + long callbackBytesWritten = 0; - try { - boolean destinationAddressChanged = false; - boolean isLastPartOfElement = false; - TopLevelStreamElement currentlyOutgonigTopLevelStreamElement = null; - StringBuilder outgoingStreamForDebugger = null; + try { + boolean destinationAddressChanged = false; + boolean isLastPartOfElement = false; + TopLevelStreamElement currentlyOutgonigTopLevelStreamElement = null; + StringBuilder outgoingStreamForDebugger = null; - writeLoop: while (true) { - final boolean moreDataAvailable = !isLastPartOfElement || !connectionInternal.outgoingElementsQueue.isEmpty(); + writeLoop: while (true) { + final boolean moreDataAvailable = !isLastPartOfElement || !connectionInternal.outgoingElementsQueue.isEmpty(); - if (filteredOutgoingBuffer != null || !networkOutgoingBuffers.isEmpty()) { - if (filteredOutgoingBuffer != null) { - networkOutgoingBuffers.add(filteredOutgoingBuffer); - networkOutgoingBuffersBytes += filteredOutgoingBuffer.remaining(); + if (filteredOutgoingBuffer != null || !networkOutgoingBuffers.isEmpty()) { + if (filteredOutgoingBuffer != null) { + networkOutgoingBuffers.add(filteredOutgoingBuffer); + networkOutgoingBuffersBytes += filteredOutgoingBuffer.remaining(); - filteredOutgoingBuffer = null; - if (moreDataAvailable && networkOutgoingBuffersBytes < 8096) { - continue; + filteredOutgoingBuffer = null; + if (moreDataAvailable && networkOutgoingBuffersBytes < 8096) { + continue; + } + } + + ByteBuffer[] output = networkOutgoingBuffers.toArray(new ByteBuffer[networkOutgoingBuffers.size()]); + long bytesWritten; + try { + bytesWritten = selectedSocketChannel.write(output); + } catch (IOException e) { + // We have seen here so far + // - IOException "Broken pipe" + handleReadWriteIoException(e); + break; + } + + if (bytesWritten == 0) { + newInterestedOps |= SelectionKey.OP_WRITE; + break; + } + + callbackBytesWritten += bytesWritten; + + networkOutgoingBuffersBytes -= bytesWritten; + + List prunedBuffers = pruneBufferList(networkOutgoingBuffers); + + for (Buffer prunedBuffer : prunedBuffers) { + List sendElements = bufferToElementMap.remove(prunedBuffer); + if (sendElements == null) { + continue; + } + for (TopLevelStreamElement elementJustSend : sendElements) { + connectionInternal.fireFirstLevelElementSendListeners(elementJustSend); + } + } + + // Prevent one callback from dominating the reactor thread. Break out of the write-loop if we have + // written a certain amount. + if (callbackBytesWritten > CALLBACK_MAX_BYTES_WRITEN) { + newInterestedOps |= SelectionKey.OP_WRITE; + callbackPreemtBecauseBytesWritten++; + break; + } + } else if (outgoingBuffer != null || pendingOutputFilterData) { + pendingOutputFilterData = false; + + if (outgoingBuffer != null) { + totalBytesWrittenBeforeFilter += outgoingBuffer.remaining(); + if (isLastPartOfElement) { + assert currentlyOutgonigTopLevelStreamElement != null; + currentlyOutgoingElements.add(currentlyOutgonigTopLevelStreamElement); + } + } + + ByteBuffer outputFilterInputData = outgoingBuffer; + // We can now null the outgoingBuffer since the filter step will take care of it from now on. + outgoingBuffer = null; + + for (ListIterator it = connectionInternal.getXmppInputOutputFilterBeginIterator(); it.hasNext();) { + XmppInputOutputFilter inputOutputFilter = it.next(); + XmppInputOutputFilter.OutputResult outputResult; + try { + outputResult = inputOutputFilter.output(outputFilterInputData, isLastPartOfElement, + destinationAddressChanged, moreDataAvailable); + } catch (IOException e) { + connectionInternal.notifyConnectionError(e); + break writeLoop; + } + newPendingOutputFilterData |= outputResult.pendingFilterData; + outputFilterInputData = outputResult.filteredOutputData; + if (outputFilterInputData != null) { + outputFilterInputData.flip(); + } + } + + // It is ok if outpuFilterInputData is 'null' here, this is expected behavior. + if (outputFilterInputData != null && outputFilterInputData.hasRemaining()) { + filteredOutgoingBuffer = outputFilterInputData; + } else { + filteredOutgoingBuffer = null; + } + + // If the filters did eventually not produce any output data but if there is + // pending output data then we have a pending write request after read. + if (filteredOutgoingBuffer == null && newPendingOutputFilterData) { + pendingWriteInterestAfterRead = true; + } + + if (filteredOutgoingBuffer != null && isLastPartOfElement) { + bufferToElementMap.put(filteredOutgoingBuffer, new ArrayList<>(currentlyOutgoingElements)); + currentlyOutgoingElements.clear(); + } + + // Reset that the destination address has changed. + if (destinationAddressChanged) { + destinationAddressChanged = false; + } + } else if (outgoingCharSequenceIterator != null) { + CharSequence nextCharSequence = outgoingCharSequenceIterator.next(); + outgoingBuffer = UTF8.encode(nextCharSequence); + if (!outgoingCharSequenceIterator.hasNext()) { + outgoingCharSequenceIterator = null; + isLastPartOfElement = true; + } else { + isLastPartOfElement = false; + } + + final SmackDebugger debugger = connectionInternal.smackDebugger; + if (debugger != null) { + if (outgoingStreamForDebugger == null) { + outgoingStreamForDebugger = new StringBuilder(); + } + outgoingStreamForDebugger.append(nextCharSequence); + + if (isLastPartOfElement) { + try { + outputDebugSplitter.append(outgoingStreamForDebugger); + } catch (IOException e) { + throw new AssertionError(e); + } + debugger.onOutgoingElementCompleted(); + outgoingStreamForDebugger = null; + } + } + } else if (!connectionInternal.outgoingElementsQueue.isEmpty()) { + currentlyOutgonigTopLevelStreamElement = connectionInternal.outgoingElementsQueue.poll(); + if (currentlyOutgonigTopLevelStreamElement instanceof Stanza) { + Stanza currentlyOutgoingStanza = (Stanza) currentlyOutgonigTopLevelStreamElement; + Jid currentDestinationAddress = currentlyOutgoingStanza.getTo(); + destinationAddressChanged = !JidUtil.equals(lastDestinationAddress, currentDestinationAddress); + lastDestinationAddress = currentDestinationAddress; + } + CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE); + if (nextCharSequence instanceof XmlStringBuilder) { + XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence; + XmlEnvironment outgoingStreamXmlEnvironment = connectionInternal.getOutgoingStreamXmlEnvironment(); + outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator(); + } else { + outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator(); + } + assert outgoingCharSequenceIterator != null; + } else { + // There is nothing more to write. + break; } } - ByteBuffer[] output = networkOutgoingBuffers.toArray(new ByteBuffer[networkOutgoingBuffers.size()]); - long bytesWritten; - try { - bytesWritten = selectedSocketChannel.write(output); - } catch (IOException e) { - // We have seen here so far - // - IOException "Broken pipe" - handleReadWriteIoException(e); - break; - } - - if (bytesWritten == 0) { + pendingOutputFilterData = newPendingOutputFilterData; + if (!pendingWriteInterestAfterRead && pendingOutputFilterData) { newInterestedOps |= SelectionKey.OP_WRITE; - break; } - callbackBytesWritten += bytesWritten; - - networkOutgoingBuffersBytes -= bytesWritten; - - List prunedBuffers = pruneBufferList(networkOutgoingBuffers); - - for (Buffer prunedBuffer : prunedBuffers) { - List sendElements = bufferToElementMap.remove(prunedBuffer); - if (sendElements == null) { - continue; + readLoop: while (true) { + // Prevent one callback from dominating the reactor thread. Break out of the read-loop if we have + // read a certain amount. + if (callbackBytesRead > CALLBACK_MAX_BYTES_READ) { + callbackPreemtBecauseBytesRead++; + break; } - for (TopLevelStreamElement elementJustSend : sendElements) { - connectionInternal.fireFirstLevelElementSendListeners(elementJustSend); - } - } - // Prevent one callback from dominating the reactor thread. Break out of the write-loop if we have - // written a certain amount. - if (callbackBytesWritten > CALLBACK_MAX_BYTES_WRITEN) { - newInterestedOps |= SelectionKey.OP_WRITE; - callbackPreemtBecauseBytesWritten++; - break; - } - } else if (outgoingBuffer != null || pendingOutputFilterData) { - pendingOutputFilterData = false; - - if (outgoingBuffer != null) { - totalBytesWrittenBeforeFilter += outgoingBuffer.remaining(); - if (isLastPartOfElement) { - assert currentlyOutgonigTopLevelStreamElement != null; - currentlyOutgoingElements.add(currentlyOutgonigTopLevelStreamElement); - } - } - - ByteBuffer outputFilterInputData = outgoingBuffer; - // We can now null the outgoingBuffer since the filter step will take care of it from now on. - outgoingBuffer = null; - - for (ListIterator it = connectionInternal.getXmppInputOutputFilterBeginIterator(); it.hasNext();) { - XmppInputOutputFilter inputOutputFilter = it.next(); - XmppInputOutputFilter.OutputResult outputResult; + int bytesRead; + incomingBuffer.clear(); try { - outputResult = inputOutputFilter.output(outputFilterInputData, isLastPartOfElement, - destinationAddressChanged, moreDataAvailable); + bytesRead = selectedSocketChannel.read(incomingBuffer); + } catch (IOException e) { + handleReadWriteIoException(e); + return; + } + + if (bytesRead < 0) { + LOGGER.finer("NIO read() returned " + bytesRead + + " for " + this + ". This probably means that the TCP connection was terminated."); + // According to the socket channel javadoc section about "asynchronous reads" a socket channel's + // read() may return -1 if the input side of a socket is shut down. + + // Note that we do not call notifyConnectionError() here because the connection may be + // cleanly shutdown which would also cause read() to return '-1. I assume that this socket + // will be selected again, on which read() would throw an IOException, which will be catched + // and invoke notifyConnectionError() (see a few lines above). + /* + IOException exception = new IOException("NIO read() returned " + bytesRead); + notifyConnectionError(exception); + */ + return; + } + + if (!pendingInputFilterData) { + if (bytesRead == 0) { + // Nothing more to read. + break; + } + } else { + pendingInputFilterData = false; + } + + // We have successfully read something. It is now possible that a filter is now also able to write + // additional data (for example SSLEngine). + if (pendingWriteInterestAfterRead) { + pendingWriteInterestAfterRead = false; + newInterestedOps |= SelectionKey.OP_WRITE; + } + + callbackBytesRead += bytesRead; + + ByteBuffer filteredIncomingBuffer = incomingBuffer; + for (ListIterator it = connectionInternal.getXmppInputOutputFilterEndIterator(); it.hasPrevious();) { + filteredIncomingBuffer.flip(); + + ByteBuffer newFilteredIncomingBuffer; + try { + newFilteredIncomingBuffer = it.previous().input(filteredIncomingBuffer); + } catch (IOException e) { + connectionInternal.notifyConnectionError(e); + return; + } + if (newFilteredIncomingBuffer == null) { + break readLoop; + } + filteredIncomingBuffer = newFilteredIncomingBuffer; + } + + final int bytesReadAfterFilter = filteredIncomingBuffer.flip().remaining(); + + totalBytesReadAfterFilter += bytesReadAfterFilter; + + try { + splitter.write(filteredIncomingBuffer); } catch (IOException e) { connectionInternal.notifyConnectionError(e); - break writeLoop; - } - newPendingOutputFilterData |= outputResult.pendingFilterData; - outputFilterInputData = outputResult.filteredOutputData; - if (outputFilterInputData != null) { - outputFilterInputData.flip(); + return; } } + } finally { + totalBytesWritten += callbackBytesWritten; + totalBytesRead += callbackBytesRead; - // It is ok if outpuFilterInputData is 'null' here, this is expected behavior. - if (outputFilterInputData != null && outputFilterInputData.hasRemaining()) { - filteredOutgoingBuffer = outputFilterInputData; - } else { - filteredOutgoingBuffer = null; - } - - // If the filters did eventually not produce any output data but if there is - // pending output data then we have a pending write request after read. - if (filteredOutgoingBuffer == null && newPendingOutputFilterData) { - pendingWriteInterestAfterRead = true; - } - - if (filteredOutgoingBuffer != null && isLastPartOfElement) { - bufferToElementMap.put(filteredOutgoingBuffer, new ArrayList<>(currentlyOutgoingElements)); - currentlyOutgoingElements.clear(); - } - - // Reset that the destination address has changed. - if (destinationAddressChanged) { - destinationAddressChanged = false; - } - } else if (outgoingCharSequenceIterator != null) { - CharSequence nextCharSequence = outgoingCharSequenceIterator.next(); - outgoingBuffer = UTF8.encode(nextCharSequence); - if (!outgoingCharSequenceIterator.hasNext()) { - outgoingCharSequenceIterator = null; - isLastPartOfElement = true; - } else { - isLastPartOfElement = false; - } - - final SmackDebugger debugger = connectionInternal.smackDebugger; - if (debugger != null) { - if (outgoingStreamForDebugger == null) { - outgoingStreamForDebugger = new StringBuilder(); - } - outgoingStreamForDebugger.append(nextCharSequence); - - if (isLastPartOfElement) { - try { - outputDebugSplitter.append(outgoingStreamForDebugger); - } catch (IOException e) { - throw new AssertionError(e); - } - debugger.onOutgoingElementCompleted(); - outgoingStreamForDebugger = null; - } - } - } else if (!connectionInternal.outgoingElementsQueue.isEmpty()) { - currentlyOutgonigTopLevelStreamElement = connectionInternal.outgoingElementsQueue.poll(); - if (currentlyOutgonigTopLevelStreamElement instanceof Stanza) { - Stanza currentlyOutgoingStanza = (Stanza) currentlyOutgonigTopLevelStreamElement; - Jid currentDestinationAddress = currentlyOutgoingStanza.getTo(); - destinationAddressChanged = !JidUtil.equals(lastDestinationAddress, currentDestinationAddress); - lastDestinationAddress = currentDestinationAddress; - } - CharSequence nextCharSequence = currentlyOutgonigTopLevelStreamElement.toXML(StreamOpen.CLIENT_NAMESPACE); - if (nextCharSequence instanceof XmlStringBuilder) { - XmlStringBuilder xmlStringBuilder = (XmlStringBuilder) nextCharSequence; - XmlEnvironment outgoingStreamXmlEnvironment = connectionInternal.getOutgoingStreamXmlEnvironment(); - outgoingCharSequenceIterator = xmlStringBuilder.toList(outgoingStreamXmlEnvironment).iterator(); - } else { - outgoingCharSequenceIterator = Collections.singletonList(nextCharSequence).iterator(); - } - assert outgoingCharSequenceIterator != null; - } else { - // There is nothing more to write. - break; - } - } - - pendingOutputFilterData = newPendingOutputFilterData; - if (!pendingWriteInterestAfterRead && pendingOutputFilterData) { - newInterestedOps |= SelectionKey.OP_WRITE; - } - - readLoop: while (true) { - // Prevent one callback from dominating the reactor thread. Break out of the read-loop if we have - // read a certain amount. - if (callbackBytesRead > CALLBACK_MAX_BYTES_READ) { - callbackPreemtBecauseBytesRead++; - break; + channelSelectedCallbackLock.unlock(); } - int bytesRead; - incomingBuffer.clear(); - try { - bytesRead = selectedSocketChannel.read(incomingBuffer); - } catch (IOException e) { - handleReadWriteIoException(e); - return; + // Indicate that there is no reactor thread racing towards handling this selection key. + final SelectionKeyAttachment selectionKeyAttachment = this.selectionKeyAttachment; + if (selectionKeyAttachment != null) { + selectionKeyAttachment.resetReactorThreadRacing(); } - if (bytesRead < 0) { - LOGGER.finer("NIO read() returned " + bytesRead - + " for " + this + ". This probably means that the TCP connection was terminated."); - // According to the socket channel javadoc section about "asynchronous reads" a socket channel's - // read() may return -1 if the input side of a socket is shut down. - // Note that we do not call notifyConnectionError() here because the connection may be - // cleanly shutdown which would also cause read() to return '-1. I assume that this socket - // will be selected again, on which read() would throw an IOException, which will be catched - // and invoke notifyConnectionError() (see a few lines above). - /* - IOException exception = new IOException("NIO read() returned " + bytesRead); - notifyConnectionError(exception); - */ - return; - } - - if (!pendingInputFilterData) { - if (bytesRead == 0) { - // Nothing more to read. - break; - } - } else { - pendingInputFilterData = false; - } - - // We have successfully read something. It is now possible that a filter is now also able to write - // additional data (for example SSLEngine). - if (pendingWriteInterestAfterRead) { - pendingWriteInterestAfterRead = false; + // Check the queue again to prevent lost wakeups caused by elements inserted before we + // called resetReactorThreadRacing() a few lines above. + if (!connectionInternal.outgoingElementsQueue.isEmpty()) { + setWriteInterestAfterChannelSelectedCallback.incrementAndGet(); newInterestedOps |= SelectionKey.OP_WRITE; } - callbackBytesRead += bytesRead; - - ByteBuffer filteredIncomingBuffer = incomingBuffer; - for (ListIterator it = connectionInternal.getXmppInputOutputFilterEndIterator(); it.hasPrevious();) { - filteredIncomingBuffer.flip(); - - ByteBuffer newFilteredIncomingBuffer; - try { - newFilteredIncomingBuffer = it.previous().input(filteredIncomingBuffer); - } catch (IOException e) { - connectionInternal.notifyConnectionError(e); - return; - } - if (newFilteredIncomingBuffer == null) { - break readLoop; - } - filteredIncomingBuffer = newFilteredIncomingBuffer; - } - - final int bytesReadAfterFilter = filteredIncomingBuffer.flip().remaining(); - - totalBytesReadAfterFilter += bytesReadAfterFilter; - - try { - splitter.write(filteredIncomingBuffer); - } catch (IOException e) { - connectionInternal.notifyConnectionError(e); - return; - } - } - } finally { - totalBytesWritten += callbackBytesWritten; - totalBytesRead += callbackBytesRead; - - channelSelectedCallbackLock.unlock(); - } - - // Indicate that there is no reactor thread racing towards handling this selection key. - final SelectionKeyAttachment selectionKeyAttachment = this.selectionKeyAttachment; - if (selectionKeyAttachment != null) { - selectionKeyAttachment.resetReactorThreadRacing(); - } - - // Check the queue again to prevent lost wakeups caused by elements inserted before we - // called resetReactorThreadRacing() a few lines above. - if (!connectionInternal.outgoingElementsQueue.isEmpty()) { - setWriteInterestAfterChannelSelectedCallback.incrementAndGet(); - newInterestedOps |= SelectionKey.OP_WRITE; - } - - connectionInternal.setInterestOps(selectionKey, newInterestedOps); - } + connectionInternal.setInterestOps(selectionKey, newInterestedOps); + }; private void handleReadWriteIoException(IOException e) { if (e instanceof ClosedChannelException && !tcpNioTransport.isConnected()) { @@ -670,7 +677,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM } @Override - public XmppTcpTransportModule.Stats getStats() { + public Stats getStats() { return XmppTcpTransportModule.this.getStats(); } @@ -767,7 +774,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM remoteAddress = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress(); selectionKey = connectionInternal.registerWithSelector(socketChannel, SelectionKey.OP_READ, - XmppTcpTransportModule.this::onChannelSelected); + channelSelectedCallback); selectionKeyAttachment = (SelectionKeyAttachment) selectionKey.attachment(); connectionInternal.setTransport(tcpNioTransport); @@ -862,7 +869,13 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException { connectionInternal.sendAndWaitForResponse(StartTls.INSTANCE, TlsProceed.class, TlsFailure.class); - SmackTlsContext smackTlsContext = connectionInternal.getSmackTlsContext(); + SmackTlsContext smackTlsContext; + try { + smackTlsContext = connectionInternal.getSmackTlsContext(); + } catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException + | CertificateException | KeyStoreException | NoSuchProviderException e) { + throw new SmackWrappedException(e); + } tlsState = new TlsState(smackTlsContext); connectionInternal.addXmppInputOutputFilter(tlsState); @@ -1303,7 +1316,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM pendingOutputFilterData = true; } - onChannelSelected(channel, key); + channelSelectedCallback.onChannelSelected(channel, key); } finally { channelSelectedCallbackLock.unlock(); } @@ -1334,7 +1347,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM return CollectionUtil.removeUntil(buffers, b -> b.hasRemaining()); } - public XmppTcpTransportModule.Stats getStats() { + public Stats getStats() { return new Stats(this); } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/IpTcpRemoteConnectionEndpoint.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/IpTcpRemoteConnectionEndpoint.java index 5f6a23ad5..2dabe1950 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/IpTcpRemoteConnectionEndpoint.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/IpTcpRemoteConnectionEndpoint.java @@ -16,14 +16,18 @@ */ package org.jivesoftware.smack.tcp.rce; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.util.rce.SingleAddressRemoteConnectionEndpoint; +import org.minidns.record.A; +import org.minidns.record.AAAA; import org.minidns.record.InternetAddressRR; -public final class IpTcpRemoteConnectionEndpoint> +public final class IpTcpRemoteConnectionEndpoint implements Rfc6120TcpRemoteConnectionEndpoint, SingleAddressRemoteConnectionEndpoint { private final CharSequence host; @@ -38,11 +42,17 @@ public final class IpTcpRemoteConnectionEndpoint> from(CharSequence host, UInt16 port, + public static IpTcpRemoteConnectionEndpoint from(CharSequence host, int port, InetAddress inetAddress) { - InternetAddressRR internetAddressResourceRecord = InternetAddressRR.from(inetAddress); + InternetAddressRR internetAddressResourceRecord; + // TODO: Use InternetAddressRR.from(InetAddress) once MiniDNS is updated. + if (inetAddress instanceof Inet4Address) { + internetAddressResourceRecord = new A((Inet4Address) inetAddress); + } else { + internetAddressResourceRecord = new AAAA((Inet6Address) inetAddress); + } - return new IpTcpRemoteConnectionEndpoint>(host, port, + return new IpTcpRemoteConnectionEndpoint(host, UInt16.from(port), internetAddressResourceRecord); } diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java index 6b7da5ccf..9328d0fe7 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/rce/RemoteXmppTcpConnectionEndpoints.java @@ -26,7 +26,6 @@ import java.util.logging.Logger; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; -import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.util.DNSUtil; import org.jivesoftware.smack.util.dns.DNSResolver; import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint; @@ -62,7 +61,7 @@ public class RemoteXmppTcpConnectionEndpoints { if (hostAddress != null) { lookupFailures = Collections.emptyList(); - IpTcpRemoteConnectionEndpoint> connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( + IpTcpRemoteConnectionEndpoint connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( hostAddress.toString(), config.getPort(), hostAddress); discoveredRemoteConnectionEndpoints = Collections.singletonList(connectionEndpoint); } else if (host != null) { @@ -73,9 +72,9 @@ public class RemoteXmppTcpConnectionEndpoints { if (hostAddresses != null) { discoveredRemoteConnectionEndpoints = new ArrayList<>(hostAddresses.size()); - UInt16 port = config.getPort(); + int port = config.getPort(); for (InetAddress inetAddress : hostAddresses) { - IpTcpRemoteConnectionEndpoint> connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( + IpTcpRemoteConnectionEndpoint connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( host, port, inetAddress); discoveredRemoteConnectionEndpoints.add(connectionEndpoint); } @@ -199,13 +198,13 @@ public class RemoteXmppTcpConnectionEndpoints { LOGGER.info("Could not resolve DNS SRV resource records for " + srvDomain + ". Consider adding those."); } - UInt16 defaultPort; + int defaultPort; switch (domainType) { case client: - defaultPort = UInt16.from(5222); + defaultPort = 5222; break; case server: - defaultPort = UInt16.from(5269); + defaultPort = 5269; break; default: throw new AssertionError(); @@ -215,7 +214,7 @@ public class RemoteXmppTcpConnectionEndpoints { List hostAddresses = dnsResolver.lookupHostAddress(domain, lookupFailures, dnssecMode); if (hostAddresses != null) { for (InetAddress inetAddress : hostAddresses) { - IpTcpRemoteConnectionEndpoint> endpoint = IpTcpRemoteConnectionEndpoint.from(domain, defaultPort, inetAddress); + IpTcpRemoteConnectionEndpoint endpoint = IpTcpRemoteConnectionEndpoint.from(domain, defaultPort, inetAddress); endpoints.add(endpoint); } } diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java index 95f74bc60..9658b9f7f 100644 --- a/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java +++ b/smack-tcp/src/test/java/org/jivesoftware/smack/tcp/PacketWriterTest.java @@ -52,12 +52,12 @@ public class PacketWriterTest { * interrupt. * * @throws InterruptedException if the calling thread was interrupted. - * @throws BrokenBarrierException in case of a broken barrier. + * @throws BrokenBarrierException * @throws NotConnectedException if the XMPP connection is not connected. * @throws XmppStringprepException if the provided string is invalid. * @throws SecurityException if there was a security violation. - * @throws NoSuchFieldException if there is no such field. - * @throws IllegalAccessException if there was an illegal access. + * @throws NoSuchFieldException + * @throws IllegalAccessException * @throws IllegalArgumentException if an illegal argument was given. */ @Test diff --git a/version b/version index 92560b19f..60c4355df 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.0-alpha4-SNAPSHOT +4.4.0-alpha3-SNAPSHOT