From dc64a43f126ae25c0e98c3be2df958be1d79849b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 17 May 2020 17:01:42 +0200 Subject: [PATCH 01/40] Prevent NPE when closing trust store stream --- .../java/org/jivesoftware/smack/AbstractXMPPConnection.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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..e7d8daebb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -126,6 +126,7 @@ import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.sasl.core.SASLAnonymous; import org.jivesoftware.smack.sasl.packet.SaslNonza; import org.jivesoftware.smack.util.Async; +import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smack.util.CollectionUtil; import org.jivesoftware.smack.util.Consumer; import org.jivesoftware.smack.util.DNSUtil; @@ -2291,7 +2292,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { try { ks.load(stream, password); } finally { - stream.close(); + CloseableUtil.maybeClose(stream); } } catch (IOException e) { LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e); @@ -2302,7 +2303,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { try { ks.load(stream, null); } finally { - stream.close(); + CloseableUtil.maybeClose(stream); } } } From dfdd0acf913e3c9aefa42e6247adccbe12061766 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 16 May 2020 14:15:50 +0200 Subject: [PATCH 02/40] Introduce AbstractStats --- .../ModularXmppClientToServerConnection.java | 18 ++---- .../smack/internal/AbstractStats.java | 52 +++++++++++++++++ .../smack/internal/package-info.java | 21 +++++++ .../smack/util/ExtendedAppendable.java | 56 +++++++++++++++++++ 4 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/internal/AbstractStats.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/internal/package-info.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/ExtendedAppendable.java 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..eff02dedb 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 @@ -65,6 +65,7 @@ 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.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Nonza; @@ -78,6 +79,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; @@ -1069,7 +1071,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 +1081,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 +1102,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/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/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/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; + } +} From 6daf19dbd3b838f168cf549591ddefd3b1b59eb6 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 16 May 2020 14:16:05 +0200 Subject: [PATCH 03/40] [core] Add javadoc to SmackReactor.cancel(ScheduledAction) --- .../src/main/java/org/jivesoftware/smack/SmackReactor.java | 6 ++++++ 1 file changed, 6 insertions(+) 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..6d870a9ca 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackReactor.java @@ -154,6 +154,12 @@ public class SmackReactor { 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); } From 4e5536e2273d5ecf3fc920750f51149aed0c33d3 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 16 May 2020 14:16:23 +0200 Subject: [PATCH 04/40] [core] Make ScheduledAction.cancel() return boolean and add javadoc --- .../java/org/jivesoftware/smack/ScheduledAction.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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..962e1d75e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ScheduledAction.java @@ -32,8 +32,13 @@ public class ScheduledAction implements Delayed { this.smackReactor = smackReactor; } - 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() { From 72c5dc5886bed855a4a756dae2e5ff0b267c872b Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 16 May 2020 21:18:27 +0200 Subject: [PATCH 05/40] [core] Introduce ScheduledAction.Kind for blocking and non-blocking actions --- .../smack/AbstractXMPPConnection.java | 2 +- .../java/org/jivesoftware/smack/Manager.java | 10 +++++++- .../jivesoftware/smack/ScheduledAction.java | 24 +++++++++++++++++-- .../org/jivesoftware/smack/SmackReactor.java | 6 ++--- 4 files changed, 35 insertions(+), 7 deletions(-) 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 e7d8daebb..f93c48408 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -2163,7 +2163,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) { 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 962e1d75e..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,16 +20,25 @@ 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; } /** @@ -68,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 6d870a9ca..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,10 +145,10 @@ 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; @@ -206,7 +206,7 @@ public class SmackReactor { } if (dueScheduledAction != null) { - dueScheduledAction.action.run(); + dueScheduledAction.run(); return; } From 46ba2736891de7a45382a216fe7e092a51d1f3ef Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 16 May 2020 21:19:06 +0200 Subject: [PATCH 06/40] [disco] Delay the entity caps renewal This avoids the calculation of the caps hash while the managers become registered with the connection and add their features. --- .../smackx/disco/ServiceDiscoveryManager.java | 58 ++++++++++++++++++- .../igniterealtime/smack/smackrepl/Nio.java | 5 +- 2 files changed, 59 insertions(+), 4 deletions(-) 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..30daeda7e 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,25 @@ 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 org.jivesoftware.smack.ConnectionCreationListener; 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.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.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; @@ -894,13 +900,33 @@ 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++; + } } + + renewEntityCapsScheduledAction = scheduleBlocking(() -> { + renewEntityCapsPerformed.incrementAndGet(); + + for (EntityCapabilitiesChangedListener entityCapabilitiesChangedListener : entityCapabilitiesChangedListeners) { + entityCapabilitiesChangedListener.onEntityCapailitiesChanged(); + } + }, RENEW_ENTITY_CAPS_DELAY_MILLIS, TimeUnit.MILLISECONDS); } public static void addDiscoInfoLookupShortcutMechanism(DiscoInfoLookupShortcutMechanism discoInfoLookupShortcutMechanism) { @@ -915,4 +941,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-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 } From 4239dac440c10398bb5807ad33cb8ebec3e48daa Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 16 May 2020 21:42:36 +0200 Subject: [PATCH 07/40] [gradle] Fix project description in POM --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index de43fba80..040ae65e8 100644 --- a/build.gradle +++ b/build.gradle @@ -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' From 87591655ad6114e17b549bfdcf61667dc29c95bc Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 17 May 2020 20:47:18 +0200 Subject: [PATCH 08/40] [core] Add StanzaFilter.asPredicate(Class) --- .../java/org/jivesoftware/smack/filter/StanzaFilter.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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); + }; + } } From 2679c72f0fa4eefbe0458cdcfedbb07899c75d01 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 17 May 2020 20:50:25 +0200 Subject: [PATCH 09/40] [disco] Move logic that was previously in EntityCapsManager in SDM --- .../smackx/caps/EntityCapsManager.java | 54 ++----------------- .../EntityCapabilitiesChangedListener.java | 6 ++- .../smackx/disco/ServiceDiscoveryManager.java | 45 +++++++++++++++- 3 files changed, 52 insertions(+), 53 deletions(-) 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..5352c85cc 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,7 +49,6 @@ 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; @@ -87,7 +84,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 +303,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. @@ -342,11 +337,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 +349,6 @@ public final class EntityCapsManager extends Manager { } }); - // This calculates the local entity caps version - updateLocalEntityCaps(); - if (autoEnableEntityCaps) enableEntityCaps(); @@ -387,26 +374,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); } }); } @@ -431,7 +408,6 @@ public final class EntityCapsManager extends Manager { // Add Entity Capabilities (XEP-0115) feature node. sdm.addFeature(NAMESPACE); - updateLocalEntityCaps(); entityCapsEnabled = true; } @@ -446,11 +422,6 @@ public final class EntityCapsManager extends Manager { return entityCapsEnabled; } - public void setEntityNode(String entityNode) { - this.entityNode = entityNode; - updateLocalEntityCaps(); - } - /** * Remove a record telling what entity caps node a user has. * @@ -522,13 +493,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(); // getLocalNodeVer() will return a result only after currentCapsVersion is set. Therefore // set it first and then call getLocalNodeVer() currentCapsVersion = generateVerificationString(discoverInfoBuilder); @@ -564,20 +532,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/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 30daeda7e..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 @@ -31,8 +31,11 @@ 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; @@ -40,10 +43,12 @@ 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; @@ -77,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"; @@ -97,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() { @@ -196,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); } /** @@ -920,11 +941,33 @@ public final class ServiceDiscoveryManager extends Manager { } } + 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.onEntityCapailitiesChanged(); + 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); } From d69f62d012f78484586171ce6e9c066a461f3754 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 21 May 2020 09:47:02 +0200 Subject: [PATCH 10/40] [repl] Add project description --- smack-repl/build.gradle | 3 +++ 1 file changed, 3 insertions(+) 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' From f97397f5ee5cfb763b67afbfa84767fb7de35053 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 20 May 2020 17:34:23 +0200 Subject: [PATCH 11/40] Smack 4.4.0-alpha3 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 60c4355df..4fe68efbd 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.0-alpha3-SNAPSHOT +4.4.0-alpha3 From cbcf1d15f549784ac6fc6f22263257041f9306b9 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 21 May 2020 10:35:16 +0200 Subject: [PATCH 12/40] Smack 4.4.0-alpha4-SNAPSHOT --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 4fe68efbd..92560b19f 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.4.0-alpha3 +4.4.0-alpha4-SNAPSHOT From 33720e8d97d0503039728b56d2c5a434dc24d366 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 21 May 2020 12:47:55 +0200 Subject: [PATCH 13/40] [core] Fix potential NPE in Java7ZlibInputOutputStream The field XMPPInputOutputStream.flushMethod was not initialized, which could cause an NPE in the "switch (flushMethod)" found in Java7ZlibInputOutputStream.getOutputStream(). --- .../smack/compression/XMPPInputOutputStream.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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() { From c4ad857c0df4a398c08e9cf0d907de1d1aae3684 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 21 May 2020 22:44:14 +0200 Subject: [PATCH 14/40] [carbons] Add CarbonExtension.Private.addTo(MessageBuilder), deprecate old --- .../smackx/carbons/packet/CarbonExtension.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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); } From ca85b8326ab77cc290ffbfa80d78558ad7a0cb86 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 21 May 2020 22:44:51 +0200 Subject: [PATCH 15/40] [test] Use correct class StanzaBuilder for static call to buildMessage() --- .../smackx/fallback_indication/FallbackIndicationTest.java | 6 +++--- .../message_fastening/MessageFasteningElementsTest.java | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) 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/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)); From b3c57ef9186a6f79de3151b084692171ca136f4d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 21 May 2020 22:45:28 +0200 Subject: [PATCH 16/40] [xhtmlim] Use stanza builder API, deprecate old --- .../smackx/xhtmlim/XHTMLManager.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) 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) { From 962071762a99f73ed4ad43eab0fbdbed31aa90fe Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 22 May 2020 09:20:02 +0200 Subject: [PATCH 17/40] [core] Optimize Presence.toXML() for empty element --- .../org/jivesoftware/smack/packet/Presence.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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..7d37ed50c 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 @@ -17,6 +17,7 @@ package org.jivesoftware.smack.packet; +import java.util.List; import java.util.Locale; import javax.net.SocketFactory; @@ -309,6 +310,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 +328,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); From 054fd9ae4400a10df5999d84f2af0f1a67116e04 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 22 May 2020 09:24:49 +0200 Subject: [PATCH 18/40] [core] Replace 'packet' with 'stanza' in Presence's javadoc --- .../main/java/org/jivesoftware/smack/packet/Presence.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 7d37ed50c..ef08cccc6 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 @@ -30,7 +30,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 @@ -56,7 +56,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. * @@ -84,7 +84,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. From 68a453d3b3aa31dc459b059a61c561a93ddf5d35 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 22 May 2020 09:25:20 +0200 Subject: [PATCH 19/40] [core] Fix deprecation javadoc in Presence It should hint towards the StanzaFactory not SocketFactory. --- .../org/jivesoftware/smack/packet/Presence.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 ef08cccc6..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. @@ -20,8 +20,6 @@ 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; @@ -100,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. @@ -116,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. @@ -192,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. @@ -210,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. @@ -237,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. @@ -266,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. From 027358fc6350e1fafe24826f98ebca39c4438c5d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 22 May 2020 15:34:27 +0200 Subject: [PATCH 20/40] [softwareinfo] Remove unnecessary throws declarations --- .../smackx/softwareinfo/SoftwareInfoManager.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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..595eb3b7c 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,7 +16,6 @@ */ package org.jivesoftware.smackx.softwareinfo; -import java.io.IOException; import java.util.Map; import java.util.WeakHashMap; @@ -26,8 +25,6 @@ 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; @@ -48,7 +45,7 @@ public final class SoftwareInfoManager extends Manager { 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,7 +54,7 @@ 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); } From 54d6bc865830fc80bdbe38bed56ef3230d284490 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 22 May 2020 15:35:56 +0200 Subject: [PATCH 21/40] [softwareinfo] Remove SoftwareInfoManager.isSupported(Jid) SoftwareInfoManager.fromJid(Jid) will return 'null' if the jid in question does not announce or support this. --- .../softwareinfo/SoftwareInfoManager.java | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) 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 595eb3b7c..c27fb6f48 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 @@ -20,7 +20,6 @@ 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; @@ -41,7 +40,6 @@ 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; @@ -59,20 +57,6 @@ public final class SoftwareInfoManager extends Manager { 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. *
@@ -83,20 +67,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) { From 0db640626264c6d9f2368448902d5a763b06d016 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 22 May 2020 15:36:39 +0200 Subject: [PATCH 22/40] [softwareinfo] Separate static and non-static fields by empty line --- .../jivesoftware/smackx/softwareinfo/SoftwareInfoManager.java | 1 + 1 file changed, 1 insertion(+) 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 c27fb6f48..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 @@ -41,6 +41,7 @@ import org.jxmpp.jid.Jid; public final class SoftwareInfoManager extends Manager { private static final Map INSTANCES = new WeakHashMap<>(); + private final ServiceDiscoveryManager serviceDiscoveryManager; public static synchronized SoftwareInfoManager getInstanceFor (XMPPConnection connection) { From a137944e5010acb67050b58a9a92c831420464a7 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 22 May 2020 15:38:16 +0200 Subject: [PATCH 23/40] [disco/caps] Fix DiscoverInfo.asBuilder() The method would not copy the extensions elements, which would lead to a false calculation of the caps hash by EntityCapsManager. --- .../org/jivesoftware/smack/packet/AbstractIqBuilder.java | 6 +++++- .../main/java/org/jivesoftware/smack/packet/IqBuilder.java | 6 +++++- .../org/jivesoftware/smackx/caps/EntityCapsManager.java | 2 +- .../org/jivesoftware/smackx/disco/packet/DiscoverInfo.java | 4 ++-- .../smackx/disco/packet/DiscoverInfoBuilder.java | 6 +++--- 5 files changed, 16 insertions(+), 8 deletions(-) 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-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/caps/EntityCapsManager.java index 5352c85cc..a83c24875 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 @@ -496,7 +496,7 @@ public final class EntityCapsManager extends Manager { private void updateLocalEntityCaps(DiscoverInfo synthesizedDiscoveryInfo) { XMPPConnection connection = connection(); - DiscoverInfoBuilder discoverInfoBuilder = synthesizedDiscoveryInfo.asBuilder(); + 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); 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 Date: Fri, 22 May 2020 15:44:23 +0200 Subject: [PATCH 24/40] [sinttest] Add unreliable workaround for XEP-0030 based operations --- .../inttest/AbstractSmackIntegrationTest.java | 52 +++++++++++++++++++ .../smackx/caps/EntityCapsTest.java | 48 +++++++++-------- .../SoftwareInfoIntegrationTest.java | 12 +++-- 3 files changed, 86 insertions(+), 26 deletions(-) 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/jivesoftware/smackx/caps/EntityCapsTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java index 0a0110882..2ab7110a4 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 { @@ -143,26 +144,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 +163,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 +211,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/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 { From ebe5c49e92e1376d2513951529edeaf245b8de5f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 23 May 2020 22:35:27 +0200 Subject: [PATCH 25/40] [checkstyle] Tighten JavadocMethod checkstyle rule --- config/checkstyle/checkstyle.xml | 4 --- .../smack/provider/ProviderManagerTest.java | 1 + .../jivesoftware/smack/DummyConnection.java | 3 ++ .../httpfileupload/HttpFileUploadManager.java | 5 ++- .../usertune/element/UserTuneElement.java | 32 +++++++++---------- .../bytestreams/socks5/Socks5ProxyTest.java | 4 +-- .../jivesoftware/smack/roster/RosterTest.java | 10 ++++++ .../smack/roster/RosterVersioningTest.java | 3 ++ .../rosterstore/DirectoryRosterStoreTest.java | 3 ++ .../smackx/caps/EntityCapsTest.java | 6 +--- .../smackx/omemo/SignalOmemoStoreTest.java | 2 +- .../smack/tcp/PacketWriterTest.java | 6 ++-- 12 files changed, 45 insertions(+), 34 deletions(-) diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index c62f00a71..c9f04d7aa 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -105,10 +105,6 @@ - - - - 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/testFixtures/java/org/jivesoftware/smack/DummyConnection.java b/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java index 135fd79d0..b49ab8d3e 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") 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..9a790dbc8 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 @@ -324,8 +324,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 +347,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 { 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/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-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/jivesoftware/smackx/caps/EntityCapsTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/caps/EntityCapsTest.java index 2ab7110a4..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 @@ -125,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 { 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 Date: Sat, 23 May 2020 22:49:44 +0200 Subject: [PATCH 26/40] [checkstyle] Enable JavadocMethod also for protected methods --- config/checkstyle/checkstyle.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index c9f04d7aa..c39a8650e 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -104,7 +104,7 @@ - + From a51663448b4f8f43408a9e8ffe1abaeda9663a78 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 23 May 2020 23:36:05 +0200 Subject: [PATCH 27/40] [checkstyle] Bump to 8.27 We can not bump to the latest version, due https://github.com/checkstyle/checkstyle/issues/8248 --- build.gradle | 2 +- config/checkstyle/checkstyle.xml | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 040ae65e8..cdf05eef4 100644 --- a/build.gradle +++ b/build.gradle @@ -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' diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index c39a8650e..ce6b4200d 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -105,8 +105,6 @@ - - From f045c0dd0818927b4d16911995af2c934c147ae4 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 12:26:36 +0200 Subject: [PATCH 28/40] Update Message Archive Management (XEP-0313) support to urn:xmpp:mam:2 Fixes SMACK-890. --- .../jivesoftware/smackx/mam/element/MamElements.java | 4 ++-- .../experimental.providers | 8 ++++---- .../jivesoftware/smackx/mam/MamFinProviderTest.java | 4 ++-- .../jivesoftware/smackx/mam/MamPrefIQProviderTest.java | 8 ++++---- .../smackx/mam/MamQueryIQProviderTest.java | 10 +++++----- .../jivesoftware/smackx/mam/MamResultProviderTest.java | 4 ++-- .../java/org/jivesoftware/smackx/mam/PagingTest.java | 6 +++--- .../org/jivesoftware/smackx/mam/QueryArchiveTest.java | 4 ++-- .../org/jivesoftware/smackx/mam/ResultsLimitTest.java | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) 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/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/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" + "" + "" + ""; From 9a8ee3c8e35b5bbf6af375e7bed243e315191f61 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 13:02:01 +0200 Subject: [PATCH 29/40] [core] Improve NumberUtil's exception message and fix javadoc --- .../org/jivesoftware/smack/util/NumberUtil.java | 14 +++++++------- .../xdatavalidation/DataValidationHelperTest.java | 3 +-- 2 files changed, 8 insertions(+), 9 deletions(-) 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-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)); From cac874bdc74a95c0c5f2f0f25e69d52968a11b97 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 13:02:19 +0200 Subject: [PATCH 30/40] [core] Use UInt16 for ConnectionConfiguration 'port' --- .../smack/ConnectionConfiguration.java | 14 ++++++++++---- .../tcp/rce/IpTcpRemoteConnectionEndpoint.java | 4 ++-- .../tcp/rce/RemoteXmppTcpConnectionEndpoints.java | 9 +++++---- 3 files changed, 17 insertions(+), 10 deletions(-) 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..e0b24a08f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -35,6 +35,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; import javax.security.auth.callback.CallbackHandler; +import org.jivesoftware.smack.datatypes.UInt16; import org.jivesoftware.smack.debugger.SmackDebuggerFactory; import org.jivesoftware.smack.packet.id.StandardStanzaIdSource; import org.jivesoftware.smack.packet.id.StanzaIdSource; @@ -101,7 +102,7 @@ public abstract class ConnectionConfiguration { protected final InetAddress hostAddress; protected final DnsName host; - protected final int port; + protected final UInt16 port; private final String keystorePath; private final String keystoreType; @@ -237,7 +238,7 @@ public abstract class ConnectionConfiguration { return hostAddress; } - public int getPort() { + public UInt16 getPort() { return port; } @@ -621,7 +622,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 +838,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(); } 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..713509800 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 @@ -42,7 +42,7 @@ 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. @@ -52,7 +52,7 @@ public final class IpTcpRemoteConnectionEndpoint internetAddressResourceRecord = new AAAA((Inet6Address) 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..cf5ce3e25 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; @@ -72,7 +73,7 @@ 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( host, port, inetAddress); @@ -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(); From c7c5b10c41ef240e93872acb02c500eb0eb8d858 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 13:11:50 +0200 Subject: [PATCH 31/40] Bump MiniDNS version to 0.4.0-alpha5 --- build.gradle | 2 +- .../tcp/rce/IpTcpRemoteConnectionEndpoint.java | 18 ++++-------------- .../rce/RemoteXmppTcpConnectionEndpoints.java | 6 +++--- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index cdf05eef4..e9b0d9b11 100644 --- a/build.gradle +++ b/build.gradle @@ -123,7 +123,7 @@ allprojects { // - 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' + miniDnsVersion = '0.4.0-alpha5' smackMinAndroidSdk = 19 junitVersion = '5.6.0' commonsIoVersion = '2.6' 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 713509800..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, UInt16 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, 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 cf5ce3e25..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 @@ -62,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) { @@ -75,7 +75,7 @@ public class RemoteXmppTcpConnectionEndpoints { discoveredRemoteConnectionEndpoints = new ArrayList<>(hostAddresses.size()); UInt16 port = config.getPort(); for (InetAddress inetAddress : hostAddresses) { - IpTcpRemoteConnectionEndpoint connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( + IpTcpRemoteConnectionEndpoint> connectionEndpoint = IpTcpRemoteConnectionEndpoint.from( host, port, inetAddress); discoveredRemoteConnectionEndpoints.add(connectionEndpoint); } @@ -215,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); } } From f750c79392ab72697be4e7c5223b2ef41de7a6b5 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 13:14:14 +0200 Subject: [PATCH 32/40] Bump jxmpp Version to 0.7.0-alpha6 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e9b0d9b11..89271358c 100644 --- a/build.gradle +++ b/build.gradle @@ -122,7 +122,7 @@ allprojects { // See also: // - https://issues.apache.org/jira/browse/MNG-6232 // - https://issues.igniterealtime.org/browse/SMACK-858 - jxmppVersion = '0.7.0-alpha5' + jxmppVersion = '0.7.0-alpha6' miniDnsVersion = '0.4.0-alpha5' smackMinAndroidSdk = 19 junitVersion = '5.6.0' From f3207e8c27d66bbe0cbdca9440b05ec490e12484 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 13:14:26 +0200 Subject: [PATCH 33/40] Bump junit version to 5.6.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 89271358c..b9de6f288 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ allprojects { 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' From 1c0bdfae40f9c2f068795f023bbd0f31f0132a8d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 18:01:00 +0200 Subject: [PATCH 34/40] [experimental] Delcare methods as static when possible --- .../smackx/fallback_indication/FallbackIndicationManager.java | 4 ++-- .../smackx/message_retraction/MessageRetractionManager.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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..25829bd63 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 @@ -139,7 +139,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 +149,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/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()) From d65f2c932e38ea63f8b2600feb2a9fdd22b686fa Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 24 May 2020 21:07:56 +0200 Subject: [PATCH 35/40] Bump Error Prone version to 2.3.4 and fix new bug patterns --- build.gradle | 2 +- .../smack/AbstractXMPPConnection.java | 13 +- .../org/jivesoftware/smack/util/TLSUtils.java | 13 +- .../jivesoftware/smack/util/DnsUtilTest.java | 1 + .../FallbackIndicationManager.java | 26 +- .../httpfileupload/HttpFileUploadManager.java | 4 +- .../sid/StableUniqueStanzaIdManager.java | 16 +- .../smackx/sid/element/OriginIdElement.java | 4 +- .../smackx/sid/StableUniqueStanzaIdTest.java | 2 +- .../smackx/caps/EntityCapsManager.java | 9 +- .../smackx/commands/AdHocCommandManager.java | 4 +- .../jivesoftware/smackx/ping/PingManager.java | 11 +- .../receipts/DeliveryReceiptManager.java | 10 +- .../rosterstore/DirectoryRosterStore.java | 20 +- .../chatstate/ChatStateIntegrationTest.java | 31 +- .../smackx/omemo/OmemoManager.java | 194 +++--- .../smackx/ox/OpenPgpManager.java | 148 +++-- .../ox_im/OXInstantMessagingManager.java | 14 +- .../smack/tcp/XmppTcpTransportModule.java | 562 +++++++++--------- 19 files changed, 499 insertions(+), 585 deletions(-) diff --git a/build.gradle b/build.gradle index b9de6f288..96dcab393 100644 --- a/build.gradle +++ b/build.gradle @@ -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') } 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 f93c48408..eed428deb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -373,17 +373,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 */ @@ -1524,7 +1513,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; 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..69b817566 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 @@ -38,7 +38,6 @@ 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; @@ -143,14 +142,6 @@ public class TLSUtils { 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 +155,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; } 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-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 25829bd63..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); } 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 9a790dbc8..8a12f164c 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 @@ -307,7 +307,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(); 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/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 a83c24875..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 @@ -54,7 +54,6 @@ 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; @@ -312,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); @@ -402,7 +401,7 @@ public final class EntityCapsManager extends Manager { } public synchronized void enableEntityCaps() { - connection().addPresenceInterceptor(presenceInterceptor, p -> { + connection().addPresenceInterceptor(this::addCapsExtension, p -> { return PresenceTypeFilter.AVAILABLE.accept(p); }); @@ -415,7 +414,7 @@ public final class EntityCapsManager extends Manager { entityCapsEnabled = false; sdm.removeFeature(NAMESPACE); - connection().removePresenceInterceptor(presenceInterceptor); + connection().removePresenceInterceptor(this::addCapsExtension); } public boolean entityCapsEnabled() { 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/ping/PingManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java index f30e58a9e..7f513802c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java @@ -390,7 +390,7 @@ public final class PingManager extends Manager { int nextPingIn = pingInterval - delta; LOGGER.fine("Scheduling ServerPingTask in " + nextPingIn + " seconds (pingInterval=" + pingInterval + ", delta=" + delta + ")"); - nextAutomaticPing = schedule(pingServerRunnable, nextPingIn, TimeUnit.SECONDS); + nextAutomaticPing = schedule(this::pingServerIfNecessary, nextPingIn, TimeUnit.SECONDS); } } @@ -467,13 +467,4 @@ public final class PingManager extends Manager { } }); } - - private final Runnable pingServerRunnable = new Runnable() { - @Override - public void run() { - LOGGER.fine("ServerPingTask run()"); - pingServerIfNecessary(); - } - }; - } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java index b069709e1..972c5d81e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/receipts/DeliveryReceiptManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2013-2014 Georg Lukas, 2015-2019 Florian Schmaus + * Copyright 2013-2014 Georg Lukas, 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. @@ -38,11 +38,9 @@ import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.MessageBuilder; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StanzaBuilder; import org.jivesoftware.smack.roster.Roster; -import org.jivesoftware.smack.util.Consumer; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; @@ -273,8 +271,6 @@ public final class DeliveryReceiptManager extends Manager { ); // @formatter:on - private static final Consumer 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-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-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-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java index 744bd6b89..88971cae8 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Paul Schaub + * Copyright 2017 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. @@ -37,17 +37,14 @@ import java.util.logging.Logger; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.MessageBuilder; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.util.Async; -import org.jivesoftware.smackx.carbons.CarbonCopyReceivedListener; import org.jivesoftware.smackx.carbons.CarbonManager; import org.jivesoftware.smackx.carbons.packet.CarbonExtension; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; @@ -74,7 +71,6 @@ import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; import org.jivesoftware.smackx.omemo.trust.OmemoTrustCallback; import org.jivesoftware.smackx.omemo.trust.TrustState; import org.jivesoftware.smackx.omemo.util.MessageOrOmemoMessage; -import org.jivesoftware.smackx.pep.PepListener; import org.jivesoftware.smackx.pep.PepManager; import org.jivesoftware.smackx.pubsub.EventElement; import org.jivesoftware.smackx.pubsub.ItemsExtension; @@ -897,23 +893,23 @@ public final class OmemoManager extends Manager { 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); + pepManager.removePepListener(this::deviceListUpdateListener); // Add listeners - pepManager.addPepListener(deviceListUpdateListener); - connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter); - carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); + pepManager.addPepListener(this::deviceListUpdateListener); + 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.getInstanceFor(connection()).removePepListener(this::deviceListUpdateListener); + connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener); + CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); } /** @@ -961,127 +957,113 @@ 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); - } - } - } - }); - } - }; + } + }); + } /** * PEPListener that listens for OMEMO deviceList updates. */ - private final PepListener deviceListUpdateListener = new PepListener() { - @Override - public void eventReceived(EntityBareJid from, EventElement event, Message message) { + private void deviceListUpdateListener(EntityBareJid from, EventElement event, Message message) { + // Unknown sender, no more work to do. + if (from == null) { + // TODO: This DOES happen for some reason. Figure out when... + return; + } - // Unknown sender, no more work to do. - if (from == null) { - // TODO: This DOES happen for some reason. Figure out when... - return; + for (ExtensionElement items : event.getExtensions()) { + if (!(items instanceof ItemsExtension)) { + continue; } - for (ExtensionElement items : event.getExtensions()) { - if (!(items instanceof ItemsExtension)) { + for (ExtensionElement item : ((ItemsExtension) items).getExtensions()) { + if (!(item instanceof PayloadItem)) { continue; } - for (ExtensionElement item : ((ItemsExtension) items).getExtensions()) { - if (!(item instanceof PayloadItem)) { + 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; } - PayloadItem payloadItem = (PayloadItem) item; + deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice()); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, + "IOException while processing OMEMO PEP device updates. Message: " + message, + e); + continue; + } + final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList); - if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement)) { - continue; - } - - // Device List - OmemoCachedDeviceList deviceList; - OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload(); - try { - getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, - receivedDeviceList); - - if (!from.asBareJid().equals(getOwnJid())) { - continue; - } - - deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice()); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, - "IOException while processing OMEMO PEP device updates. Message: " + message, - e); - continue; - } - final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList); - - if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) { - LOGGER.log(Level.FINE, "Republish deviceList due to changes:" + - " Received: " + Arrays.toString(receivedDeviceList.copyDeviceIds().toArray()) + - " Published: " + Arrays.toString(newDeviceList.copyDeviceIds().toArray())); - Async.go(new Runnable() { - @Override - public void run() { - try { - OmemoService.publishDeviceList(connection(), newDeviceList); - } catch (InterruptedException | XMPPException.XMPPErrorException | - SmackException.NotConnectedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException e) { - LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e); - } + 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-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java index d16296a05..25be6ee16 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; @@ -180,7 +179,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 +278,7 @@ public final class OpenPgpManager extends Manager { publishPublicKey(pepManager, pubkeyElement, primaryFingerprint); // Subscribe to public key changes - PepManager.getInstanceFor(connection()).addPepListener(metadataListener); + PepManager.getInstanceFor(connection()).addPepListener(this::metadataListener); ServiceDiscoveryManager.getInstanceFor(connection()) .addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY); } @@ -381,7 +380,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.getInstanceFor(connection()).removePepListener(this::metadataListener); } /** @@ -502,25 +501,22 @@ public final class OpenPgpManager extends Manager { * * @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(); + private void metadataListener(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"); - } + processPublicKeysListElement(from, listElement); + } + }, "ProcessOXMetadata"); } - }; + } private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) { OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible()); @@ -548,62 +544,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-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XmppTcpTransportModule.java index ead3a6438..3df54dc1a 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,6 +21,7 @@ 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; @@ -61,7 +62,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; @@ -280,294 +280,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 +675,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM } @Override - public Stats getStats() { + public XmppTcpTransportModule.Stats getStats() { return XmppTcpTransportModule.this.getStats(); } @@ -774,7 +772,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); @@ -1316,7 +1314,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM pendingOutputFilterData = true; } - channelSelectedCallback.onChannelSelected(channel, key); + onChannelSelected(channel, key); } finally { channelSelectedCallbackLock.unlock(); } @@ -1347,7 +1345,7 @@ public class XmppTcpTransportModule extends ModularXmppClientToServerConnectionM return CollectionUtil.removeUntil(buffers, b -> b.hasRemaining()); } - public Stats getStats() { + public XmppTcpTransportModule.Stats getStats() { return new Stats(this); } From 70188dbe574a43a62690b3f24dd93363bbf0bf4f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 25 May 2020 11:14:47 +0200 Subject: [PATCH 36/40] [travis] Only run javadocAll on Java 11 or higher It appears that 'javadoc' from older JREs is not able to consume the javadoc generated by newer JREs: javadoc: warning - Error fetching URL: https://jxmpp.org/releases/0.7.0-alpha6/javadoc/ javadoc: warning - Error fetching URL: https://minidns.org/releases/0.4.0-alpha5/javadoc/ --- .travis.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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) From 7156849c77a7dffb014c35df58115eda6d316a83 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 25 May 2020 13:51:25 +0200 Subject: [PATCH 37/40] [core] Set default SecurityMode to 'required' in ConnectionConfiguration --- .../java/org/jivesoftware/smack/ConnectionConfiguration.java | 2 +- .../java/org/jivesoftware/smack/DummyConnection.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 e0b24a08f..89b6ec278 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -600,7 +600,7 @@ 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(); 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 b49ab8d3e..d0864842b 100644 --- a/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/testFixtures/java/org/jivesoftware/smack/DummyConnection.java @@ -225,6 +225,7 @@ public class DummyConnection extends AbstractXMPPConnection { ConnectionConfiguration.Builder { private Builder() { + setSecurityMode(SecurityMode.disabled); } @Override From f5448c5faa94371192861b77aa297b5f6b357cd5 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 25 May 2020 14:47:36 +0200 Subject: [PATCH 38/40] [core] Rework TLS logic This moves the logic in AbstractXMPPConnection.getSmackTlsContext() into the ConnectionConfiguration constructor. Also introduce SslContextFactory and use it in ConnectionConfiguration. --- .../smack/AbstractXMPPConnection.java | 175 +------------ .../smack/ConnectionConfiguration.java | 234 +++++++++++++----- .../ModularXmppClientToServerConnection.java | 10 +- ...rXmppClientToServerConnectionInternal.java | 13 +- .../smack/internal/SmackTlsContext.java | 32 +++ .../smack/util/SslContextFactory.java | 25 ++ .../org/jivesoftware/smack/util/TLSUtils.java | 33 +-- .../httpfileupload/HttpFileUploadManager.java | 6 - .../smack/inttest/AbstractSmackIntTest.java | 4 +- .../smack/inttest/Configuration.java | 12 +- .../HttpFileUploadIntegrationTest.java | 4 +- .../smack/smackrepl/TlsTest.java | 2 +- .../smack/tcp/XMPPTCPConnection.java | 1 + .../smack/tcp/XmppTcpTransportModule.java | 15 +- 14 files changed, 252 insertions(+), 314 deletions(-) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/internal/SmackTlsContext.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/util/SslContextFactory.java 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 eed428deb..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; @@ -126,19 +103,14 @@ import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.sasl.core.SASLAnonymous; import org.jivesoftware.smack.sasl.packet.SaslNonza; import org.jivesoftware.smack.util.Async; -import org.jivesoftware.smack.util.CloseableUtil; 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; @@ -2203,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 { - 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); - } - } - } - } - - 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 89b6ec278..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,21 +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; @@ -104,11 +132,6 @@ public abstract class ConnectionConfiguration { protected final DnsName host; protected final UInt16 port; - private final String keystorePath; - private final String keystoreType; - private final String pkcs11Library; - private final SSLContext customSSLContext; - /** * Used to get information from the user */ @@ -138,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; /** * @@ -166,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; @@ -202,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; @@ -223,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() { @@ -287,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. * @@ -602,10 +690,10 @@ public abstract class ConnectionConfiguration { public abstract static class Builder, C extends ConnectionConfiguration> { 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; @@ -929,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(); } @@ -1172,5 +1279,4 @@ public abstract class ConnectionConfiguration { protected abstract B getThis(); } - } 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 eff02dedb..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; @@ -66,6 +61,7 @@ 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; @@ -186,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(); } 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/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/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 69b817566..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,12 +34,9 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -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; @@ -131,14 +124,10 @@ 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; } @@ -267,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-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java index 8a12f164c..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; @@ -428,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-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/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/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-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 3df54dc1a..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 @@ -24,11 +24,6 @@ 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; @@ -50,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; @@ -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; @@ -867,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); From 15499ad1f879849743493f841a0f98100fce633d Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 25 May 2020 16:16:56 +0200 Subject: [PATCH 39/40] =?UTF-8?q?[openpgp]=20Remove=20info=20log=20"Receiv?= =?UTF-8?q?ed=20OpenPPG=20metadata=20update=20from=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/jivesoftware/smackx/ox/OpenPgpManager.java | 2 -- 1 file changed, 2 deletions(-) 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 25be6ee16..9cf2783ee 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 @@ -503,8 +503,6 @@ public final class OpenPgpManager extends Manager { */ private void metadataListener(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() { From ccc785062e9b04513302c898e3015c2ff2d7fb91 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 25 May 2020 20:32:47 +0200 Subject: [PATCH 40/40] [extensions] Deprecate old-style PepManager PEP listeners --- .../jivesoftware/smackx/pep/PepManager.java | 6 + .../smackx/omemo/OmemoManager.java | 104 ++++++------------ .../smackx/omemo/util/OmemoConstants.java | 1 - .../smackx/ox/OpenPgpManager.java | 31 +----- 4 files changed, 48 insertions(+), 94 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java index 20b009287..404da53fb 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pep/PepManager.java @@ -253,7 +253,10 @@ public final class PepManager extends Manager { * * @param pepListener a roster exchange listener. * @return true if pepListener was added. + * @deprecated use {@link #addPepEventListener(String, Class, PepEventListener)} instead. */ + // TODO: Remove in Smack 4.5 + @Deprecated public boolean addPepListener(PepListener pepListener) { return pepListeners.add(pepListener); } @@ -263,7 +266,10 @@ public final class PepManager extends Manager { * * @param pepListener a roster exchange listener. * @return true, if pepListener was removed. + * @deprecated use {@link #removePepEventListener(PepEventListener)} instead. */ + // TODO: Remove in Smack 4.5. + @Deprecated public boolean removePepListener(PepListener pepListener) { return pepListeners.remove(pepListener); } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java index 88971cae8..35f7f407e 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java @@ -17,7 +17,6 @@ package org.jivesoftware.smackx.omemo; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL; -import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST_NOTIFY; import java.io.IOException; import java.security.NoSuchAlgorithmException; @@ -39,7 +38,6 @@ import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.MessageBuilder; import org.jivesoftware.smack.packet.Stanza; @@ -71,10 +69,9 @@ import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; import org.jivesoftware.smackx.omemo.trust.OmemoTrustCallback; import org.jivesoftware.smackx.omemo.trust.TrustState; import org.jivesoftware.smackx.omemo.util.MessageOrOmemoMessage; +import org.jivesoftware.smackx.omemo.util.OmemoConstants; +import org.jivesoftware.smackx.pep.PepEventListener; import org.jivesoftware.smackx.pep.PepManager; -import org.jivesoftware.smackx.pubsub.EventElement; -import org.jivesoftware.smackx.pubsub.ItemsExtension; -import org.jivesoftware.smackx.pubsub.PayloadItem; import org.jivesoftware.smackx.pubsub.PubSubException; import org.jivesoftware.smackx.pubsub.packet.PubSub; @@ -101,6 +98,8 @@ public final class OmemoManager extends Manager { private final HashSet omemoMessageListeners = new HashSet<>(); private final HashSet omemoMucMessageListeners = new HashSet<>(); + private final PepManager pepManager; + private OmemoTrustCallback trustCallback; private BareJid ownJid; @@ -116,6 +115,7 @@ public final class OmemoManager extends Manager { super(connection); service = OmemoService.getInstance(); + pepManager = PepManager.getInstanceFor(connection); this.deviceId = deviceId; @@ -134,9 +134,6 @@ public final class OmemoManager extends Manager { // StanzaListeners resumeStanzaAndPEPListeners(); - - // Announce OMEMO support - ServiceDiscoveryManager.getInstanceFor(connection).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); } /** @@ -247,7 +244,6 @@ public final class OmemoManager extends Manager { } getOmemoService().init(new LoggedInOmemoManager(this)); - ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); } /** @@ -889,16 +885,14 @@ public final class OmemoManager extends Manager { * after {@link #stopStanzaAndPEPListeners()} was called. */ public void resumeStanzaAndPEPListeners() { - PepManager pepManager = PepManager.getInstanceFor(connection()); CarbonManager carbonManager = CarbonManager.getInstanceFor(connection()); // Remove listeners to avoid them getting added twice connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener); carbonManager.removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); - pepManager.removePepListener(this::deviceListUpdateListener); // Add listeners - pepManager.addPepListener(this::deviceListUpdateListener); + pepManager.addPepEventListener(OmemoConstants.PEP_NODE_DEVICE_LIST, OmemoDeviceListElement.class, pepOmemoDeviceListEventListener); connection().addAsyncStanzaListener(this::internalOmemoMessageStanzaListener, OmemoManager::isOmemoMessage); carbonManager.addCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); } @@ -907,7 +901,7 @@ public final class OmemoManager extends Manager { * Remove active stanza listeners needed for OMEMO. */ public void stopStanzaAndPEPListeners() { - PepManager.getInstanceFor(connection()).removePepListener(this::deviceListUpdateListener); + pepManager.removePepEventListener(pepOmemoDeviceListEventListener); connection().removeAsyncStanzaListener(this::internalOmemoMessageStanzaListener); CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(this::internalOmemoCarbonCopyListener); } @@ -992,71 +986,45 @@ public final class OmemoManager extends Manager { }); } - /** - * PEPListener that listens for OMEMO deviceList updates. - */ - private void deviceListUpdateListener(EntityBareJid from, EventElement event, Message message) { - // Unknown sender, no more work to do. - if (from == null) { - // TODO: This DOES happen for some reason. Figure out when... - return; - } - - for (ExtensionElement items : event.getExtensions()) { - if (!(items instanceof ItemsExtension)) { - continue; - } - - for (ExtensionElement item : ((ItemsExtension) items).getExtensions()) { - if (!(item instanceof PayloadItem)) { - continue; - } - - PayloadItem payloadItem = (PayloadItem) item; - - if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement)) { - continue; - } - - // Device List - OmemoCachedDeviceList deviceList; - OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload(); - try { - getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, + @SuppressWarnings("UnnecessaryLambda") + private final PepEventListener pepOmemoDeviceListEventListener = + (from, receivedDeviceList, id, message) -> { + // Device List + OmemoCachedDeviceList deviceList; + try { + getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, receivedDeviceList); - if (!from.asBareJid().equals(getOwnJid())) { - continue; - } + if (!from.asBareJid().equals(getOwnJid())) { + return; + } - deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice()); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, + 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); + e); + return; + } + final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList); - if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) { - LOGGER.log(Level.FINE, "Republish deviceList due to changes:" + + 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 | + 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); - } - } - }); + LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e); + } } - } + }); } - } + }; /** * StanzaFilter that filters messages containing a OMEMO element. 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 9cf2783ee..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 @@ -68,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; @@ -172,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}. * @@ -278,7 +279,7 @@ public final class OpenPgpManager extends Manager { publishPublicKey(pepManager, pubkeyElement, primaryFingerprint); // Subscribe to public key changes - PepManager.getInstanceFor(connection()).addPepListener(this::metadataListener); + pepManager.addPepEventListener(PEP_NODE_PUBLIC_KEYS, PublicKeysListElement.class, pepPublicKeyListElementListener); ServiceDiscoveryManager.getInstanceFor(connection()) .addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY); } @@ -380,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(this::metadataListener); + pepManager.removePepEventListener(pepPublicKeyListElementListener); } /** @@ -496,26 +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 void metadataListener(final EntityBareJid from, final EventElement event, final Message message) { - if (PEP_NODE_PUBLIC_KEYS.equals(event.getEvent().getNode())) { - 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 {