1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-29 23:42:06 +01:00

Compare commits

..

No commits in common. "870756997faec1e1bfabfac0cd6c2395b04da873" and "0cd3318b128e789a44dd97775ffbb4496a783418" have entirely different histories.

77 changed files with 2064 additions and 1560 deletions

View file

@ -6,11 +6,6 @@
<module name="SuppressionFilter">
<property name="file" value="config/suppressions.xml"/>
</module>
<module name="SuppressWithPlainTextCommentFilter">
<property name="offCommentFormat" value="CHECKSTYLE\:OFF\:(\w+)"/>
<property name="onCommentFormat" value="CHECKSTYLE\:ON\:(\w+)"/>
<property name="checkFormat" value="$1"/>
</module>
<module name="Header">
<property name="headerFile" value="config/${checkstyleLicenseHeader}.txt"/>
<property name="ignoreLines" value="3"/>
@ -66,14 +61,6 @@
<property name="format" value="^\s*//[^\s]"/>
<property name="message" value="Comment start ('//') followed by non-space character. You would not continue after a punctuation without a space, would you?"/>
</module>
<!-- Check for synchronized keyword on Manager's static
getInstanceFor() method. Note that if XMPPConnection is every
replaced with something else, then we need to change it here
too. -->
<module name="RegexpSingleline">
<property name="format" value="^\s*public(?!.*synchronized).*getInstanceFor\(XMPPConnection.*$"/>
<property name="message" value="getInstanceFor() should be synchronized"/>
</module>
<module name="JavadocPackage"/>
<module name="TreeWalker">
<module name="SuppressionCommentFilter"/>

View file

@ -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
--------------------------

View file

@ -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.

View file

@ -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',

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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<StanzaListener> 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<StanzaListener> 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<Runnable> 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);
}

View file

@ -55,16 +55,6 @@ public class AsyncButOrdered<K> {
private final Map<K, Boolean> 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<K> {
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);
}
}

View file

@ -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)}.
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* 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.
* <p>
* 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.
* </p>
*
* @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.
* <p>

View file

@ -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;
}
}

View file

@ -70,27 +70,6 @@ import org.jxmpp.jid.EntityFullJid;
* disconnected and then connected again. Listeners of the XMPPConnection will be retained across
* connections.
* </p>
* <h2>Incoming Stanza Listeners</h2>
* Most callbacks (listeners, handlers, ) than you can add to a connection come in three different variants:
* <ul>
* <li>standard</li>
* <li>async (asynchronous)</li>
* <li>sync (synchronous)</li>
* </ul>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
*
* @author Matt Tucker
* @author Guenther Niess

View file

@ -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 {

View file

@ -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();

View file

@ -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());
}

View file

@ -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();
}
}

View file

@ -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<SecureRandom> SECURE_RANDOM = new ThreadLocal<SecureRandom>() {
@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> RANDOM = new ThreadLocal<Random>() {
@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();
}
}

View file

@ -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<Random> randGen = new ThreadLocal<Random>() {
@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<SecureRandom> SECURE_RANDOM = new ThreadLocal<SecureRandom>() {
@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];

View file

@ -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);
}
}

View file

@ -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()

View file

@ -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);
}
}
});
}
}

View file

@ -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.
* <p>
* 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
* <a href="https://stackoverflow.com/questions/1481178/how-to-force-garbage-collection-in-java">stackoverflow
* question</a>. Hence the {@link #triggerGarbageCollection()} method defined in this class is not portable and depends
* on implementation depended Java Virtual Machine behavior.
* </p>
*
* @see <a href="https://issues.igniterealtime.org/browse/SMACK-383">SMACK-383 Jira Issue</a>
*/
public class MemoryLeakTestUtil {
private static final Logger LOGGER = Logger.getLogger(MemoryLeakTestUtil.class.getName());
public static <M extends Manager> void noResourceLeakTest(Function<DummyConnection, M> managerSupplier)
throws XmppStringprepException, IllegalArgumentException, InterruptedException {
final int numConnections = 10;
ReferenceQueue<DummyConnection> connectionsReferenceQueue = new ReferenceQueue<>();
ReferenceQueue<Manager> 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<PhantomReference<DummyConnection>> connectionsPhantomReferences = new HashSet<>();
@SuppressWarnings("ModifiedButNotUsed")
Set<PhantomReference<Manager>> managersPhantomReferences = new HashSet<>();
List<DummyConnection> connections = new ArrayList<>(numConnections);
for (int i = 0; i < numConnections; i++) {
DummyConnection connection = new DummyConnection("foo" + i, "bar", "baz");
PhantomReference<DummyConnection> connectionPhantomReference = new PhantomReference<>(connection, connectionsReferenceQueue);
connectionsPhantomReferences.add(connectionPhantomReference);
Manager manager = managerSupplier.apply(connection);
PhantomReference<Manager> managerPhantomReference = new PhantomReference<Manager>(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<Object> 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()");
}
}

View file

@ -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);
}
}

