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/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" }); 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(); ``` 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._ } }; 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. 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 +

+ + +

Improvement +

+ +

4.3.3 -- 2019-03-14

Bug 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 eff83268d..0d8209ba9 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -1192,7 +1192,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(); @@ -1251,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); 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 e302a1159..5c07cb38d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -750,7 +750,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. @@ -762,16 +762,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(); } /** @@ -795,23 +808,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/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-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java index b2873ac59..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 @@ -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,16 @@ public class StringUtils { } return cs.toString(); } + + /** + * 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 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) { 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()); 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); + } } 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()); 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 getJoinedRooms() { 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-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index d9e54e886..790e4d059 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 @@ -1680,7 +1680,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<>(); 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/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/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-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-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> INSTANCES = new WeakHashMap<>(); private final OmemoService service; @@ -234,23 +234,22 @@ public final class OmemoManager extends Manager { * @throws XMPPException.XMPPErrorException * @throws SmackException.NotLoggedInException * @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 { - 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); + PubSubException.NotALeafNodeException, IOException { + 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); } /** @@ -279,8 +278,9 @@ public final class OmemoManager extends Manager { * @see #requestDeviceListUpdateFor(BareJid) * @param contact contact we want to get a set of device of. * @return set of known devices of that contact. + * @throws IOException */ - public Set getDevicesOf(BareJid contact) { + public Set getDevicesOf(BareJid contact) throws IOException { OmemoCachedDeviceList list = getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), contact); HashSet devices = new HashSet<>(); @@ -304,16 +304,15 @@ 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 { - synchronized (LOCK) { + SmackException.NoResponseException, SmackException.NotLoggedInException, IOException { Set recipients = new HashSet<>(); recipients.add(recipient); return encrypt(recipients, message); - } } /** @@ -328,19 +327,18 @@ 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) + public synchronized OmemoMessage.Sent encrypt(Set recipients, String message) throws CryptoFailedException, UndecidedOmemoIdentityException, InterruptedException, SmackException.NotConnectedException, - SmackException.NoResponseException, SmackException.NotLoggedInException { - 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); + SmackException.NoResponseException, SmackException.NotLoggedInException, IOException { + LoggedInOmemoManager guard = new LoggedInOmemoManager(this); + Set devices = getDevicesOf(getOwnJid()); + for (BareJid recipient : recipients) { + devices.addAll(getDevicesOf(recipient)); } + return service.createOmemoMessage(guard, devices, message); } /** @@ -357,24 +355,23 @@ 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) + public synchronized OmemoMessage.Sent encrypt(MultiUserChat muc, String message) throws UndecidedOmemoIdentityException, CryptoFailedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, NoOmemoSupportException, - SmackException.NotLoggedInException { - 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); + SmackException.NotLoggedInException, IOException { + 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); } /** @@ -390,10 +387,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 +402,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,24 +485,22 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws NoSuchAlgorithmException * @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 { - synchronized (LOCK) { - Message message = new Message(); - message.setFrom(getOwnJid()); - message.setTo(recipient.getJid()); + CryptoFailedException, CannotEstablishOmemoSessionException, IOException { + 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); } /** @@ -516,14 +513,13 @@ public final class OmemoManager extends Manager { * @throws SmackException.NoResponseException * @throws PubSubException.NotALeafNodeException * @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 { - synchronized (LOCK) { - OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); - return !deviceList.getActiveDevices().isEmpty(); - } + SmackException.NotConnectedException, SmackException.NoResponseException, IOException { + OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); + return !deviceList.getActiveDevices().isEmpty(); } /** @@ -569,16 +565,15 @@ 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 { - synchronized (LOCK) { - if (getOwnJid() == null) { - throw new SmackException.NotLoggedInException(); - } - - return getOmemoService().getOmemoStoreBackend().getFingerprint(getOwnDevice()); + public synchronized OmemoFingerprint getOwnFingerprint() + throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, IOException { + if (getOwnJid() == null) { + throw new SmackException.NotLoggedInException(); } + + return getOmemoService().getOmemoStoreBackend().getFingerprint(getOwnDevice()); } /** @@ -591,22 +586,22 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws InterruptedException * @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 { - synchronized (LOCK) { - if (getOwnJid() == null) { - throw new SmackException.NotLoggedInException(); - } - - if (device.equals(getOwnDevice())) { - return getOwnFingerprint(); - } - - return getOmemoService().getOmemoStoreBackend().getFingerprintAndMaybeBuildSession(new LoggedInOmemoManager(this), device); + SmackException.NoResponseException, IOException { + if (getOwnJid() == null) { + throw new SmackException.NotLoggedInException(); } + + if (device.equals(getOwnDevice())) { + return getOwnFingerprint(); + } + + return getOmemoService().getOmemoStoreBackend() + .getFingerprintAndMaybeBuildSession(new LoggedInOmemoManager(this), device); } /** @@ -621,31 +616,30 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException * @throws InterruptedException * @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 { - 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; + SmackException.NoResponseException, IOException { + 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; } /** @@ -692,13 +686,12 @@ public final class OmemoManager extends Manager { * @throws XMPPException.XMPPErrorException * @throws SmackException.NotConnectedException * @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 { - synchronized (LOCK) { - getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); - } + SmackException.NotConnectedException, SmackException.NoResponseException, IOException { + getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact); } /** @@ -709,13 +702,13 @@ public final class OmemoManager extends Manager { * @throws XMPPException.XMPPErrorException * @throws SmackException.NotConnectedException * @throws SmackException.NoResponseException + * @throws IOException + * @throws PubSubException.NotALeafNodeException */ public void purgeDeviceList() throws SmackException.NotLoggedInException, InterruptedException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException { - synchronized (LOCK) { - getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this)); - } + SmackException.NotConnectedException, SmackException.NoResponseException, IOException, PubSubException.NotALeafNodeException { + getOmemoService().purgeDeviceList(new LoggedInOmemoManager(this)); } /** @@ -729,22 +722,23 @@ public final class OmemoManager extends Manager { * @throws SmackException.NotConnectedException XMPP error * @throws SmackException.NoResponseException XMPP error * @throws SmackException.NotLoggedInException + * @throws IOException + * @throws PubSubException.NotALeafNodeException */ - public void rotateSignedPreKey() + public synchronized void rotateSignedPreKey() throws CorruptedOmemoKeyException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { - 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); + SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, + IOException, PubSubException.NotALeafNodeException { + 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); } /** @@ -791,10 +785,8 @@ public final class OmemoManager extends Manager { * * @return deviceId */ - public Integer getDeviceId() { - synchronized (LOCK) { - return deviceId; - } + public synchronized Integer getDeviceId() { + return deviceId; } /** @@ -802,28 +794,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; } /** @@ -956,8 +944,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 +967,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 +1006,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())) { @@ -1038,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/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..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 @@ -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; @@ -73,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; @@ -234,11 +237,12 @@ public abstract class OmemoService contactsDevices, @@ -341,7 +347,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 +519,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(); @@ -574,12 +583,13 @@ public abstract class OmemoService(bundle)); + SmackException.NoResponseException, NotALeafNodeException { + PepManager pm = PepManager.getInstanceFor(connection); + pm.publish(userDevice.getBundleNodeName(), new PayloadItem<>(bundle)); } /** @@ -628,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)); } /** @@ -643,10 +652,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 +841,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 +872,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,10 +1074,10 @@ 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 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."); diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoCarbonCopyStanzaReceivedListener.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoCarbonCopyStanzaReceivedListener.java index 4d766025c..cdfc31389 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoCarbonCopyStanzaReceivedListener.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoCarbonCopyStanzaReceivedListener.java @@ -16,6 +16,8 @@ */ package org.jivesoftware.smackx.omemo.internal.listener; +import java.io.IOException; + import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smackx.carbons.packet.CarbonExtension; @@ -26,5 +28,6 @@ import org.jivesoftware.smackx.omemo.OmemoManager; */ public interface OmemoCarbonCopyStanzaReceivedListener { - void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction, Message carbonCopy, Message wrappingMessage, OmemoManager.LoggedInOmemoManager omemoManager); + void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction, Message carbonCopy, Message wrappingMessage, + OmemoManager.LoggedInOmemoManager omemoManager) throws IOException; } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoMessageStanzaReceivedListener.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoMessageStanzaReceivedListener.java index 031f259d8..c6d7ecc58 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoMessageStanzaReceivedListener.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/listener/OmemoMessageStanzaReceivedListener.java @@ -16,11 +16,13 @@ */ package org.jivesoftware.smackx.omemo.internal.listener; +import java.io.IOException; + import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smackx.omemo.OmemoManager; public interface OmemoMessageStanzaReceivedListener { - void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager omemoManager); + void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager omemoManager) throws IOException; } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java index 197aab42d..043d6a32b 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java @@ -20,6 +20,7 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.CIPHERMOD 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.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; @@ -219,10 +220,11 @@ public class OmemoMessageBuilder 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)); 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/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/OmemoClient.java b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/OmemoClient.java new file mode 100644 index 000000000..7518ba18e --- /dev/null +++ b/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/OmemoClient.java @@ -0,0 +1,218 @@ +/** + * + * 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.jivesoftware.smackx.pubsub.PubSubException; + +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 | PubSubException.NotALeafNodeException e) { + LOGGER.log(Level.SEVERE, "Unexpected Exception", e); + } + } + } + + private static void print(String msg) { + // CHECKSTYLE:OFF + System.out.println(msg); + // CHECKSTYLE:ON + } +} 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;