diff --git a/config/checkstyle.xml b/config/checkstyle.xml index d925f4909..041e31ffb 100644 --- a/config/checkstyle.xml +++ b/config/checkstyle.xml @@ -6,11 +6,6 @@ - - - - - @@ -66,14 +61,6 @@ - - - - - diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index 44739287b..05eb8e311 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -103,7 +103,6 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental | [OMEMO Multi End Message and Object Encryption](omemo.md) | [XEP-0384](https://xmpp.org/extensions/xep-0384.html) | n/a | Encrypt messages using OMEMO encryption (currently only with smack-omemo-signal -> GPLv3). | | [Consistent Color Generation](consistent_colors.md) | [XEP-0392](https://xmpp.org/extensions/xep-0392.html) | 0.4.0 | Generate consistent colors for identifiers like usernames to provide a consistent user experience. | | [Message Markup](messagemarkup.md) | [XEP-0394](https://xmpp.org/extensions/xep-0394.html) | 0.1.0 | Style message bodies while keeping body and markup information separated. | -| DNS Queries over XMPP (DoX) | [XEP-0418](https://xmpp.org/extensions/xep-0418.html) | 0.1.0 | Send DNS queries and responses over XMPP. | Unofficial XMPP Extensions -------------------------- diff --git a/documentation/processing.md b/documentation/processing.md index 10d6a66ae..9a16917cf 100644 --- a/documentation/processing.md +++ b/documentation/processing.md @@ -20,7 +20,7 @@ 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("mary@jivesoftware.com")); // Assume we've created an XMPPConnection named "connection". // First, register a stanza collector using the filter we created. diff --git a/settings.gradle b/settings.gradle index 6188d60b6..53b0bd554 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,7 +12,6 @@ include 'smack-core', 'smack-debug-slf4j', 'smack-resolver-dnsjava', 'smack-resolver-minidns', - 'smack-resolver-minidns-dox', 'smack-resolver-javax', 'smack-sasl-javax', 'smack-sasl-provided', diff --git a/smack-android/src/main/java/org/jivesoftware/smack/util/stringencoder/android/AndroidBase64Encoder.java b/smack-android/src/main/java/org/jivesoftware/smack/util/stringencoder/android/AndroidBase64Encoder.java index 8e4d47d6c..5d143737d 100644 --- a/smack-android/src/main/java/org/jivesoftware/smack/util/stringencoder/android/AndroidBase64Encoder.java +++ b/smack-android/src/main/java/org/jivesoftware/smack/util/stringencoder/android/AndroidBase64Encoder.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2019 Florian Schmaus + * Copyright © 2014 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,17 +42,18 @@ public final class AndroidBase64Encoder implements org.jivesoftware.smack.util.s } @Override - public String encodeToString(byte[] input) { - return Base64.encodeToString(input, BASE64_ENCODER_FLAGS); + public byte[] decode(byte[] input, int offset, int len) { + return Base64.decode(input, offset, len, 0); } @Override - public String encodeToStringWithoutPadding(byte[] input) { - return Base64.encodeToString(input, BASE64_ENCODER_FLAGS | Base64.NO_PADDING); + public String encodeToString(byte[] input, int offset, int len) { + return Base64.encodeToString(input, offset, len, BASE64_ENCODER_FLAGS); } @Override - public byte[] encode(byte[] input) { - return Base64.encode(input, BASE64_ENCODER_FLAGS); + public byte[] encode(byte[] input, int offset, int len) { + return Base64.encode(input, offset, len, BASE64_ENCODER_FLAGS); } + } diff --git a/smack-core/build.gradle b/smack-core/build.gradle index 69ee5d468..4fcf904ce 100644 --- a/smack-core/build.gradle +++ b/smack-core/build.gradle @@ -20,6 +20,7 @@ dependencies { testCompile "org.powermock:powermock-module-junit4-rule:$powerMockVersion" testCompile "org.powermock:powermock-api-mockito2:$powerMockVersion" testCompile 'com.jamesmurty.utils:java-xmlbuilder:1.2' + testCompile 'net.iharder:base64:2.3.8' } class CreateFileTask extends DefaultTask { 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 e82ad15aa..49b1e516f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AbstractXMPPConnection.java @@ -41,7 +41,6 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArraySet; @@ -344,17 +343,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { protected final AsyncButOrdered inOrderListeners = new AsyncButOrdered<>(); - /** - * An executor which uses {@link #asyncGoLimited(Runnable)} to limit the number of asynchronously processed runnables - * per connection. - */ - private final Executor limitedExcutor = new Executor() { - @Override - public void execute(Runnable runnable) { - asyncGoLimited(runnable); - } - }; - /** * The used host to establish the connection to */ @@ -1348,7 +1336,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { executorService = ASYNC_BUT_ORDERED.asExecutorFor(this); break; case async: - executorService = limitedExcutor; + executorService = CACHED_EXECUTOR_SERVICE; break; } final IQRequestHandler finalIqRequestHandler = iqRequestHandler; @@ -1365,7 +1353,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { return; } - assert response.isResponseIQ(); + assert (response.getType() == IQ.Type.result || response.getType() == IQ.Type.error); response.setTo(iqRequest.getFrom()); response.setStanzaId(iqRequest.getStanzaId()); @@ -1391,7 +1379,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { final Collection listenersToNotify = new LinkedList<>(); extractMatchingListeners(packet, asyncRecvListeners, listenersToNotify); for (final StanzaListener listener : listenersToNotify) { - asyncGoLimited(new Runnable() { + asyncGo(new Runnable() { @Override public void run() { try { @@ -1887,75 +1875,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection { return getClass().getSimpleName() + '[' + localEndpointString + "] (" + getConnectionCounter() + ')'; } - /** - * A queue of deferred runnables that where not executed immediately because {@link #currentAsyncRunnables} reached - * {@link #maxAsyncRunnables}. Note that we use a {@code LinkedList} in order to avoid space blowups in case the - * list ever becomes very big and shrinks again. - */ - private final Queue deferredAsyncRunnables = new LinkedList<>(); - - private int deferredAsyncRunnablesCount; - - private int deferredAsyncRunnablesCountPrevious; - - private int maxAsyncRunnables = SmackConfiguration.getDefaultConcurrencyLevelLimit(); - - private int currentAsyncRunnables; - - protected void asyncGoLimited(final Runnable runnable) { - Runnable wrappedRunnable = new Runnable() { - @Override - public void run() { - runnable.run(); - - synchronized (deferredAsyncRunnables) { - Runnable defferredRunnable = deferredAsyncRunnables.poll(); - if (defferredRunnable == null) { - currentAsyncRunnables--; - } else { - deferredAsyncRunnablesCount--; - asyncGo(defferredRunnable); - } - } - } - }; - - synchronized (deferredAsyncRunnables) { - if (currentAsyncRunnables < maxAsyncRunnables) { - currentAsyncRunnables++; - asyncGo(wrappedRunnable); - } else { - deferredAsyncRunnablesCount++; - deferredAsyncRunnables.add(wrappedRunnable); - } - - final int HIGH_WATERMARK = 100; - final int INFORM_WATERMARK = 20; - - final int deferredAsyncRunnablesCount = this.deferredAsyncRunnablesCount; - - if (deferredAsyncRunnablesCount >= HIGH_WATERMARK - && deferredAsyncRunnablesCountPrevious < HIGH_WATERMARK) { - LOGGER.log(Level.WARNING, "High watermark of " + HIGH_WATERMARK + " simultaneous executing runnables reached"); - } else if (deferredAsyncRunnablesCount >= INFORM_WATERMARK - && deferredAsyncRunnablesCountPrevious < INFORM_WATERMARK) { - LOGGER.log(Level.INFO, INFORM_WATERMARK + " simultaneous executing runnables reached"); - } - - deferredAsyncRunnablesCountPrevious = deferredAsyncRunnablesCount; - } - } - - public void setMaxAsyncOperations(int maxAsyncOperations) { - if (maxAsyncOperations < 1) { - throw new IllegalArgumentException("Max async operations must be greater than 0"); - } - - synchronized (deferredAsyncRunnables) { - maxAsyncRunnables = maxAsyncOperations; - } - } - protected static void asyncGo(Runnable runnable) { CACHED_EXECUTOR_SERVICE.execute(runnable); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java b/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java index 6fb6244ab..00fb94549 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/AsyncButOrdered.java @@ -55,16 +55,6 @@ public class AsyncButOrdered { private final Map threadActiveMap = new WeakHashMap<>(); - private final Executor executor; - - public AsyncButOrdered() { - this(null); - } - - public AsyncButOrdered(Executor executor) { - this.executor = executor; - } - /** * Invoke the given {@link Runnable} asynchronous but ordered in respect to the given key. * @@ -96,11 +86,7 @@ public class AsyncButOrdered { if (newHandler) { Handler handler = new Handler(keyQueue, key); threadActiveMap.put(key, true); - if (executor == null) { - AbstractXMPPConnection.asyncGo(handler); - } else { - executor.execute(handler); - } + AbstractXMPPConnection.asyncGo(handler); } } 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 b87f2ccd2..28c7ea782 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ConnectionConfiguration.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2017-2019 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2017-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. @@ -49,29 +49,9 @@ import org.minidns.dnsname.DnsName; import org.minidns.util.InetAddressUtil; /** - * The connection configuration used for XMPP client-to-server connections. A well configured XMPP service will - * typically only require you to provide two parameters: The XMPP address, also known as the JID, of the user and the - * password. All other configuration parameters could ideally be determined automatically by Smack. Hence it is often - * enough to call {@link Builder#setXmppAddressAndPassword(CharSequence, String)}. - *

- * Technically there are typically at least two parameters required: Some kind of credentials for authentication. And - * the XMPP service domain. The credentials often consists of a username and password use for the SASL authentication. - * But there are also other authentication mechanisms, like client side certificates, which do not require a particular - * username and password. - *

- *

- * There are some misconceptions about XMPP client-to-server parameters: The first is that the username used for - * authentication will be equal to the localpart of the bound XMPP address after authentication. While this is usually - * true, it is not required. Technically the username used for authentication and the resulting XMPP address are - * completely independent from each other. The second common misconception steers from the terms "XMPP host" and "XMPP - * service domain": An XMPP service host is a system which hosts one or multiple XMPP domains. The "XMPP service domain" - * will be usually the domainpart of the bound JID. This domain is used to verify the remote endpoint, typically using - * TLS. This third misconception is that the XMPP service domain is required to become the domainpart of the bound JID. - * Again, while this is very common to be true, it is not strictly required. - *

+ * Configuration to use while establishing the connection to the server. * * @author Gaston Dombiak - * @author Florian Schmaus */ public abstract class ConnectionConfiguration { @@ -554,39 +534,6 @@ public abstract class ConnectionConfiguration { } } - /** - * Convenience method to configure the username, password and XMPP service domain. - * - * @param jid the XMPP address of the user. - * @param password the password of the user. - * @return a reference to this builder. - * @throws XmppStringprepException in case the XMPP address is not valid. - * @see #setXmppAddressAndPassword(EntityBareJid, String) - * @since 4.4.0 - */ - public B setXmppAddressAndPassword(CharSequence jid, String password) throws XmppStringprepException { - return setXmppAddressAndPassword(JidCreate.entityBareFrom(jid), password); - } - - /** - * Convenience method to configure the username, password and XMPP service domain. The localpart of the provided - * JID is used as username and the domanipart is used as XMPP service domain. - *

- * Please note that this does and can not configure the client XMPP address. XMPP services are not required to - * assign bound JIDs where the localpart matches the username and the domainpart matches the verified domainpart. - * Although most services will follow that pattern. - *

- * - * @param jid - * @param password - * @return a reference to this builder. - * @since 4.4.0 - */ - public B setXmppAddressAndPassword(EntityBareJid jid, String password) { - setUsernameAndPassword(jid.getLocalpart(), password); - return setXmppDomain(jid.asDomainBareJid()); - } - /** * Set the XMPP entities username and password. *

diff --git a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java index 10695df47..01f2995a4 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/SmackConfiguration.java @@ -365,19 +365,4 @@ public final class SmackConfiguration { public static void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) { SmackConfiguration.unknownIqRequestReplyMode = Objects.requireNonNull(unknownIqRequestReplyMode, "Must set mode"); } - - private static final int defaultConcurrencyLevelLimit; - - static { - int availableProcessors = Runtime.getRuntime().availableProcessors(); - if (availableProcessors < 8) { - defaultConcurrencyLevelLimit = 8; - } else { - defaultConcurrencyLevelLimit = (int) (availableProcessors * 1.1); - } - } - - public static int getDefaultConcurrencyLevelLimit() { - return defaultConcurrencyLevelLimit; - } } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java index cd7c6cf7e..c6c2db78f 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/XMPPConnection.java @@ -70,27 +70,6 @@ import org.jxmpp.jid.EntityFullJid; * disconnected and then connected again. Listeners of the XMPPConnection will be retained across * connections. *

- *

Incoming Stanza Listeners

- * Most callbacks (listeners, handlers, …) than you can add to a connection come in three different variants: - *
    - *
  • standard
  • - *
  • async (asynchronous)
  • - *
  • sync (synchronous)
  • - *
- *

- * Standard callbacks are invoked concurrently, but it is ensured that the same callback is never run concurrently. - * The callback's identity is used as key for that. The events delivered to the callback preserve the order of the - * causing events of the connection. - *

- *

- * Asynchronous callbacks are run decoupled from the connections main event loop. Hence a callback triggered by - * stanza B may (appear to) invoked before a callback triggered by stanza A, even though stanza A arrived before B. - *

- *

- * Synchronous callbacks are run synchronous to the main event loop of a connection. Hence they are invoked in the - * exact order of how events happen there, most importantly the arrival order of incoming stanzas. You should only - * use synchronous callbacks in rare situations. - *

* * @author Matt Tucker * @author Guenther Niess diff --git a/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java b/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java index def9b310e..fb5d80a69 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/debugger/ConsoleDebugger.java @@ -16,11 +16,12 @@ */ package org.jivesoftware.smack.debugger; +import java.io.PrintWriter; +import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Date; import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.util.ExceptionUtil; /** * Very simple debugger that prints to the console (stdout) the sent and received stanzas. Use @@ -54,8 +55,12 @@ public class ConsoleDebugger extends AbstractDebugger { @Override protected void log(String logMessage, Throwable throwable) { - String stacktrace = ExceptionUtil.getStackTrace(throwable); - log(logMessage + '\n' + stacktrace); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + // CHECKSTYLE:OFF + throwable.printStackTrace(pw); + // CHECKSTYLE:ON + log(logMessage + sw); } public static final class Factory implements SmackDebuggerFactory { diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java index e75e7a0e9..ece30af7b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/AbstractError.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import org.jivesoftware.smack.util.ExceptionUtil; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.PacketUtil; import org.jivesoftware.smack.util.XmlStringBuilder; @@ -151,17 +150,6 @@ public class AbstractError { return getThis(); } - public B setDescriptiveEnText(String descriptiveEnText, Exception exception) { - StringBuilder sb = new StringBuilder(512); - sb.append(descriptiveEnText) - .append('\n'); - - String stacktrace = ExceptionUtil.getStackTrace(exception); - sb.append(stacktrace); - - return setDescriptiveEnText(sb.toString()); - } - public B setTextNamespace(String textNamespace) { this.textNamespace = textNamespace; return getThis(); diff --git a/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java b/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java index 9b15c9c95..3626b9b9b 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java @@ -105,16 +105,6 @@ public abstract class IQ extends Stanza { } } - /** - * Return true if this IQ is a request, i.e. an IQ of type {@link Type#result} or {@link Type#error}. - * - * @return true if IQ type is 'result' or 'error', false otherwise. - * @since 4.4 - */ - public boolean isResponseIQ() { - return !isRequestIQ(); - } - public final String getChildElementName() { return childElementName; } @@ -297,7 +287,7 @@ public abstract class IQ extends Stanza { * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ. */ public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Builder error) { - if (!request.isRequestIQ()) { + if (!(request.getType() == Type.get || request.getType() == Type.set)) { throw new IllegalArgumentException( "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML()); } diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/ExceptionUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/ExceptionUtil.java deleted file mode 100644 index 1c282381a..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/ExceptionUtil.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smack.util; - -import java.io.PrintWriter; -import java.io.StringWriter; - -public class ExceptionUtil { - - public static String getStackTrace(Throwable throwable) { - StringWriter stringWriter = new StringWriter(); - PrintWriter printWriter = new PrintWriter(stringWriter); - - // CHECKSTYLE:OFF - throwable.printStackTrace(printWriter); - // CHECKSTYLE:ON - - printWriter.flush(); - - StringBuffer stringBuffer = stringWriter.getBuffer(); - return stringBuffer.toString(); - } - -} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/RandomUtil.java b/smack-core/src/main/java/org/jivesoftware/smack/util/RandomUtil.java deleted file mode 100644 index 857557869..000000000 --- a/smack-core/src/main/java/org/jivesoftware/smack/util/RandomUtil.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * - * Copyright 2003-2007 Jive Software, 2016-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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smack.util; - -import java.security.SecureRandom; -import java.util.Random; - -public class RandomUtil { - - static final ThreadLocal SECURE_RANDOM = new ThreadLocal() { - @Override - protected SecureRandom initialValue() { - return new SecureRandom(); - } - }; - - /** - * Pseudo-random number generator object for use with randomString(). - * The Random class is not considered to be cryptographically secure, so - * only use these random Strings for low to medium security applications. - */ - static final ThreadLocal RANDOM = new ThreadLocal() { - @Override - protected Random initialValue() { - return new Random(); - } - }; - - public static int nextSecureRandomInt(int bound) { - return SECURE_RANDOM.get().nextInt(bound); - } - - public static int nextSecureRandomInt() { - return SECURE_RANDOM.get().nextInt(); - } -} 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 a3d7ad096..affe6a047 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 @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2016-2019 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 2016-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. @@ -18,6 +18,7 @@ package org.jivesoftware.smack.util; import java.io.UnsupportedEncodingException; +import java.security.SecureRandom; import java.util.Collection; import java.util.Iterator; import java.util.Random; @@ -252,6 +253,18 @@ public class StringUtils { } } + /** + * Pseudo-random number generator object for use with randomString(). + * The Random class is not considered to be cryptographically secure, so + * only use these random Strings for low to medium security applications. + */ + private static final ThreadLocal randGen = new ThreadLocal() { + @Override + protected Random initialValue() { + return new Random(); + } + }; + /** * Array of numbers and letters of mixed case. Numbers appear in the list * twice so that there is a more equal chance that a number will be picked. @@ -275,16 +288,23 @@ public class StringUtils { * @return a random String of numbers and letters of the specified length. */ public static String insecureRandomString(int length) { - return randomString(length, RandomUtil.RANDOM.get()); + return randomString(length, randGen.get()); } + private static final ThreadLocal SECURE_RANDOM = new ThreadLocal() { + @Override + protected SecureRandom initialValue() { + return new SecureRandom(); + } + }; + public static String randomString(final int length) { - return randomString(length, RandomUtil.SECURE_RANDOM.get()); + return randomString(length, SECURE_RANDOM.get()); } public static String randomString(final int length, Random random) { - if (length == 0) { - return ""; + if (length < 1) { + return null; } byte[] randomBytes = new byte[length]; 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 fb6ae327b..80b6928d6 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 @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2019 Florian Schmaus + * Copyright © 2014-2015 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,19 +39,29 @@ public class Base64 { } public static final String encodeToString(byte[] input) { - return base64encoder.encodeToString(input); + byte[] bytes = encode(input); + try { + return new String(bytes, StringUtils.USASCII); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } public static final String encodeToString(byte[] input, int offset, int len) { - return encodeToString(slice(input, offset, len)); - } - - public static final String encodeToStringWithoutPadding(byte[] input) { - return base64encoder.encodeToStringWithoutPadding(input); + byte[] bytes = encode(input, offset, len); + try { + return new String(bytes, StringUtils.USASCII); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } public static final byte[] encode(byte[] input) { - return base64encoder.encode(input); + return encode(input, 0, input.length); + } + + public static final byte[] encode(byte[] input, int offset, int len) { + return base64encoder.encode(input, offset, len); } public static final String decodeToString(String string) { @@ -63,43 +73,34 @@ 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) { + public static final String decodeToString(byte[] input, int offset, int len) { + byte[] bytes = decode(input, offset, len); try { - return base64encoder.decode(string); - } catch (IllegalArgumentException e) { - return null; + return new String(bytes, StringUtils.UTF8); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("UTF-8 not supported", e); } } + public static final byte[] decode(String string) { + return base64encoder.decode(string); + } + public static final byte[] decode(byte[] input) { - String string; - try { - string = new String(input, StringUtils.USASCII); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - return decode(string); + return base64encoder.decode(input, 0, input.length); } - private static byte[] slice(byte[] input, int offset, int len) { - if (offset == 0 && len == input.length) { - return input; - } - - byte[] res = new byte[len]; - System.arraycopy(input, offset, res, 0, len); - return res; + public static final byte[] decode(byte[] input, int offset, int len) { + return base64encoder.decode(input, offset, len); } public interface Encoder { byte[] decode(String string); - String encodeToString(byte[] input); + byte[] decode(byte[] input, int offset, int len); - String encodeToStringWithoutPadding(byte[] input); + String encodeToString(byte[] input, int offset, int len); - byte[] encode(byte[] input); + byte[] encode(byte[] input, int offset, int len); } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java index 3dc30cc76..81c1f9ead 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/DummyConnection.java @@ -64,11 +64,6 @@ public class DummyConnection extends AbstractXMPPConnection { this(getDummyConfigurationBuilder().build()); } - public DummyConnection(CharSequence username, String password, String serviceName) throws XmppStringprepException { - this(getDummyConfigurationBuilder().setUsernameAndPassword(username, password).setXmppDomain( - JidCreate.domainBareFrom(serviceName)).build()); - } - private EntityFullJid getUserJid() { try { return JidCreate.entityFullFrom(config.getUsername() diff --git a/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java b/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java index ae60c4543..20aeedefb 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/test/util/SmackTestSuite.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2014-2019 Florian Schmaus + * Copyright © 2014 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,14 @@ */ package org.jivesoftware.smack.test.util; -import java.util.Base64; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.stringencoder.Base64.Encoder; +import net.iharder.Base64; + /** * The SmackTestSuite takes care of initializing Smack for the unit tests. For example the Base64 * encoder is configured. @@ -31,24 +35,53 @@ public class SmackTestSuite { @Override public byte[] decode(String string) { - return Base64.getDecoder().decode(string); + try { + return Base64.decode(string); + } + catch (IllegalArgumentException e) { + // Expected by e.g. the unit test. + // " Base64-encoded string must have at least four characters, but length specified was 1", + // should not cause an exception, but instead null should be returned. Maybe + // this should be changed in a later Smack release, so that the actual exception + // is handled. + return null; + } + catch (IOException e) { + throw new IllegalStateException(e); + } } @Override - public String encodeToString(byte[] input) { - return Base64.getEncoder().encodeToString(input); + public byte[] decode(byte[] input, int offset, int len) { + try { + return Base64.decode(input, offset, len, 0); + } + catch (IllegalArgumentException e) { + // Expected by e.g. the unit test. + // " Base64-encoded string must have at least four characters, but length specified was 1", + // should not cause an exception, but instead null should be returned. Maybe + // this should be changed in a later Smack release, so that the actual exception + // is handled. + return null; + } catch (IOException e) { + throw new IllegalStateException(e); + } } @Override - public String encodeToStringWithoutPadding(byte[] input) { - return Base64.getEncoder().withoutPadding().encodeToString(input); + public String encodeToString(byte[] input, int offset, int len) { + return Base64.encodeBytes(input, offset, len); } @Override - public byte[] encode(byte[] input) { - return Base64.getEncoder().encode(input); + public byte[] encode(byte[] input, int offset, int len) { + String string = encodeToString(input, offset, len); + try { + return string.getBytes(StringUtils.USASCII); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } - }); } } diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/MemoryLeakTestUtil.java b/smack-core/src/test/java/org/jivesoftware/smack/util/MemoryLeakTestUtil.java deleted file mode 100644 index e014caca5..000000000 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/MemoryLeakTestUtil.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smack.util; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.lang.ref.PhantomReference; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Function; -import java.util.logging.Logger; - -import org.jivesoftware.smack.DummyConnection; -import org.jivesoftware.smack.Manager; - -import org.jxmpp.stringprep.XmppStringprepException; - -/** - * Utility class to test for memory leaks caused by Smack. - *

- * Note that this test is based on the assumption that it is possible to trigger a full garbage collection run, which is - * not the case. See also this - * stackoverflow - * question. Hence the {@link #triggerGarbageCollection()} method defined in this class is not portable and depends - * on implementation depended Java Virtual Machine behavior. - *

- * - * @see SMACK-383 Jira Issue - */ -public class MemoryLeakTestUtil { - - private static final Logger LOGGER = Logger.getLogger(MemoryLeakTestUtil.class.getName()); - - public static void noResourceLeakTest(Function managerSupplier) - throws XmppStringprepException, IllegalArgumentException, InterruptedException { - final int numConnections = 10; - - ReferenceQueue connectionsReferenceQueue = new ReferenceQueue<>(); - ReferenceQueue managerReferenceQueue = new ReferenceQueue<>(); - - // Those two sets ensure that we hold a strong reference to the created PhantomReferences until the end of the - // test. - @SuppressWarnings("ModifiedButNotUsed") - Set> connectionsPhantomReferences = new HashSet<>(); - @SuppressWarnings("ModifiedButNotUsed") - Set> managersPhantomReferences = new HashSet<>(); - - List connections = new ArrayList<>(numConnections); - for (int i = 0; i < numConnections; i++) { - DummyConnection connection = new DummyConnection("foo" + i, "bar", "baz"); - - PhantomReference connectionPhantomReference = new PhantomReference<>(connection, connectionsReferenceQueue); - connectionsPhantomReferences.add(connectionPhantomReference); - - Manager manager = managerSupplier.apply(connection); - PhantomReference managerPhantomReference = new PhantomReference(manager, managerReferenceQueue); - managersPhantomReferences.add(managerPhantomReference); - - connections.add(connection); - } - - // Clear the only references to the created connections. - connections = null; - - triggerGarbageCollection(); - - // Now the connections should have been gc'ed, but not managers not yet. - assertReferencesQueueSize(connectionsReferenceQueue, numConnections); - assertReferencesQueueIsEmpty(managerReferenceQueue); - - // We new create another connection and explicitly a new Manager. This will trigger the cleanup mechanism in the - // WeakHashMaps used by the Manager's iNSTANCE field. This should clean up all references to the Managers. - DummyConnection connection = new DummyConnection("last", "bar", "baz"); - @SuppressWarnings("unused") - Manager manager = managerSupplier.apply(connection); - - // The previous Managers should now be reclaimable by the garbage collector. First trigger a GC run. - triggerGarbageCollection(); - - // Now the Managers should have been freed and this means we should see their phantom references in the - // reference queue. - assertReferencesQueueSize(managerReferenceQueue, numConnections); - } - - private static void assertReferencesQueueSize(ReferenceQueue referenceQueue, int expectedSize) throws IllegalArgumentException, InterruptedException { - final int timeout = 60000; - for (int itemsRemoved = 0; itemsRemoved < expectedSize; ++itemsRemoved) { - Reference reference = referenceQueue.remove(timeout); - assertNotNull("No reference found after " + timeout + "ms", reference); - reference.clear(); - } - - Reference reference = referenceQueue.poll(); - assertNull("Reference queue is not empty when it should be", reference); - } - - private static void assertReferencesQueueIsEmpty(ReferenceQueue referenceQueue) { - Reference reference = referenceQueue.poll(); - assertNull(reference); - } - - private static void triggerGarbageCollection() { - Object object = new Object(); - WeakReference weakReference = new WeakReference<>(object); - object = null; - - int gcCalls = 0; - do { - if (gcCalls > 1000) { - throw new AssertionError("No observed gargabe collection after " + gcCalls + " calls of System.gc()"); - } - System.gc(); - gcCalls++; - } while (weakReference.get() != null); - - // Note that this is no guarantee that a *full* garbage collection run has been made, which is what we actually - // need here in order to prevent false negatives. - LOGGER.finer("Observed garbage collection after " + gcCalls + " calls of System.gc()"); - } -} 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 7679a118e..0cff5a730 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 @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2019 Florian Schmaus. + * Copyright 2003-2007 Jive Software. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,7 +88,13 @@ public class StringUtilsTest { @Test public void testRandomString() { - String result; + // Boundary test + String result = StringUtils.randomString(-1); + assertNull(result); + + // Zero length string test + result = StringUtils.randomString(0); + assertNull(result); // Test various lengths - make sure the same length is returned result = StringUtils.randomString(4); @@ -98,17 +104,4 @@ public class StringUtilsTest { result = StringUtils.randomString(128); assertTrue(result.length() == 128); } - - @Test(expected = NegativeArraySizeException.class) - public void testNegativeArraySizeException() { - // Boundary test - StringUtils.randomString(-1); - } - - @Test - public void testZeroLengthRandomString() { - // Zero length string test - String result = StringUtils.randomString(0); - assertEquals("", result); - } } diff --git a/smack-experimental/build.gradle b/smack-experimental/build.gradle index 0e902139c..2f4d4e0d5 100644 --- a/smack-experimental/build.gradle +++ b/smack-experimental/build.gradle @@ -11,5 +11,5 @@ dependencies { testCompile project(path: ":smack-core", configuration: "archives") testCompile project(path: ":smack-extensions", configuration: "testRuntime") - compile "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion" + compile "org.bouncycastle:bcprov-jdk15on:1.57" } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/DnsOverXmppManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/DnsOverXmppManager.java deleted file mode 100644 index 634eab400..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/DnsOverXmppManager.java +++ /dev/null @@ -1,169 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.dox; - -import java.io.IOException; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.logging.Logger; - -import org.jivesoftware.smack.Manager; -import org.jivesoftware.smack.SmackException.NoResponseException; -import org.jivesoftware.smack.SmackException.NotConnectedException; -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException.XMPPErrorException; -import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; -import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.StanzaError; -import org.jivesoftware.smack.packet.StanzaError.Condition; -import org.jivesoftware.smack.packet.StanzaError.Type; -import org.jivesoftware.smack.util.RandomUtil; -import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; -import org.jivesoftware.smackx.dox.element.DnsIq; - -import org.jxmpp.jid.Jid; -import org.minidns.dnsmessage.DnsMessage; -import org.minidns.dnsmessage.Question; - -public final class DnsOverXmppManager extends Manager { - - private static final Logger LOGGER = Logger.getLogger(DnsOverXmppManager.class.getName()); - - private static final Map INSTANCES = new WeakHashMap<>(); - - public static synchronized DnsOverXmppManager getInstanceFor(XMPPConnection connection) { - DnsOverXmppManager manager = INSTANCES.get(connection); - if (manager == null) { - manager = new DnsOverXmppManager(connection); - INSTANCES.put(connection, manager); - } - return manager; - } - - private static final String NAMESPACE = DnsIq.NAMESPACE; - - private static DnsOverXmppResolver defaultResolver; - - public void setDefaultDnsOverXmppResolver(DnsOverXmppResolver resolver) { - defaultResolver = resolver; - } - - private final ServiceDiscoveryManager serviceDiscoveryManager; - - private DnsOverXmppResolver resolver = defaultResolver; - - private boolean enabled; - - private final AbstractIqRequestHandler dnsIqRequestHandler = new AbstractIqRequestHandler( - DnsIq.ELEMENT, DnsIq.NAMESPACE, IQ.Type.get, Mode.async) { - - @Override - public IQ handleIQRequest(IQ iqRequest) { - DnsOverXmppResolver resolver = DnsOverXmppManager.this.resolver; - if (resolver == null) { - LOGGER.info("Resolver was null while attempting to handle " + iqRequest); - return null; - } - - DnsIq dnsIqRequest = (DnsIq) iqRequest; - DnsMessage query = dnsIqRequest.getDnsMessage(); - - DnsMessage response; - try { - response = resolver.resolve(query); - } catch (IOException exception) { - StanzaError.Builder errorBuilder = StanzaError.getBuilder() - .setType(Type.CANCEL) - .setCondition(Condition.internal_server_error) - .setDescriptiveEnText("Exception while resolving your DNS query", exception) - ; - - IQ errorResponse = IQ.createErrorResponse(iqRequest, errorBuilder); - return errorResponse; - } - - if (query.id != response.id) { - // The ID may not match because the resolver returned a cached result. - response = response.asBuilder().setId(query.id).build(); - } - - DnsIq dnsIqResult = new DnsIq(response); - dnsIqResult.setType(IQ.Type.result); - return dnsIqResult; - } - }; - - private DnsOverXmppManager(XMPPConnection connection) { - super(connection); - this.serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); - } - - public synchronized void setDnsOverXmppResolver(DnsOverXmppResolver resolver) { - this.resolver = resolver; - if (resolver == null) { - disable(); - } - } - - public synchronized void enable() { - if (enabled) return; - - if (resolver == null) { - throw new IllegalStateException("No DnsOverXmppResolver configured"); - } - - XMPPConnection connection = connection(); - if (connection == null) return; - - connection.registerIQRequestHandler(dnsIqRequestHandler); - serviceDiscoveryManager.addFeature(NAMESPACE); - } - - public synchronized void disable() { - if (!enabled) return; - - XMPPConnection connection = connection(); - if (connection == null) return; - - serviceDiscoveryManager.removeFeature(NAMESPACE); - connection.unregisterIQRequestHandler(dnsIqRequestHandler); - } - - public boolean isSupported(Jid jid) - throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - return serviceDiscoveryManager.supportsFeature(jid, NAMESPACE); - } - - public DnsMessage query(Jid jid, Question question) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - DnsMessage queryMessage = DnsMessage.builder() - .addQuestion(question) - .setId(RandomUtil.nextSecureRandomInt()) - .setRecursionDesired(true) - .build(); - return query(jid, queryMessage); - } - - public DnsMessage query(Jid jid, DnsMessage query) - throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - DnsIq queryIq = new DnsIq(query, jid); - - DnsIq responseIq = connection().sendIqRequestAndWaitForResponse(queryIq); - - return responseIq.getDnsMessage(); - } -} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/DnsOverXmppResolver.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/DnsOverXmppResolver.java deleted file mode 100644 index 26f6cfa5e..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/DnsOverXmppResolver.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.dox; - -import java.io.IOException; - -import org.minidns.dnsmessage.DnsMessage; - -public interface DnsOverXmppResolver { - - DnsMessage resolve(DnsMessage query) throws IOException; - -} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/DnsIq.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/DnsIq.java deleted file mode 100644 index 77c626eae..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/DnsIq.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.dox.element; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.util.stringencoder.Base64; - -import org.jxmpp.jid.Jid; -import org.minidns.dnsmessage.DnsMessage; - -public class DnsIq extends IQ { - - public static final String ELEMENT = "dns"; - public static final String NAMESPACE = "urn:xmpp:dox:0"; - - private final DnsMessage dnsMessage; - - private String base64DnsMessage; - - public DnsIq(String base64DnsMessage) throws IOException { - this(Base64.decode(base64DnsMessage)); - this.base64DnsMessage = base64DnsMessage; - } - - public DnsIq(byte[] dnsMessage) throws IOException { - this(new DnsMessage(dnsMessage)); - } - - public DnsIq(DnsMessage dnsQuery, Jid to) { - this(dnsQuery); - setTo(to); - setType(Type.get); - } - - public DnsIq(DnsMessage dnsMessage) { - super(ELEMENT, NAMESPACE); - this.dnsMessage = dnsMessage; - } - - public DnsMessage getDnsMessage() { - return dnsMessage; - } - - @SuppressWarnings("ByteBufferBackingArray") - public String getDnsMessageBase64Encoded() { - if (base64DnsMessage == null) { - ByteBuffer byteBuffer = dnsMessage.getInByteBuffer(); - byte[] bytes = byteBuffer.array(); - base64DnsMessage = Base64.encodeToStringWithoutPadding(bytes); - } - return base64DnsMessage; - } - - @Override - protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) { - xml.rightAngleBracket(); - - xml.escape(getDnsMessageBase64Encoded()); - - return xml; - } - -} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/package-info.java deleted file mode 100644 index 163c14fb8..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/element/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * XEP-0418: DNS Queries over XMPP (DoX) XML providers. - */ -package org.jivesoftware.smackx.dox.element; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/package-info.java deleted file mode 100644 index 0aa8e5137..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Smack's API for XEP-0418: DNS Queries over XMPP (Dox). - */ -package org.jivesoftware.smackx.dox; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/provider/DnsIqProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/provider/DnsIqProvider.java deleted file mode 100644 index 3800060eb..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/provider/DnsIqProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.dox.provider; - -import java.io.IOException; - -import org.jivesoftware.smack.packet.XmlEnvironment; -import org.jivesoftware.smack.parsing.SmackParsingException; -import org.jivesoftware.smack.provider.IQProvider; -import org.jivesoftware.smackx.dox.element.DnsIq; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -public class DnsIqProvider extends IQProvider { - - @Override - public DnsIq parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) - throws XmlPullParserException, IOException, SmackParsingException { - String base64DnsMessage = parser.nextText(); - return new DnsIq(base64DnsMessage); - } - -} diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/provider/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/provider/package-info.java deleted file mode 100644 index c0c8ce8fe..000000000 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/dox/provider/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * XEP-0418: DNS Queries over XMPP (DoX) XML providers. - */ -package org.jivesoftware.smackx.dox.provider; diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java index a872b376c..e4607ad4d 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/hashes/HashManager.java @@ -1,6 +1,6 @@ /** * - * Copyright © 2017 Paul Schaub, 2019 Florian Schmaus + * Copyright © 2017 Paul Schaub * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_512; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.Security; import java.util.Arrays; import java.util.Collections; @@ -60,6 +61,7 @@ public final class HashManager extends Manager { static { Security.addProvider(new BouncyCastleProvider()); } + public static final String PROVIDER = "BC"; public static final String PREFIX_NS_ALGO = "urn:xmpp:hash-function-text-names:"; @@ -222,52 +224,52 @@ public final class HashManager extends Manager { try { switch (algorithm) { case MD5: - md = MessageDigest.getInstance("MD5"); + md = MessageDigest.getInstance("MD5", PROVIDER); break; case SHA_1: - md = MessageDigest.getInstance("SHA-1"); + md = MessageDigest.getInstance("SHA-1", PROVIDER); break; case SHA_224: - md = MessageDigest.getInstance("SHA-224"); + md = MessageDigest.getInstance("SHA-224", PROVIDER); break; case SHA_256: - md = MessageDigest.getInstance("SHA-256"); + md = MessageDigest.getInstance("SHA-256", PROVIDER); break; case SHA_384: - md = MessageDigest.getInstance("SHA-384"); + md = MessageDigest.getInstance("SHA-384", PROVIDER); break; case SHA_512: - md = MessageDigest.getInstance("SHA-512"); + md = MessageDigest.getInstance("SHA-512", PROVIDER); break; case SHA3_224: - md = MessageDigest.getInstance("SHA3-224"); + md = MessageDigest.getInstance("SHA3-224", PROVIDER); break; case SHA3_256: - md = MessageDigest.getInstance("SHA3-256"); + md = MessageDigest.getInstance("SHA3-256", PROVIDER); break; case SHA3_384: - md = MessageDigest.getInstance("SHA3-384"); + md = MessageDigest.getInstance("SHA3-384", PROVIDER); break; case SHA3_512: - md = MessageDigest.getInstance("SHA3-512"); + md = MessageDigest.getInstance("SHA3-512", PROVIDER); break; case BLAKE2B160: - md = MessageDigest.getInstance("BLAKE2b-160"); + md = MessageDigest.getInstance("BLAKE2b-160", PROVIDER); break; case BLAKE2B256: - md = MessageDigest.getInstance("BLAKE2b-256"); + md = MessageDigest.getInstance("BLAKE2b-256", PROVIDER); break; case BLAKE2B384: - md = MessageDigest.getInstance("BLAKE2b-384"); + md = MessageDigest.getInstance("BLAKE2b-384", PROVIDER); break; case BLAKE2B512: - md = MessageDigest.getInstance("BLAKE2b-512"); + md = MessageDigest.getInstance("BLAKE2b-512", PROVIDER); break; default: throw new AssertionError("Invalid enum value: " + algorithm); } return md; - } catch (NoSuchAlgorithmException e) { + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { throw new AssertionError(e); } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java index a50ea0519..7b96cdbd4 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jingle_filetransfer/JingleFileTransferManager.java @@ -32,7 +32,7 @@ public final class JingleFileTransferManager extends Manager { super(connection); } - public static synchronized JingleFileTransferManager getInstanceFor(XMPPConnection connection) { + public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) { JingleFileTransferManager manager = INSTANCES.get(connection); if (manager == null) { manager = new JingleFileTransferManager(connection); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java index dd7db3426..3e3e52a22 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java @@ -178,9 +178,7 @@ public final class MamManager extends Manager { * @param connection the XMPP connection to get the archive for. * @return the instance of MamManager. */ - // CHECKSTYLE:OFF:RegexpSingleline public static MamManager getInstanceFor(XMPPConnection connection) { - // CHECKSTYLE:ON:RegexpSingleline return getInstanceFor(connection, (Jid) null); } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/ReferenceManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/ReferenceManager.java index b588aa5e3..d3bdfce12 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/ReferenceManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/reference/ReferenceManager.java @@ -51,7 +51,7 @@ public final class ReferenceManager extends Manager { * @param connection xmpp connection * @return reference manager instance */ - public static synchronized ReferenceManager getInstanceFor(XMPPConnection connection) { + public static ReferenceManager getInstanceFor(XMPPConnection connection) { ReferenceManager manager = INSTANCES.get(connection); if (manager == null) { manager = new ReferenceManager(connection); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java index a9fd4fcfa..99ec03f5d 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/sid/StableUniqueStanzaIdManager.java @@ -78,7 +78,7 @@ public final class StableUniqueStanzaIdManager extends Manager { * @param connection xmpp-connection * @return manager instance for the connection */ - public static synchronized StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) { + public static StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) { StableUniqueStanzaIdManager manager = INSTANCES.get(connection); if (manager == null) { manager = new StableUniqueStanzaIdManager(connection); diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/SpoilerManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/SpoilerManager.java index fa5c8680b..f3bb80c85 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/SpoilerManager.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/spoiler/SpoilerManager.java @@ -61,7 +61,7 @@ public final class SpoilerManager extends Manager { * @param connection xmpp connection * @return SpoilerManager */ - public static synchronized SpoilerManager getInstanceFor(XMPPConnection connection) { + public static SpoilerManager getInstanceFor(XMPPConnection connection) { SpoilerManager manager = INSTANCES.get(connection); if (manager == null) { manager = new SpoilerManager(connection); diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers index 974c149f2..a47159e88 100644 --- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers +++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers @@ -317,11 +317,4 @@ org.jivesoftware.smackx.message_markup.provider.MarkupElementProvider - - - dns - urn:xmpp:dox:0 - org.jivesoftware.smackx.dox.provider.DnsIqProvider - - diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java index 38b965577..674e5ca9a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/ServiceDiscoveryManager.java @@ -1,6 +1,6 @@ /** * - * Copyright 2003-2007 Jive Software, 2018-2019 Florian Schmaus. + * Copyright 2003-2007 Jive Software, 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. @@ -227,7 +227,7 @@ public final class ServiceDiscoveryManager extends Manager { /** * Returns the type of client that will be returned when asked for the client identity in a * disco request. The valid types are defined by the category client. Follow this link to learn - * the possible types: XMPP Registry for Service Discovery Identities + * the possible types: Jabber::Registrar. * * @return the type of client that will be returned when asked for the client identity in a * disco request. @@ -271,8 +271,8 @@ public final class ServiceDiscoveryManager extends Manager { */ public Set getIdentities() { Set res = new HashSet<>(identities); - // Add the main identity that must exist - res.add(identity); + // Add the default identity that must exist + res.add(defaultIdentity); return Collections.unmodifiableSet(res); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java index c31a45aad..1f02a0edb 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/disco/packet/DiscoverInfo.java @@ -263,7 +263,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable { * Represents the identity of a given XMPP entity. An entity may have many identities but all * the identities SHOULD have the same name.

* - * Refer to XMPP Registry for Service Discovery Identities + * Refer to Jabber::Registrar * in order to get the official registry of values for the category and type * attributes. * @@ -327,7 +327,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable { /** * Returns the entity's category. To get the official registry of values for the - * 'category' attribute refer to XMPP Registry for Service Discovery Identities. + * 'category' attribute refer to Jabber::Registrar * * @return the entity's category. */ @@ -346,7 +346,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable { /** * Returns the entity's type. To get the official registry of values for the - * 'type' attribute refer to XMPP Registry for Service Discovery Identities. + * 'type' attribute refer to Jabber::Registrar * * @return the entity's type. */ diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java index 2afe22663..e6b75b46b 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/geoloc/GeoLocationManager.java @@ -124,7 +124,7 @@ public final class GeoLocationManager extends Manager { private LeafNode getNode() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotALeafNodeException { - return PubSubManager.getInstanceFor(connection()).getOrCreateLeafNode(GeoLocation.NAMESPACE); + return PubSubManager.getInstance(connection()).getOrCreateLeafNode(GeoLocation.NAMESPACE); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleTransportMethodManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleTransportMethodManager.java index 9a9941a07..848dc1671 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleTransportMethodManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleTransportMethodManager.java @@ -48,7 +48,7 @@ public final class JingleTransportMethodManager extends Manager { super(connection); } - public static synchronized JingleTransportMethodManager getInstanceFor(XMPPConnection connection) { + public static JingleTransportMethodManager getInstanceFor(XMPPConnection connection) { JingleTransportMethodManager manager = INSTANCES.get(connection); if (manager == null) { manager = new JingleTransportMethodManager(connection); diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportManager.java index 63dada1ae..500a7b0a8 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_ibb/JingleIBBTransportManager.java @@ -38,7 +38,7 @@ public final class JingleIBBTransportManager extends JingleTransportManager getJoinedRooms(EntityFullJid user) throws NoResponseException, XMPPErrorException, + public List getJoinedRooms(EntityJid user) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { // Send the disco packet to the user DiscoverItems result = serviceDiscoveryManager.discoverItems(user, DISCO_NODE); 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 26eba67e9..f581e9830 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 @@ -120,7 +120,7 @@ public final class PepManager extends Manager { // TODO Add filter to check if from supports PubSub as per xep163 2 2.4 connection.addSyncStanzaListener(packetListener, FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER); - pepPubSubManager = PubSubManager.getInstanceFor(connection, null); + pepPubSubManager = PubSubManager.getInstance(connection, null); } public PubSubManager getPepPubSubManager() { 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 2904e0392..77895035f 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 @@ -88,9 +88,7 @@ public final class PubSubManager extends Manager { * @param connection * @return the default PubSub manager. */ - // CHECKSTYLE:OFF:RegexpSingleline - public static PubSubManager getInstanceFor(XMPPConnection connection) { - // CHECKSTYLE:ON:RegexpSingleline + public static PubSubManager getInstance(XMPPConnection connection) { DomainBareJid pubSubService = null; if (connection.isAuthenticated()) { try { @@ -112,7 +110,7 @@ public final class PubSubManager extends Manager { throw new RuntimeException(e); } } - return getInstanceFor(connection, pubSubService); + return getInstance(connection, pubSubService); } /** @@ -123,9 +121,7 @@ public final class PubSubManager extends Manager { * @param pubSubService the PubSub service, may be null. * @return a PubSub manager for the connection and service. */ - // CHECKSTYLE:OFF:RegexpSingleline - public static PubSubManager getInstanceFor(XMPPConnection connection, BareJid pubSubService) { - // CHECKSTYLE:ON:RegexpSingleline + public static PubSubManager getInstance(XMPPConnection connection, BareJid pubSubService) { if (pubSubService != null && connection.isAuthenticated() && connection.getUser().asBareJid().equals(pubSubService)) { // PEP service. pubSubService = null; @@ -151,28 +147,6 @@ public final class PubSubManager extends Manager { return pubSubManager; } - /** - * Deprecated. - * - * @deprecated use {@link #getInstanceFor(XMPPConnection)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public static PubSubManager getInstance(XMPPConnection connection) { - return getInstanceFor(connection); - } - - /** - * Deprecated. - * - * @deprecated use {@link #getInstanceFor(XMPPConnection, BareJid)} instead. - */ - @Deprecated - // TODO: Remove in Smack 4.5. - public static PubSubManager getInstance(XMPPConnection connection, BareJid pubSubService) { - return getInstanceFor(connection, pubSubService); - } - /** * Create a pubsub manager associated to the specified connection where * the pubsub requests require a specific to address for packets. diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/muc/MucMemoryLeakTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/muc/MucMemoryLeakTest.java deleted file mode 100644 index 2f6fa7634..000000000 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/muc/MucMemoryLeakTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.muc; - -import org.jivesoftware.smack.test.util.SmackTestSuite; -import org.jivesoftware.smack.util.MemoryLeakTestUtil; - -import org.junit.Test; -import org.jxmpp.stringprep.XmppStringprepException; - -public class MucMemoryLeakTest extends SmackTestSuite { - - @Test - public void mucMemoryLeakTest() throws XmppStringprepException, IllegalArgumentException, InterruptedException { - MemoryLeakTestUtil.noResourceLeakTest((c) -> MultiUserChatManager.getInstanceFor(c)); - } - -} 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 04cf9cb89..e9652e652 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 @@ -560,8 +560,7 @@ public final class Roster extends Manager { } /** - * Add a roster loaded listener. Roster loaded listeners are invoked once the {@link Roster} - * was successfully loaded. + * Add a roster loaded listener. * * @param rosterLoadedListener the listener to add. * @return true if the listener was not already added. @@ -588,20 +587,6 @@ public final class Roster extends Manager { } } - /** - * Add a {@link PresenceEventListener}. Such a listener will be fired whenever certain - * presence events happen.

- * Among those events are: - *

    - *
  • 'available' presence received - *
  • 'unavailable' presence received - *
  • 'error' presence received - *
  • 'subscribed' presence received - *
  • 'unsubscribed' presence received - *
- * @param presenceEventListener listener to add. - * @return true if the listener was not already added. - */ public boolean addPresenceEventListener(PresenceEventListener presenceEventListener) { return presenceEventListeners.add(presenceEventListener); } @@ -1531,29 +1516,7 @@ public final class Roster extends Manager { final Presence presence = (Presence) packet; final Jid from = presence.getFrom(); - final BareJid key; - if (from != null) { - key = from.asBareJid(); - } else { - XMPPConnection connection = connection(); - if (connection == null) { - LOGGER.finest("Connection was null while trying to handle exotic presence stanza: " + presence); - return; - } - // Assume the presence come "from the users account on the server" since no from was set (RFC 6120 § - // 8.1.2.1 4.). Note that getUser() may return null, but should never return null in this case as where - // connected. - EntityFullJid myJid = connection.getUser(); - if (myJid == null) { - LOGGER.info( - "Connection had no local address in Roster's presence listener." - + " Possibly we received a presence without from before being authenticated." - + " Presence: " + presence); - return; - } - LOGGER.info("Exotic presence stanza without from received: " + presence); - key = myJid.asBareJid(); - } + final BareJid key = from != null ? from.asBareJid() : null; asyncButOrdered.performAsyncButOrdered(key, new Runnable() { @Override diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java index aa0965306..acb62b869 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/XmppConnectionStressTest.java @@ -151,37 +151,18 @@ public class XmppConnectionStressTest { // Sanity check: All markers before must be true, all markers including the messageNumber marker must be false. for (int i = 0; i < fromMarkers.length; i++) { - final String inOrderViolation; - if (i < messageNumber && !fromMarkers[i]) { - // A previous message was missing. - inOrderViolation = "not yet message #"; - } else if (i >= messageNumber && fromMarkers[i]) { - // We already received a new message. - // TODO: Can it ever happen that this is taken? Wouldn't we prior run into the "a previous - // message is missing" case? - inOrderViolation = "we already received a later (or the same) message #"; - } else { - continue; + if ((i < messageNumber && !fromMarkers[i]) + || (i >= messageNumber && fromMarkers[i])) { + // TODO: Better exception. + Exception exception = new Exception("out of order"); + receiveExceptions.put(connection, exception); + // TODO: Current Smack design does not guarantee that the listener won't be invoked again. + // This is because the decission to invoke a sync listeners is done at a different place + // then invoking the listener. + connection.removeSyncStanzaListener(this); + receivedSemaphore.release(); + return; } - - - StringBuilder exceptionMessage = new StringBuilder(); - exceptionMessage.append("We received message #").append(messageNumber).append(" but "); - exceptionMessage.append(inOrderViolation); - exceptionMessage.append(i); - exceptionMessage.append("\nMessage with id ").append(stanza.getStanzaId()) - .append(" from ").append(from) - .append(" to ").append(stanza.getTo()); - - Exception exception = new Exception(exceptionMessage.toString()); - receiveExceptions.put(connection, exception); - // TODO: Current Smack design does not guarantee that the listener won't be invoked again. - // This is because the decission to invoke a sync listeners is done at a different place - // then invoking the listener. - connection.removeSyncStanzaListener(this); - receivedSemaphore.release(); - // TODO: Do not return here? - return; } fromMarkers[messageNumber] = true; diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java index 4ea5531c3..ad076ccf2 100644 --- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java +++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/XmppConnectionManager.java @@ -240,10 +240,6 @@ public class XmppConnectionManager { } connections.clear(); - - if (accountRegistrationConnection != null) { - accountRegistrationConnection.disconnect(); - } } 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 c4cf718d3..0c2e7fa34 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 @@ -125,7 +125,7 @@ public class OmemoManagerSetupHelper { } public static void cleanUpPubSub(OmemoManager omemoManager) { - PubSubManager pm = PubSubManager.getInstanceFor(omemoManager.getConnection(),omemoManager.getOwnJid()); + PubSubManager pm = PubSubManager.getInstance(omemoManager.getConnection(),omemoManager.getOwnJid()); try { omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid()); } catch (SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException e) { diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java index 92ca0c643..e13351c91 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/pubsub/PubSubIntegrationTest.java @@ -17,16 +17,12 @@ package org.jivesoftware.smackx.pubsub; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.util.List; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; -import org.jivesoftware.smack.packet.StanzaError; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTest; @@ -46,70 +42,33 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest { if (pubSubService == null) { throw new TestNotPossibleException("No PubSub service found"); } - pubSubManagerOne = PubSubManager.getInstanceFor(conOne, pubSubService); + pubSubManagerOne = PubSubManager.getInstance(conOne, pubSubService); if (!pubSubManagerOne.canCreateNodesAndPublishItems()) { throw new TestNotPossibleException("PubSub service does not allow node creation"); } } - /** - * Asserts that an event notification (publication without item) can be published to - * a node that is both 'notification-only' as well as 'transient'. - */ @SmackIntegrationTest - public void transientNotificationOnlyNodeWithoutItemTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - final String nodename = "sinttest-transient-notificationonly-withoutitem-nodename-" + testRunId; + public void simplePubSubNodeTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { + final String nodename = "sinttest-simple-nodename-" + testRunId; + final String itemId = "sintest-simple-itemid-" + testRunId; ConfigureForm defaultConfiguration = pubSubManagerOne.getDefaultConfiguration(); ConfigureForm config = new ConfigureForm(defaultConfiguration.createAnswerForm()); - // Configure the node as "Notification-Only Node". + // Configure the node as "Notification-Only Node", which in turn means that + // items do not need payload, to prevent payload-required error responses when + // publishing the item. config.setDeliverPayloads(false); - // Configure the node as "transient" (set persistent_items to 'false') - config.setPersistentItems(false); - Node node = pubSubManagerOne.createNode(nodename, config); - try { - LeafNode leafNode = (LeafNode) node; - leafNode.publish(); - List items = leafNode.getItems(); - assertTrue(items.isEmpty()); - } - finally { - pubSubManagerOne.deleteNode(nodename); - } - } - - /** - * Asserts that an error is returned when a publish request to a node that is both - * 'notification-only' as well as 'transient' contains an item element. - * - *

From XEP-0060 § 7.1.3.6:

- *
- * If the event type is notification + transient and the publisher provides an item, - * the service MUST bounce the publication request with a <bad-request/> error - * and a pubsub-specific error condition of <item-forbidden/>. - *
- * - * @see - * 7.1.3.6 Request Does Not Match Configuration - */ - @SmackIntegrationTest - public void transientNotificationOnlyNodeWithItemTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { - final String nodename = "sinttest-transient-notificationonly-withitem-nodename-" + testRunId; - final String itemId = "sinttest-transient-notificationonly-withitem-itemid-" + testRunId; - ConfigureForm defaultConfiguration = pubSubManagerOne.getDefaultConfiguration(); - ConfigureForm config = new ConfigureForm(defaultConfiguration.createAnswerForm()); - // Configure the node as "Notification-Only Node". - config.setDeliverPayloads(false); - // Configure the node as "transient" (set persistent_items to 'false') + // Set persistent_items to 'false' (was previously 'true') as workaround for ejabberd issue #2799 + // (https://github.com/processone/ejabberd/issues/2799). config.setPersistentItems(false); Node node = pubSubManagerOne.createNode(nodename, config); try { LeafNode leafNode = (LeafNode) node; leafNode.publish(new Item(itemId)); - fail("An exception should have been thrown."); - } - catch (XMPPErrorException e) { - assertEquals(StanzaError.Type.MODIFY, e.getStanzaError().getType()); - assertNotNull(e.getStanzaError().getExtension("item-forbidden", "http://jabber.org/protocol/pubsub#errors")); + List items = leafNode.getItems(); + assertEquals(1, items.size()); + Item item = items.get(0); + assertEquals(itemId, item.getId()); } finally { pubSubManagerOne.deleteNode(nodename); diff --git a/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Base64.java b/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Base64.java new file mode 100644 index 000000000..058dc67cf --- /dev/null +++ b/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Base64.java @@ -0,0 +1,1695 @@ +/** + * + * Copyright the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.util.stringencoder.java7; + +import java.nio.charset.Charset; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Encodes and decodes to and from Base64 notation. + *

+ * This code was obtained from http://iharder.net/base64 + *

+ * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.2.1 + */ +// CHECKSTYLE:OFF +public final class Base64 +{ + private static final Logger LOGGER = Logger.getLogger(Base64.class.getName()); + + /* ******** P U B L I C F I E L D S ******** */ + + /** No options specified. Value is zero. */ + public static final int NO_OPTIONS = 0; + + /** Specify encoding. */ + public static final int ENCODE = 1; + + + /** Specify decoding. */ + public static final int DECODE = 0; + + + /** Specify that data should be gzip-compressed. */ + public static final int GZIP = 2; + + + /** Don't break lines when encoding (violates strict Base64 specification). */ + public static final int DONT_BREAK_LINES = 8; + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public static final int URL_SAFE = 16; + + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + public static final int ORDERED = 32; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private static final int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private static final byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private static final byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private static final String PREFERRED_ENCODING = "UTF-8"; + + + // I think I end up not using the BAD_ENCODING indicator. + // private static final byte BAD_ENCODING = -9; // Indicates error in encoding + private static final byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private static final byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + + /** The 64 valid Base64 values. */ + // private static final byte[] ALPHABET; + /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ + private static final byte[] _STANDARD_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private static final byte[] _STANDARD_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ + private static final byte[] _URL_SAFE_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' + }; + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ + private static final byte[] _URL_SAFE_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + + +/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ + + /** + * I don't get the point of this technique, but it is described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + private static final byte[] _ORDERED_ALPHABET = + { + (byte)'-', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', + (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'_', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' + }; + + /** + * Used in decoding the "ordered" dialect of Base64. + */ + private static final byte[] _ORDERED_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' + 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' + 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ + + + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private static byte[] getAlphabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; + else return _STANDARD_ALPHABET; + + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private static byte[] getDecodabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; + else return _STANDARD_DECODABET; + + } // end getAlphabet + + + + /** Defeats instantiation. */ + private Base64(){} + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) + { + encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); + return b4; + } // end encode3to4 + + + /** + *

Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accommodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes.

+ *

This is the lowest level of the encoding methods with + * all possible parameters.

+ * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + @SuppressWarnings("UnnecessaryParentheses") + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options ) + { + byte[] ALPHABET = getAlphabet( options ); + + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeObject( myObj, Base64.GZIP ) or + *

+ * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + { + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + java.util.zip.GZIPOutputStream gzos = null; + + // Isolate options + int gzip = (options & GZIP); + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + + // GZip? + if( gzip == GZIP ) + { + gzos = new java.util.zip.GZIPOutputStream( b64os ); + oos = new java.io.ObjectOutputStream( gzos ); + } // end if: gzip + else + oos = new java.io.ObjectOutputStream( b64os ); + + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) + { + LOGGER.log(Level.SEVERE, "Error encoding object", e); + return null; + } // end catch + finally + { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray(), Charset.defaultCharset() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) + { + return encodeBytes( source, 0, source.length, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param options Specified options + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) + { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) + { + return encodeBytes( source, off, len, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

+ * Valid options:

+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     Note: Technically, this makes your encoding non-compliant.
+     * 
+ *

+ * Example: encodeBytes( myData, Base64.GZIP ) or + *

+ * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options; alphabet type is pulled from this (standard, url-safe, ordered) + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + @SuppressWarnings("UnnecessaryParentheses") + public static String encodeBytes( byte[] source, int off, int len, int options ) + { + // Isolate options + int dontBreakLines = ( options & DONT_BREAK_LINES ); + int gzip = ( options & GZIP ); + + // Compress? + if( gzip == GZIP ) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) + { + LOGGER.log(Level.SEVERE, "Error encoding bytes", e); + return null; + } // end catch + finally + { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray(), Charset.defaultCharset() ); + } // end catch + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + // Convert option to boolean in way that code likes it. + boolean breakLines = dontBreakLines == 0; + + int len43 = len * 4 / 3; + byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) + { + encode3to4( source, d+off, 3, outBuff, e, options ); + + lineLength += 4; + if( breakLines && lineLength == MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) + { + encode3to4( source, d+off, len - d, outBuff, e, options ); + e += 4; + } // end if: some padding needed + + + // Return value according to relevant encoding. + try + { + return new String( outBuff, 0, e, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( outBuff, 0, e , Charset.defaultCharset()); + } // end catch + + } // end else: don't compress + + } // end encodeBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accommodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + *

This is the lowest level of the decoding methods with + * all possible parameters.

+ * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return the number of decoded bytes converted + * @since 1.3 + */ + @SuppressWarnings("UnnecessaryParentheses") + private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else + { + try{ + // Two ways to do the same thing. Don't know which way I like best. + // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + }catch( Exception e){ + LOGGER.log(Level.SEVERE, e.getMessage(), e); + LOGGER.severe(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + LOGGER.severe(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + LOGGER.severe(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + LOGGER.severe(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); + return -1; + } // end catch + } + } // end decodeToBytes + + + + + /** + * Very low-level access to decoding ASCII characters in + * the form of a byte array. Does not support automatically + * gunzipping or any other "fancy" features. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @return decoded data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + int len34 = len * 3 / 4; + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; + + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + for( i = off; i < off+len; i++ ) + { + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; + + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better + { + if( sbiDecode >= EQUALS_SIGN_ENC ) + { + b4[ b4Posn++ ] = sbiCrop; + if( b4Posn > 3 ) + { + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) + break; + } // end if: quartet built + + } // end if: equals sign or better + + } // end if: white space, equals sign or better + else + { + LOGGER.warning("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)"); + return null; + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s ) + { + return decode( s, NO_OPTIONS ); + } + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @param options encode options such as URL_SAFE + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s, int options ) + { + byte[] bytes; + try + { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) + { + bytes = s.getBytes( Charset.defaultCharset() ); + } // end catch + // + + // Decode + bytes = decode( bytes, 0, bytes.length, options ); + + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + if( bytes != null && bytes.length >= 4 ) + { + + int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) + { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) + { + // Just return originally-decoded bytes + } // end catch + finally + { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + { + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream( objBytes ); + ois = new java.io.ObjectInputStream( bais ); + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) + { + LOGGER.log(Level.SEVERE, "Error reading object", e); + } // end catch + catch( java.lang.ClassNotFoundException e ) + { + LOGGER.log(Level.SEVERE, "Class not found for encoded object", e); + } // end catch + finally + { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean encodeToFile( byte[] dataToEncode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + success = true; + } // end try + catch( java.io.IOException e ) + { + + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean decodeToFile( String dataToDecode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + success = true; + } // end try + catch( java.io.IOException e ) + { + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + * @param filename Filename for reading encoded data + * @return decoded byte array or null if unsuccessful + * + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + { + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + LOGGER.warning("File is too big for this convenience method (" + file.length() + " bytes)."); + return null; + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + catch( java.io.IOException e ) + { + LOGGER.log(Level.SEVERE, "Error decoding from file " + filename, e); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + * @param filename Filename for reading binary data + * @return base64-encoded string or null if unsuccessful + * + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + { + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + catch( java.io.IOException e ) + { + LOGGER.log(Level.SEVERE, "Error encoding from file " + filename, e); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + /** + * Reads infile and encodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @since 2.2 + */ + public static void encodeFileToFile( String infile, String outfile ) + { + String encoded = Base64.encodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. + } // end try + catch( java.io.IOException ex ) { + LOGGER.log(Level.SEVERE, "Error encoding file " + infile, ex); + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end encodeFileToFile + + + /** + * Reads infile and decodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @since 2.2 + */ + public static void decodeFileToFile( String infile, String outfile ) + { + byte[] decoded = Base64.decodeFromFile( infile ); + java.io.OutputStream out = null; + try{ + out = new java.io.BufferedOutputStream( + new java.io.FileOutputStream( outfile ) ); + out.write( decoded ); + } // end try + catch( java.io.IOException ex ) { + LOGGER.log(Level.SEVERE, "Error decoding file " + infile, ex); + } // end catch + finally { + try { out.close(); } + catch( Exception ex ){} + } // end finally + } // end decodeFileToFile + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + private int options; // Record options used to create the stream. + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) + { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) + { + super( in ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + this.options = options; // Record for later, mostly to determine which alphabet to use + this.decodabet = getDecodabet(options); + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + @Override + public int read() throws java.io.IOException + { + // Do we need to get data? + if( position < 0 ) + { + if( encode ) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) + { + try + { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) + { + b3[i] = (byte)b; + numBinaryBytes++; + } // end if: not end of stream + + } // end try: read + catch( java.io.IOException e ) + { + // Only a problem if we got no data at all. + if( i == 0 ) + throw e; + + } // end catch + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) + { + encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) + { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) + break; // Reads a -1 if end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) + { + numSigBytes = decode4to3( b4, 0, buffer, 0, options ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) + { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ) + return -1; + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) + position = -1; + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + // When JDK1.4 is more accepted, use an assertion here. + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + @Override + public int read( byte[] dest, int off, int len ) throws java.io.IOException + { + int i; + int b; + for( i = 0; i < len; i++ ) + { + b = read(); + + // if( b < 0 && i == 0 ) + // return -1; + + if( b >= 0 ) + dest[off + i] = (byte)b; + else if( i == 0 ) + return -1; + else + break; // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + private int options; // Record for later + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) + { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + *

+ * Valid options:

+         *   ENCODE or DECODE: Encode or Decode as data is read.
+         *   DONT_BREAK_LINES: don't break lines at 76 characters
+         *     (only meaningful when encoding)
+         *     Note: Technically, this makes your encoding non-compliant.
+         * 
+ *

+ * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) + { + super( out ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + this.options = options; + this.decodabet = getDecodabet(options); + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + @Override + public void write(int theByte) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to encode. + { + out.write( encode3to4( b4, buffer, bufferLength, options ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to output. + { + int len = Base64.decode4to3( buffer, 0, b4, 0, options ); + out.write( b4, 0, len ); + // out.write( Base64.decode4to3( buffer ) ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) + { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + @Override + public void write( byte[] theBytes, int off, int len ) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) + { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + */ + public void flushBase64() throws java.io.IOException + { + if( position > 0 ) + { + if( encode ) + { + out.write( encode3to4( b4, buffer, position, options ) ); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + @Override + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 +// CHECKSTYLE:ON diff --git a/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java b/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java index 6b8d07252..da7ba78a6 100644 --- a/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java +++ b/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64Encoder.java @@ -16,7 +16,9 @@ */ package org.jivesoftware.smack.util.stringencoder.java7; -import java.util.Base64; +import java.io.UnsupportedEncodingException; + +import org.jivesoftware.smack.util.StringUtils; /** * A Base 64 encoding implementation. @@ -26,15 +28,10 @@ public final class Java7Base64Encoder implements org.jivesoftware.smack.util.str private static final Java7Base64Encoder instance = new Java7Base64Encoder(); - private final Base64.Encoder encoder; - private final Base64.Encoder encoderWithoutPadding; - - private final Base64.Decoder decoder; + private static final int BASE64_ENCODER_FLAGS = Base64.DONT_BREAK_LINES; private Java7Base64Encoder() { - encoder = Base64.getEncoder(); - encoderWithoutPadding = encoder.withoutPadding(); - decoder = Base64.getDecoder(); + // Use getInstance() } public static Java7Base64Encoder getInstance() { @@ -43,21 +40,27 @@ public final class Java7Base64Encoder implements org.jivesoftware.smack.util.str @Override public byte[] decode(String string) { - return decoder.decode(string); + return Base64.decode(string); } @Override - public String encodeToString(byte[] input) { - return encoder.encodeToString(input); + public byte[] decode(byte[] input, int offset, int len) { + return Base64.decode(input, offset, len, 0); } @Override - public String encodeToStringWithoutPadding(byte[] input) { - return encoderWithoutPadding.encodeToString(input); + public String encodeToString(byte[] input, int offset, int len) { + return Base64.encodeBytes(input, offset, len, BASE64_ENCODER_FLAGS); } @Override - public byte[] encode(byte[] input) { - return encoder.encode(input); + public byte[] encode(byte[] input, int offset, int len) { + String string = encodeToString(input, offset, len); + try { + return string.getBytes(StringUtils.USASCII); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } } + } diff --git a/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java b/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java index 1d2d9a530..5b724501a 100644 --- a/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java +++ b/smack-java7/src/main/java/org/jivesoftware/smack/util/stringencoder/java7/Java7Base64UrlSafeEncoder.java @@ -17,7 +17,6 @@ package org.jivesoftware.smack.util.stringencoder.java7; import java.io.UnsupportedEncodingException; -import java.util.Base64; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.stringencoder.StringEncoder; @@ -38,12 +37,10 @@ public final class Java7Base64UrlSafeEncoder implements StringEncoder { private static final Java7Base64UrlSafeEncoder instance = new Java7Base64UrlSafeEncoder(); - private final Base64.Encoder encoder; - private final Base64.Decoder decoder; + private static final int BASE64_ENCODER_FLAGS = Base64.URL_SAFE | Base64.DONT_BREAK_LINES; private Java7Base64UrlSafeEncoder() { - encoder = Base64.getUrlEncoder(); - decoder = Base64.getUrlDecoder(); + // Use getInstance() } public static Java7Base64UrlSafeEncoder getInstance() { @@ -59,14 +56,13 @@ public final class Java7Base64UrlSafeEncoder implements StringEncoder { catch (UnsupportedEncodingException e) { throw new AssertionError(e); } - return encoder.encodeToString(bytes); + return Base64.encodeBytes(bytes, BASE64_ENCODER_FLAGS); } @Override public String decode(String s) { - byte[] bytes = decoder.decode(s); try { - return new String(bytes, StringUtils.UTF8); + return new String(Base64.decode(s, BASE64_ENCODER_FLAGS), StringUtils.UTF8); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); diff --git a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java index 296630f26..84f679c1e 100644 --- a/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java +++ b/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/JingleSession.java @@ -620,7 +620,7 @@ public final class JingleSession extends JingleNegotiator implements MediaReceiv * A XMPP connection * @return a Jingle session */ - public static synchronized JingleSession getInstanceFor(XMPPConnection con) { + public static JingleSession getInstanceFor(XMPPConnection con) { if (con == null) { throw new IllegalArgumentException("XMPPConnection cannot be null"); } diff --git a/smack-omemo/build.gradle b/smack-omemo/build.gradle index 354c3db20..e4279f74a 100644 --- a/smack-omemo/build.gradle +++ b/smack-omemo/build.gradle @@ -3,7 +3,7 @@ dependencies { compile project(":smack-extensions") compile project(":smack-experimental") - compile "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion" + compile "org.bouncycastle:bcprov-jdk15on:1.60" testCompile project(path: ":smack-core", configuration: "testRuntime") } 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 e4c93a0d1..27f775f3e 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 @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Paul Schaub, 2019 Florian Schmaus + * Copyright 2017 Paul Schaub * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.Security; import java.util.ArrayList; import java.util.Collection; @@ -307,7 +308,7 @@ public abstract class OmemoService(userDevice, gullibleTrustCallback, getOmemoRatchet(manager), messageKey, iv, null); - } catch (InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | UnsupportedEncodingException | IllegalBlockSizeException e) { + } catch (InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | UnsupportedEncodingException | NoSuchProviderException | IllegalBlockSizeException e) { throw new CryptoFailedException(e); } @@ -369,7 +370,7 @@ public abstract class OmemoService( userDevice, manager.getTrustCallback(), getOmemoRatchet(managerGuard.get()), messageKey, iv, message); - } catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException | + } catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException | NoSuchProviderException | NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException e) { throw new CryptoFailedException(e); } @@ -556,7 +557,7 @@ public abstract class OmemoService(bundle)); } @@ -606,7 +607,7 @@ public abstract class OmemoService(deviceList)); } @@ -785,6 +786,33 @@ public abstract class OmemoService buildMissingSessionsWithContact(XMPPConnection connection, + OmemoDevice userDevice, + BareJid contact) + throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + + OmemoCachedDeviceList contactsDeviceIds = getOmemoStoreBackend().loadCachedDeviceList(userDevice, contact); + Set contactsDevices = new HashSet<>(); + for (int deviceId : contactsDeviceIds.getActiveDevices()) { + contactsDevices.add(new OmemoDevice(contact, deviceId)); + } + + return buildMissingSessionsWithDevices(connection, userDevice, contactsDevices); + } + /** * Build sessions with all devices from the set, we don't have a session with yet. * Return the set of all devices we have a session with afterwards. @@ -826,6 +854,34 @@ public abstract class OmemoService buildMissingSessionsWithContacts(XMPPConnection connection, + OmemoDevice userDevice, + Set contacts) + throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { + + Set devicesWithSessions = new HashSet<>(); + + for (BareJid contact : contacts) { + Set devices = buildMissingSessionsWithContact(connection, userDevice, contact); + devicesWithSessions.addAll(devices); + } + + return devicesWithSessions; + } + /** * Return a set of all devices from the provided set, which trust level is undecided. * A device is also considered undecided, if its fingerprint cannot be loaded. @@ -858,6 +914,37 @@ public abstract class OmemoService getUntrustedDeviced(OmemoDevice userDevice, OmemoTrustCallback trustCallback, Set devices) { + Set untrustedDevices = new HashSet<>(); + + for (OmemoDevice device : devices) { + + OmemoFingerprint fingerprint; + try { + fingerprint = getOmemoStoreBackend().getFingerprint(userDevice, device); + } catch (CorruptedOmemoKeyException | NoIdentityKeyException e) { + // TODO: Best solution? + untrustedDevices.add(device); + continue; + } + + if (trustCallback.getTrust(device, fingerprint) == TrustState.untrusted) { + untrustedDevices.add(device); + } + } + + return untrustedDevices; + } + /** * Return true, if the OmemoManager of userDevice has a session with the contactsDevice. * diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CipherAndAuthTag.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CipherAndAuthTag.java index 3f03f2f72..964a33910 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CipherAndAuthTag.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CipherAndAuthTag.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Paul Schaub, 2019 Florian Schmaus + * Copyright 2017 Paul Schaub * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,11 @@ package org.jivesoftware.smackx.omemo.internal; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.CIPHERMODE; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE; +import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; @@ -48,13 +50,13 @@ public class CipherAndAuthTag { Cipher cipher; try { - cipher = Cipher.getInstance(CIPHERMODE); + cipher = Cipher.getInstance(CIPHERMODE, PROVIDER); SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); } catch (NoSuchAlgorithmException | java.security.InvalidKeyException | InvalidAlgorithmParameterException | - NoSuchPaddingException e) { + NoSuchPaddingException | NoSuchProviderException e) { throw new CryptoFailedException(e); } diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java index 885260568..c362145c6 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java @@ -58,5 +58,6 @@ public final class OmemoConstants { public static final String KEYTYPE = "AES"; public static final int KEYLENGTH = 128; public static final String CIPHERMODE = "AES/GCM/NoPadding"; + public static final String PROVIDER = "BC"; } } 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 7a625ee15..8d254f44c 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 @@ -1,6 +1,6 @@ /** * - * Copyright 2017 Paul Schaub, 2019 Florian Schmaus + * Copyright 2017 Paul Schaub * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.jivesoftware.smackx.omemo.util; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.CIPHERMODE; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYLENGTH; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE; +import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; @@ -95,6 +96,7 @@ public class OmemoMessageBuilder ratchet, String message) throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, - UnsupportedEncodingException, InvalidAlgorithmParameterException { + UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException { this(userDevice, callback, ratchet, generateKey(KEYTYPE, KEYLENGTH), generateIv(), message); } @@ -147,6 +150,7 @@ public class OmemoMessageBuilder to, String rpad, Date timestamp, List payload) { + public CryptElement(Set to, String rpad, Date timestamp, List payload) { super(to, rpad, timestamp, payload); } - public CryptElement(Set to, List payload) { + public CryptElement(Set to, List payload) { super(to, payload); } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java index a79dc8997..951dc90f8 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/EncryptedOpenPgpContentElement.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,13 @@ */ package org.jivesoftware.smackx.ox.element; +import java.security.SecureRandom; import java.util.Date; import java.util.List; import java.util.Set; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.util.Objects; -import org.jivesoftware.smack.util.RandomUtil; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; @@ -38,14 +38,14 @@ public abstract class EncryptedOpenPgpContentElement extends OpenPgpContentEleme private final String rpad; - protected EncryptedOpenPgpContentElement(Set to, String rpad, Date timestamp, List payload) { + protected EncryptedOpenPgpContentElement(Set to, String rpad, Date timestamp, List payload) { super(Objects.requireNonNullNorEmpty( to, "Encrypted OpenPGP content elements must have at least one 'to' attribute."), timestamp, payload); this.rpad = Objects.requireNonNull(rpad); } - protected EncryptedOpenPgpContentElement(Set to, List payload) { + protected EncryptedOpenPgpContentElement(Set to, List payload) { super(Objects.requireNonNullNorEmpty( to, "Encrypted OpenPGP content elements must have at least one 'to' attribute."), new Date(), payload); @@ -53,7 +53,8 @@ public abstract class EncryptedOpenPgpContentElement extends OpenPgpContentEleme } private static String createRandomPadding() { - int len = RandomUtil.nextSecureRandomInt(256); + SecureRandom secRan = new SecureRandom(); + int len = secRan.nextInt(256); return StringUtils.randomString(len); } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java index db7014eef..2b61db669 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,13 +48,13 @@ public abstract class OpenPgpContentElement implements ExtensionElement { public static final String ATTR_STAMP = "stamp"; public static final String ELEM_PAYLOAD = "payload"; - private final Set to; + private final Set to; private final Date timestamp; private final MultiMap payload; private String timestampString; - protected OpenPgpContentElement(Set to, Date timestamp, List payload) { + protected OpenPgpContentElement(Set to, Date timestamp, List payload) { this.to = to; this.timestamp = Objects.requireNonNull(timestamp); this.payload = new MultiMap<>(); @@ -68,7 +68,7 @@ public abstract class OpenPgpContentElement implements ExtensionElement { * * @return recipients. */ - public final Set getTo() { + public final Set getTo() { return to; } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/SigncryptElement.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/SigncryptElement.java index c92f02f0f..d0d165a86 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/SigncryptElement.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/SigncryptElement.java @@ -1,6 +1,6 @@ /** * - * Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub. + * Copyright 2017 Florian Schmaus, 2018 Paul Schaub. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,11 +35,11 @@ public class SigncryptElement extends EncryptedOpenPgpContentElement { public static final String ELEMENT = "signcrypt"; - public SigncryptElement(Set to, String rpad, Date timestamp, List payload) { + public SigncryptElement(Set to, String rpad, Date timestamp, List payload) { super(to, rpad, timestamp, payload); } - public SigncryptElement(Set to, List payload) { + public SigncryptElement(Set to, List payload) { super(to, payload); } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java index 9880facf1..088c23e8f 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/util/OpenPgpPubSubUtil.java @@ -204,7 +204,7 @@ public class OpenPgpPubSubUtil { public static PublicKeysListElement fetchPubkeysList(XMPPConnection connection, BareJid contact) throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException, PubSubException.NotALeafNodeException, SmackException.NotConnectedException, PubSubException.NotAPubSubNodeException { - PubSubManager pm = PubSubManager.getInstanceFor(connection, contact); + PubSubManager pm = PubSubManager.getInstance(connection, contact); LeafNode node = getLeafNode(pm, PEP_NODE_PUBLIC_KEYS); List> list = node.getItems(1); @@ -274,7 +274,7 @@ public class OpenPgpPubSubUtil { public static PubkeyElement fetchPubkey(XMPPConnection connection, BareJid contact, OpenPgpV4Fingerprint v4_fingerprint) throws InterruptedException, XMPPException.XMPPErrorException, PubSubException.NotAPubSubNodeException, PubSubException.NotALeafNodeException, SmackException.NotConnectedException, SmackException.NoResponseException { - PubSubManager pm = PubSubManager.getInstanceFor(connection, contact); + PubSubManager pm = PubSubManager.getInstance(connection, contact); String nodeName = PEP_NODE_PUBLIC_KEY(v4_fingerprint); LeafNode node = getLeafNode(pm, nodeName); diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java index 591ead494..0056a121c 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox_im/OXInstantMessagingManager.java @@ -135,7 +135,7 @@ public final class OXInstantMessagingManager extends Manager { * @param connection XMPP connection * @return manager instance */ - public static synchronized OXInstantMessagingManager getInstanceFor(XMPPConnection connection) { + public static OXInstantMessagingManager getInstanceFor(XMPPConnection connection) { OXInstantMessagingManager manager = INSTANCES.get(connection); if (manager == null) { diff --git a/smack-repl/build.gradle b/smack-repl/build.gradle index 2b36f85ef..aac8253e9 100644 --- a/smack-repl/build.gradle +++ b/smack-repl/build.gradle @@ -14,7 +14,6 @@ dependencies { compile project(':smack-bosh') compile project(':smack-java7') compile project(':smack-resolver-minidns') - compile project(':smack-resolver-minidns-dox') compile project(':smack-extensions') compile project(':smack-experimental') compile project(':smack-legacy') 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 deleted file mode 100644 index ddab89d4d..000000000 --- a/smack-repl/src/main/java/org/igniterealtime/smack/smackrepl/DoX.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.igniterealtime.smack.smackrepl; - -import java.io.IOException; - -import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.SmackException; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.debugger.ConsoleDebugger; -import org.jivesoftware.smack.tcp.XMPPTCPConnection; -import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; -import org.jivesoftware.smackx.dox.DnsOverXmppManager; -import org.jivesoftware.smackx.dox.resolver.minidns.DnsOverXmppMiniDnsResolver; - -import org.jxmpp.jid.Jid; -import org.jxmpp.jid.impl.JidCreate; -import org.minidns.dnsmessage.DnsMessage; -import org.minidns.dnsmessage.Question; -import org.minidns.record.Record; - -public class DoX { - - public static void main(String[] args) throws XMPPException, SmackException, IOException, InterruptedException { - SmackConfiguration.DEBUG = true; - - XMPPTCPConnection connection = new XMPPTCPConnection(args[0], args[1]); - connection.setReplyTimeout(60000); - - connection.connect().login(); - - DnsOverXmppManager dox = DnsOverXmppManager.getInstanceFor(connection); - - Jid target = JidCreate.from("dns@moparisthebest.com/listener"); - Question question = new Question("geekplace.eu", Record.TYPE.A); - - DnsMessage response = dox.query(target, question); - - // CHECKSTYLE:OFF - System.out.println(response); - // CHECKSTYLE:ON - - connection.disconnect(); - } - - public static XMPPTCPConnection runDoxResolver(String jid, String password) - throws XMPPException, SmackException, IOException, InterruptedException { - XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() - .setXmppAddressAndPassword(jid, password) - .setResource("dns") - .setDebuggerFactory(ConsoleDebugger.Factory.INSTANCE) - .build(); - XMPPTCPConnection connection = new XMPPTCPConnection(config); - - connection.connect().login(); - - DnsOverXmppManager dox = DnsOverXmppManager.getInstanceFor(connection); - dox.setDnsOverXmppResolver(DnsOverXmppMiniDnsResolver.INSTANCE); - - dox.enable(); - - return connection; - } -} diff --git a/smack-resolver-minidns-dox/build.gradle b/smack-resolver-minidns-dox/build.gradle deleted file mode 100644 index 3d070c5bd..000000000 --- a/smack-resolver-minidns-dox/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -description = """\ -DNS over XMPP (DoX) support using MiniDNS.""" - -dependencies { - compile project(path: ':smack-resolver-minidns') - compile project(path: ':smack-experimental') - testCompile project(path: ":smack-core", configuration: "testRuntime") -} diff --git a/smack-resolver-minidns-dox/src/main/java/org/jivesoftware/smackx/dox/resolver/minidns/DnsOverXmppMiniDnsResolver.java b/smack-resolver-minidns-dox/src/main/java/org/jivesoftware/smackx/dox/resolver/minidns/DnsOverXmppMiniDnsResolver.java deleted file mode 100644 index adf8e202b..000000000 --- a/smack-resolver-minidns-dox/src/main/java/org/jivesoftware/smackx/dox/resolver/minidns/DnsOverXmppMiniDnsResolver.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.dox.resolver.minidns; - -import java.io.IOException; - -import org.jivesoftware.smackx.dox.DnsOverXmppResolver; - -import org.minidns.DnsClient; -import org.minidns.dnsmessage.DnsMessage; -import org.minidns.dnsmessage.Question; -import org.minidns.dnsqueryresult.DnsQueryResult; -import org.minidns.dnssec.DnssecClient; -import org.minidns.dnssec.DnssecQueryResult; - -public final class DnsOverXmppMiniDnsResolver implements DnsOverXmppResolver { - - public static final DnsOverXmppMiniDnsResolver INSTANCE = new DnsOverXmppMiniDnsResolver(new DnsClient(), new DnssecClient()); - - private final DnsClient dnsClient; - private final DnssecClient dnssecClient; - - DnsOverXmppMiniDnsResolver(DnsClient dnsClient, DnssecClient dnssecClient) { - this.dnsClient = dnsClient; - this.dnssecClient = dnssecClient; - } - - @Override - public DnsMessage resolve(DnsMessage query) throws IOException { - Question question = query.getQuestion(); - - final DnsQueryResult dnsQueryResult; - if (query.isDnssecOk()) { - DnssecQueryResult dnssecQueryResult = dnssecClient.queryDnssec(question); - dnsQueryResult = dnssecQueryResult.dnsQueryResult; - } else { - dnsQueryResult = dnsClient.query(question); - } - - return dnsQueryResult.response; - } - -} diff --git a/smack-resolver-minidns-dox/src/main/java/org/jivesoftware/smackx/dox/resolver/minidns/package-info.java b/smack-resolver-minidns-dox/src/main/java/org/jivesoftware/smackx/dox/resolver/minidns/package-info.java deleted file mode 100644 index e101e6f20..000000000 --- a/smack-resolver-minidns-dox/src/main/java/org/jivesoftware/smackx/dox/resolver/minidns/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * XEP-0418: DNS Queries over XMPP (Dox) using MiniDNS. - */ -package org.jivesoftware.smackx.dox.resolver.minidns; diff --git a/smack-resolver-minidns-dox/src/test/java/org/jivesoftware/smackx/dox/resolver/minidns/DnsOverXmppMiniDnsResolverTest.java b/smack-resolver-minidns-dox/src/test/java/org/jivesoftware/smackx/dox/resolver/minidns/DnsOverXmppMiniDnsResolverTest.java deleted file mode 100644 index e9d91f6ef..000000000 --- a/smack-resolver-minidns-dox/src/test/java/org/jivesoftware/smackx/dox/resolver/minidns/DnsOverXmppMiniDnsResolverTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/** - * - * 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jivesoftware.smackx.dox.resolver.minidns; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.net.InetAddress; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.Test; -import org.minidns.DnsCache; -import org.minidns.DnsClient; -import org.minidns.MiniDnsFuture; -import org.minidns.dnsmessage.DnsMessage; -import org.minidns.dnsmessage.DnsMessage.RESPONSE_CODE; -import org.minidns.dnsmessage.Question; -import org.minidns.dnsname.DnsName; -import org.minidns.dnsqueryresult.CachedDnsQueryResult; -import org.minidns.dnsqueryresult.DnsQueryResult; -import org.minidns.dnssec.DnssecClient; -import org.minidns.dnssec.DnssecValidationFailedException; -import org.minidns.record.Record.TYPE; -import org.minidns.source.DnsDataSource; - -public final class DnsOverXmppMiniDnsResolverTest { - - @Test - public void dnsOverXmppMiniDnsResolverTest() throws IOException { - TestDnsDataSource dnsSource = new TestDnsDataSource(); - TestDnsDataSource dnssecSource = new TestDnsDataSource(); - - DnsClient dnsClient = new DnsClient(NoopDnsCache.INSTANCE); - dnsClient.setDataSource(dnsSource); - - DnssecClient dnssecClient = new DnssecClient(NoopDnsCache.INSTANCE); - dnssecClient.setDataSource(dnssecSource); - - DnsOverXmppMiniDnsResolver doxResolver = new DnsOverXmppMiniDnsResolver(dnsClient, dnssecClient); - - Question question = new Question("example.org", TYPE.A); - - { - DnsMessage nondnssecQuery = question.asQueryMessage(); - - doxResolver.resolve(nondnssecQuery); - - assertTrue(dnsSource.getAndResetWasQueried()); - assertFalse(dnssecSource.getAndResetWasQueried()); - } - - { - DnsMessage.Builder dnssecQueryBuilder = question.asMessageBuilder(); - dnssecQueryBuilder.getEdnsBuilder().setDnssecOk(); - DnsMessage dnssecQuery = dnssecQueryBuilder.build(); - - DnssecValidationFailedException dnssecValidationFailedException = null; - try { - doxResolver.resolve(dnssecQuery); - } catch (DnssecValidationFailedException e) { - dnssecValidationFailedException = e; - } - // This exception is expected since we don't have a realy DNS source. - assertNotNull(dnssecValidationFailedException); - - assertFalse(dnsSource.getAndResetWasQueried()); - assertTrue(dnssecSource.getAndResetWasQueried()); - } - } - - public static class TestDnsDataSource implements DnsDataSource { - - private final AtomicBoolean wasQueried = new AtomicBoolean(); - - public boolean getAndResetWasQueried() { - return wasQueried.getAndSet(false); - } - - private void setWasQueried() { - wasQueried.set(true); - } - - @Override - public DnsQueryResult query(DnsMessage query, InetAddress address, int port) throws IOException { - setWasQueried(); - return new TestDnsQueryResult(query); - } - - @Override - public MiniDnsFuture queryAsync(DnsMessage query, InetAddress address, int port, - OnResponseCallback onResponseCallback) { - setWasQueried(); - DnsQueryResult result = new TestDnsQueryResult(query); - return MiniDnsFuture.from(result); - } - - @Override - public int getUdpPayloadSize() { - return 0; - } - - @Override - public int getTimeout() { - return 0; - } - - @Override - public void setTimeout(int timeout) { - } - - private static class TestDnsQueryResult extends DnsQueryResult { - - protected TestDnsQueryResult(DnsMessage query) { - super(QueryMethod.testWorld, query, createNxDomainAnswerFor(query)); - } - - private static DnsMessage createNxDomainAnswerFor(DnsMessage query) { - Question question = query.getQuestion(); - - DnsMessage response = DnsMessage.builder() - .setQuestion(question) - .setRecursionAvailable(true) - .setResponseCode(RESPONSE_CODE.NX_DOMAIN) - .build(); - - return response; - } - } - } - - // TODO: Workaround for NPE-if-no-cache-set bug in MiniDNS. Remove we use a MiniDNS version where this is fixed, - // i.e. one that has 864fbb5 ("Fix NPE in AbstractDnsClient if cache is 'null'") - private static class NoopDnsCache extends DnsCache { - - private static final NoopDnsCache INSTANCE = new NoopDnsCache(); - - @Override - protected void putNormalized(DnsMessage normalizedQuery, DnsQueryResult result) { - } - - @Override - public void offer(DnsMessage query, DnsQueryResult result, DnsName authoritativeZone) { - } - - @Override - protected CachedDnsQueryResult getNormalized(DnsMessage normalizedQuery) { - return null; - } - - } -} diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java index dd7b7a30a..183275771 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java @@ -121,6 +121,7 @@ import org.jivesoftware.smack.util.dns.HostAddress; import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.parts.Resourcepart; import org.jxmpp.stringprep.XmppStringprepException; +import org.jxmpp.util.XmppStringUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -318,7 +319,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { * @throws XmppStringprepException */ public XMPPTCPConnection(CharSequence jid, String password) throws XmppStringprepException { - this(XMPPTCPConnectionConfiguration.builder().setXmppAddressAndPassword(jid, password).build()); + this(XmppStringUtils.parseLocalpart(jid.toString()), password, XmppStringUtils.parseDomain(jid.toString())); } /** @@ -481,6 +482,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { } private void shutdown(boolean instant) { + if (disconnectedButResumeable) { + return; + } + // First shutdown the writer, this will result in a closing stream element getting send to // the server LOGGER.finer("PacketWriter shutdown()"); @@ -498,15 +503,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { CloseableUtil.maybeClose(socket, LOGGER); setWasAuthenticated(); - - // Wait for reader and writer threads to be terminated. - readerWriterSemaphore.acquireUninterruptibly(2); - readerWriterSemaphore.release(2); - - if (disconnectedButResumeable) { - return; - } - // If we are able to resume the stream, then don't set // connected/authenticated/usingTLS to false since we like behave like we are still // connected (e.g. sendStanza should not throw a NotConnectedException). @@ -527,6 +523,10 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { writer = null; initState(); + + // Wait for reader and writer threads to be terminated. + readerWriterSemaphore.acquireUninterruptibly(2); + readerWriterSemaphore.release(2); } @Override @@ -860,8 +860,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { protected class PacketReader { - private final String threadName = "Smack Reader (" + getConnectionCounter() + ')'; - XmlPullParser parser; private volatile boolean done; @@ -876,15 +874,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { Async.go(new Runnable() { @Override public void run() { - LOGGER.finer(threadName + " start"); try { parsePackets(); } finally { - LOGGER.finer(threadName + " exit"); XMPPTCPConnection.this.readerWriterSemaphore.release(); } } - }, threadName); + }, "Smack Reader (" + getConnectionCounter() + ")"); } /** @@ -1132,8 +1128,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { protected class PacketWriter { public static final int QUEUE_SIZE = XMPPTCPConnection.QUEUE_SIZE; - private final String threadName = "Smack Writer (" + getConnectionCounter() + ')'; - private final ArrayBlockingQueueWithShutdown queue = new ArrayBlockingQueueWithShutdown<>( QUEUE_SIZE, true); @@ -1179,15 +1173,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection { Async.go(new Runnable() { @Override public void run() { - LOGGER.finer(threadName + " start"); try { writePackets(); } finally { - LOGGER.finer(threadName + " exit"); XMPPTCPConnection.this.readerWriterSemaphore.release(); } } - }, threadName); + }, "Smack Writer (" + getConnectionCounter() + ")"); } private boolean done() { diff --git a/version.gradle b/version.gradle index 2bb14c10d..28e90a9a4 100644 --- a/version.gradle +++ b/version.gradle @@ -10,7 +10,6 @@ allprojects { // - https://issues.igniterealtime.org/browse/SMACK-858 jxmppVersion = '0.7.0-alpha5' miniDnsVersion = '0.4.0-alpha3' - bouncyCastleVersion = '1.61' smackMinAndroidSdk = 19 } }