View file

@ -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"
}

View file

@ -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<XMPPConnection, DnsOverXmppManager> 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();
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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<DnsIq> {
@Override
public DnsIq parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
String base64DnsMessage = parser.nextText();
return new DnsIq(base64DnsMessage);
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -317,11 +317,4 @@
<className>org.jivesoftware.smackx.message_markup.provider.MarkupElementProvider</className>
</extensionProvider>
<!-- XEP-0418: DNS Queries over XMPP (DoX) -->
<iqProvider>
<elementName>dns</elementName>
<namespace>urn:xmpp:dox:0</namespace>
<className>org.jivesoftware.smackx.dox.provider.DnsIqProvider</className>
</iqProvider>
</smackProviders>

View file

@ -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: <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>
* the possible types: <a href="http://xmpp.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
*
* @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<DiscoverInfo.Identity> getIdentities() {
Set<Identity> 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);
}

View file

@ -263,7 +263,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
* Represents the identity of a given XMPP entity. An entity may have many identities but all
* the identities SHOULD have the same name.<p>
*
* Refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>
* Refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
* in order to get the official registry of values for the <i>category</i> and <i>type</i>
* attributes.
*
@ -327,7 +327,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
/**
* Returns the entity's category. To get the official registry of values for the
* 'category' attribute refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>.
* 'category' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
*
* @return the entity's category.
*/
@ -346,7 +346,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
/**
* Returns the entity's type. To get the official registry of values for the
* 'type' attribute refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>.
* 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
*
* @return the entity's type.
*/

View file

@ -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);
}
}

View file

@ -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);

View file

@ -38,7 +38,7 @@ public final class JingleIBBTransportManager extends JingleTransportManager<Jing
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleIBBTransportProvider());
}
public static synchronized JingleIBBTransportManager getInstanceFor(XMPPConnection connection) {
public static JingleIBBTransportManager getInstanceFor(XMPPConnection connection) {
JingleIBBTransportManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new JingleIBBTransportManager(connection);

View file

@ -63,7 +63,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleS5BTransportProvider());
}
public static synchronized JingleS5BTransportManager getInstanceFor(XMPPConnection connection) {
public static JingleS5BTransportManager getInstanceFor(XMPPConnection connection) {
JingleS5BTransportManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new JingleS5BTransportManager(connection);

View file

@ -102,7 +102,7 @@ public final class MoodManager extends Manager {
});
}
public static synchronized MoodManager getInstanceFor(XMPPConnection connection) {
public static MoodManager getInstanceFor(XMPPConnection connection) {
MoodManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new MoodManager(connection);
@ -147,7 +147,7 @@ public final class MoodManager extends Manager {
throws SmackException.NotLoggedInException, InterruptedException, PubSubException.NotALeafNodeException,
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
if (pubSubManager == null) {
pubSubManager = PubSubManager.getInstanceFor(getAuthenticatedConnectionOrThrow(), connection().getUser().asBareJid());
pubSubManager = PubSubManager.getInstance(getAuthenticatedConnectionOrThrow(), connection().getUser().asBareJid());
}
LeafNode node = pubSubManager.getOrCreateLeafNode(MOOD_NODE);

View file

@ -1,6 +1,6 @@
/**
*
* Copyright © 2014-2019 Florian Schmaus
* Copyright © 2014-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.
@ -59,7 +59,6 @@ import org.jivesoftware.smackx.muc.packet.MUCUser;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.EntityJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.parts.Resourcepart;
@ -305,7 +304,7 @@ public final class MultiUserChatManager extends Manager {
* @throws NotConnectedException
* @throws InterruptedException
*/
public List<EntityBareJid> getJoinedRooms(EntityFullJid user) throws NoResponseException, XMPPErrorException,
public List<EntityBareJid> getJoinedRooms(EntityJid user) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException {
// Send the disco packet to the user
DiscoverItems result = serviceDiscoveryManager.discoverItems(user, DISCO_NODE);

View file

@ -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() {

View file

@ -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 <code>null</code>.
* @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.

View file

@ -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));
}
}

View file

@ -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.<p>
* Among those events are:
* <ul>
* <li> 'available' presence received
* <li> 'unavailable' presence received
* <li> 'error' presence received
* <li> 'subscribed' presence received
* <li> 'unsubscribed' presence received
* </ul>
* @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

View file

@ -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;

View file

@ -240,10 +240,6 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
}
connections.clear();
if (accountRegistrationConnection != null) {
accountRegistrationConnection.disconnect();
}
}

