diff --git a/.travis.yml b/.travis.yml index 10f7b90f5..94cc1fb74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,16 @@ 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: gradle check publishToMavenLocal javadocAll --stacktrace +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 after_success: - JAVAC_VERSION=$((javac -version) 2>&1) diff --git a/build.gradle b/build.gradle index de43fba80..96dcab393 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-alpha5' - miniDnsVersion = '0.4.0-alpha3' + jxmppVersion = '0.7.0-alpha6' + miniDnsVersion = '0.4.0-alpha5' smackMinAndroidSdk = 19 - junitVersion = '5.6.0' + junitVersion = '5.6.2' 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.3' + errorprone 'com.google.errorprone:error_prone_core:2.3.4' errorproneJavac('com.google.errorprone:javac:9+181-r4173-1') } @@ -409,7 +409,7 @@ subprojects { apply plugin: 'org.kordamp.gradle.clirr' checkstyle { - toolVersion = '8.22' + toolVersion = '8.27' } 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 c62f00a71..ce6b4200d 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -104,13 +104,7 @@ - - - - - - - + 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 87f6f091e..136976a09 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -16,24 +16,9 @@ */ 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; @@ -56,18 +41,9 @@ 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; @@ -92,6 +68,7 @@ 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; @@ -128,16 +105,12 @@ 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; @@ -372,17 +345,6 @@ 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 */ @@ -1523,7 +1485,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { executorService = ASYNC_BUT_ORDERED.asExecutorFor(this); break; case async: - executorService = limitedExcutor; + executorService = this::asyncGoLimited; break; } final IQRequestHandler finalIqRequestHandler = iqRequestHandler; @@ -2162,7 +2124,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { } protected static ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit) { - return SMACK_REACTOR.schedule(runnable, delay, unit); + return SMACK_REACTOR.schedule(runnable, delay, unit, ScheduledAction.Kind.NonBlocking); } protected void onStreamOpen(XmlPullParser parser) { @@ -2213,148 +2175,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { outgoingStreamXmlEnvironment = xmlEnvironmentBuilder.build(); } - 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); + protected final SmackTlsContext getSmackTlsContext() { + return config.smackTlsContext; } } 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 3d59269c7..e6009754b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -17,9 +17,24 @@ 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; @@ -31,20 +46,34 @@ 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; @@ -101,12 +130,7 @@ public abstract class ConnectionConfiguration { protected final InetAddress hostAddress; protected final DnsName host; - protected final int port; - - private final String keystorePath; - private final String keystoreType; - private final String pkcs11Library; - private final SSLContext customSSLContext; + protected final UInt16 port; /** * Used to get information from the user @@ -137,9 +161,9 @@ public abstract class ConnectionConfiguration { private final SecurityMode securityMode; - private final DnssecMode dnssecMode; + final SmackTlsContext smackTlsContext; - private final X509TrustManager customX509TrustManager; + private final DnssecMode dnssecMode; /** * @@ -165,6 +189,17 @@ 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; @@ -201,13 +236,7 @@ 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; @@ -222,11 +251,115 @@ public abstract class ConnectionConfiguration { // If the enabledSaslmechanisms are set, then they must not be empty assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty(); + } - if (dnssecMode != DnssecMode.disabled && customSSLContext != null) { - throw new IllegalStateException("You can not use a custom SSL context with DNSSEC enabled"); + 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"); } + 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() { @@ -237,7 +370,7 @@ public abstract class ConnectionConfiguration { return hostAddress; } - public int getPort() { + public UInt16 getPort() { return port; } @@ -286,50 +419,6 @@ 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. * @@ -599,12 +688,12 @@ public abstract class ConnectionConfiguration { * @param the resulting connection configuration type parameter. */ public abstract static class Builder, C extends ConnectionConfiguration> { - private SecurityMode securityMode = SecurityMode.ifpossible; + private SecurityMode securityMode = SecurityMode.required; private DnssecMode dnssecMode = DnssecMode.disabled; - private String keystorePath = System.getProperty("javax.net.ssl.keyStore"); - private String keystoreType = KeyStore.getDefaultType(); + private String keystorePath; + private String keystoreType; private String pkcs11Library = "pkcs11.config"; - private SSLContext customSSLContext; + private SslContextFactory sslContextFactory; private String[] enabledSSLProtocols; private String[] enabledSSLCiphers; private HostnameVerifier hostnameVerifier; @@ -621,7 +710,7 @@ public abstract class ConnectionConfiguration { private DomainBareJid xmppServiceDomain; private InetAddress hostAddress; private DnsName host; - private int port = 5222; + private UInt16 port = UInt16.from(5222); private boolean allowEmptyOrNullUsername = false; private boolean saslMechanismsSealed; private Set enabledSaslMechanisms; @@ -837,7 +926,12 @@ public abstract class ConnectionConfiguration { throw new IllegalArgumentException( "Port must be a 16-bit unsigned integer (i.e. between 0-65535. Port was: " + port); } - this.port = port; + UInt16 portUint16 = UInt16.from(port); + return setPort(portUint16); + } + + public B setPort(UInt16 port) { + this.port = Objects.requireNonNull(port); return getThis(); } @@ -923,9 +1017,28 @@ 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) { - this.customSSLContext = Objects.requireNonNull(context, "The SSLContext must not be null"); + 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"); return getThis(); } @@ -1166,5 +1279,4 @@ 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 d3c4beec1..a306bc3af 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/Manager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/Manager.java @@ -54,6 +54,14 @@ public abstract class Manager { } protected static final ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit) { - return AbstractXMPPConnection.SMACK_REACTOR.schedule(runnable, delay, 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); } } 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 5b5f0b238..da0377fab 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java @@ -20,20 +20,34 @@ 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 { - final Runnable action; + enum Kind { + NonBlocking, + Blocking, + } + + private final Runnable action; final Date releaseTime; final SmackReactor smackReactor; + final Kind kind; - ScheduledAction(Runnable action, Date releaseTime, SmackReactor smackReactor) { + ScheduledAction(Runnable action, Date releaseTime, SmackReactor smackReactor, Kind kind) { this.action = action; this.releaseTime = releaseTime; this.smackReactor = smackReactor; + this.kind = kind; } - public void cancel() { - smackReactor.cancel(this); + /** + * 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 boolean isDue() { @@ -63,4 +77,15 @@ 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 76d028204..eaff9ca3d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java @@ -145,15 +145,21 @@ public class SmackReactor { } } - ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit) { + ScheduledAction schedule(Runnable runnable, long delay, TimeUnit unit, ScheduledAction.Kind scheduledActionKind) { long releaseTimeEpoch = System.currentTimeMillis() + unit.toMillis(delay); Date releaseTimeDate = new Date(releaseTimeEpoch); - ScheduledAction scheduledAction = new ScheduledAction(runnable, releaseTimeDate, this); + ScheduledAction scheduledAction = new ScheduledAction(runnable, releaseTimeDate, this, scheduledActionKind); 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); } @@ -200,7 +206,7 @@ public class SmackReactor { } if (dueScheduledAction != null) { - dueScheduledAction.action.run(); + dueScheduledAction.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 9694b47b8..061d25e7a 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,11 +17,6 @@ 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; @@ -65,6 +60,8 @@ 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; @@ -78,6 +75,7 @@ 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; @@ -184,9 +182,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne } @Override - public SmackTlsContext getSmackTlsContext() - throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException, - UnrecoverableKeyException, KeyStoreException, NoSuchProviderException { + public SmackTlsContext getSmackTlsContext() { return ModularXmppClientToServerConnection.this.getSmackTlsContext(); } @@ -1069,7 +1065,7 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne return new Stats(transportsStats, filterStats); } - public static final class Stats { + public static final class Stats extends AbstractStats { public final Map, XmppClientToServerTransport.Stats> transportsStats; public final Map filtersStats; @@ -1079,7 +1075,8 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne this.filtersStats = Collections.unmodifiableMap(filtersStats); } - public void appendStatsTo(Appendable appendable) throws IOException { + @Override + public void appendStatsTo(ExtendedAppendable appendable) throws IOException { StringUtils.appendHeading(appendable, "Connection stats", '#').append('\n'); for (Map.Entry, XmppClientToServerTransport.Stats> entry : transportsStats.entrySet()) { @@ -1099,16 +1096,5 @@ 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 57452f270..b08676914 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,20 +16,12 @@ */ 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; @@ -41,6 +33,7 @@ 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; @@ -107,9 +100,7 @@ public abstract class ModularXmppClientToServerConnectionInternal { public abstract void newStreamOpenWaitForFeaturesSequence(String waitFor) throws InterruptedException, ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException; - public abstract SmackTlsContext getSmackTlsContext() - throws KeyManagementException, NoSuchAlgorithmException, CertificateException, IOException, - UnrecoverableKeyException, KeyStoreException, NoSuchProviderException; + public abstract SmackTlsContext getSmackTlsContext(); 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 837028c64..5f133a640 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-2018 Florian Schmaus + * Copyright 2013-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. @@ -20,9 +20,11 @@ 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; + protected static FlushMethod flushMethod = FlushMethod.SYNC_FLUSH; /** * Set the used flushed method when compressing data. The default is full flush which may not @@ -33,7 +35,7 @@ public abstract class XMPPInputOutputStream { * @param flushMethod TODO javadoc me please */ public static void setFlushMethod(FlushMethod flushMethod) { - XMPPInputOutputStream.flushMethod = flushMethod; + XMPPInputOutputStream.flushMethod = Objects.requireNonNull(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 f37d08b11..147d0c9f1 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,4 +66,13 @@ 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 new file mode 100644 index 000000000..1cddd964c --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/internal/AbstractStats.java @@ -0,0 +1,52 @@ +/** + * + * 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 new file mode 100644 index 000000000..b0425b162 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/internal/SmackTlsContext.java @@ -0,0 +1,32 @@ +/** + * + * 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 new file mode 100644 index 000000000..6311fbe06 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/internal/package-info.java @@ -0,0 +1,21 @@ +/** + * + * 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 d975db0ef..5eac12435 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 Florian Schmaus + * Copyright 2019-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. @@ -23,6 +23,10 @@ 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 2e24f53b2..644f077ea 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 Florian Schmaus + * Copyright 2019-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. @@ -22,6 +22,10 @@ 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 9a1d1a43d..0f819b429 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. + * Copyright 2003-2007 Jive Software, 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. @@ -17,10 +17,9 @@ 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; @@ -29,7 +28,7 @@ import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.jid.Jid; /** - * Represents XMPP presence packets. Every presence stanza has a type, which is one of + * Represents XMPP presence stanzas. 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 @@ -55,7 +54,7 @@ import org.jxmpp.jid.Jid; * {@link Mode#dnd dnd} (do not disturb). *

* - * Presence packets are used for two purposes. First, to notify the server of + * Presence stanzas 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. * @@ -83,7 +82,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 StanzaBuilder} or {@link SocketFactory} instead. + * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -99,7 +98,7 @@ public final class Presence extends MessageOrPresence * @param to the recipient. * @param type the type. * @since 4.2 - * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. + * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -115,7 +114,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 StanzaBuilder} or {@link SocketFactory} instead. + * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -191,7 +190,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 StanzaBuilder} or {@link SocketFactory} instead. + * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -209,7 +208,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 StanzaBuilder} or {@link SocketFactory} instead. + * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -236,7 +235,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 StanzaBuilder} or {@link SocketFactory} instead. + * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -265,7 +264,7 @@ public final class Presence extends MessageOrPresence * to be the same thing as {@link Presence.Mode#available}. * * @param mode the mode. - * @deprecated use {@link StanzaBuilder} or {@link SocketFactory} instead. + * @deprecated use {@link PresenceBuilder} or {@link org.jivesoftware.smack.XMPPConnection#getStanzaFactory} instead. */ @Deprecated // TODO: Remove in Smack 4.5. @@ -309,6 +308,16 @@ 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); @@ -317,7 +326,7 @@ public final class Presence extends MessageOrPresence buf.element("show", mode); } - buf.append(getExtensions()); + buf.append(extensions); // 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 new file mode 100644 index 000000000..4cf97622c --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/ExtendedAppendable.java @@ -0,0 +1,56 @@ +/** + * + * 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 831ccf0ad..98635309d 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-2019 Florian Schmaus + * Copyright © 2015-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. @@ -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 TODO javadoc me please + * @param value the long to check. * @return the input value. */ public static long requireUInt32(long value) { if (value < 0) { - throw new IllegalArgumentException("unsigned 32-bit integers can't be negative"); + throw new IllegalArgumentException("unsigned 32-bit integers can't be negative: " + value); } if (value > ((1L << 32) - 1)) { - throw new IllegalArgumentException("unsigned 32-bit integers can't be greater than 2^32 - 1"); + throw new IllegalArgumentException("unsigned 32-bit integers can't be greater than 2^32 - 1: " + value); } 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 TODO javadoc me please + * @param value the int to check. * @return the input value. */ public static int requireUShort16(int value) { if (value < 0) { - throw new IllegalArgumentException("unsigned 16-bit integers can't be negative"); + throw new IllegalArgumentException("unsigned 16-bit integers can't be negative: " + value); } if (value > ((1 << 16) - 1)) { - throw new IllegalArgumentException("unsigned 16-bit integers can't be greater than 2^16 - 1"); + throw new IllegalArgumentException("unsigned 16-bit integers can't be greater than 2^16 - 1: " + value); } 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 new file mode 100644 index 000000000..a3c308b9f --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/SslContextFactory.java @@ -0,0 +1,25 @@ +/** + * + * 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 60c644320..c1af8ba61 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,12 +22,8 @@ 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; @@ -38,13 +34,9 @@ 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; @@ -132,25 +124,13 @@ 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) throws NoSuchAlgorithmException, KeyManagementException { - SSLContext context = SSLContext.getInstance(TLS); - context.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom()); - builder.setCustomSSLContext(context); + public static > B acceptAllCertificates(B builder) { + builder.setCustomX509TrustManager(new AcceptAllTrustManager()); 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. *

@@ -164,7 +144,9 @@ public class TLSUtils { * @return the given builder. */ public static > B disableHostnameVerificationForTlsCertificates(B builder) { - builder.setHostnameVerifier(DOES_NOT_VERIFY_VERIFIER); + builder.setHostnameVerifier((hostname, session) -> { + return true; + }); return builder; } @@ -274,24 +256,6 @@ 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 5210e6344..1c005322d 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,6 +29,7 @@ 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 eef18918f..6a7fe8e54 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,6 +25,7 @@ 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 135fd79d0..d0864842b 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java @@ -150,6 +150,7 @@ 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() { @@ -162,6 +163,8 @@ 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") @@ -222,6 +225,7 @@ 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 960922d07..2b7220ba1 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,6 +18,7 @@ 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; @@ -158,8 +159,21 @@ public class CarbonExtension implements ExtensionElement { * 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 + * @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 da58e6ae9..e0859dd26 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,7 +25,6 @@ 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; @@ -68,23 +67,20 @@ public final class FallbackIndicationManager extends Manager { private final StanzaFilter fallbackIndicationElementFilter = new AndFilter(StanzaTypeFilter.MESSAGE, new StanzaExtensionFilter(FallbackIndicationElement.ELEMENT, FallbackIndicationElement.NAMESPACE)); - 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 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 FallbackIndicationManager(XMPPConnection connection) { super(connection); - connection.addAsyncStanzaListener(fallbackIndicationElementListener, fallbackIndicationElementFilter); + connection.addAsyncStanzaListener(this::fallbackIndicationElementListener, fallbackIndicationElementFilter); ServiceDiscoveryManager.getInstanceFor(connection).addFeature(FallbackIndicationElement.NAMESPACE); } @@ -139,7 +135,7 @@ public final class FallbackIndicationManager extends Manager { * @param fallbackMessageBody fallback message body * @return builder with set body and added fallback element */ - public MessageBuilder addFallbackIndicationWithBody(MessageBuilder messageBuilder, String fallbackMessageBody) { + public static MessageBuilder addFallbackIndicationWithBody(MessageBuilder messageBuilder, String fallbackMessageBody) { return addFallbackIndication(messageBuilder).setBody(fallbackMessageBody); } @@ -149,7 +145,7 @@ public final class FallbackIndicationManager extends Manager { * @param messageBuilder message builder * @return message builder with added fallback element */ - public MessageBuilder addFallbackIndication(MessageBuilder messageBuilder) { + public static 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 299466bbb..23379a4cc 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,7 +37,6 @@ 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; @@ -307,7 +306,9 @@ 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"); - Objects.requireNonNull(fileSize, "Filesize Stream cannot be null"); + if (fileSize < 0) { + throw new IllegalArgumentException("File size cannot be negative"); + } final Slot slot = requestSlot(fileName, fileSize, "application/octet-stream"); upload(inputStream, fileSize, slot, listener); return slot.getGetUrl(); @@ -324,8 +325,7 @@ 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.NotConnectedException if the XMPP connection is not connected. - * @throws SmackException.NoResponseException if there was no response from the remote entity. + * @throws SmackException if smack exception. */ 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.NoResponseException if there was no response from the remote entity. + * @throws SmackException if smack exception. */ public Slot requestSlot(String filename, long fileSize, String contentType) throws SmackException, InterruptedException, XMPPException.XMPPErrorException { @@ -427,11 +427,6 @@ 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 034245a5a..9fcff7b9c 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 Florian Schmaus and Fernando Ramirez + * Copyright © 2016-2020 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:1"; + public static final String NAMESPACE = "urn:xmpp:mam:2"; /** * 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 e5d211161..7fcac31de 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 void addRetractionElementToMessage(OriginIdElement retractedMessageId, MessageBuilder carrierMessageBuilder) { + public static 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 d8911aa3e..35242eba9 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 + * Copyright 2018 Paul Schaub, 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. @@ -29,10 +29,6 @@ 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; @@ -62,14 +58,8 @@ 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() { @@ -113,7 +103,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(ADD_ORIGIN_ID_INTERCEPTOR, ADD_ORIGIN_ID_PREDICATE); + connection().addMessageInterceptor(OriginIdElement::addTo, ADD_ORIGIN_ID_FILTER::accept); ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE); } @@ -122,7 +112,7 @@ public final class StableUniqueStanzaIdManager extends Manager { */ public synchronized void disable() { ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE); - connection().removeMessageInterceptor(ADD_ORIGIN_ID_INTERCEPTOR); + connection().removeMessageInterceptor(OriginIdElement::addTo); } /** 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 b157b40a0..1a448b516 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 #addOriginId(MessageBuilder)} instead. + * @deprecated use {@link #addTo(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 addOriginId(MessageBuilder messageBuilder) { + public static OriginIdElement addTo(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 a6791cbb7..8e6d3df51 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:1 + urn:xmpp:mam:2 org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider query - urn:xmpp:mam:1 + urn:xmpp:mam:2 org.jivesoftware.smackx.mam.provider.MamQueryIQProvider fin - urn:xmpp:mam:1 + urn:xmpp:mam:2 org.jivesoftware.smackx.mam.provider.MamFinIQProvider result - urn:xmpp:mam:1 + urn:xmpp:mam:2 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 342e783b5..7695ac47d 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.MessageBuilder; +import org.jivesoftware.smack.packet.StanzaBuilder; import org.jivesoftware.smackx.fallback_indication.element.FallbackIndicationElement; @@ -30,11 +30,11 @@ public class FallbackIndicationTest { @Test public void testFallbackIndicationElementFromMessageTest() { - Message messageWithoutFallback = MessageBuilder.buildMessage() + Message messageWithoutFallback = StanzaBuilder.buildMessage() .build(); assertNull(FallbackIndicationElement.fromMessage(messageWithoutFallback)); - Message messageWithFallback = MessageBuilder.buildMessage() + Message messageWithFallback = StanzaBuilder.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 f0cd5df68..cbb1b5c1d 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 1c0ff85f8..cd5d4c0ca 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 c9f3be99e..eeac45f5e 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:1" + "" + + "urn:xmpp:mam:2" + "" + "" + "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:1" + "" + "" + + "urn:xmpp:mam:2" + "" + "" + "" + "" + "" + "" + "" + "" + ""; @@ -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:1"); + assertEquals(fields2.get(0).getValues().get(0).toString(), "urn:xmpp:mam:2"); 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 7fbcd7c7e..f7a0a28b4 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 1f1450efd..9a3a4e61a 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:1" + "" + "" + "" + + "urn:xmpp:mam:2" + "" + "" + "" + "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:1"); + assertEquals(mamQueryIQ.getDataForm().getFields().get(0).getValues().get(0).toString(), "urn:xmpp:mam:2"); 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 8fe38eeec..1603fb99f 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 8228fccfb..7a36752da 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,6 +29,7 @@ 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; @@ -166,10 +167,10 @@ public class MessageFasteningElementsTest { @Test public void hasFasteningElementTest() { - MessageBuilder messageBuilderWithFasteningElement = MessageBuilder.buildMessage() + MessageBuilder messageBuilderWithFasteningElement = StanzaBuilder.buildMessage() .setBody("Hi!") .addExtension(FasteningElement.builder().setOriginId("origin-id-1").build()); - MessageBuilder messageBuilderWithoutFasteningElement = MessageBuilder.buildMessage() + MessageBuilder messageBuilderWithoutFasteningElement = StanzaBuilder.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 c9c4367a8..a95e5caac 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.addOriginId(messageBuilder); + OriginIdElement.addTo(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 d7ee5b45a..c63471bcb 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,8 +33,6 @@ 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; @@ -51,13 +49,11 @@ 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; @@ -87,7 +83,6 @@ 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; @@ -307,7 +302,6 @@ 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. @@ -317,11 +311,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 final Consumer presenceInterceptor = presenceBuilder -> { + private void addCapsExtension(PresenceBuilder presenceBuilder) { CapsVersionAndHash capsVersionAndHash = getCapsVersionAndHash(); CapsExtension caps = new CapsExtension(entityNode, capsVersionAndHash.version, capsVersionAndHash.hash); presenceBuilder.overrideExtension(caps); - }; + } private EntityCapsManager(XMPPConnection connection) { super(connection); @@ -342,11 +336,6 @@ 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( @@ -359,9 +348,6 @@ public final class EntityCapsManager extends Manager { } }); - // This calculates the local entity caps version - updateLocalEntityCaps(); - if (autoEnableEntityCaps) enableEntityCaps(); @@ -387,26 +373,16 @@ 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 onEntityCapailitiesChanged() { + public void onEntityCapabilitiesChanged(DiscoverInfo synthesizedDiscoveryInfo) { if (!entityCapsEnabled()) { return; } - updateLocalEntityCaps(); + updateLocalEntityCaps(synthesizedDiscoveryInfo); } }); } @@ -425,13 +401,12 @@ public final class EntityCapsManager extends Manager { } public synchronized void enableEntityCaps() { - connection().addPresenceInterceptor(presenceInterceptor, p -> { + connection().addPresenceInterceptor(this::addCapsExtension, p -> { return PresenceTypeFilter.AVAILABLE.accept(p); }); // Add Entity Capabilities (XEP-0115) feature node. sdm.addFeature(NAMESPACE); - updateLocalEntityCaps(); entityCapsEnabled = true; } @@ -439,18 +414,13 @@ public final class EntityCapsManager extends Manager { entityCapsEnabled = false; sdm.removeFeature(NAMESPACE); - connection().removePresenceInterceptor(presenceInterceptor); + connection().removePresenceInterceptor(this::addCapsExtension); } 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. * @@ -522,13 +492,10 @@ public final class EntityCapsManager extends Manager { * presence is send to inform others about your new Entity Caps node string. * */ - private void updateLocalEntityCaps() { + private void updateLocalEntityCaps(DiscoverInfo synthesizedDiscoveryInfo) { XMPPConnection connection = connection(); - DiscoverInfoBuilder discoverInfoBuilder = DiscoverInfo.builder("synthetized-disco-info-response") - .ofType(IQ.Type.result); - sdm.addDiscoverInfoTo(discoverInfoBuilder); - + DiscoverInfoBuilder discoverInfoBuilder = synthesizedDiscoveryInfo.asBuilder("synthesized-disco-info-result"); // getLocalNodeVer() will return a result only after currentCapsVersion is set. Therefore // set it first and then call getLocalNodeVer() currentCapsVersion = generateVerificationString(discoverInfoBuilder); @@ -564,20 +531,6 @@ 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 0f4614181..c2627dd5b 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 final Runnable sessionSweeper = () -> { + private void 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(sessionSweeper, 10, TimeUnit.SECONDS); + schedule(this::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 e075117f2..bb790813f 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 Florian Schmaus. + * Copyright 2018-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. @@ -16,8 +16,10 @@ */ package org.jivesoftware.smackx.disco; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo; + public interface EntityCapabilitiesChangedListener { - void onEntityCapailitiesChanged(); + void onEntityCapabilitiesChanged(DiscoverInfo synthesizedDiscoveryInfo); } 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 84b529aad..61103d2ee 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,6 +16,7 @@ */ package org.jivesoftware.smackx.disco; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -28,20 +29,30 @@ 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; @@ -71,6 +82,8 @@ 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"; @@ -91,6 +104,8 @@ 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() { @@ -190,6 +205,18 @@ 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); } /** @@ -894,13 +921,55 @@ 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 void renewEntityCapsVersion() { - for (EntityCapabilitiesChangedListener entityCapabilitiesChangedListener : entityCapabilitiesChangedListeners) { - entityCapabilitiesChangedListener.onEntityCapailitiesChanged(); + private synchronized void renewEntityCapsVersion() { + renewEntityCapsRequested++; + if (renewEntityCapsScheduledAction != null) { + boolean canceled = renewEntityCapsScheduledAction.cancel(); + if (canceled) { + scheduledRenewEntityCapsAvoided++; + } } + + 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) { @@ -915,4 +984,30 @@ 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 796e51857..7de48f1be 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() { - return new DiscoverInfoBuilder(this); + public DiscoverInfoBuilder asBuilder(String stanzaId) { + return new DiscoverInfoBuilder(this, stanzaId); } // 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 66f965de4..b8e179c70 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 Florian Schmaus + * Copyright 2019-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. @@ -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 @@ -284,7 +280,7 @@ public final class DeliveryReceiptManager extends Manager { * @see #dontAutoAddDeliveryReceiptRequests() */ public void autoAddDeliveryReceiptRequests() { - connection().addMessageInterceptor(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER, m -> { + connection().addMessageInterceptor(DeliveryReceiptRequest::addTo, m -> { return MESSAGES_TO_REQUEST_RECEIPTS_FOR.accept(m); }); } @@ -296,7 +292,7 @@ public final class DeliveryReceiptManager extends Manager { * @see #autoAddDeliveryReceiptRequests() */ public void dontAutoAddDeliveryReceiptRequests() { - connection().removeMessageInterceptor(AUTO_ADD_DELIVERY_RECEIPT_REQUESTS_LISTENER); + connection().removeMessageInterceptor(DeliveryReceiptRequest::addTo); } /** 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 11af9b810..b6c05922e 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,18 +16,14 @@ */ 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; @@ -44,11 +40,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) throws IOException, XmlPullParserException, SmackParsingException { + public static synchronized SoftwareInfoManager getInstanceFor (XMPPConnection connection) { SoftwareInfoManager manager = INSTANCES.get(connection); if (manager == null) { manager = new SoftwareInfoManager(connection); @@ -57,25 +53,11 @@ public final class SoftwareInfoManager extends Manager { return manager; } - private SoftwareInfoManager(XMPPConnection connection) throws IOException, XmlPullParserException, SmackParsingException { + private SoftwareInfoManager(XMPPConnection connection) { 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. *
@@ -86,20 +68,16 @@ public final class SoftwareInfoManager extends Manager { } /** - * Get SoftwareInfoForm from Jid provided. - *
+ * Get Software Information from the provided XMPP address. Returns null in case the queried entity does not announce that information. + * * @param jid jid to get software information from - * @return {@link SoftwareInfoForm} Form containing software information + * @return {@link SoftwareInfoForm} Form containing software information or null. * @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, FeatureNotSupportedException { - if (!isSupported(jid)) { - throw new FeatureNotSupportedException(SoftwareInfoForm.FORM_TYPE, jid); - } + public SoftwareInfoForm fromJid(Jid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 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 36daa66af..a564f5331 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. - * @return builder. + * @param artist the artist. + * @return a reference to this 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. - * @return builder. + * @param length the length. + * @return a reference to this 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. - * @return builder. + * @param length the length. + * @return a reference to this 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. - * @return builder. + * @param rating the rating. + * @return a reference to this 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. - * @return builder. + * @param source the source. + * @return a reference to this 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. - * @return builder. + * @param title the title. + * @return a reference to this 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. - * @return builder. + * @param track the track. + * @return a reference to this 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. - * @return builder. + * @param uri the URI. + * @return a reference to this 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 de2def875..695af1124 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,6 +26,8 @@ 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; @@ -57,7 +59,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(Message message) { + public static List getBodies(MessageView message) { XHTMLExtension xhtmlExtension = XHTMLExtension.from(message); if (xhtmlExtension != null) return xhtmlExtension.getBodies(); @@ -68,9 +70,29 @@ public class XHTMLManager { /** * Adds an XHTML body to the message. * - * @param message the message that will receive the XHTML body + * @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 99dd32d22..b72c35b69 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 + * @throws UnknownHostException if unknown host. */ @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 + * @throws UnknownHostException if unknown host. */ @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 a12effdb6..42f08f499 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,9 +44,8 @@ public class DataValidationHelperTest { () -> element.checkConsistency(field)); assertEquals("Field type 'jid-single' is not consistent with validation method 'basic'.", vce.getMessage()); - IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, + 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 dec89bc68..a78a44c28 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 + * Copyright 2013-2015 the original author or authors, 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. @@ -17,7 +17,6 @@ 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; @@ -56,15 +55,10 @@ 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 final FileFilter rosterDirFilter = new FileFilter() { - - @Override - public boolean accept(File file) { - String name = file.getName(); - return name.startsWith(ENTRY_PREFIX); - } - - }; + private static boolean rosterDirFilter(File file) { + String name = file.getName(); + return name.startsWith(ENTRY_PREFIX); + } /** * @param baseDir TODO javadoc me please @@ -122,7 +116,7 @@ public final class DirectoryRosterStore implements RosterStore { public List getEntries() { List entries = new ArrayList<>(); - for (File file : fileDir.listFiles(rosterDirFilter)) { + for (File file : fileDir.listFiles(DirectoryRosterStore::rosterDirFilter)) { Item entry = readEntry(file); if (entry == null) { // Roster directory store corrupt. Abort and signal this by returning null. @@ -168,7 +162,7 @@ public final class DirectoryRosterStore implements RosterStore { @Override public boolean resetEntries(Collection items, String version) { - for (File file : fileDir.listFiles(rosterDirFilter)) { + for (File file : fileDir.listFiles(DirectoryRosterStore::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 8bd98748c..adb75cb09 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,6 +95,8 @@ 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 { @@ -131,6 +133,8 @@ 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 { @@ -203,6 +207,8 @@ 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 { @@ -279,6 +285,7 @@ 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 { @@ -327,6 +334,7 @@ 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 { @@ -398,6 +406,7 @@ 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) @@ -466,6 +475,7 @@ 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 91be648ed..aa2a51687 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,6 +97,8 @@ 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 { @@ -172,6 +174,7 @@ 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 bf116b874..9dc9ba3c7 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,6 +57,7 @@ public class DirectoryRosterStoreTest { /** * Tests that opening an uninitialized directory fails. + * @throws IOException if IO exception. */ @Test public void testStoreUninitialized() throws IOException { @@ -66,6 +67,7 @@ public class DirectoryRosterStoreTest { /** * Tests that an initialized directory is empty. + * @throws IOException if IO exception. */ @Test public void testStoreInitializedEmpty() throws IOException { @@ -80,6 +82,7 @@ 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 2bdb340bd..2b94b2190 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.tlsContext != null && urlConnection instanceof HttpsURLConnection) { + if (sinttestConfiguration.sslContextFactory != null && urlConnection instanceof HttpsURLConnection) { HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection; - httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.tlsContext.getSocketFactory()); + httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.sslContextFactory.createSslContext().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 befb94c08..593d5b847 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,7 +20,15 @@ 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 { @@ -58,4 +66,48 @@ 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 258fb3c1f..9f5c32633 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,6 +36,7 @@ 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; @@ -72,7 +73,7 @@ public final class Configuration { public final String serviceTlsPin; - public final SSLContext tlsContext; + public final SslContextFactory sslContextFactory; public final SecurityMode securityMode; @@ -121,9 +122,10 @@ public final class Configuration { "'service' must be set. Either via 'properties' files or via system property 'sinttest.service'."); serviceTlsPin = builder.serviceTlsPin; if (serviceTlsPin != null) { - tlsContext = Java7Pinning.forPin(serviceTlsPin); + SSLContext sslContext = Java7Pinning.forPin(serviceTlsPin); + sslContextFactory = () -> sslContext; } else { - tlsContext = null; + sslContextFactory = null; } securityMode = builder.securityMode; if (builder.replyTimeout > 0) { @@ -168,8 +170,8 @@ public final class Configuration { this.testPackages = builder.testPackages; this.configurationApplier = b -> { - if (tlsContext != null) { - b.setCustomSSLContext(tlsContext); + if (sslContextFactory != null) { + b.setSslContextFactory(sslContextFactory); } 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 0a0110882..4c373a9ce 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,6 +31,7 @@ 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; @@ -40,6 +41,7 @@ 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; @@ -49,7 +51,6 @@ 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 { @@ -124,11 +125,7 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { /** * Test if entity caps actually prevent a disco info request and reply. * - * @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. - * + * @throws Exception if exception. */ @SmackIntegrationTest public void testPreventDiscoInfo() throws Exception { @@ -143,26 +140,7 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { }, new AndFilter(new StanzaTypeFilter(DiscoverInfo.class), IQTypeFilter.GET)); - 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); - } + addFeatureAndWaitForPresence(conOne, conTwo, dummyFeature); dropCapsCache(); // discover that @@ -181,10 +159,10 @@ public class EntityCapsTest extends AbstractSmackIntegrationTest { } @SmackIntegrationTest - public void testCapsChanged() { + public void testCapsChanged() throws Exception { final String dummyFeature = getNewDummyFeature(); String nodeVerBefore = EntityCapsManager.getNodeVersionByJid(conTwo.getUser()); - sdmTwo.addFeature(dummyFeature); + addFeatureAndWaitForPresence(conOne, conTwo, dummyFeature); String nodeVerAfter = EntityCapsManager.getNodeVersionByJid(conTwo.getUser()); assertFalse(nodeVerBefore.equals(nodeVerAfter)); @@ -229,4 +207,24 @@ 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 6b153b3a2..11e1340f4 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,7 +21,6 @@ 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; @@ -34,25 +33,19 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { // Listener for composing chat state private final SimpleResultSyncPoint composingSyncPoint = new SimpleResultSyncPoint(); - private final ChatStateListener composingListener = new ChatStateListener() { - @Override - public void stateChanged(Chat chat, ChatState state, Message message) { - if (state.equals(ChatState.composing)) { - composingSyncPoint.signal(); - } + private void composingListener(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 final ChatStateListener activeListener = new ChatStateListener() { - @Override - public void stateChanged(Chat chat, ChatState state, Message message) { - if (state.equals(ChatState.active)) { - activeSyncPoint.signal(); - } + private void activeListener(Chat chat, ChatState state, Message message) { + if (state.equals(ChatState.active)) { + activeSyncPoint.signal(); } - }; + } public ChatStateIntegrationTest(SmackIntegrationTestEnvironment environment) { @@ -65,8 +58,8 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { ChatStateManager manTwo = ChatStateManager.getInstance(conTwo); // Add chatState listeners. - manTwo.addChatStateListener(composingListener); - manTwo.addChatStateListener(activeListener); + manTwo.addChatStateListener(this::composingListener); + manTwo.addChatStateListener(this::activeListener); Chat chatOne = ChatManager.getInstanceFor(conOne) .chatWith(conTwo.getUser().asEntityBareJid()); @@ -86,7 +79,7 @@ public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest { @AfterClass public void cleanup() { ChatStateManager manTwo = ChatStateManager.getInstance(conTwo); - manTwo.removeChatStateListener(composingListener); - manTwo.removeChatStateListener(activeListener); + manTwo.removeChatStateListener(this::composingListener); + manTwo.removeChatStateListener(this::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 7736c0657..243bfec36 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,7 +57,9 @@ public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest + " does not accept files of size " + FILE_SIZE + ". It only accepts files with a maximum size of " + uploadService.getMaxFileSize()); } - hfumOne.setTlsContext(environment.configuration.tlsContext); + if (environment.configuration.sslContextFactory != null) { + hfumOne.setTlsContext(environment.configuration.sslContextFactory.createSslContext()); + } } @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 e2e37fae5..f289c96be 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,13 +16,14 @@ */ package org.jivesoftware.smackx.softwareInfo; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; 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; @@ -54,9 +55,14 @@ public class SoftwareInfoIntegrationTest extends AbstractSmackIntegrationTest { @SmackIntegrationTest public void test() throws Exception { SoftwareInfoForm softwareInfoSent = createSoftwareInfoForm(); - sim1.publishSoftwareInformationForm(softwareInfoSent); + performActionAndWaitForPresence(conTwo, conOne, new ThrowingRunnable() { + @Override + public void runOrThrow() throws Exception { + sim1.publishSoftwareInformationForm(softwareInfoSent); + } + }); SoftwareInfoForm softwareInfoFormReceived = sim2.fromJid(conOne.getUser()); - assertTrue(softwareInfoFormReceived.equals(softwareInfoSent)); + assertEquals(softwareInfoSent, softwareInfoFormReceived); } 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 37b493a26..a4a9bedea 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; @@ -120,6 +115,7 @@ public final class OmemoManager extends Manager { super(connection); service = OmemoService.getInstance(); + pepManager = PepManager.getInstanceFor(connection); this.deviceId = deviceId; @@ -138,9 +134,6 @@ public final class OmemoManager extends Manager { // StanzaListeners resumeStanzaAndPEPListeners(); - - // Announce OMEMO support - ServiceDiscoveryManager.getInstanceFor(connection).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); } /** @@ -251,7 +244,6 @@ public final class OmemoManager extends Manager { } getOmemoService().init(new LoggedInOmemoManager(this)); - ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); } /** @@ -893,27 +885,25 @@ 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(internalOmemoMessageStanzaListener); - carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); - pepManager.removePepListener(deviceListUpdateListener); + connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener); + carbonManager.removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); // Add listeners - pepManager.addPepListener(deviceListUpdateListener); - connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter); - carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); + pepManager.addPepEventListener(OmemoConstants.PEP_NODE_DEVICE_LIST, OmemoDeviceListElement.class, pepOmemoDeviceListEventListener); + connection().addAsyncStanzaListener(this::internalOmemoMessageStanzaListener, OmemoManager::isOmemoMessage); + carbonManager.addCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); } /** * Remove active stanza listeners needed for OMEMO. */ public void stopStanzaAndPEPListeners() { - PepManager.getInstanceFor(connection()).removePepListener(deviceListUpdateListener); - connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener); - CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); + pepManager.removePepEventListener(pepOmemoDeviceListEventListener); + connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener); + CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); } /** @@ -961,127 +951,87 @@ public final class OmemoManager extends Manager { /** * StanzaListener that listens for incoming Stanzas which contain OMEMO elements. */ - private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() { + 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); + } + } + }); + } - @Override - public void processStanza(final Stanza packet) { - Async.go(new Runnable() { - @Override - public void run() { + /** + * 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().onOmemoMessageStanzaReceived(packet, + getOmemoService().onOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage, 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); - } - } - } - }); - } - }; + @SuppressWarnings("UnnecessaryLambda") + private final PepEventListener pepOmemoDeviceListEventListener = + (from, receivedDeviceList, id, message) -> { + // Device List + OmemoCachedDeviceList deviceList; + try { + getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, + receivedDeviceList); - /** - * 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... + if (!from.asBareJid().equals(getOwnJid())) { 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, + 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); + 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())); - 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); - } - } - }); + 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 final StanzaFilter omemoMessageStanzaFilter = new StanzaFilter() { - @Override - public boolean accept(Stanza stanza) { - return stanza instanceof Message && OmemoManager.stanzaContainsOmemoElement(stanza); - } - }; + private static boolean isOmemoMessage(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 f91dc199f..352853450 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,7 +31,6 @@ 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 d16296a05..a3a8a7c1b 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-2019 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2017-2020 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,7 +39,6 @@ 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; @@ -69,12 +68,10 @@ 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; @@ -173,6 +170,9 @@ 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(incomingOpenPgpMessageListener); + ChatManager.getInstanceFor(connection).addIncomingListener(this::incomingChatMessageListener); pepManager = PepManager.getInstanceFor(connection); } @@ -279,7 +279,7 @@ public final class OpenPgpManager extends Manager { publishPublicKey(pepManager, pubkeyElement, primaryFingerprint); // Subscribe to public key changes - PepManager.getInstanceFor(connection()).addPepListener(metadataListener); + pepManager.addPepEventListener(PEP_NODE_PUBLIC_KEYS, PublicKeysListElement.class, pepPublicKeyListElementListener); 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.getInstanceFor(connection()).removePepListener(metadataListener); + pepManager.removePepEventListener(pepPublicKeyListElementListener); } /** @@ -497,31 +497,6 @@ 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 { @@ -548,62 +523,60 @@ public final class OpenPgpManager extends Manager { return provider.decryptAndOrVerify(element, getOpenPgpSelf(), sender); } - 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); - - 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 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; - } - - else { - throw new AssertionError("Invalid element received: " + contentElement.getClass().getName()); - } - } - }); + 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; } - }; + + 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); + } + + 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 CryptElement) { + for (CryptElementReceivedListener l : cryptElementReceivedListeners) { + l.cryptElementReceived(contact, message, (CryptElement) contentElement, + decrypted.getMetadata()); + } + return; + } + + 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 1ccdabe0f..4383a02b3 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,7 +44,6 @@ 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; @@ -127,7 +126,7 @@ public final class OXInstantMessagingManager extends Manager { private OXInstantMessagingManager(final XMPPConnection connection) { super(connection); openPgpManager = OpenPgpManager.getInstanceFor(connection); - openPgpManager.registerSigncryptReceivedListener(signcryptElementReceivedListener); + openPgpManager.registerSigncryptReceivedListener(this::signcryptElementReceivedListener); announceSupportForOxInstantMessaging(); } @@ -358,12 +357,9 @@ public final class OXInstantMessagingManager extends Manager { message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging."); } - 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); - } + private void signcryptElementReceivedListener(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 3176e1985..2ba95bcc4 100644 --- a/smack-repl/build.gradle +++ b/smack-repl/build.gradle @@ -2,6 +2,9 @@ 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 d9d766447..af4b939d6 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,6 +42,8 @@ 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 { @@ -128,9 +130,10 @@ 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); + System.out.println("NIO successfully finished, yeah!\n" + connectionStats + '\n' + serviceDiscoveryManagerStats); // 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 557f1812d..08bd25b6d 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.setCustomSSLContext(sslContext); + builder.setSslContextFactory(() -> 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 f71e3f196..ea34b9518 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,6 +81,7 @@ 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 ead3a6438..74286fdd9 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,13 +21,9 @@ 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; @@ -49,7 +45,6 @@ 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; @@ -61,7 +56,6 @@ 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; @@ -75,6 +69,7 @@ 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; @@ -280,294 +275,292 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM } }; - 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; + 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; - if (!channelSelectedCallbackLock.tryLock()) { - rejectedChannelSelectedCallbacks.incrementAndGet(); + if (!channelSelectedCallbackLock.tryLock()) { + rejectedChannelSelectedCallbacks.incrementAndGet(); + return; + } + + handledChannelSelectedCallbacks++; + + long callbackBytesRead = 0; + long callbackBytesWritten = 0; + + try { + boolean destinationAddressChanged = false; + boolean isLastPartOfElement = false; + TopLevelStreamElement currentlyOutgonigTopLevelStreamElement = null; + StringBuilder outgoingStreamForDebugger = null; + + writeLoop: while (true) { + final boolean moreDataAvailable = !isLastPartOfElement || !connectionInternal.outgoingElementsQueue.isEmpty(); + + if (filteredOutgoingBuffer != null || !networkOutgoingBuffers.isEmpty()) { + if (filteredOutgoingBuffer != null) { + networkOutgoingBuffers.add(filteredOutgoingBuffer); + networkOutgoingBuffersBytes += filteredOutgoingBuffer.remaining(); + + 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; + } + } + + 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; + } + + int bytesRead; + incomingBuffer.clear(); + try { + bytesRead = selectedSocketChannel.read(incomingBuffer); + } catch (IOException e) { + handleReadWriteIoException(e); return; } - handledChannelSelectedCallbacks++; - - long callbackBytesRead = 0; - long callbackBytesWritten = 0; - - try { - boolean destinationAddressChanged = false; - boolean isLastPartOfElement = false; - TopLevelStreamElement currentlyOutgonigTopLevelStreamElement = null; - StringBuilder outgoingStreamForDebugger = null; - - writeLoop: while (true) { - final boolean moreDataAvailable = !isLastPartOfElement || !connectionInternal.outgoingElementsQueue.isEmpty(); - - if (filteredOutgoingBuffer != null || !networkOutgoingBuffers.isEmpty()) { - if (filteredOutgoingBuffer != null) { - networkOutgoingBuffers.add(filteredOutgoingBuffer); - networkOutgoingBuffersBytes += filteredOutgoingBuffer.remaining(); - - 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; - } - } - - 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; - } - - int bytesRead; - incomingBuffer.clear(); - try { - 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); - return; - } - } - } finally { - totalBytesWritten += callbackBytesWritten; - totalBytesRead += callbackBytesRead; - - channelSelectedCallbackLock.unlock(); + 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; } - // Indicate that there is no reactor thread racing towards handling this selection key. - final SelectionKeyAttachment selectionKeyAttachment = this.selectionKeyAttachment; - if (selectionKeyAttachment != null) { - selectionKeyAttachment.resetReactorThreadRacing(); + if (!pendingInputFilterData) { + if (bytesRead == 0) { + // Nothing more to read. + break; + } + } else { + pendingInputFilterData = 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(); + // 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; } - connectionInternal.setInterestOps(selectionKey, newInterestedOps); - }; + 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); + } private void handleReadWriteIoException(IOException e) { if (e instanceof ClosedChannelException && !tcpNioTransport.isConnected()) { @@ -677,7 +670,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM } @Override - public Stats getStats() { + public XmppTcpTransportModule.Stats getStats() { return XmppTcpTransportModule.this.getStats(); } @@ -774,7 +767,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM remoteAddress = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress(); selectionKey = connectionInternal.registerWithSelector(socketChannel, SelectionKey.OP_READ, - channelSelectedCallback); + XmppTcpTransportModule.this::onChannelSelected); selectionKeyAttachment = (SelectionKeyAttachment) selectionKey.attachment(); connectionInternal.setTransport(tcpNioTransport); @@ -869,13 +862,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM ConnectionUnexpectedTerminatedException, NoResponseException, NotConnectedException { connectionInternal.sendAndWaitForResponse(StartTls.INSTANCE, TlsProceed.class, TlsFailure.class); - SmackTlsContext smackTlsContext; - try { - smackTlsContext = connectionInternal.getSmackTlsContext(); - } catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException - | CertificateException | KeyStoreException | NoSuchProviderException e) { - throw new SmackWrappedException(e); - } + SmackTlsContext smackTlsContext = connectionInternal.getSmackTlsContext(); tlsState = new TlsState(smackTlsContext); connectionInternal.addXmppInputOutputFilter(tlsState); @@ -1316,7 +1303,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM pendingOutputFilterData = true; } - channelSelectedCallback.onChannelSelected(channel, key); + onChannelSelected(channel, key); } finally { channelSelectedCallbackLock.unlock(); } @@ -1347,7 +1334,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM return CollectionUtil.removeUntil(buffers, b -> b.hasRemaining()); } - public Stats getStats() { + public XmppTcpTransportModule.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 2dabe1950..5f6a23ad5 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,18 +16,14 @@ */ 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; @@ -42,17 +38,11 @@ public final class IpTcpRemoteConnectionEndpoint this.internetAddressResourceRecord = internetAddressResourceRecord; } - public static IpTcpRemoteConnectionEndpoint from(CharSequence host, int port, + public static IpTcpRemoteConnectionEndpoint> from(CharSequence host, UInt16 port, InetAddress 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); - } + InternetAddressRR internetAddressResourceRecord = InternetAddressRR.from(inetAddress); - return new IpTcpRemoteConnectionEndpoint(host, UInt16.from(port), + return new IpTcpRemoteConnectionEndpoint>(host, 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 9328d0fe7..6b7da5ccf 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,6 +26,7 @@ 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; @@ -61,7 +62,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) { @@ -72,9 +73,9 @@ public class RemoteXmppTcpConnectionEndpoints { if (hostAddresses != null) { discoveredRemoteConnectionEndpoints = new ArrayList<>(hostAddresses.size()); - int port = config.getPort(); + UInt16 port = config.getPort(); for (InetAddress inetAddress : hostAddresses) { - IpTcpRemoteConnectionEndpoint connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( + IpTcpRemoteConnectionEndpoint> connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( host, port, inetAddress); discoveredRemoteConnectionEndpoints.add(connectionEndpoint); } @@ -198,13 +199,13 @@ public class RemoteXmppTcpConnectionEndpoints { LOGGER.info("Could not resolve DNS SRV resource records for " + srvDomain + ". Consider adding those."); } - int defaultPort; + UInt16 defaultPort; switch (domainType) { case client: - defaultPort = 5222; + defaultPort = UInt16.from(5222); break; case server: - defaultPort = 5269; + defaultPort = UInt16.from(5269); break; default: throw new AssertionError(); @@ -214,7 +215,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 9658b9f7f..95f74bc60 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 + * @throws BrokenBarrierException in case of a broken barrier. * @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 - * @throws IllegalAccessException + * @throws NoSuchFieldException if there is no such field. + * @throws IllegalAccessException if there was an illegal access. * @throws IllegalArgumentException if an illegal argument was given. */ @Test diff --git a/version b/version index 60c4355df..92560b19f 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.0-alpha3-SNAPSHOT +4.4.0-alpha4-SNAPSHOT