From af5f161fff5b30bc3f87e6a713786226d9ef664b Mon Sep 17 00:00:00 2001 From: adiaholic Date: Fri, 26 Apr 2019 20:30:26 +0530 Subject: [PATCH 01/23] Prevent extremely long reply timeouts from being set. Smack will throw an IllegalArguementException if extremely long reply timeouts are tried to be set. I assumed currentTimeMilli() to be the boundary condition as per SMACK-718 --- .../org/jivesoftware/smack/AbstractXMPPConnection.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 dfe4d5662..dbbf0e7d5 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -1146,7 +1146,12 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { @Override public void setReplyTimeout(long timeout) { - replyTimeout = timeout; + if (Long.MAX_VALUE - System.currentTimeMillis() < timeout) { + throw new IllegalArgumentException("Extremely long reply timeout"); + } + else { + replyTimeout = timeout; + } } private SmackConfiguration.UnknownIqRequestReplyMode unknownIqRequestReplyMode = SmackConfiguration.getUnknownIqRequestReplyMode(); From 8831961afeda11e9055ff105da3352d3bd0e91e5 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 27 May 2019 11:16:33 +0200 Subject: [PATCH 02/23] Smack 4.3.4 --- resources/releasedocs/changelog.html | 26 ++++++++++++++++++++++++++ version.gradle | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html index 706a4e2c6..e11abe56a 100644 --- a/resources/releasedocs/changelog.html +++ b/resources/releasedocs/changelog.html @@ -141,6 +141,32 @@ hr {
+

4.3.4 -- 2019-05-27

+ +

Bug +

+
    +
  • [SMACK-861] - Potential NPE in Roster.getPresencesInternal(BareJid) +
  • +
  • [SMACK-863] - ServiceDiscoveryManger does not use the main identity, causing setIdentity() to have no effect +
  • +
  • [SMACK-864] - Potential Denial of Service (DOS) by remote entities caused by unlimited threads for asynchronous operations +
  • +
  • [SMACK-865] - Some Manager.getInsanceFor() methods are missing the 'synchronized' keyword +
  • +
  • [SMACK-868] - XHTMLText.appendOpenBodyTag() produces invalid XML +
  • +
  • [SMACK-870] - TLS X.509 certificate verification should be performed with the ACE representation of the XMPP service domain when possible +
  • +
+ +

Improvement +

+
    +
  • [SMACK-869] - Exceptions in async tasks should not go uncaught to the call stack to avoid program termination +
  • +
+

4.3.3 -- 2019-03-14

Bug diff --git a/version.gradle b/version.gradle index 82e39bdf3..e24c93d15 100644 --- a/version.gradle +++ b/version.gradle @@ -1,7 +1,7 @@ allprojects { ext { shortVersion = '4.3.4' - isSnapshot = true + isSnapshot = false // When using dynamic versions for those, do *not* use [1.0, // 2.0), since this will also pull in 2.0-alpha1. Instead use // [1.0, 1.0.99]. From b8ebaf3aa7246f4459659a849ae5b8451e09466e Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 27 May 2019 11:45:54 +0200 Subject: [PATCH 03/23] Smack 4.3.5-SNAPSHOT --- version.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.gradle b/version.gradle index e24c93d15..3acaa256d 100644 --- a/version.gradle +++ b/version.gradle @@ -1,7 +1,7 @@ allprojects { ext { - shortVersion = '4.3.4' - isSnapshot = false + shortVersion = '4.3.5' + isSnapshot = true // When using dynamic versions for those, do *not* use [1.0, // 2.0), since this will also pull in 2.0-alpha1. Instead use // [1.0, 1.0.99]. From 0ad1a0bd4c78264006bb38c70f6d5cece642068b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 3 Jul 2019 23:37:51 +0200 Subject: [PATCH 04/23] Add note about BookmarkManager to MUCManager --- .../java/org/jivesoftware/smackx/muc/MultiUserChatManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java index 33e36e76b..40e5efd89 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChatManager.java @@ -282,6 +282,9 @@ public final class MultiUserChatManager extends Manager { * Returns a Set of the rooms where the user has joined. The Iterator will contain Strings where each String * represents a room (e.g. room@muc.jabber.org). * + * Note: In order to get a list of bookmarked (but not necessarily joined) conferences, use + * {@link org.jivesoftware.smackx.bookmarks.BookmarkManager#getBookmarkedConferences()}. + * * @return a List of the rooms where the user has joined using a given connection. */ public Set getJoinedRooms() { From b8cf599691164b4dfed7c0bb7c8ba44a38aa478a Mon Sep 17 00:00:00 2001 From: Benjamin JALON Date: Sat, 3 Aug 2019 11:42:25 +0200 Subject: [PATCH 05/23] Update providers.md --- documentation/providers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/providers.md b/documentation/providers.md index f5e57e705..8eb6a5108 100644 --- a/documentation/providers.md +++ b/documentation/providers.md @@ -46,9 +46,9 @@ ProviderManager.addExtensionProvider("element", "namespace", new MyExtProvider() * Add a loader - You can add a ProviderLoader which will inject a means of loading multiple providers (both types) into the manager. This is the mechanism used by Smack to load from the Smack specific file format (via ProviderFileLoader). Implementers can provide the means to load providers from any source they wish, or simply reuse the ProviderFileLoader to load from their own provider files. - +``` ProviderManager.addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:com/myco/provider/myco_custom.providers", null))); - +``` * VM Argument - You can add a provider file via the VM argument _smack.provider.file_. This will load the file at the specified URL during startup when Smack initializes. This also assumes the default configuration, since it requires that the **VmArgInitializer** was part of the startup configuration. From afa5c0f5ad9a50a2704c3582c8a693f8f9629d5d Mon Sep 17 00:00:00 2001 From: Benjamin JALON Date: Sat, 3 Aug 2019 11:44:46 +0200 Subject: [PATCH 06/23] Update processing.md --- documentation/processing.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentation/processing.md b/documentation/processing.md index 10d6a66ae..c5c246713 100644 --- a/documentation/processing.md +++ b/documentation/processing.md @@ -20,7 +20,8 @@ and a stanza listener: ``` // Create a stanza filter to listen for new messages from a particular // user. We use an AndFilter to combine two other filters._ -StanzaFilter filter = new AndFilter(StanzaTypeFilter.MESSAGE, FromMatchesFilter.create("mary@jivesoftware.com")); +StanzaFilter filter = new AndFilter(StanzaTypeFilter.MESSAGE, + FromMatchesFilter.create(JidCreate.entityBareFrom("mary@jivesoftware.com"))); // Assume we've created an XMPPConnection named "connection". // First, register a stanza collector using the filter we created. @@ -29,7 +30,7 @@ StanzaCollector myCollector = connection.createStanzaCollector(filter); // Next, create a stanza listener. We use an anonymous inner class for brevity. StanzaListener myListener = new StanzaListener() { - **public** **void** processStanza(Stanza stanza) { + public void processStanza(Stanza stanza) { // Do something with the incoming stanza here._ } }; From f7a699af8a108b4daef2410f63c458e169aa19a4 Mon Sep 17 00:00:00 2001 From: Benjamin JALON Date: Sun, 4 Aug 2019 14:35:04 +0200 Subject: [PATCH 07/23] Update gettingstarted.md --- documentation/gettingstarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/gettingstarted.md b/documentation/gettingstarted.md index 143b2d094..93125e353 100644 --- a/documentation/gettingstarted.md +++ b/documentation/gettingstarted.md @@ -62,7 +62,7 @@ XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() .setPort(8222) .build(); -AbstractXMPPConnection conn2 = **new** XMPPTCPConnection(config); +AbstractXMPPConnection conn2 = new XMPPTCPConnection(config); conn2.connect().login(); ``` From 144e99c0203e817184d48392ce804d07a8caf5e8 Mon Sep 17 00:00:00 2001 From: adiaholic Date: Fri, 2 Aug 2019 11:20:37 +0530 Subject: [PATCH 08/23] DocFix in `NodeInformationProvider` --- .../org/jivesoftware/smackx/disco/NodeInformationProvider.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/NodeInformationProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/NodeInformationProvider.java index cc8a8a5a7..3b2587d2e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/NodeInformationProvider.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/NodeInformationProvider.java @@ -28,8 +28,7 @@ import org.jivesoftware.smackx.disco.packet.DiscoverItems; /** * The NodeInformationProvider is responsible for providing supported identities, features * and hosted items (i.e. DiscoverItems.Item) about a given node. This information will be - * requested each time this XMPPP client receives a disco info or items requests on the - * given node. each time this XMPPP client receives a disco info or items requests on the + * requested each time this XMPP client receives a disco info or items requests on the * given node. * * @author Gaston Dombiak From ca7529c1920acaa1ec1226d2cedaa6a15c70a43a Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 4 Aug 2019 23:03:42 +0200 Subject: [PATCH 09/23] Use Integer.compare(int, int) now that we are on Android 19 --- .../main/java/org/jivesoftware/smack/sasl/SASLMechanism.java | 3 +-- .../smackx/disco/DiscoInfoLookupShortcutMechanism.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java index e86229078..bfd13958e 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/SASLMechanism.java @@ -257,9 +257,8 @@ public abstract class SASLMechanism implements Comparable { @Override public final int compareTo(SASLMechanism other) { - // Switch to Integer.compare(int, int) once Smack is on Android 19 or higher. Integer ourPriority = getPriority(); - return ourPriority.compareTo(other.getPriority()); + return Integer.compare(ourPriority, other.getPriority()); } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java index 1404f97e8..eaf525f11 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/DiscoInfoLookupShortcutMechanism.java @@ -47,8 +47,7 @@ public abstract class DiscoInfoLookupShortcutMechanism implements Comparable Date: Sun, 4 Aug 2019 23:04:00 +0200 Subject: [PATCH 10/23] Use HttpURLConnection.setFixedLengthStreamingMode(long) now that we are on Android 19. --- .../smackx/httpfileupload/HttpFileUploadManager.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) 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 88b4ecc8e..eb467dd0f 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 @@ -387,11 +387,6 @@ public final class HttpFileUploadManager extends Manager { private void uploadFile(final File file, final Slot slot, UploadProgressListener listener) throws IOException { final long fileSize = file.length(); - // TODO Remove once Smack's minimum Android API level is 19 or higher. See also comment below. - if (fileSize >= Integer.MAX_VALUE) { - throw new IllegalArgumentException("File size " + fileSize + " must be less than " + Integer.MAX_VALUE); - } - final int fileSizeInt = (int) fileSize; // Construct the FileInputStream first to make sure we can actually read the file. final FileInputStream fis = new FileInputStream(file); @@ -403,8 +398,7 @@ public final class HttpFileUploadManager extends Manager { urlConnection.setRequestMethod("PUT"); urlConnection.setUseCaches(false); urlConnection.setDoOutput(true); - // TODO Change to using fileSize once Smack's minimum Android API level is 19 or higher. - urlConnection.setFixedLengthStreamingMode(fileSizeInt); + urlConnection.setFixedLengthStreamingMode(fileSize); urlConnection.setRequestProperty("Content-Type", "application/octet-stream;"); for (Entry header : slot.getHeaders().entrySet()) { urlConnection.setRequestProperty(header.getKey(), header.getValue()); From 1bce378e6ddeeb3aa5842c1c1c41263aae09fe91 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 5 Aug 2019 08:28:42 +0200 Subject: [PATCH 11/23] smack-omemo*: Do not swallow IOException deep within the library Those exception are caused by I/O operations in the OmemoStore, which is now declaring that it throws those (since it is not uncommon for I/O operations to cause IOExceptions). After all, this is nicely demonstrated as this change is caused by switching with this commit to the Android API 19 compatible methods in FileBasedOmemoStore, which throw. The library can not decide what to do in case of those exceptions, hence it is sensible to expose them to the user. --- .../AbstractTwoUsersOmemoIntegrationTest.java | 3 +- .../smackx/omemo/OmemoMamDecryptionTest.java | 3 +- .../smackx/omemo/OmemoManagerSetupHelper.java | 7 +- .../omemo/ReadOnlyDeviceIntegrationTest.java | 6 +- .../omemo/signal/SignalOmemoRatchet.java | 3 +- .../signal/SignalOmemoStoreConnector.java | 62 ++++- .../smackx/omemo/CachingOmemoStore.java | 47 ++-- .../smackx/omemo/FileBasedOmemoStore.java | 232 +++--------------- .../smackx/omemo/OmemoManager.java | 71 ++++-- .../smackx/omemo/OmemoRatchet.java | 7 +- .../smackx/omemo/OmemoService.java | 65 +++-- .../jivesoftware/smackx/omemo/OmemoStore.java | 105 +++++--- ...OmemoCarbonCopyStanzaReceivedListener.java | 5 +- .../OmemoMessageStanzaReceivedListener.java | 4 +- .../omemo/util/OmemoMessageBuilder.java | 4 +- .../smackx/omemo/OmemoStoreTest.java | 6 +- 16 files changed, 299 insertions(+), 331 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java index 4903fe72b..68cd5f30c 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/AbstractTwoUsersOmemoIntegrationTest.java @@ -19,6 +19,7 @@ package org.jivesoftware.smackx.omemo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import java.io.IOException; import java.util.logging.Level; import org.jivesoftware.smack.SmackException; @@ -64,7 +65,7 @@ public abstract class AbstractTwoUsersOmemoIntegrationTest extends AbstractOmemo } @AfterClass - public void cleanUp() { + public void cleanUp() throws IOException { alice.stopStanzaAndPEPListeners(); bob.stopStanzaAndPEPListeners(); OmemoManagerSetupHelper.cleanUpPubSub(alice); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java index 2c3017e74..b562db8f8 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoMamDecryptionTest.java @@ -18,6 +18,7 @@ package org.jivesoftware.smackx.omemo; import static org.junit.Assert.assertEquals; +import java.io.IOException; import java.util.List; import org.jivesoftware.smack.SmackException; @@ -50,7 +51,7 @@ public class OmemoMamDecryptionTest extends AbstractTwoUsersOmemoIntegrationTest @SmackIntegrationTest public void mamDecryptionTest() throws XMPPException.XMPPErrorException, SmackException.NotLoggedInException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, - CryptoFailedException, UndecidedOmemoIdentityException { + CryptoFailedException, UndecidedOmemoIdentityException, IOException { // Make sure, Bobs server stores messages in the archive MamManager bobsMamManager = MamManager.getInstanceFor(bob.getConnection()); bobsMamManager.enableMamForAllMessages(); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java index 83234924d..97fa93820 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.HashMap; import org.jivesoftware.smack.SmackException; @@ -46,7 +47,7 @@ public class OmemoManagerSetupHelper { public static void trustAllIdentities(OmemoManager alice, OmemoManager bob) throws InterruptedException, SmackException.NotConnectedException, SmackException.NotLoggedInException, SmackException.NoResponseException, CannotEstablishOmemoSessionException, CorruptedOmemoKeyException, - XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException { + XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException, IOException { Roster roster = Roster.getInstanceFor(alice.getConnection()); if (alice.getOwnJid() != bob.getOwnJid() && @@ -66,7 +67,7 @@ public class OmemoManagerSetupHelper { public static void trustAllIdentitiesWithTests(OmemoManager alice, OmemoManager bob) throws InterruptedException, SmackException.NotConnectedException, SmackException.NotLoggedInException, SmackException.NoResponseException, CannotEstablishOmemoSessionException, CorruptedOmemoKeyException, - XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException { + XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException, IOException { alice.requestDeviceListUpdateFor(bob.getOwnJid()); HashMap fps1 = alice.getActiveFingerprints(bob.getOwnJid()); @@ -124,7 +125,7 @@ public class OmemoManagerSetupHelper { } } - public static void cleanUpPubSub(OmemoManager omemoManager) { + public static void cleanUpPubSub(OmemoManager omemoManager) throws IOException { PubSubManager pm = PubSubManager.getInstanceFor(omemoManager.getConnection(), omemoManager.getOwnJid()); try { omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid()); diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java index 3164d6668..815d4a032 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java @@ -20,6 +20,8 @@ import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; +import java.io.IOException; + import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException; @@ -37,7 +39,9 @@ public class ReadOnlyDeviceIntegrationTest extends AbstractTwoUsersOmemoIntegrat } @SmackIntegrationTest - public void test() throws InterruptedException, SmackException.NoResponseException, SmackException.NotLoggedInException, SmackException.NotConnectedException, CryptoFailedException, UndecidedOmemoIdentityException { + public void test() throws InterruptedException, SmackException.NoResponseException, + SmackException.NotLoggedInException, SmackException.NotConnectedException, CryptoFailedException, + UndecidedOmemoIdentityException, IOException { boolean prevIgnoreReadOnlyConf = OmemoConfiguration.getIgnoreReadOnlyDevices(); int prevMaxMessageCounter = OmemoConfiguration.getMaxReadOnlyMessageCount(); diff --git a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java index de20f69a9..c219b6f14 100644 --- a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java +++ b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java @@ -20,6 +20,7 @@ */ package org.jivesoftware.smackx.omemo.signal; +import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -73,7 +74,7 @@ public class SignalOmemoRatchet @Override public byte[] doubleRatchetDecrypt(OmemoDevice sender, byte[] encryptedKey) throws CorruptedOmemoKeyException, NoRawSessionException, CryptoFailedException, - UntrustedOmemoIdentityException { + UntrustedOmemoIdentityException, IOException { SessionCipher cipher = getCipher(sender); byte[] decryptedKey; diff --git a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java index 0f607603a..1aae64856 100644 --- a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java +++ b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java @@ -20,6 +20,7 @@ */ package org.jivesoftware.smackx.omemo.signal; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.TreeMap; @@ -79,7 +80,7 @@ public class SignalOmemoStoreConnector public IdentityKeyPair getIdentityKeyPair() { try { return omemoStore.loadOmemoIdentityKeyPair(getOurDevice()); - } catch (CorruptedOmemoKeyException e) { + } catch (CorruptedOmemoKeyException | IOException e) { LOGGER.log(Level.SEVERE, "IdentityKeyPair seems to be invalid.", e); return null; } @@ -103,7 +104,11 @@ public class SignalOmemoStoreConnector throw new AssertionError(e); } - omemoStore.storeOmemoIdentityKey(getOurDevice(), device, identityKey); + try { + omemoStore.storeOmemoIdentityKey(getOurDevice(), device, identityKey); + } catch (IOException e) { + throw new IllegalStateException(e); + } return true; } @@ -118,7 +123,12 @@ public class SignalOmemoStoreConnector @Override public PreKeyRecord loadPreKey(int i) throws InvalidKeyIdException { - PreKeyRecord preKey = omemoStore.loadOmemoPreKey(getOurDevice(), i); + PreKeyRecord preKey; + try { + preKey = omemoStore.loadOmemoPreKey(getOurDevice(), i); + } catch (IOException e) { + throw new IllegalStateException(e); + } if (preKey == null) { throw new InvalidKeyIdException("No PreKey with Id " + i + " found."); @@ -129,7 +139,11 @@ public class SignalOmemoStoreConnector @Override public void storePreKey(int i, PreKeyRecord preKeyRecord) { - omemoStore.storeOmemoPreKey(getOurDevice(), i, preKeyRecord); + try { + omemoStore.storeOmemoPreKey(getOurDevice(), i, preKeyRecord); + } catch (IOException e) { + throw new IllegalStateException(e); + } } @Override @@ -155,7 +169,12 @@ public class SignalOmemoStoreConnector throw new AssertionError(e); } - SessionRecord record = omemoStore.loadRawSession(getOurDevice(), device); + SessionRecord record; + try { + record = omemoStore.loadRawSession(getOurDevice(), device); + } catch (IOException e) { + throw new IllegalStateException(e); + } if (record != null) { return record; @@ -173,7 +192,11 @@ public class SignalOmemoStoreConnector throw new AssertionError(e); } - return new ArrayList<>(omemoStore.loadAllRawSessionsOf(getOurDevice(), jid).keySet()); + try { + return new ArrayList<>(omemoStore.loadAllRawSessionsOf(getOurDevice(), jid).keySet()); + } catch (IOException e) { + throw new IllegalStateException(e); + } } @Override @@ -185,7 +208,11 @@ public class SignalOmemoStoreConnector throw new AssertionError(e); } - omemoStore.storeRawSession(getOurDevice(), device, sessionRecord); + try { + omemoStore.storeRawSession(getOurDevice(), device, sessionRecord); + } catch (IOException e) { + throw new IllegalStateException(e); + } } @Override @@ -226,7 +253,12 @@ public class SignalOmemoStoreConnector @Override public SignedPreKeyRecord loadSignedPreKey(int i) throws InvalidKeyIdException { - SignedPreKeyRecord signedPreKeyRecord = omemoStore.loadOmemoSignedPreKey(getOurDevice(), i); + SignedPreKeyRecord signedPreKeyRecord; + try { + signedPreKeyRecord = omemoStore.loadOmemoSignedPreKey(getOurDevice(), i); + } catch (IOException e) { + throw new IllegalStateException(e); + } if (signedPreKeyRecord == null) { throw new InvalidKeyIdException("No signed preKey with id " + i + " found."); } @@ -236,14 +268,22 @@ public class SignalOmemoStoreConnector @Override public List loadSignedPreKeys() { - TreeMap signedPreKeyRecordHashMap = - omemoStore.loadOmemoSignedPreKeys(getOurDevice()); + TreeMap signedPreKeyRecordHashMap; + try { + signedPreKeyRecordHashMap = omemoStore.loadOmemoSignedPreKeys(getOurDevice()); + } catch (IOException e) { + throw new IllegalStateException(e); + } return new ArrayList<>(signedPreKeyRecordHashMap.values()); } @Override public void storeSignedPreKey(int i, SignedPreKeyRecord signedPreKeyRecord) { - omemoStore.storeOmemoSignedPreKey(getOurDevice(), i, signedPreKeyRecord); + try { + omemoStore.storeOmemoSignedPreKey(getOurDevice(), i, signedPreKeyRecord); + } catch (IOException e) { + throw new IllegalStateException(e); + } } @Override diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java index 7ab0bcde6..d83984e6c 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/CachingOmemoStore.java @@ -16,6 +16,7 @@ */ package org.jivesoftware.smackx.omemo; +import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.SortedSet; @@ -80,7 +81,7 @@ public class CachingOmemoStore loadOmemoPreKeys(OmemoDevice userDevice) { + public TreeMap loadOmemoPreKeys(OmemoDevice userDevice) throws IOException { TreeMap preKeys = getCache(userDevice).preKeys; if (preKeys.isEmpty() && persistent != null) { @@ -272,7 +273,7 @@ public class CachingOmemoStore loadOmemoSignedPreKeys(OmemoDevice userDevice) { + public TreeMap loadOmemoSignedPreKeys(OmemoDevice userDevice) throws IOException { TreeMap sigPreKeys = getCache(userDevice).signedPreKeys; if (sigPreKeys.isEmpty() && persistent != null) { @@ -299,7 +300,7 @@ public class CachingOmemoStore contactSessions = getCache(userDevice).sessions.get(contactsDevice.getJid()); if (contactSessions == null) { contactSessions = new HashMap<>(); @@ -334,7 +335,7 @@ public class CachingOmemoStore loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) { + public HashMap loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException { HashMap sessions = getCache(userDevice).sessions.get(contact); if (sessions == null) { sessions = new HashMap<>(); @@ -349,7 +350,7 @@ public class CachingOmemoStore sessions = getCache(userDevice).sessions.get(contactsDevicece.getJid()); if (sessions == null) { sessions = new HashMap<>(); @@ -391,7 +392,7 @@ public class CachingOmemoStore loadOmemoPreKeys(OmemoDevice userDevice) { + public TreeMap loadOmemoPreKeys(OmemoDevice userDevice) throws IOException { File preKeyDirectory = hierarchy.getPreKeysDirectory(userDevice); TreeMap preKeys = new TreeMap<>(); @@ -221,7 +219,7 @@ public abstract class FileBasedOmemoStore loadOmemoSignedPreKeys(OmemoDevice userDevice) { + public TreeMap loadOmemoSignedPreKeys(OmemoDevice userDevice) throws IOException { File signedPreKeysDirectory = hierarchy.getSignedPreKeysDirectory(userDevice); TreeMap signedPreKeys = new TreeMap<>(); @@ -263,7 +261,7 @@ public abstract class FileBasedOmemoStore loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) { + public HashMap loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException { File contactsDirectory = hierarchy.getContactsDir(userDevice, contact); HashMap sessions = new HashMap<>(); String[] devices = contactsDirectory.list(); @@ -322,7 +320,7 @@ public abstract class FileBasedOmemoStore integers = readIntegers(messageCounterFile); @@ -375,7 +373,7 @@ public abstract class FileBasedOmemoStore integers) { - if (target == null) { - LOGGER.log(Level.WARNING, "Could not write integers to null-path."); - return; - } - - DataOutputStream out = null; - - try { - out = new DataOutputStream(new FileOutputStream(target)); - for (int i : integers) { - out.writeInt(i); - } - - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Could not write integers to file.", e); - } finally { - CloseableUtil.maybeClose(out, LOGGER); - } - } - - private static Set readIntegers(File target) { - if (target == null) { - LOGGER.log(Level.WARNING, "Could not read integers from null-path."); - return null; - } - - HashSet integers = new HashSet<>(); - DataInputStream in = null; - - try { - in = new DataInputStream(new FileInputStream(target)); - - try { - while (true) { - integers.add(in.readInt()); - } - } catch (EOFException e) { - // Reached end of the list. - } - - } catch (FileNotFoundException e) { - integers = null; - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Could not read integers.", e); - } finally { - CloseableUtil.maybeClose(in, LOGGER); - } - - return integers; - } - - /** - * One day... *sheds a tear* - * TODO Use methods below once Smack's minimum Android API level is 19 or higher - */ - /* - private static void writeLong(File target, long i) - throws IOException - { + private static void writeLong(File target, long i) throws IOException { if (target == null) { throw new IOException("Could not write long to null-path."); } @@ -591,9 +430,7 @@ public abstract class FileBasedOmemoStore integers) - throws IOException - { + private static void writeIntegers(File target, Set integers) throws IOException { if (target == null) { throw new IOException("Could not write integers to null-path."); } @@ -657,9 +488,7 @@ public abstract class FileBasedOmemoStore readIntegers(File target) - throws IOException - { + private static Set readIntegers(File target) throws IOException { if (target == null) { throw new IOException("Could not write integers to null-path."); } @@ -682,7 +511,6 @@ public abstract class FileBasedOmemoStore getDevicesOf(BareJid contact) { + public Set getDevicesOf(BareJid contact) throws IOException { OmemoCachedDeviceList list = getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), contact); HashSet devices = new HashSet<>(); @@ -304,11 +307,12 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws SmackException.NoResponseException * @throws SmackException.NotLoggedInException + * @throws IOException */ public OmemoMessage.Sent encrypt(BareJid recipient, String message) throws CryptoFailedException, UndecidedOmemoIdentityException, InterruptedException, SmackException.NotConnectedException, - SmackException.NoResponseException, SmackException.NotLoggedInException { + SmackException.NoResponseException, SmackException.NotLoggedInException, IOException { synchronized (LOCK) { Set recipients = new HashSet<>(); recipients.add(recipient); @@ -328,11 +332,12 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws SmackException.NoResponseException * @throws SmackException.NotLoggedInException + * @throws IOException */ public OmemoMessage.Sent encrypt(Set recipients, String message) throws CryptoFailedException, UndecidedOmemoIdentityException, InterruptedException, SmackException.NotConnectedException, - SmackException.NoResponseException, SmackException.NotLoggedInException { + SmackException.NoResponseException, SmackException.NotLoggedInException, IOException { synchronized (LOCK) { LoggedInOmemoManager guard = new LoggedInOmemoManager(this); Set devices = getDevicesOf(getOwnJid()); @@ -357,12 +362,13 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws NoOmemoSupportException When the muc doesn't support OMEMO. * @throws SmackException.NotLoggedInException + * @throws IOException */ public OmemoMessage.Sent encrypt(MultiUserChat muc, String message) throws UndecidedOmemoIdentityException, CryptoFailedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, NoOmemoSupportException, - SmackException.NotLoggedInException { + SmackException.NotLoggedInException, IOException { synchronized (LOCK) { if (!multiUserChatSupportsOmemo(muc)) { throw new NoOmemoSupportException(); @@ -390,10 +396,11 @@ public final class OmemoManager extends Manager { * @throws CorruptedOmemoKeyException if our or their key is corrupted * @throws NoRawSessionException if the message was not a preKeyMessage, but we had no session with the contact * @throws CryptoFailedException if decryption fails + * @throws IOException */ public OmemoMessage.Received decrypt(BareJid sender, OmemoElement omemoElement) throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, NoRawSessionException, - CryptoFailedException { + CryptoFailedException, IOException { LoggedInOmemoManager managerGuard = new LoggedInOmemoManager(this); return getOmemoService().decryptMessage(managerGuard, sender, omemoElement); } @@ -404,9 +411,10 @@ public final class OmemoManager extends Manager { * @param mamQuery The MAM query * @return list of decrypted OmemoMessages * @throws SmackException.NotLoggedInException if the Manager is not authenticated. + * @throws IOException */ public List decryptMamQueryResult(MamManager.MamQuery mamQuery) - throws SmackException.NotLoggedInException { + throws SmackException.NotLoggedInException, IOException { return new ArrayList<>(getOmemoService().decryptMamQueryResult(new LoggedInOmemoManager(this), mamQuery)); } @@ -486,11 +494,12 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws NoSuchAlgorithmException * @throws SmackException.NotConnectedException + * @throws IOException */ public void sendRatchetUpdateMessage(OmemoDevice recipient) throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException, NoSuchAlgorithmException, SmackException.NotConnectedException, - CryptoFailedException, CannotEstablishOmemoSessionException { + CryptoFailedException, CannotEstablishOmemoSessionException, IOException { synchronized (LOCK) { Message message = new Message(); message.setFrom(getOwnJid()); @@ -516,10 +525,11 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws PubSubException.NotALeafNodeException * @throws XMPPException.XMPPErrorException + * @throws IOException */ public boolean contactSupportsOmemo(BareJid contact) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException { + SmackException.NotConnectedException, SmackException.NoResponseException, IOException { synchronized (LOCK) { OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); return !deviceList.getActiveDevices().isEmpty(); @@ -569,9 +579,10 @@ public final class OmemoManager extends Manager { * @return fingerprint * @throws SmackException.NotLoggedInException if we don't know our bareJid yet. * @throws CorruptedOmemoKeyException if our identityKey is corrupted. + * @throws IOException */ public OmemoFingerprint getOwnFingerprint() - throws SmackException.NotLoggedInException, CorruptedOmemoKeyException { + throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, IOException { synchronized (LOCK) { if (getOwnJid() == null) { throw new SmackException.NotLoggedInException(); @@ -591,11 +602,12 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws InterruptedException * @throws SmackException.NoResponseException + * @throws IOException */ public OmemoFingerprint getFingerprint(OmemoDevice device) throws CannotEstablishOmemoSessionException, SmackException.NotLoggedInException, CorruptedOmemoKeyException, SmackException.NotConnectedException, InterruptedException, - SmackException.NoResponseException { + SmackException.NoResponseException, IOException { synchronized (LOCK) { if (getOwnJid() == null) { throw new SmackException.NotLoggedInException(); @@ -621,11 +633,12 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws InterruptedException * @throws SmackException.NoResponseException + * @throws IOException */ public HashMap getActiveFingerprints(BareJid contact) throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException, InterruptedException, - SmackException.NoResponseException { + SmackException.NoResponseException, IOException { synchronized (LOCK) { if (getOwnJid() == null) { throw new SmackException.NotLoggedInException(); @@ -692,10 +705,11 @@ public final class OmemoManager extends Manager { * @throws XMPPException.XMPPErrorException * @throws SmackException.NotConnectedException * @throws SmackException.NoResponseException + * @throws IOException */ public void requestDeviceListUpdateFor(BareJid contact) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException { + SmackException.NotConnectedException, SmackException.NoResponseException, IOException { synchronized (LOCK) { getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); } @@ -709,10 +723,11 @@ public final class OmemoManager extends Manager { * @throws XMPPException.XMPPErrorException * @throws SmackException.NotConnectedException * @throws SmackException.NoResponseException + * @throws IOException */ public void purgeDeviceList() throws SmackException.NotLoggedInException, InterruptedException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException { + SmackException.NotConnectedException, SmackException.NoResponseException, IOException { synchronized (LOCK) { getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this)); } @@ -729,10 +744,11 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException XMPP error * @throws SmackException.NoResponseException XMPP error * @throws SmackException.NotLoggedInException + * @throws IOException */ public void rotateSignedPreKey() throws CorruptedOmemoKeyException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException { synchronized (LOCK) { if (!connection().isAuthenticated()) { throw new SmackException.NotLoggedInException(); @@ -956,8 +972,8 @@ public final class OmemoManager extends Manager { try { getOmemoService().onOmemoMessageStanzaReceived(packet, new LoggedInOmemoManager(OmemoManager.this)); - } catch (SmackException.NotLoggedInException e) { - LOGGER.warning("Received OMEMO stanza while being offline: " + e); + } catch (SmackException.NotLoggedInException | IOException e) { + LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e); } } }); @@ -979,8 +995,8 @@ public final class OmemoManager extends Manager { try { getOmemoService().onOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage, new LoggedInOmemoManager(OmemoManager.this)); - } catch (SmackException.NotLoggedInException e) { - LOGGER.warning("Received OMEMO carbon copy while being offline: " + e); + } catch (SmackException.NotLoggedInException | IOException e) { + LOGGER.log(Level.SEVERE, "Exception while processing OMEMO stanza", e); } } } @@ -1018,14 +1034,23 @@ public final class OmemoManager extends Manager { } // Device List + OmemoCachedDeviceList deviceList; OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload(); - getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, receivedDeviceList); + try { + getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, + receivedDeviceList); - if (!from.asBareJid().equals(getOwnJid())) { + 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; } - - OmemoCachedDeviceList deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice()); final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList); if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) { diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoRatchet.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoRatchet.java index 28ffc50ef..7ad0f6b6e 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoRatchet.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoRatchet.java @@ -16,6 +16,7 @@ */ package org.jivesoftware.smackx.omemo; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -65,10 +66,11 @@ public abstract class OmemoRatchet decryptExceptions = new ArrayList<>(); diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java index 91eb51034..1130e63fc 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java @@ -19,6 +19,7 @@ package org.jivesoftware.smackx.omemo; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYLENGTH; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE; +import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -234,11 +235,12 @@ public abstract class OmemoService contactsDevices, @@ -341,7 +345,7 @@ public abstract class OmemoService contactsDevices, byte[] key, byte[] iv) throws InterruptedException, UndecidedOmemoIdentityException, CryptoFailedException, - SmackException.NotConnectedException, SmackException.NoResponseException { + SmackException.NotConnectedException, SmackException.NoResponseException, IOException { return encrypt(managerGuard, contactsDevices, key, iv, null); } @@ -511,12 +517,13 @@ public abstract class OmemoService contactsDevices, String message) throws InterruptedException, UndecidedOmemoIdentityException, CryptoFailedException, - SmackException.NotConnectedException, SmackException.NoResponseException { + SmackException.NotConnectedException, SmackException.NoResponseException, IOException { byte[] key, iv; iv = OmemoMessageBuilder.generateIv(); @@ -643,10 +650,11 @@ public abstract class OmemoService buildMissingSessionsWithDevices(XMPPConnection connection, OmemoDevice userDevice, Set devices) - throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException { Set devicesWithSession = new HashSet<>(); for (OmemoDevice device : devices) { @@ -828,8 +839,9 @@ public abstract class OmemoService getUndecidedDevices(OmemoDevice userDevice, OmemoTrustCallback callback, Set devices) { + private Set getUndecidedDevices(OmemoDevice userDevice, OmemoTrustCallback callback, Set devices) throws IOException { Set undecidedDevices = new HashSet<>(); for (OmemoDevice device : devices) { @@ -858,8 +870,9 @@ public abstract class OmemoService decryptMamQueryResult(OmemoManager.LoggedInOmemoManager managerGuard, - MamManager.MamQuery mamQuery) { + MamManager.MamQuery mamQuery) throws IOException { List result = new ArrayList<>(); for (Message message : mamQuery.getMessages()) { if (OmemoManager.stanzaContainsOmemoElement(message)) { @@ -1055,7 +1072,7 @@ public abstract class OmemoService preKeyHashMap) { + public void storeOmemoPreKeys(OmemoDevice userDevice, TreeMap preKeyHashMap) throws IOException { for (Map.Entry entry : preKeyHashMap.entrySet()) { storeOmemoPreKey(userDevice, entry.getKey(), entry.getValue()); } @@ -422,8 +444,9 @@ public abstract class OmemoStore loadOmemoPreKeys(OmemoDevice userDevice); + public abstract TreeMap loadOmemoPreKeys(OmemoDevice userDevice) throws IOException; /** * Return the signedPreKey with the id 'singedPreKeyId'. @@ -431,10 +454,11 @@ public abstract class OmemoStore loadOmemoSignedPreKeys(OmemoDevice userDevice); + public abstract TreeMap loadOmemoSignedPreKeys(OmemoDevice userDevice) throws IOException; /** * Generate a new signed preKey. @@ -465,8 +490,9 @@ public abstract class OmemoStore loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact); + public abstract HashMap loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) throws IOException; /** * Store a crypto-lib specific session to storage. @@ -500,8 +528,9 @@ public abstract class OmemoStore sessions = store.loadAllRawSessionsOf(alice, bob.getJid()); assertNotNull(sessions); assertEquals(0, sessions.size()); } @Test - public void loadNonExistentRawSessionReturnsNullTest() { + public void loadNonExistentRawSessionReturnsNullTest() throws IOException { T_Sess session = store.loadRawSession(alice, bob); assertNull(session); } @Test - public void loadStoreMessageCounterTest() { + public void loadStoreMessageCounterTest() throws IOException { assertEquals(0, store.loadOmemoMessageCounter(alice, bob)); store.storeOmemoMessageCounter(alice, bob, 20); assertEquals(20, store.loadOmemoMessageCounter(alice, bob)); From e6ac568f3222730722daeabfdee4255cfb473fd4 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 4 Aug 2019 15:28:21 +0200 Subject: [PATCH 12/23] Change license of smack-repl to GPLv3 --- build.gradle | 1 + .../smack-repl-gplv3-license-header.txt | 20 +++++++++++++++++ .../igniterealtime/smack/smackrepl/DoX.java | 22 +++++++++++-------- .../igniterealtime/smack/smackrepl/IoT.java | 22 +++++++++++-------- .../igniterealtime/smack/smackrepl/Nio.java | 22 +++++++++++-------- .../smack/smackrepl/SmackRepl.java | 22 +++++++++++-------- .../smack/smackrepl/StateGraph.java | 22 +++++++++++-------- .../smack/smackrepl/TlsTest.java | 22 +++++++++++-------- .../smack/smackrepl/XmppTools.java | 22 +++++++++++-------- .../smack/smackrepl/package-info.java | 22 +++++++++++-------- .../smack/smackrepl/SmackReplTest.java | 22 +++++++++++-------- 11 files changed, 138 insertions(+), 81 deletions(-) create mode 100644 config/checkstyle/smack-repl-gplv3-license-header.txt diff --git a/build.gradle b/build.gradle index 3da33bce7..fa4d82cbc 100644 --- a/build.gradle +++ b/build.gradle @@ -99,6 +99,7 @@ allprojects { gplLicensedProjects = [ ':smack-omemo-signal', ':smack-omemo-signal-integration-test', + ':smack-repl' ].collect{ project(it) } // When this list is empty, then move the according javadoc // tool Werror option into the global configure section. diff --git a/config/checkstyle/smack-repl-gplv3-license-header.txt b/config/checkstyle/smack-repl-gplv3-license-header.txt new file mode 100644 index 000000000..1799ffa34 --- /dev/null +++ b/config/checkstyle/smack-repl-gplv3-license-header.txt @@ -0,0 +1,20 @@ +/** + * + * Copyright 20XX John Doe + * + * This file is part of smack-repl. + * + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/DoX.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/DoX.java index ddab89d4d..aa0525eb2 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/DoX.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/DoX.java @@ -2,17 +2,21 @@ * * Copyright 2019 Florian Schmaus * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/IoT.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/IoT.java index 163ca53b6..a77f5f1ac 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/IoT.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/IoT.java @@ -2,17 +2,21 @@ * * Copyright 2016 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 + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; 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 8bb78013b..05f0096e7 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 @@ -2,17 +2,21 @@ * * Copyright 2018 Florian Schmaus * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java index 583c701b9..8573ae65a 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/SmackRepl.java @@ -2,17 +2,21 @@ * * Copyright 2016 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 + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/StateGraph.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/StateGraph.java index 93596c8a0..2908c087f 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/StateGraph.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/StateGraph.java @@ -2,17 +2,21 @@ * * Copyright 2018 Florian Schmaus * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; 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 6be8f52e5..557f1812d 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/TlsTest.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/TlsTest.java @@ -2,17 +2,21 @@ * * Copyright 2016 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 + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/XmppTools.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/XmppTools.java index 30ad442d6..958dbe1b1 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/XmppTools.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/XmppTools.java @@ -2,17 +2,21 @@ * * Copyright 2016 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 + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/package-info.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/package-info.java index 0ce9b9854..c162ff55f 100644 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/package-info.java +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/package-info.java @@ -2,17 +2,21 @@ * * Copyright 2016 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 + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** diff --git a/smack-repl/src/test/java/org/igniterealtime/smack/smackrepl/SmackReplTest.java b/smack-repl/src/test/java/org/igniterealtime/smack/smackrepl/SmackReplTest.java index f470d5422..1e8227014 100644 --- a/smack-repl/src/test/java/org/igniterealtime/smack/smackrepl/SmackReplTest.java +++ b/smack-repl/src/test/java/org/igniterealtime/smack/smackrepl/SmackReplTest.java @@ -2,17 +2,21 @@ * * Copyright 2016 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 + * This file is part of smack-repl. * - * http://www.apache.org/licenses/LICENSE-2.0 + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.igniterealtime.smack.smackrepl; From 3a0356488da91c3e6f42ecae90e16dbe42ef9596 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 4 Aug 2019 15:28:46 +0200 Subject: [PATCH 13/23] Add OmemoClient repl class --- smack-repl/build.gradle | 1 + .../smack/smackrepl/OmemoClient.java | 216 ++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/OmemoClient.java diff --git a/smack-repl/build.gradle b/smack-repl/build.gradle index 2b36f85ef..53579773d 100644 --- a/smack-repl/build.gradle +++ b/smack-repl/build.gradle @@ -19,6 +19,7 @@ dependencies { compile project(':smack-experimental') compile project(':smack-legacy') compile project(':smack-integration-test') + compile project(':smack-omemo-signal') compile "org.scala-lang:scala-library:$scalaVersion" compile "com.lihaoyi:ammonite_$scalaVersion:1.3.2" testCompile project(path: ":smack-core", configuration: "testRuntime") diff --git a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/OmemoClient.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/OmemoClient.java new file mode 100644 index 000000000..2bb415468 --- /dev/null +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/OmemoClient.java @@ -0,0 +1,216 @@ +/** + * + * Copyright 2019 Paul Schaub + * + * This file is part of smack-repl. + * + * smack-repl is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.igniterealtime.smack.smackrepl; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.SmackException.NotConnectedException; +import org.jivesoftware.smack.SmackException.NotLoggedInException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.tcp.XMPPTCPConnection; +import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension; +import org.jivesoftware.smackx.muc.MultiUserChat; +import org.jivesoftware.smackx.omemo.OmemoManager; +import org.jivesoftware.smackx.omemo.OmemoMessage; +import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException; +import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; +import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException; +import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException; +import org.jivesoftware.smackx.omemo.internal.OmemoDevice; +import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener; +import org.jivesoftware.smackx.omemo.listener.OmemoMucMessageListener; +import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore; +import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore; +import org.jivesoftware.smackx.omemo.signal.SignalOmemoService; +import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; +import org.jivesoftware.smackx.omemo.trust.OmemoTrustCallback; +import org.jivesoftware.smackx.omemo.trust.TrustState; + +import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; + +public class OmemoClient { + + public static final Logger LOGGER = Logger.getLogger(OmemoClient.class.getName()); + + private static final Scanner scanner = new Scanner(System.in, "UTF-8"); + private final XMPPTCPConnection connection; + private final OmemoManager omemoManager; + + public static void main(String[] args) + throws XMPPException, SmackException, IOException, InterruptedException, CorruptedOmemoKeyException { + SmackConfiguration.DEBUG = true; + if (args.length != 2) { + print("Missing arguments: "); + return; + } + SignalOmemoService.acknowledgeLicense(); + SignalOmemoService.setup(); + SignalOmemoService omemoService = (SignalOmemoService) SignalOmemoService.getInstance(); + Path omemoStoreDirectory = Files.createTempDirectory("omemo-store"); + omemoService.setOmemoStoreBackend(new SignalCachingOmemoStore(new SignalFileBasedOmemoStore(omemoStoreDirectory.toFile()))); + + EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked(args[0]); + String password = args[1]; + OmemoClient client = new OmemoClient(jid, password); + try { + client.start(); + + while (true) { + String input = scanner.nextLine(); + if (input.startsWith("/quit")) { + break; + } + if (input.isEmpty()) { + continue; + } + client.handleInput(input); + } + } finally { + client.stop(); + } + } + + public OmemoClient(EntityBareJid jid, String password) { + connection = new XMPPTCPConnection(XMPPTCPConnectionConfiguration.builder() + .setXmppAddressAndPassword(jid, password).build()); + connection.setReplyTimeout(10 * 1000); + omemoManager = OmemoManager.getInstanceFor(connection); + omemoManager.setTrustCallback(new OmemoTrustCallback() { + // In a real app you'd want to persist these decisions + private final Map trustStateMap = new HashMap<>(); + @Override + public TrustState getTrust(OmemoDevice device, OmemoFingerprint fingerprint) { + return trustStateMap.get(fingerprint) != null ? trustStateMap.get(fingerprint) : TrustState.undecided; + } + + @Override + public void setTrust(OmemoDevice device, OmemoFingerprint fingerprint, TrustState state) { + trustStateMap.put(fingerprint, state); + } + }); + omemoManager.addOmemoMessageListener(new OmemoMessageListener() { + @Override + public void onOmemoMessageReceived(Stanza s, OmemoMessage.Received m) { + print(m.getSenderDevice() + ": " + (m.getBody() != null ? m.getBody() : "")); + } + + @Override + public void onOmemoCarbonCopyReceived(CarbonExtension.Direction d, Message cc, Message wm, OmemoMessage.Received m) { + onOmemoMessageReceived(cc, m); + } + }); + omemoManager.addOmemoMucMessageListener(new OmemoMucMessageListener() { + @Override + public void onOmemoMucMessageReceived(MultiUserChat muc, Stanza s, OmemoMessage.Received m) { + print(s.getFrom() + ":" + m.getSenderDevice().getDeviceId() + ": " + (m.getBody() != null ? m.getBody() : "")); + } + }); + } + + public void start() + throws XMPPException, SmackException, IOException, InterruptedException, CorruptedOmemoKeyException { + connection.connect().login(); + omemoManager.initialize(); + print("Logged in!"); + } + + public void stop() { + connection.disconnect(); + } + + public void handleInput(String input) + throws NotConnectedException, NotLoggedInException, InterruptedException, IOException { + String[] com = input.split(" ", 3); + switch (com[0]) { + case "/omemo": + if (com.length < 3) { + print("Usage: /omemo "); + return; + } + + BareJid recipient = JidCreate.bareFrom(com[1]); + String body = com[2]; + + try { + Message omemoMessage = omemoManager.encrypt(recipient, body).asMessage(recipient); + connection.sendStanza(omemoMessage); + } catch (UndecidedOmemoIdentityException e) { + print("Undecided Identities!\n" + Arrays.toString(e.getUndecidedDevices().toArray())); + } catch (CryptoFailedException | SmackException.NoResponseException e) { + LOGGER.log(Level.SEVERE, "Unexpected Exception", e); + } + break; + case "/trust": + print("Trust"); + if (com.length != 2) { + print("Usage: /trust "); + } + + BareJid contact = JidCreate.bareFrom(com[1]); + + HashMap devices; + try { + devices = omemoManager.getActiveFingerprints(contact); + } catch (CorruptedOmemoKeyException | CannotEstablishOmemoSessionException | SmackException.NoResponseException e) { + LOGGER.log(Level.SEVERE, "Unexpected Exception", e); + return; + } + for (OmemoDevice d : devices.keySet()) { + print("Trust (1) or distrust (2)?\n" + devices.get(d).blocksOf8Chars()); + if (Integer.parseInt(scanner.nextLine()) == 1) { + omemoManager.trustOmemoIdentity(d, devices.get(d)); + } else { + omemoManager.distrustOmemoIdentity(d, devices.get(d)); + } + } + print("Done."); + break; + case "/purge": + try { + omemoManager.purgeDeviceList(); + print("Purged."); + } catch (XMPPException.XMPPErrorException | SmackException.NoResponseException e) { + LOGGER.log(Level.SEVERE, "Unexpected Exception", e); + } + } + } + + private static void print(String msg) { + // CHECKSTYLE:OFF + System.out.println(msg); + // CHECKSTYLE:ON + } +} From 1893fa74745d8da65e0faab02e5f6f1690026b33 Mon Sep 17 00:00:00 2001 From: Benjamin JALON Date: Mon, 5 Aug 2019 12:57:48 +0200 Subject: [PATCH 14/23] Update muc.md --- documentation/extensions/muc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/extensions/muc.md b/documentation/extensions/muc.md index 02f6d6d8f..a89f2b22f 100644 --- a/documentation/extensions/muc.md +++ b/documentation/extensions/muc.md @@ -81,7 +81,7 @@ completed with default values: MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection); // Create a MultiUserChat using an XMPPConnection for a room -MultiUserChat muc = = manager.getMultiUserChat(mucJid); +MultiUserChat muc = manager.getMultiUserChat(mucJid); // Prepare a list of owners of the new room Set owners = JidUtil.jidSetFrom(new String[] { "me@example.org", "juliet@example.org" }); From 1bc8a22b289ff9cb744171b2f5d60f0c46ab77d2 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 5 Aug 2019 22:20:21 +0200 Subject: [PATCH 15/23] Add StringUtils.deleteXmlWhitespace(String) --- .../org/jivesoftware/smack/util/StringUtils.java | 7 +++++++ .../org/jivesoftware/smack/util/StringUtilsTest.java | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index b2873ac59..26b7d4bee 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Iterator; import java.util.Random; +import java.util.regex.Pattern; /** * A collection of utility methods for String objects. @@ -541,4 +542,10 @@ public class StringUtils { } return cs.toString(); } + + private static final Pattern XMLWHITESPACE = Pattern.compile("[\t\n\r ]"); + + public static String deleteXmlWhitespace(String string) { + return XMLWHITESPACE.matcher(string).replaceAll(""); + } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/StringUtilsTest.java b/smack-core/src/test/java/org/jivesoftware/smack/util/StringUtilsTest.java index 226cb07b0..e61b655fe 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/StringUtilsTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/util/StringUtilsTest.java @@ -109,4 +109,16 @@ public class StringUtilsTest { String result = StringUtils.randomString(0); assertEquals("", result); } + + @Test + public void testeDeleteXmlWhitespace() { + String noWhitespace = StringUtils.deleteXmlWhitespace(" foo\nbar "); + assertEquals("foobar", noWhitespace); + + noWhitespace = StringUtils.deleteXmlWhitespace(" \tbaz\rbarz\t "); + assertEquals("bazbarz", noWhitespace); + + noWhitespace = StringUtils.deleteXmlWhitespace("SNAFU"); + assertEquals("SNAFU", noWhitespace); + } } From d7b7abc7ebd2efd04b9fcf835f97dbb45683e296 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 5 Aug 2019 22:20:36 +0200 Subject: [PATCH 16/23] Delete XML whitespace before feeding the Base64 decoder --- .../java/org/jivesoftware/smack/util/StringUtils.java | 10 ++++++++-- .../jivesoftware/smack/util/stringencoder/Base64.java | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index 26b7d4bee..261f8570d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java @@ -543,9 +543,15 @@ public class StringUtils { return cs.toString(); } - private static final Pattern XMLWHITESPACE = Pattern.compile("[\t\n\r ]"); + /** + * Defined by XML 1.0 § 2.3 as: + * S ::= (#x20 | #x9 | #xD | #xA)+ + * + * @see XML 1.0 § 2.3 + */ + private static final Pattern XML_WHITESPACE = Pattern.compile("[\t\n\r ]"); public static String deleteXmlWhitespace(String string) { - return XMLWHITESPACE.matcher(string).replaceAll(""); + return XML_WHITESPACE.matcher(string).replaceAll(""); } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/stringencoder/Base64.java b/smack-core/src/main/java/org/jivesoftware/smack/util/stringencoder/Base64.java index eb71b5f7f..050321bcb 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/stringencoder/Base64.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/stringencoder/Base64.java @@ -19,6 +19,7 @@ package org.jivesoftware.smack.util.stringencoder; import java.nio.charset.StandardCharsets; import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.util.StringUtils; public class Base64 { @@ -57,6 +58,9 @@ public class Base64 { // TODO: We really should not mask the IllegalArgumentException. But some unit test depend on this behavior, like // ibb.packet.DataPacketExtension.shouldReturnNullIfDataIsInvalid(). public static final byte[] decode(String string) { + // xs:base64Binary may include XML whitespace which we need to delete before feeding the string into the Base64 + // decoder. See also XML Schema Part 2: Datatypes Second Edition § 3.2.16. + string = StringUtils.deleteXmlWhitespace(string); try { return base64encoder.decode(string); } catch (IllegalArgumentException e) { From 99232683915286e61d17a0e4c7fcf61f6529a31f Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 6 Aug 2019 08:23:10 +0200 Subject: [PATCH 17/23] Remove LOCK in OmemoManager and use Manager instance instead --- .../MessageEncryptionIntegrationTest.java | 3 +- .../SessionRenegotiationIntegrationTest.java | 3 +- .../smackx/omemo/OmemoManager.java | 223 ++++++++---------- .../smackx/omemo/OmemoService.java | 6 +- 4 files changed, 103 insertions(+), 132 deletions(-) diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java index 33c06b560..db9a6522d 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/MessageEncryptionIntegrationTest.java @@ -55,6 +55,7 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg * Bob still has B2 * @throws Exception */ + @SuppressWarnings("SynchronizeOnNonFinalField") @SmackIntegrationTest public void messageTest() throws Exception { OmemoBundleElement a1 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice()); @@ -74,7 +75,7 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg OmemoBundleElement a1_ = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice()); OmemoBundleElement b2; - synchronized (bob.LOCK) { // Circumvent race condition where bundle gets replenished after getting stored in b2 + synchronized (bob) { // Circumvent race condition where bundle gets replenished after getting stored in b2 b2 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice()); } diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java index 28e4674c6..ae1324b22 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/SessionRenegotiationIntegrationTest.java @@ -31,6 +31,7 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn super(environment); } + @SuppressWarnings("SynchronizeOnNonFinalField") @SmackIntegrationTest public void sessionRenegotiationTest() throws Exception { @@ -50,7 +51,7 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn bob.removeOmemoMessageListener(listener1); // Remove the session on Bobs side. - synchronized (bob.LOCK) { + synchronized (bob) { bob.getOmemoService().getOmemoStoreBackend().removeRawSession(bob.getOwnDevice(), alice.getOwnDevice()); } 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 5b1807a8d..c2fe1635a 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 @@ -97,7 +97,6 @@ public final class OmemoManager extends Manager { private static final Logger LOGGER = Logger.getLogger(OmemoManager.class.getName()); private static final Integer UNKNOWN_DEVICE_ID = -1; - final Object LOCK = new Object(); private static final WeakHashMap> INSTANCES = new WeakHashMap<>(); private final OmemoService service; @@ -237,22 +236,20 @@ public final class OmemoManager extends Manager { * @throws PubSubException.NotALeafNodeException * @throws IOException */ - public void initialize() + public synchronized void initialize() throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException, IOException { - synchronized (LOCK) { - if (!connection().isAuthenticated()) { - throw new SmackException.NotLoggedInException(); - } - - if (getTrustCallback() == null) { - throw new IllegalStateException("No TrustCallback set."); - } - - getOmemoService().init(new LoggedInOmemoManager(this)); - ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); + if (!connection().isAuthenticated()) { + throw new SmackException.NotLoggedInException(); } + + if (getTrustCallback() == null) { + throw new IllegalStateException("No TrustCallback set."); + } + + getOmemoService().init(new LoggedInOmemoManager(this)); + ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY); } /** @@ -313,11 +310,9 @@ public final class OmemoManager extends Manager { throws CryptoFailedException, UndecidedOmemoIdentityException, InterruptedException, SmackException.NotConnectedException, SmackException.NoResponseException, SmackException.NotLoggedInException, IOException { - synchronized (LOCK) { Set recipients = new HashSet<>(); recipients.add(recipient); return encrypt(recipients, message); - } } /** @@ -334,18 +329,16 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotLoggedInException * @throws IOException */ - public OmemoMessage.Sent encrypt(Set recipients, String message) + public synchronized OmemoMessage.Sent encrypt(Set recipients, String message) throws CryptoFailedException, UndecidedOmemoIdentityException, InterruptedException, SmackException.NotConnectedException, SmackException.NoResponseException, SmackException.NotLoggedInException, IOException { - synchronized (LOCK) { - LoggedInOmemoManager guard = new LoggedInOmemoManager(this); - Set devices = getDevicesOf(getOwnJid()); - for (BareJid recipient : recipients) { - devices.addAll(getDevicesOf(recipient)); - } - return service.createOmemoMessage(guard, devices, message); + LoggedInOmemoManager guard = new LoggedInOmemoManager(this); + Set devices = getDevicesOf(getOwnJid()); + for (BareJid recipient : recipients) { + devices.addAll(getDevicesOf(recipient)); } + return service.createOmemoMessage(guard, devices, message); } /** @@ -364,23 +357,21 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotLoggedInException * @throws IOException */ - public OmemoMessage.Sent encrypt(MultiUserChat muc, String message) + public synchronized OmemoMessage.Sent encrypt(MultiUserChat muc, String message) throws UndecidedOmemoIdentityException, CryptoFailedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, NoOmemoSupportException, SmackException.NotLoggedInException, IOException { - synchronized (LOCK) { - if (!multiUserChatSupportsOmemo(muc)) { - throw new NoOmemoSupportException(); - } - - Set recipients = new HashSet<>(); - - for (EntityFullJid e : muc.getOccupants()) { - recipients.add(muc.getOccupant(e).getJid().asBareJid()); - } - return encrypt(recipients, message); + if (!multiUserChatSupportsOmemo(muc)) { + throw new NoOmemoSupportException(); } + + Set recipients = new HashSet<>(); + + for (EntityFullJid e : muc.getOccupants()) { + recipients.add(muc.getOccupant(e).getJid().asBareJid()); + } + return encrypt(recipients, message); } /** @@ -496,23 +487,20 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws IOException */ - public void sendRatchetUpdateMessage(OmemoDevice recipient) + public synchronized void sendRatchetUpdateMessage(OmemoDevice recipient) throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException, NoSuchAlgorithmException, SmackException.NotConnectedException, CryptoFailedException, CannotEstablishOmemoSessionException, IOException { - synchronized (LOCK) { - Message message = new Message(); - message.setFrom(getOwnJid()); - message.setTo(recipient.getJid()); + Message message = new Message(); + message.setFrom(getOwnJid()); + message.setTo(recipient.getJid()); - OmemoElement element = getOmemoService() - .createRatchetUpdateElement(new LoggedInOmemoManager(this), recipient); - message.addExtension(element); + OmemoElement element = getOmemoService().createRatchetUpdateElement(new LoggedInOmemoManager(this), recipient); + message.addExtension(element); - // Set MAM Storage hint - StoreHint.set(message); - connection().sendStanza(message); - } + // Set MAM Storage hint + StoreHint.set(message); + connection().sendStanza(message); } /** @@ -527,13 +515,11 @@ public final class OmemoManager extends Manager { * @throws XMPPException.XMPPErrorException * @throws IOException */ - public boolean contactSupportsOmemo(BareJid contact) + public synchronized boolean contactSupportsOmemo(BareJid contact) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException { - synchronized (LOCK) { - OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); - return !deviceList.getActiveDevices().isEmpty(); - } + OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); + return !deviceList.getActiveDevices().isEmpty(); } /** @@ -581,15 +567,13 @@ public final class OmemoManager extends Manager { * @throws CorruptedOmemoKeyException if our identityKey is corrupted. * @throws IOException */ - public OmemoFingerprint getOwnFingerprint() + public synchronized OmemoFingerprint getOwnFingerprint() throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, IOException { - synchronized (LOCK) { - if (getOwnJid() == null) { - throw new SmackException.NotLoggedInException(); - } - - return getOmemoService().getOmemoStoreBackend().getFingerprint(getOwnDevice()); + if (getOwnJid() == null) { + throw new SmackException.NotLoggedInException(); } + + return getOmemoService().getOmemoStoreBackend().getFingerprint(getOwnDevice()); } /** @@ -604,21 +588,20 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws IOException */ - public OmemoFingerprint getFingerprint(OmemoDevice device) + public synchronized OmemoFingerprint getFingerprint(OmemoDevice device) throws CannotEstablishOmemoSessionException, SmackException.NotLoggedInException, CorruptedOmemoKeyException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException { - synchronized (LOCK) { - if (getOwnJid() == null) { - throw new SmackException.NotLoggedInException(); - } - - if (device.equals(getOwnDevice())) { - return getOwnFingerprint(); - } - - return getOmemoService().getOmemoStoreBackend().getFingerprintAndMaybeBuildSession(new LoggedInOmemoManager(this), device); + if (getOwnJid() == null) { + throw new SmackException.NotLoggedInException(); } + + if (device.equals(getOwnDevice())) { + return getOwnFingerprint(); + } + + return getOmemoService().getOmemoStoreBackend() + .getFingerprintAndMaybeBuildSession(new LoggedInOmemoManager(this), device); } /** @@ -635,30 +618,28 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws IOException */ - public HashMap getActiveFingerprints(BareJid contact) + public synchronized HashMap getActiveFingerprints(BareJid contact) throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException { - synchronized (LOCK) { - if (getOwnJid() == null) { - throw new SmackException.NotLoggedInException(); - } - - HashMap fingerprints = new HashMap<>(); - OmemoCachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend() - .loadCachedDeviceList(getOwnDevice(), contact); - - for (int id : deviceList.getActiveDevices()) { - OmemoDevice device = new OmemoDevice(contact, id); - OmemoFingerprint fingerprint = getFingerprint(device); - - if (fingerprint != null) { - fingerprints.put(device, fingerprint); - } - } - - return fingerprints; + if (getOwnJid() == null) { + throw new SmackException.NotLoggedInException(); } + + HashMap fingerprints = new HashMap<>(); + OmemoCachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), + contact); + + for (int id : deviceList.getActiveDevices()) { + OmemoDevice device = new OmemoDevice(contact, id); + OmemoFingerprint fingerprint = getFingerprint(device); + + if (fingerprint != null) { + fingerprints.put(device, fingerprint); + } + } + + return fingerprints; } /** @@ -707,12 +688,10 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws IOException */ - public void requestDeviceListUpdateFor(BareJid contact) + public synchronized void requestDeviceListUpdateFor(BareJid contact) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException { - synchronized (LOCK) { - getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); - } + getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); } /** @@ -728,9 +707,7 @@ public final class OmemoManager extends Manager { public void purgeDeviceList() throws SmackException.NotLoggedInException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, IOException { - synchronized (LOCK) { - getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this)); - } + getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this)); } /** @@ -746,21 +723,19 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotLoggedInException * @throws IOException */ - public void rotateSignedPreKey() + public synchronized void rotateSignedPreKey() throws CorruptedOmemoKeyException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException { - synchronized (LOCK) { - if (!connection().isAuthenticated()) { - throw new SmackException.NotLoggedInException(); - } - - // generate key - getOmemoService().getOmemoStoreBackend().changeSignedPreKey(getOwnDevice()); - - // publish - OmemoBundleElement bundle = getOmemoService().getOmemoStoreBackend().packOmemoBundle(getOwnDevice()); - OmemoService.publishBundle(connection(), getOwnDevice(), bundle); + if (!connection().isAuthenticated()) { + throw new SmackException.NotLoggedInException(); } + + // generate key + getOmemoService().getOmemoStoreBackend().changeSignedPreKey(getOwnDevice()); + + // publish + OmemoBundleElement bundle = getOmemoService().getOmemoStoreBackend().packOmemoBundle(getOwnDevice()); + OmemoService.publishBundle(connection(), getOwnDevice(), bundle); } /** @@ -807,10 +782,8 @@ public final class OmemoManager extends Manager { * * @return deviceId */ - public Integer getDeviceId() { - synchronized (LOCK) { - return deviceId; - } + public synchronized Integer getDeviceId() { + return deviceId; } /** @@ -818,28 +791,24 @@ public final class OmemoManager extends Manager { * * @return omemoDevice */ - public OmemoDevice getOwnDevice() { - synchronized (LOCK) { - BareJid jid = getOwnJid(); - if (jid == null) { - return null; - } - return new OmemoDevice(jid, getDeviceId()); + public synchronized OmemoDevice getOwnDevice() { + BareJid jid = getOwnJid(); + if (jid == null) { + return null; } + return new OmemoDevice(jid, getDeviceId()); } /** * Set the deviceId of the manager to nDeviceId. * @param nDeviceId new deviceId */ - void setDeviceId(int nDeviceId) { - synchronized (LOCK) { - // Move this instance inside the HashMaps - INSTANCES.get(connection()).remove(getDeviceId()); - INSTANCES.get(connection()).put(nDeviceId, this); + synchronized void setDeviceId(int nDeviceId) { + // Move this instance inside the HashMaps + INSTANCES.get(connection()).remove(getDeviceId()); + INSTANCES.get(connection()).put(nDeviceId, this); - this.deviceId = nDeviceId; - } + this.deviceId = nDeviceId; } /** diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java index 1130e63fc..4429b2e83 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java @@ -1075,7 +1075,7 @@ public abstract class OmemoService Date: Thu, 22 Aug 2019 10:30:46 +0200 Subject: [PATCH 18/23] Deprecate setHostAddressByNameOrIp() and move logic into setHost() in ConnectionConfiguration.Builder. --- .../smack/ConnectionConfiguration.java | 46 ++++++++++--------- .../smack/ConnectionConfigurationTest.java | 6 +-- 2 files changed, 27 insertions(+), 25 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 d14a82d46..77adc4382 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -646,7 +646,7 @@ public abstract class ConnectionConfiguration { /** * Set the Internet address of the host providing the XMPP service. If set, then this will overwrite anything - * set via {@link #setHost(String)}. + * set via {@link #setHost(CharSequence)}. * * @param address the Internet address of the host providing the XMPP service. * @return a reference to this builder. @@ -658,16 +658,29 @@ public abstract class ConnectionConfiguration { } /** - * Set the name of the host providing the XMPP service. Note that this method does only allow DNS names and not - * IP addresses. Use {@link #setHostAddress(InetAddress)} if you want to explicitly set the Internet address of - * the host providing the XMPP service. + * Set the name of the host providing the XMPP service. This method takes DNS names and + * IP addresses. * * @param host the DNS name of the host providing the XMPP service. * @return a reference to this builder. */ - public B setHost(String host) { - DnsName hostDnsName = DnsName.from(host); - return setHost(hostDnsName); + public B setHost(CharSequence host) { + String fqdnOrIpString = host.toString(); + if (InetAddressUtil.isIpAddress(fqdnOrIpString)) { + InetAddress hostInetAddress; + try { + hostInetAddress = InetAddress.getByName(fqdnOrIpString); + } + catch (UnknownHostException e) { + // Should never happen. + throw new AssertionError(e); + } + setHostAddress(hostInetAddress); + } else { + DnsName dnsName = DnsName.from(fqdnOrIpString); + setHost(dnsName); + } + return getThis(); } /** @@ -691,23 +704,12 @@ public abstract class ConnectionConfiguration { * @see #setHost(DnsName) * @see #setHostAddress(InetAddress) * @since 4.3.2 + * @deprecated use {@link #setHost(CharSequence)} instead. */ + @Deprecated + // TODO: Remove in Smack 4.5. public B setHostAddressByNameOrIp(CharSequence fqdnOrIp) { - String fqdnOrIpString = fqdnOrIp.toString(); - if (InetAddressUtil.isIpAddress(fqdnOrIp)) { - InetAddress hostInetAddress; - try { - hostInetAddress = InetAddress.getByName(fqdnOrIpString); - } - catch (UnknownHostException e) { - // Should never happen. - throw new AssertionError(e); - } - setHostAddress(hostInetAddress); - } else { - setHost(fqdnOrIpString); - } - return getThis(); + return setHost(fqdnOrIp); } public B setPort(int port) { diff --git a/smack-core/src/test/java/org/jivesoftware/smack/ConnectionConfigurationTest.java b/smack-core/src/test/java/org/jivesoftware/smack/ConnectionConfigurationTest.java index 6637eae56..163937c07 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/ConnectionConfigurationTest.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/ConnectionConfigurationTest.java @@ -1,6 +1,6 @@ /** * - * Copyright 2018 Florian Schmaus. + * Copyright 2018-2019 Florian Schmaus. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ public class ConnectionConfigurationTest { DummyConnectionConfiguration.Builder builder = newUnitTestBuilder(); final String ip = "192.168.0.1"; - builder.setHostAddressByNameOrIp(ip); + builder.setHost(ip); DummyConnectionConfiguration connectionConfiguration = builder.build(); assertEquals('/' + ip, connectionConfiguration.getHostAddress().toString()); @@ -39,7 +39,7 @@ public class ConnectionConfigurationTest { DummyConnectionConfiguration.Builder builder = newUnitTestBuilder(); final String fqdn = "foo.example.org"; - builder.setHostAddressByNameOrIp(fqdn); + builder.setHost(fqdn); DummyConnectionConfiguration connectionConfiguration = builder.build(); assertEquals(fqdn, connectionConfiguration.getHost().toString()); From d40b16b5a798e795ceb3b6a09dda77ec2f800e58 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 22 Aug 2019 10:50:03 +0200 Subject: [PATCH 19/23] Fix log message format in Roster: s/{}/{0}/ The MessageFormat used by the JUL Logger requires integers between the curly brackets (unlike SLF4J). --- .../src/main/java/org/jivesoftware/smack/roster/Roster.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index 73486cd12..04e2dd8e9 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java @@ -1620,7 +1620,7 @@ public final class Roster extends Manager { @Override public void onSuccess(IQ packet) { final XMPPConnection connection = connection(); - LOGGER.log(Level.FINE, "RosterResultListener received {}", packet); + LOGGER.log(Level.FINE, "RosterResultListener received {0}", packet); Collection addedEntries = new ArrayList<>(); Collection updatedEntries = new ArrayList<>(); Collection deletedEntries = new ArrayList<>(); From 8f2eefaeb01eaff688bfa9b5fabe0c19342aae8c Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 22 Aug 2019 15:28:54 +0200 Subject: [PATCH 20/23] Fix whitespace errors in AbstractXMPPConnection --- .../java/org/jivesoftware/smack/AbstractXMPPConnection.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 2b65eb2b8..86b2097b2 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -1192,11 +1192,11 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { @Override public void setReplyTimeout(long timeout) { - if (Long.MAX_VALUE - System.currentTimeMillis() < timeout) { - throw new IllegalArgumentException("Extremely long reply timeout"); + if (Long.MAX_VALUE - System.currentTimeMillis() < timeout) { + throw new IllegalArgumentException("Extremely long reply timeout"); } else { - replyTimeout = timeout; + replyTimeout = timeout; } } From a160358d5d32e9049cf59683f3063b7c1b35135c Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 24 Aug 2019 12:22:38 +0200 Subject: [PATCH 21/23] OMEMO: Allow prekey ID 0 --- .../jivesoftware/smackx/omemo/element/OmemoBundleElement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java index efe872ec3..b9c27060d 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/element/OmemoBundleElement.java @@ -60,8 +60,8 @@ public abstract class OmemoBundleElement implements ExtensionElement { * @param preKeysB64 HashMap of base64 encoded preKeys */ public OmemoBundleElement(int signedPreKeyId, String signedPreKeyB64, String signedPreKeySigB64, String identityKeyB64, HashMap preKeysB64) { - if (signedPreKeyId <= 0) { - throw new IllegalArgumentException("signedPreKeyId MUST be greater than 0."); + if (signedPreKeyId < 0) { + throw new IllegalArgumentException("signedPreKeyId MUST be greater than or equal to 0."); } this.signedPreKeyId = signedPreKeyId; this.signedPreKeyB64 = StringUtils.requireNotNullNorEmpty(signedPreKeyB64, "signedPreKeyB64 MUST NOT be null nor empty."); From 851bd3c5dd662edd0eecfee3267979b33e87e482 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 27 Aug 2019 09:00:22 +0200 Subject: [PATCH 22/23] Add IllegalArgumentException to parsing exceptions As it is thrown by many Element constructors. --- .../java/org/jivesoftware/smack/AbstractXMPPConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 86b2097b2..0d8209ba9 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -1256,7 +1256,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { try { stanza = PacketParserUtils.parseStanza(parser, incomingStreamXmlEnvironment); } - catch (XmlPullParserException | SmackParsingException | IOException e) { + catch (XmlPullParserException | SmackParsingException | IOException | IllegalArgumentException e) { CharSequence content = PacketParserUtils.parseContentDepth(parser, parserDepth); UnparseableStanza message = new UnparseableStanza(content, e); From 4249c1a845320e8d92f415b59d7fa06e3bcfe194 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Wed, 28 Aug 2019 22:50:58 +0200 Subject: [PATCH 23/23] Improve PepManager.publish() by using PubSubManager.tryToPublishAndPossibleAutoCreate(). This also swaps the parameters of the method. Thanks to Guus der Kinderen for suggesting this. --- .../jivesoftware/smackx/pep/PepManager.java | 15 +++++---- .../smackx/pubsub/PubSubManager.java | 30 ++++++++++++------ .../smackx/omemo/OmemoManager.java | 9 ++++-- .../smackx/omemo/OmemoService.java | 31 ++++++++++++------- .../smack/smackrepl/OmemoClient.java | 4 ++- 5 files changed, 57 insertions(+), 32 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 0cfb921d4..d41d4b89d 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 @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2015-2018 Florian Schmaus + * Copyright 2003-2007 Jive Software, 2015-2019 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,6 @@ import org.jivesoftware.smackx.pubsub.EventElement; import org.jivesoftware.smackx.pubsub.Item; import org.jivesoftware.smackx.pubsub.LeafNode; import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException; -import org.jivesoftware.smackx.pubsub.PubSubException.NotAPubSubNodeException; import org.jivesoftware.smackx.pubsub.PubSubFeature; import org.jivesoftware.smackx.pubsub.PubSubManager; import org.jivesoftware.smackx.pubsub.filter.EventExtensionFilter; @@ -151,19 +150,19 @@ public final class PepManager extends Manager { /** * Publish an event. * + * @param nodeId the ID of the node to publish on. * @param item the item to publish. - * @param node the node to publish on. + * @return the leaf node the item was published on. * @throws NotConnectedException * @throws InterruptedException * @throws XMPPErrorException * @throws NoResponseException - * @throws NotAPubSubNodeException * @throws NotALeafNodeException */ - public void publish(Item item, String node) throws NotConnectedException, InterruptedException, - NoResponseException, XMPPErrorException, NotAPubSubNodeException, NotALeafNodeException { - LeafNode pubSubNode = pepPubSubManager.getLeafNode(node); - pubSubNode.publish(item); + public LeafNode publish(String nodeId, Item item) throws NotConnectedException, InterruptedException, + NoResponseException, XMPPErrorException, NotALeafNodeException { + // PEP nodes are auto created if not existent. Hence Use PubSubManager.tryToPublishAndPossibleAutoCreate() here. + return pepPubSubManager.tryToPublishAndPossibleAutoCreate(nodeId, item); } /** diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java index a65872cae..85e37e25e 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/PubSubManager.java @@ -186,6 +186,17 @@ public final class PubSubManager extends Manager { pubSubService = toAddress; } + private void checkIfXmppErrorBecauseOfNotLeafNode(String nodeId, XMPPErrorException xmppErrorException) + throws XMPPErrorException, NotALeafNodeException { + Condition condition = xmppErrorException.getStanzaError().getCondition(); + if (condition == Condition.feature_not_implemented) { + // XEP-0060 § 6.5.9.5: Item retrieval not supported, e.g. because node is a collection node + throw new PubSubException.NotALeafNodeException(nodeId, pubSubService); + } + + throw xmppErrorException; + } + /** * Creates an instant node, if supported. * @@ -387,13 +398,7 @@ public final class PubSubManager extends Manager { // Try to ensure that this is not a collection node by asking for one item form the node. leafNode.getItems(1); } catch (XMPPErrorException e) { - Condition condition = e.getStanzaError().getCondition(); - if (condition == Condition.feature_not_implemented) { - // XEP-0060 § 6.5.9.5: Item retrieval not supported, e.g. because node is a collection node - throw new PubSubException.NotALeafNodeException(id, pubSubService); - } - - throw e; + checkIfXmppErrorBecauseOfNotLeafNode(id, e); } nodeMap.put(id, leafNode); @@ -430,12 +435,19 @@ public final class PubSubManager extends Manager { * @throws XMPPErrorException * @throws NotConnectedException * @throws InterruptedException + * @throws NotALeafNodeException * @since 4.2.1 */ public LeafNode tryToPublishAndPossibleAutoCreate(String id, I item) - throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, + NotALeafNodeException { LeafNode leafNode = new LeafNode(this, id); - leafNode.publish(item); + + try { + leafNode.publish(item); + } catch (XMPPErrorException e) { + checkIfXmppErrorBecauseOfNotLeafNode(id, e); + } // If LeafNode.publish() did not throw then we have successfully published an item and possible auto-created // (XEP-0163 § 3., XEP-0060 § 7.1.4) the node. So we can put the node into the nodeMap. 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 c2fe1635a..f79fa9043 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 @@ -703,10 +703,11 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws SmackException.NoResponseException * @throws IOException + * @throws PubSubException.NotALeafNodeException */ public void purgeDeviceList() throws SmackException.NotLoggedInException, InterruptedException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException, IOException { + SmackException.NotConnectedException, SmackException.NoResponseException, IOException, PubSubException.NotALeafNodeException { getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this)); } @@ -722,10 +723,12 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException XMPP error * @throws SmackException.NotLoggedInException * @throws IOException + * @throws PubSubException.NotALeafNodeException */ public synchronized void rotateSignedPreKey() throws CorruptedOmemoKeyException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, IOException { + SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, + IOException, PubSubException.NotALeafNodeException { if (!connection().isAuthenticated()) { throw new SmackException.NotLoggedInException(); } @@ -1032,7 +1035,7 @@ public final class OmemoManager extends Manager { try { OmemoService.publishDeviceList(connection(), newDeviceList); } catch (InterruptedException | XMPPException.XMPPErrorException | - SmackException.NotConnectedException | SmackException.NoResponseException e) { + SmackException.NotConnectedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException e) { LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e); } } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java index 4429b2e83..d3ce4a18b 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java @@ -74,9 +74,11 @@ 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.omemo.util.OmemoMessageBuilder; +import org.jivesoftware.smackx.pep.PepManager; import org.jivesoftware.smackx.pubsub.LeafNode; import org.jivesoftware.smackx.pubsub.PayloadItem; import org.jivesoftware.smackx.pubsub.PubSubException; +import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException; import org.jivesoftware.smackx.pubsub.PubSubManager; import org.jxmpp.jid.BareJid; @@ -581,12 +583,13 @@ public abstract class OmemoService(bundle)); + SmackException.NoResponseException, NotALeafNodeException { + PepManager pm = PepManager.getInstanceFor(connection); + pm.publish(userDevice.getBundleNodeName(), new PayloadItem<>(bundle)); } /** @@ -635,10 +638,9 @@ public abstract class OmemoService(deviceList)); + SmackException.NoResponseException, NotALeafNodeException { + PepManager pm = PepManager.getInstanceFor(connection); + pm.publish(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList)); } /** @@ -1119,7 +1121,9 @@ public abstract class OmemoService