View file

@ -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) {

View file

@ -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<Item> 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.
*
* <p>From XEP-0060 § 7.1.3.6:</p>
* <blockquote>
* If the event type is notification + transient and the publisher provides an item,
* the service MUST bounce the publication request with a &lt;bad-request/&gt; error
* and a pubsub-specific error condition of &lt;item-forbidden/&gt;.
* </blockquote>
*
* @see <a href="https://xmpp.org/extensions/xep-0060.html#publisher-publish-error-badrequest">
* 7.1.3.6 Request Does Not Match Configuration</a>
*/
@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<Item> items = leafNode.getItems();
assertEquals(1, items.size());
Item item = items.get(0);
assertEquals(itemId, item.getId());
}
finally {
pubSubManagerOne.deleteNode(nodename);

View file

@ -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);
}
}
}

View file

@ -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<String> {
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<String> {
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);

View file

@ -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");
}

View file

@ -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")
}

View file

@ -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<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
try {
builder = new OmemoMessageBuilder<>(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<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
try {
builder = new OmemoMessageBuilder<>(
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<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException,
PubSubException.NotAPubSubNodeException {
PubSubManager pm = PubSubManager.getInstanceFor(connection, contactsDevice.getJid());
PubSubManager pm = PubSubManager.getInstance(connection, contactsDevice.getJid());
LeafNode node = pm.getLeafNode(contactsDevice.getBundleNodeName());
if (node == null) {
@ -584,7 +585,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
static void publishBundle(XMPPConnection connection, OmemoDevice userDevice, OmemoBundleElement bundle)
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException {
PubSubManager pm = PubSubManager.getInstanceFor(connection, connection.getUser().asBareJid());
PubSubManager pm = PubSubManager.getInstance(connection, connection.getUser().asBareJid());
pm.tryToPublishAndPossibleAutoCreate(userDevice.getBundleNodeName(), new PayloadItem<>(bundle));
}
@ -606,7 +607,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
SmackException.NotConnectedException, XMPPException.XMPPErrorException,
PubSubException.NotAPubSubNodeException {
PubSubManager pm = PubSubManager.getInstanceFor(connection, contact);
PubSubManager pm = PubSubManager.getInstance(connection, contact);
String nodeName = OmemoConstants.PEP_NODE_DEVICE_LIST;
LeafNode node = pm.getLeafNode(nodeName);
@ -636,7 +637,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
SmackException.NoResponseException {
PubSubManager.getInstanceFor(connection, connection.getUser().asBareJid())
PubSubManager.getInstance(connection, connection.getUser().asBareJid())
.tryToPublishAndPossibleAutoCreate(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList));
}
@ -785,6 +786,33 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
processBundle(omemoManager, randomPreKeyBundle, contactsDevice);
}
/**
* Build OMEMO sessions with all devices of the contact, we haven't had sessions with before.
* This method returns a set of OmemoDevices. This set contains all devices, with which we either had sessions
* before, plus those devices with which we just built sessions.
*
* @param connection authenticated XMPP connection.
* @param userDevice our OmemoDevice
* @param contact the BareJid of the contact with whom we want to build sessions with.
* @return set of devices with a session.
* @throws SmackException.NotConnectedException
* @throws InterruptedException
* @throws SmackException.NoResponseException
*/
private Set<OmemoDevice> buildMissingSessionsWithContact(XMPPConnection connection,
OmemoDevice userDevice,
BareJid contact)
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
OmemoCachedDeviceList contactsDeviceIds = getOmemoStoreBackend().loadCachedDeviceList(userDevice, contact);
Set<OmemoDevice> 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<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
return devicesWithSession;
}
/**
* Build OMEMO sessions with all devices of the contacts, we haven't had sessions with before.
* This method returns a set of OmemoDevices. This set contains all devices, with which we either had sessions
* before, plus those devices with which we just built sessions.
*
* @param connection authenticated XMPP connection.
* @param userDevice our OmemoDevice
* @param contacts set of BareJids of contacts, we want to build sessions with.
* @return set of devices, we have sessions with.
* @throws SmackException.NotConnectedException
* @throws InterruptedException
* @throws SmackException.NoResponseException
*/
private Set<OmemoDevice> buildMissingSessionsWithContacts(XMPPConnection connection,
OmemoDevice userDevice,
Set<BareJid> contacts)
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
Set<OmemoDevice> devicesWithSessions = new HashSet<>();
for (BareJid contact : contacts) {
Set<OmemoDevice> 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<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
return undecidedDevices;
}
/**
* Return a set of all devices from the provided set, which are untrusted.
* A device is also considered untrusted, if its fingerprint cannot be loaded.
*
* @param userDevice our own OmemoDevice
* @param trustCallback OmemoTrustCallback to query trust decisions from
* @param devices set of OmemoDevices
* @return set of OmemoDevices from devices, which contains all devices which are untrusted
*/
private Set<OmemoDevice> getUntrustedDeviced(OmemoDevice userDevice, OmemoTrustCallback trustCallback, Set<OmemoDevice> devices) {
Set<OmemoDevice> 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.
*

View file

@ -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);
}

View file

@ -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";
}
}

View file

@ -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<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @throws NoSuchAlgorithmException
* @throws IllegalBlockSizeException
* @throws UnsupportedEncodingException
* @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException
*/
public OmemoMessageBuilder(OmemoDevice userDevice,
@ -105,7 +107,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
String message)
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
IllegalBlockSizeException,
UnsupportedEncodingException, InvalidAlgorithmParameterException {
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
this.userDevice = userDevice;
this.trustCallback = callback;
this.ratchet = ratchet;
@ -128,6 +130,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @throws NoSuchAlgorithmException
* @throws IllegalBlockSizeException
* @throws UnsupportedEncodingException
* @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException
*/
public OmemoMessageBuilder(OmemoDevice userDevice,
@ -135,7 +138,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> 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<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
*
* @param message plaintext message
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException
* @throws InvalidKeyException
@ -154,7 +158,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @throws BadPaddingException
* @throws IllegalBlockSizeException
*/
private void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
private void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
if (message == null) {
return;
}
@ -162,7 +166,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
// Encrypt message body
SecretKey secretKey = new SecretKeySpec(messageKey, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(initializationVector);
Cipher cipher = Cipher.getInstance(CIPHERMODE);
Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] body;

View file

@ -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.
@ -189,7 +189,7 @@ public final class OpenPgpManager extends Manager {
* @param connection xmpp connection.
* @return instance of the manager.
*/
public static synchronized OpenPgpManager getInstanceFor(XMPPConnection connection) {
public static OpenPgpManager getInstanceFor(XMPPConnection connection) {
OpenPgpManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new OpenPgpManager(connection);

View file

@ -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.
@ -32,11 +32,11 @@ public class CryptElement extends EncryptedOpenPgpContentElement {
public static final String ELEMENT = "crypt";
public CryptElement(Set<? extends Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
public CryptElement(Set<Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
super(to, rpad, timestamp, payload);
}
public CryptElement(Set<? extends Jid> to, List<ExtensionElement> payload) {
public CryptElement(Set<Jid> to, List<ExtensionElement> payload) {
super(to, payload);
}

View file

@ -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<? extends Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
protected EncryptedOpenPgpContentElement(Set<Jid> to, String rpad, Date timestamp, List<ExtensionElement> 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<? extends Jid> to, List<ExtensionElement> payload) {
protected EncryptedOpenPgpContentElement(Set<Jid> to, List<ExtensionElement> 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);
}

View file

@ -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<? extends Jid> to;
private final Set<Jid> to;
private final Date timestamp;
private final MultiMap<String, ExtensionElement> payload;
private String timestampString;
protected OpenPgpContentElement(Set<? extends Jid> to, Date timestamp, List<ExtensionElement> payload) {
protected OpenPgpContentElement(Set<Jid> to, Date timestamp, List<ExtensionElement> 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<? extends Jid> getTo() {
public final Set<Jid> getTo() {
return to;
}

View file

@ -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<? extends Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
public SigncryptElement(Set<Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
super(to, rpad, timestamp, payload);
}
public SigncryptElement(Set<? extends Jid> to, List<ExtensionElement> payload) {
public SigncryptElement(Set<Jid> to, List<ExtensionElement> payload) {
super(to, payload);
}

View file

@ -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<PayloadItem<PublicKeysListElement>> 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);

View file

@ -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) {

View file

@ -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')

View file

@ -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;
}
}

View file

@ -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")
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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<DnsQueryResult, IOException> 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;
}
}
}

View file

@ -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<Element> 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() {

View file

@ -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
}
}