mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 14:02:06 +01:00
Compare commits
42 commits
0cd3318b12
...
870756997f
Author | SHA1 | Date | |
---|---|---|---|
|
870756997f | ||
|
fc70484cf6 | ||
|
2dbc32340d | ||
|
d97fb126a1 | ||
|
99bf8316f5 | ||
|
9c30e4f3cc | ||
|
11775ed6b0 | ||
|
488055948d | ||
|
7fd0676ff4 | ||
|
6076a9dfa5 | ||
|
a1c88f1fad | ||
|
fc45e1b905 | ||
|
0ec7e84cbc | ||
|
9ad162af6e | ||
|
474ea68d4a | ||
|
62fd897cf7 | ||
|
75b1d8ce13 | ||
|
1122bf394c | ||
|
3075430713 | ||
|
d1f2631771 | ||
|
87e0ac9ba1 | ||
|
7d5274dad1 | ||
|
a9673408cc | ||
|
0d17f195b0 | ||
|
d6b6fdca17 | ||
|
38384a1eed | ||
|
af0fb7543c | ||
|
fea7362293 | ||
af88227919 | |||
|
d10319f1a0 | ||
|
c499556d07 | ||
|
7d7fbe6828 | ||
|
9f8d13b8cd | ||
|
d6a90942a4 | ||
|
9be498c440 | ||
|
25b3f35421 | ||
|
7f542e403f | ||
|
5d46e281fc | ||
|
ab7d81e7b5 | ||
|
89c0fa4b99 | ||
|
14f288a763 | ||
|
927eb5e7d7 |
77 changed files with 1560 additions and 2064 deletions
|
@ -6,6 +6,11 @@
|
||||||
<module name="SuppressionFilter">
|
<module name="SuppressionFilter">
|
||||||
<property name="file" value="config/suppressions.xml"/>
|
<property name="file" value="config/suppressions.xml"/>
|
||||||
</module>
|
</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">
|
<module name="Header">
|
||||||
<property name="headerFile" value="config/${checkstyleLicenseHeader}.txt"/>
|
<property name="headerFile" value="config/${checkstyleLicenseHeader}.txt"/>
|
||||||
<property name="ignoreLines" value="3"/>
|
<property name="ignoreLines" value="3"/>
|
||||||
|
@ -61,6 +66,14 @@
|
||||||
<property name="format" value="^\s*//[^\s]"/>
|
<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?"/>
|
<property name="message" value="Comment start ('//') followed by non-space character. You would not continue after a punctuation without a space, would you?"/>
|
||||||
</module>
|
</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="JavadocPackage"/>
|
||||||
<module name="TreeWalker">
|
<module name="TreeWalker">
|
||||||
<module name="SuppressionCommentFilter"/>
|
<module name="SuppressionCommentFilter"/>
|
||||||
|
|
|
@ -103,6 +103,7 @@ 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). |
|
| [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. |
|
| [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. |
|
| [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
|
Unofficial XMPP Extensions
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -20,7 +20,7 @@ and a stanza listener:
|
||||||
```
|
```
|
||||||
// Create a stanza filter to listen for new messages from a particular
|
// Create a stanza filter to listen for new messages from a particular
|
||||||
// user. We use an AndFilter to combine two other filters._
|
// 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".
|
// Assume we've created an XMPPConnection named "connection".
|
||||||
|
|
||||||
// First, register a stanza collector using the filter we created.
|
// First, register a stanza collector using the filter we created.
|
||||||
|
|
|
@ -12,6 +12,7 @@ include 'smack-core',
|
||||||
'smack-debug-slf4j',
|
'smack-debug-slf4j',
|
||||||
'smack-resolver-dnsjava',
|
'smack-resolver-dnsjava',
|
||||||
'smack-resolver-minidns',
|
'smack-resolver-minidns',
|
||||||
|
'smack-resolver-minidns-dox',
|
||||||
'smack-resolver-javax',
|
'smack-resolver-javax',
|
||||||
'smack-sasl-javax',
|
'smack-sasl-javax',
|
||||||
'smack-sasl-provided',
|
'smack-sasl-provided',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014 Florian Schmaus
|
* Copyright © 2014-2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -42,18 +42,17 @@ public final class AndroidBase64Encoder implements org.jivesoftware.smack.util.s
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] decode(byte[] input, int offset, int len) {
|
public String encodeToString(byte[] input) {
|
||||||
return Base64.decode(input, offset, len, 0);
|
return Base64.encodeToString(input, BASE64_ENCODER_FLAGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeToString(byte[] input, int offset, int len) {
|
public String encodeToStringWithoutPadding(byte[] input) {
|
||||||
return Base64.encodeToString(input, offset, len, BASE64_ENCODER_FLAGS);
|
return Base64.encodeToString(input, BASE64_ENCODER_FLAGS | Base64.NO_PADDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encode(byte[] input, int offset, int len) {
|
public byte[] encode(byte[] input) {
|
||||||
return Base64.encode(input, offset, len, BASE64_ENCODER_FLAGS);
|
return Base64.encode(input, BASE64_ENCODER_FLAGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ dependencies {
|
||||||
testCompile "org.powermock:powermock-module-junit4-rule:$powerMockVersion"
|
testCompile "org.powermock:powermock-module-junit4-rule:$powerMockVersion"
|
||||||
testCompile "org.powermock:powermock-api-mockito2:$powerMockVersion"
|
testCompile "org.powermock:powermock-api-mockito2:$powerMockVersion"
|
||||||
testCompile 'com.jamesmurty.utils:java-xmlbuilder:1.2'
|
testCompile 'com.jamesmurty.utils:java-xmlbuilder:1.2'
|
||||||
testCompile 'net.iharder:base64:2.3.8'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateFileTask extends DefaultTask {
|
class CreateFileTask extends DefaultTask {
|
||||||
|
|
|
@ -41,6 +41,7 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
@ -343,6 +344,17 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
|
|
||||||
protected final AsyncButOrdered<StanzaListener> inOrderListeners = new AsyncButOrdered<>();
|
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
|
* The used host to establish the connection to
|
||||||
*/
|
*/
|
||||||
|
@ -1336,7 +1348,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
executorService = ASYNC_BUT_ORDERED.asExecutorFor(this);
|
executorService = ASYNC_BUT_ORDERED.asExecutorFor(this);
|
||||||
break;
|
break;
|
||||||
case async:
|
case async:
|
||||||
executorService = CACHED_EXECUTOR_SERVICE;
|
executorService = limitedExcutor;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final IQRequestHandler finalIqRequestHandler = iqRequestHandler;
|
final IQRequestHandler finalIqRequestHandler = iqRequestHandler;
|
||||||
|
@ -1353,7 +1365,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (response.getType() == IQ.Type.result || response.getType() == IQ.Type.error);
|
assert response.isResponseIQ();
|
||||||
|
|
||||||
response.setTo(iqRequest.getFrom());
|
response.setTo(iqRequest.getFrom());
|
||||||
response.setStanzaId(iqRequest.getStanzaId());
|
response.setStanzaId(iqRequest.getStanzaId());
|
||||||
|
@ -1379,7 +1391,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
final Collection<StanzaListener> listenersToNotify = new LinkedList<>();
|
final Collection<StanzaListener> listenersToNotify = new LinkedList<>();
|
||||||
extractMatchingListeners(packet, asyncRecvListeners, listenersToNotify);
|
extractMatchingListeners(packet, asyncRecvListeners, listenersToNotify);
|
||||||
for (final StanzaListener listener : listenersToNotify) {
|
for (final StanzaListener listener : listenersToNotify) {
|
||||||
asyncGo(new Runnable() {
|
asyncGoLimited(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
@ -1875,6 +1887,75 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
return getClass().getSimpleName() + '[' + localEndpointString + "] (" + getConnectionCounter() + ')';
|
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) {
|
protected static void asyncGo(Runnable runnable) {
|
||||||
CACHED_EXECUTOR_SERVICE.execute(runnable);
|
CACHED_EXECUTOR_SERVICE.execute(runnable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,16 @@ public class AsyncButOrdered<K> {
|
||||||
|
|
||||||
private final Map<K, Boolean> threadActiveMap = new WeakHashMap<>();
|
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.
|
* Invoke the given {@link Runnable} asynchronous but ordered in respect to the given key.
|
||||||
*
|
*
|
||||||
|
@ -86,7 +96,11 @@ public class AsyncButOrdered<K> {
|
||||||
if (newHandler) {
|
if (newHandler) {
|
||||||
Handler handler = new Handler(keyQueue, key);
|
Handler handler = new Handler(keyQueue, key);
|
||||||
threadActiveMap.put(key, true);
|
threadActiveMap.put(key, true);
|
||||||
AbstractXMPPConnection.asyncGo(handler);
|
if (executor == null) {
|
||||||
|
AbstractXMPPConnection.asyncGo(handler);
|
||||||
|
} else {
|
||||||
|
executor.execute(handler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2017-2018 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2017-2019 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -49,9 +49,29 @@ import org.minidns.dnsname.DnsName;
|
||||||
import org.minidns.util.InetAddressUtil;
|
import org.minidns.util.InetAddressUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration to use while establishing the connection to the server.
|
* 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>
|
||||||
*
|
*
|
||||||
* @author Gaston Dombiak
|
* @author Gaston Dombiak
|
||||||
|
* @author Florian Schmaus
|
||||||
*/
|
*/
|
||||||
public abstract class ConnectionConfiguration {
|
public abstract class ConnectionConfiguration {
|
||||||
|
|
||||||
|
@ -534,6 +554,39 @@ 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.
|
* Set the XMPP entities username and password.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -365,4 +365,19 @@ public final class SmackConfiguration {
|
||||||
public static void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) {
|
public static void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) {
|
||||||
SmackConfiguration.unknownIqRequestReplyMode = Objects.requireNonNull(unknownIqRequestReplyMode, "Must set mode");
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,27 @@ import org.jxmpp.jid.EntityFullJid;
|
||||||
* disconnected and then connected again. Listeners of the XMPPConnection will be retained across
|
* disconnected and then connected again. Listeners of the XMPPConnection will be retained across
|
||||||
* connections.
|
* connections.
|
||||||
* </p>
|
* </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 Matt Tucker
|
||||||
* @author Guenther Niess
|
* @author Guenther Niess
|
||||||
|
|
|
@ -16,12 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.debugger;
|
package org.jivesoftware.smack.debugger;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
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
|
* Very simple debugger that prints to the console (stdout) the sent and received stanzas. Use
|
||||||
|
@ -55,12 +54,8 @@ public class ConsoleDebugger extends AbstractDebugger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void log(String logMessage, Throwable throwable) {
|
protected void log(String logMessage, Throwable throwable) {
|
||||||
StringWriter sw = new StringWriter();
|
String stacktrace = ExceptionUtil.getStackTrace(throwable);
|
||||||
PrintWriter pw = new PrintWriter(sw);
|
log(logMessage + '\n' + stacktrace);
|
||||||
// CHECKSTYLE:OFF
|
|
||||||
throwable.printStackTrace(pw);
|
|
||||||
// CHECKSTYLE:ON
|
|
||||||
log(logMessage + sw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Factory implements SmackDebuggerFactory {
|
public static final class Factory implements SmackDebuggerFactory {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.ExceptionUtil;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.PacketUtil;
|
import org.jivesoftware.smack.util.PacketUtil;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
@ -150,6 +151,17 @@ public class AbstractError {
|
||||||
return getThis();
|
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) {
|
public B setTextNamespace(String textNamespace) {
|
||||||
this.textNamespace = textNamespace;
|
this.textNamespace = textNamespace;
|
||||||
return getThis();
|
return getThis();
|
||||||
|
|
|
@ -105,6 +105,16 @@ 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() {
|
public final String getChildElementName() {
|
||||||
return childElementName;
|
return childElementName;
|
||||||
}
|
}
|
||||||
|
@ -287,7 +297,7 @@ public abstract class IQ extends Stanza {
|
||||||
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
|
* @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) {
|
public static ErrorIQ createErrorResponse(final IQ request, final StanzaError.Builder error) {
|
||||||
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
|
if (!request.isRequestIQ()) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
|
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2016-2018 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2016-2019 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,7 +18,6 @@
|
||||||
package org.jivesoftware.smack.util;
|
package org.jivesoftware.smack.util;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
@ -253,18 +252,6 @@ 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
|
* 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.
|
* twice so that there is a more equal chance that a number will be picked.
|
||||||
|
@ -288,23 +275,16 @@ public class StringUtils {
|
||||||
* @return a random String of numbers and letters of the specified length.
|
* @return a random String of numbers and letters of the specified length.
|
||||||
*/
|
*/
|
||||||
public static String insecureRandomString(int length) {
|
public static String insecureRandomString(int length) {
|
||||||
return randomString(length, randGen.get());
|
return randomString(length, RandomUtil.RANDOM.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) {
|
public static String randomString(final int length) {
|
||||||
return randomString(length, SECURE_RANDOM.get());
|
return randomString(length, RandomUtil.SECURE_RANDOM.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String randomString(final int length, Random random) {
|
public static String randomString(final int length, Random random) {
|
||||||
if (length < 1) {
|
if (length == 0) {
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] randomBytes = new byte[length];
|
byte[] randomBytes = new byte[length];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014-2015 Florian Schmaus
|
* Copyright © 2014-2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -39,29 +39,19 @@ public class Base64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String encodeToString(byte[] input) {
|
public static final String encodeToString(byte[] input) {
|
||||||
byte[] bytes = encode(input);
|
return base64encoder.encodeToString(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) {
|
public static final String encodeToString(byte[] input, int offset, int len) {
|
||||||
byte[] bytes = encode(input, offset, len);
|
return encodeToString(slice(input, offset, len));
|
||||||
try {
|
}
|
||||||
return new String(bytes, StringUtils.USASCII);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
public static final String encodeToStringWithoutPadding(byte[] input) {
|
||||||
throw new AssertionError(e);
|
return base64encoder.encodeToStringWithoutPadding(input);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final byte[] encode(byte[] input) {
|
public static final byte[] encode(byte[] input) {
|
||||||
return encode(input, 0, input.length);
|
return base64encoder.encode(input);
|
||||||
}
|
|
||||||
|
|
||||||
public static final byte[] encode(byte[] input, int offset, int len) {
|
|
||||||
return base64encoder.encode(input, offset, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String decodeToString(String string) {
|
public static final String decodeToString(String string) {
|
||||||
|
@ -73,34 +63,43 @@ public class Base64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String decodeToString(byte[] input, int offset, int len) {
|
// TODO: We really should not mask the IllegalArgumentException. But some unit test depend on this behavior, like
|
||||||
byte[] bytes = decode(input, offset, len);
|
// ibb.packet.DataPacketExtension.shouldReturnNullIfDataIsInvalid().
|
||||||
|
public static final byte[] decode(String string) {
|
||||||
try {
|
try {
|
||||||
return new String(bytes, StringUtils.UTF8);
|
return base64encoder.decode(string);
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new IllegalStateException("UTF-8 not supported", e);
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final byte[] decode(String string) {
|
|
||||||
return base64encoder.decode(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final byte[] decode(byte[] input) {
|
public static final byte[] decode(byte[] input) {
|
||||||
return base64encoder.decode(input, 0, input.length);
|
String string;
|
||||||
|
try {
|
||||||
|
string = new String(input, StringUtils.USASCII);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
return decode(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final byte[] decode(byte[] input, int offset, int len) {
|
private static byte[] slice(byte[] input, int offset, int len) {
|
||||||
return base64encoder.decode(input, offset, len);
|
if (offset == 0 && len == input.length) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] res = new byte[len];
|
||||||
|
System.arraycopy(input, offset, res, 0, len);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Encoder {
|
public interface Encoder {
|
||||||
byte[] decode(String string);
|
byte[] decode(String string);
|
||||||
|
|
||||||
byte[] decode(byte[] input, int offset, int len);
|
String encodeToString(byte[] input);
|
||||||
|
|
||||||
String encodeToString(byte[] input, int offset, int len);
|
String encodeToStringWithoutPadding(byte[] input);
|
||||||
|
|
||||||
byte[] encode(byte[] input, int offset, int len);
|
byte[] encode(byte[] input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,11 @@ public class DummyConnection extends AbstractXMPPConnection {
|
||||||
this(getDummyConfigurationBuilder().build());
|
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() {
|
private EntityFullJid getUserJid() {
|
||||||
try {
|
try {
|
||||||
return JidCreate.entityFullFrom(config.getUsername()
|
return JidCreate.entityFullFrom(config.getUsername()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014 Florian Schmaus
|
* Copyright © 2014-2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,14 +16,10 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.test.util;
|
package org.jivesoftware.smack.test.util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.util.Base64;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64.Encoder;
|
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
|
* The SmackTestSuite takes care of initializing Smack for the unit tests. For example the Base64
|
||||||
* encoder is configured.
|
* encoder is configured.
|
||||||
|
@ -35,53 +31,24 @@ public class SmackTestSuite {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] decode(String string) {
|
public byte[] decode(String string) {
|
||||||
try {
|
return Base64.getDecoder().decode(string);
|
||||||
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
|
@Override
|
||||||
public byte[] decode(byte[] input, int offset, int len) {
|
public String encodeToString(byte[] input) {
|
||||||
try {
|
return Base64.getEncoder().encodeToString(input);
|
||||||
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
|
@Override
|
||||||
public String encodeToString(byte[] input, int offset, int len) {
|
public String encodeToStringWithoutPadding(byte[] input) {
|
||||||
return Base64.encodeBytes(input, offset, len);
|
return Base64.getEncoder().withoutPadding().encodeToString(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encode(byte[] input, int offset, int len) {
|
public byte[] encode(byte[] input) {
|
||||||
String string = encodeToString(input, offset, len);
|
return Base64.getEncoder().encode(input);
|
||||||
try {
|
|
||||||
return string.getBytes(StringUtils.USASCII);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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()");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software.
|
* Copyright 2003-2007 Jive Software, 2019 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -88,13 +88,7 @@ public class StringUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRandomString() {
|
public void testRandomString() {
|
||||||
// Boundary test
|
String result;
|
||||||
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
|
// Test various lengths - make sure the same length is returned
|
||||||
result = StringUtils.randomString(4);
|
result = StringUtils.randomString(4);
|
||||||
|
@ -104,4 +98,17 @@ public class StringUtilsTest {
|
||||||
result = StringUtils.randomString(128);
|
result = StringUtils.randomString(128);
|
||||||
assertTrue(result.length() == 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,5 @@ dependencies {
|
||||||
testCompile project(path: ":smack-core", configuration: "archives")
|
testCompile project(path: ":smack-core", configuration: "archives")
|
||||||
testCompile project(path: ":smack-extensions", configuration: "testRuntime")
|
testCompile project(path: ":smack-extensions", configuration: "testRuntime")
|
||||||
|
|
||||||
compile "org.bouncycastle:bcprov-jdk15on:1.57"
|
compile "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* 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;
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* 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;
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* 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;
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2017 Paul Schaub
|
* Copyright © 2017 Paul Schaub, 2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -35,7 +35,6 @@ import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_512;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -61,7 +60,6 @@ public final class HashManager extends Manager {
|
||||||
static {
|
static {
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
}
|
}
|
||||||
public static final String PROVIDER = "BC";
|
|
||||||
|
|
||||||
public static final String PREFIX_NS_ALGO = "urn:xmpp:hash-function-text-names:";
|
public static final String PREFIX_NS_ALGO = "urn:xmpp:hash-function-text-names:";
|
||||||
|
|
||||||
|
@ -224,52 +222,52 @@ public final class HashManager extends Manager {
|
||||||
try {
|
try {
|
||||||
switch (algorithm) {
|
switch (algorithm) {
|
||||||
case MD5:
|
case MD5:
|
||||||
md = MessageDigest.getInstance("MD5", PROVIDER);
|
md = MessageDigest.getInstance("MD5");
|
||||||
break;
|
break;
|
||||||
case SHA_1:
|
case SHA_1:
|
||||||
md = MessageDigest.getInstance("SHA-1", PROVIDER);
|
md = MessageDigest.getInstance("SHA-1");
|
||||||
break;
|
break;
|
||||||
case SHA_224:
|
case SHA_224:
|
||||||
md = MessageDigest.getInstance("SHA-224", PROVIDER);
|
md = MessageDigest.getInstance("SHA-224");
|
||||||
break;
|
break;
|
||||||
case SHA_256:
|
case SHA_256:
|
||||||
md = MessageDigest.getInstance("SHA-256", PROVIDER);
|
md = MessageDigest.getInstance("SHA-256");
|
||||||
break;
|
break;
|
||||||
case SHA_384:
|
case SHA_384:
|
||||||
md = MessageDigest.getInstance("SHA-384", PROVIDER);
|
md = MessageDigest.getInstance("SHA-384");
|
||||||
break;
|
break;
|
||||||
case SHA_512:
|
case SHA_512:
|
||||||
md = MessageDigest.getInstance("SHA-512", PROVIDER);
|
md = MessageDigest.getInstance("SHA-512");
|
||||||
break;
|
break;
|
||||||
case SHA3_224:
|
case SHA3_224:
|
||||||
md = MessageDigest.getInstance("SHA3-224", PROVIDER);
|
md = MessageDigest.getInstance("SHA3-224");
|
||||||
break;
|
break;
|
||||||
case SHA3_256:
|
case SHA3_256:
|
||||||
md = MessageDigest.getInstance("SHA3-256", PROVIDER);
|
md = MessageDigest.getInstance("SHA3-256");
|
||||||
break;
|
break;
|
||||||
case SHA3_384:
|
case SHA3_384:
|
||||||
md = MessageDigest.getInstance("SHA3-384", PROVIDER);
|
md = MessageDigest.getInstance("SHA3-384");
|
||||||
break;
|
break;
|
||||||
case SHA3_512:
|
case SHA3_512:
|
||||||
md = MessageDigest.getInstance("SHA3-512", PROVIDER);
|
md = MessageDigest.getInstance("SHA3-512");
|
||||||
break;
|
break;
|
||||||
case BLAKE2B160:
|
case BLAKE2B160:
|
||||||
md = MessageDigest.getInstance("BLAKE2b-160", PROVIDER);
|
md = MessageDigest.getInstance("BLAKE2b-160");
|
||||||
break;
|
break;
|
||||||
case BLAKE2B256:
|
case BLAKE2B256:
|
||||||
md = MessageDigest.getInstance("BLAKE2b-256", PROVIDER);
|
md = MessageDigest.getInstance("BLAKE2b-256");
|
||||||
break;
|
break;
|
||||||
case BLAKE2B384:
|
case BLAKE2B384:
|
||||||
md = MessageDigest.getInstance("BLAKE2b-384", PROVIDER);
|
md = MessageDigest.getInstance("BLAKE2b-384");
|
||||||
break;
|
break;
|
||||||
case BLAKE2B512:
|
case BLAKE2B512:
|
||||||
md = MessageDigest.getInstance("BLAKE2b-512", PROVIDER);
|
md = MessageDigest.getInstance("BLAKE2b-512");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new AssertionError("Invalid enum value: " + algorithm);
|
throw new AssertionError("Invalid enum value: " + algorithm);
|
||||||
}
|
}
|
||||||
return md;
|
return md;
|
||||||
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public final class JingleFileTransferManager extends Manager {
|
||||||
super(connection);
|
super(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized JingleFileTransferManager getInstanceFor(XMPPConnection connection) {
|
||||||
JingleFileTransferManager manager = INSTANCES.get(connection);
|
JingleFileTransferManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new JingleFileTransferManager(connection);
|
manager = new JingleFileTransferManager(connection);
|
||||||
|
|
|
@ -178,7 +178,9 @@ public final class MamManager extends Manager {
|
||||||
* @param connection the XMPP connection to get the archive for.
|
* @param connection the XMPP connection to get the archive for.
|
||||||
* @return the instance of MamManager.
|
* @return the instance of MamManager.
|
||||||
*/
|
*/
|
||||||
|
// CHECKSTYLE:OFF:RegexpSingleline
|
||||||
public static MamManager getInstanceFor(XMPPConnection connection) {
|
public static MamManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
// CHECKSTYLE:ON:RegexpSingleline
|
||||||
return getInstanceFor(connection, (Jid) null);
|
return getInstanceFor(connection, (Jid) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ public final class ReferenceManager extends Manager {
|
||||||
* @param connection xmpp connection
|
* @param connection xmpp connection
|
||||||
* @return reference manager instance
|
* @return reference manager instance
|
||||||
*/
|
*/
|
||||||
public static ReferenceManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized ReferenceManager getInstanceFor(XMPPConnection connection) {
|
||||||
ReferenceManager manager = INSTANCES.get(connection);
|
ReferenceManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new ReferenceManager(connection);
|
manager = new ReferenceManager(connection);
|
||||||
|
|
|
@ -78,7 +78,7 @@ public final class StableUniqueStanzaIdManager extends Manager {
|
||||||
* @param connection xmpp-connection
|
* @param connection xmpp-connection
|
||||||
* @return manager instance for the connection
|
* @return manager instance for the connection
|
||||||
*/
|
*/
|
||||||
public static StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) {
|
||||||
StableUniqueStanzaIdManager manager = INSTANCES.get(connection);
|
StableUniqueStanzaIdManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new StableUniqueStanzaIdManager(connection);
|
manager = new StableUniqueStanzaIdManager(connection);
|
||||||
|
|
|
@ -61,7 +61,7 @@ public final class SpoilerManager extends Manager {
|
||||||
* @param connection xmpp connection
|
* @param connection xmpp connection
|
||||||
* @return SpoilerManager
|
* @return SpoilerManager
|
||||||
*/
|
*/
|
||||||
public static SpoilerManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized SpoilerManager getInstanceFor(XMPPConnection connection) {
|
||||||
SpoilerManager manager = INSTANCES.get(connection);
|
SpoilerManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new SpoilerManager(connection);
|
manager = new SpoilerManager(connection);
|
||||||
|
|
|
@ -317,4 +317,11 @@
|
||||||
<className>org.jivesoftware.smackx.message_markup.provider.MarkupElementProvider</className>
|
<className>org.jivesoftware.smackx.message_markup.provider.MarkupElementProvider</className>
|
||||||
</extensionProvider>
|
</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>
|
</smackProviders>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2018 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2018-2019 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* 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
|
* disco request. The valid types are defined by the category client. Follow this link to learn
|
||||||
* the possible types: <a href="http://xmpp.org/registrar/disco-categories.html#client">Jabber::Registrar</a>.
|
* the possible types: <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>
|
||||||
*
|
*
|
||||||
* @return the type of client that will be returned when asked for the client identity in a
|
* @return the type of client that will be returned when asked for the client identity in a
|
||||||
* disco request.
|
* disco request.
|
||||||
|
@ -271,8 +271,8 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
*/
|
*/
|
||||||
public Set<DiscoverInfo.Identity> getIdentities() {
|
public Set<DiscoverInfo.Identity> getIdentities() {
|
||||||
Set<Identity> res = new HashSet<>(identities);
|
Set<Identity> res = new HashSet<>(identities);
|
||||||
// Add the default identity that must exist
|
// Add the main identity that must exist
|
||||||
res.add(defaultIdentity);
|
res.add(identity);
|
||||||
return Collections.unmodifiableSet(res);
|
return Collections.unmodifiableSet(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Represents the identity of a given XMPP entity. An entity may have many identities but all
|
||||||
* the identities SHOULD have the same name.<p>
|
* the identities SHOULD have the same name.<p>
|
||||||
*
|
*
|
||||||
* Refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
|
* Refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>
|
||||||
* in order to get the official registry of values for the <i>category</i> and <i>type</i>
|
* in order to get the official registry of values for the <i>category</i> and <i>type</i>
|
||||||
* attributes.
|
* 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
|
* Returns the entity's category. To get the official registry of values for the
|
||||||
* 'category' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
|
* 'category' attribute refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>.
|
||||||
*
|
*
|
||||||
* @return the entity's category.
|
* @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
|
* Returns the entity's type. To get the official registry of values for the
|
||||||
* 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a>
|
* 'type' attribute refer to <a href="https://xmpp.org/registrar/disco-categories.html">XMPP Registry for Service Discovery Identities</a>.
|
||||||
*
|
*
|
||||||
* @return the entity's type.
|
* @return the entity's type.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -124,7 +124,7 @@ public final class GeoLocationManager extends Manager {
|
||||||
|
|
||||||
private LeafNode getNode()
|
private LeafNode getNode()
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotALeafNodeException {
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotALeafNodeException {
|
||||||
return PubSubManager.getInstance(connection()).getOrCreateLeafNode(GeoLocation.NAMESPACE);
|
return PubSubManager.getInstanceFor(connection()).getOrCreateLeafNode(GeoLocation.NAMESPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public final class JingleTransportMethodManager extends Manager {
|
||||||
super(connection);
|
super(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JingleTransportMethodManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized JingleTransportMethodManager getInstanceFor(XMPPConnection connection) {
|
||||||
JingleTransportMethodManager manager = INSTANCES.get(connection);
|
JingleTransportMethodManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new JingleTransportMethodManager(connection);
|
manager = new JingleTransportMethodManager(connection);
|
||||||
|
|
|
@ -38,7 +38,7 @@ public final class JingleIBBTransportManager extends JingleTransportManager<Jing
|
||||||
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleIBBTransportProvider());
|
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleIBBTransportProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JingleIBBTransportManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized JingleIBBTransportManager getInstanceFor(XMPPConnection connection) {
|
||||||
JingleIBBTransportManager manager = INSTANCES.get(connection);
|
JingleIBBTransportManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new JingleIBBTransportManager(connection);
|
manager = new JingleIBBTransportManager(connection);
|
||||||
|
|
|
@ -63,7 +63,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
|
||||||
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleS5BTransportProvider());
|
JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleS5BTransportProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JingleS5BTransportManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized JingleS5BTransportManager getInstanceFor(XMPPConnection connection) {
|
||||||
JingleS5BTransportManager manager = INSTANCES.get(connection);
|
JingleS5BTransportManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new JingleS5BTransportManager(connection);
|
manager = new JingleS5BTransportManager(connection);
|
||||||
|
|
|
@ -102,7 +102,7 @@ public final class MoodManager extends Manager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MoodManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized MoodManager getInstanceFor(XMPPConnection connection) {
|
||||||
MoodManager manager = INSTANCES.get(connection);
|
MoodManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new MoodManager(connection);
|
manager = new MoodManager(connection);
|
||||||
|
@ -147,7 +147,7 @@ public final class MoodManager extends Manager {
|
||||||
throws SmackException.NotLoggedInException, InterruptedException, PubSubException.NotALeafNodeException,
|
throws SmackException.NotLoggedInException, InterruptedException, PubSubException.NotALeafNodeException,
|
||||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
if (pubSubManager == null) {
|
if (pubSubManager == null) {
|
||||||
pubSubManager = PubSubManager.getInstance(getAuthenticatedConnectionOrThrow(), connection().getUser().asBareJid());
|
pubSubManager = PubSubManager.getInstanceFor(getAuthenticatedConnectionOrThrow(), connection().getUser().asBareJid());
|
||||||
}
|
}
|
||||||
|
|
||||||
LeafNode node = pubSubManager.getOrCreateLeafNode(MOOD_NODE);
|
LeafNode node = pubSubManager.getOrCreateLeafNode(MOOD_NODE);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014-2018 Florian Schmaus
|
* Copyright © 2014-2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -59,6 +59,7 @@ import org.jivesoftware.smackx.muc.packet.MUCUser;
|
||||||
|
|
||||||
import org.jxmpp.jid.DomainBareJid;
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
import org.jxmpp.jid.EntityFullJid;
|
||||||
import org.jxmpp.jid.EntityJid;
|
import org.jxmpp.jid.EntityJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
import org.jxmpp.jid.parts.Resourcepart;
|
import org.jxmpp.jid.parts.Resourcepart;
|
||||||
|
@ -304,7 +305,7 @@ public final class MultiUserChatManager extends Manager {
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
public List<EntityBareJid> getJoinedRooms(EntityJid user) throws NoResponseException, XMPPErrorException,
|
public List<EntityBareJid> getJoinedRooms(EntityFullJid user) throws NoResponseException, XMPPErrorException,
|
||||||
NotConnectedException, InterruptedException {
|
NotConnectedException, InterruptedException {
|
||||||
// Send the disco packet to the user
|
// Send the disco packet to the user
|
||||||
DiscoverItems result = serviceDiscoveryManager.discoverItems(user, DISCO_NODE);
|
DiscoverItems result = serviceDiscoveryManager.discoverItems(user, DISCO_NODE);
|
||||||
|
|
|
@ -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
|
// 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);
|
connection.addSyncStanzaListener(packetListener, FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER);
|
||||||
|
|
||||||
pepPubSubManager = PubSubManager.getInstance(connection, null);
|
pepPubSubManager = PubSubManager.getInstanceFor(connection, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PubSubManager getPepPubSubManager() {
|
public PubSubManager getPepPubSubManager() {
|
||||||
|
|
|
@ -88,7 +88,9 @@ public final class PubSubManager extends Manager {
|
||||||
* @param connection
|
* @param connection
|
||||||
* @return the default PubSub manager.
|
* @return the default PubSub manager.
|
||||||
*/
|
*/
|
||||||
public static PubSubManager getInstance(XMPPConnection connection) {
|
// CHECKSTYLE:OFF:RegexpSingleline
|
||||||
|
public static PubSubManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
// CHECKSTYLE:ON:RegexpSingleline
|
||||||
DomainBareJid pubSubService = null;
|
DomainBareJid pubSubService = null;
|
||||||
if (connection.isAuthenticated()) {
|
if (connection.isAuthenticated()) {
|
||||||
try {
|
try {
|
||||||
|
@ -110,7 +112,7 @@ public final class PubSubManager extends Manager {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getInstance(connection, pubSubService);
|
return getInstanceFor(connection, pubSubService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,7 +123,9 @@ public final class PubSubManager extends Manager {
|
||||||
* @param pubSubService the PubSub service, may be <code>null</code>.
|
* @param pubSubService the PubSub service, may be <code>null</code>.
|
||||||
* @return a PubSub manager for the connection and service.
|
* @return a PubSub manager for the connection and service.
|
||||||
*/
|
*/
|
||||||
public static PubSubManager getInstance(XMPPConnection connection, BareJid pubSubService) {
|
// CHECKSTYLE:OFF:RegexpSingleline
|
||||||
|
public static PubSubManager getInstanceFor(XMPPConnection connection, BareJid pubSubService) {
|
||||||
|
// CHECKSTYLE:ON:RegexpSingleline
|
||||||
if (pubSubService != null && connection.isAuthenticated() && connection.getUser().asBareJid().equals(pubSubService)) {
|
if (pubSubService != null && connection.isAuthenticated() && connection.getUser().asBareJid().equals(pubSubService)) {
|
||||||
// PEP service.
|
// PEP service.
|
||||||
pubSubService = null;
|
pubSubService = null;
|
||||||
|
@ -147,6 +151,28 @@ public final class PubSubManager extends Manager {
|
||||||
return pubSubManager;
|
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
|
* Create a pubsub manager associated to the specified connection where
|
||||||
* the pubsub requests require a specific to address for packets.
|
* the pubsub requests require a specific to address for packets.
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -560,7 +560,8 @@ public final class Roster extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a roster loaded listener.
|
* Add a roster loaded listener. Roster loaded listeners are invoked once the {@link Roster}
|
||||||
|
* was successfully loaded.
|
||||||
*
|
*
|
||||||
* @param rosterLoadedListener the listener to add.
|
* @param rosterLoadedListener the listener to add.
|
||||||
* @return true if the listener was not already added.
|
* @return true if the listener was not already added.
|
||||||
|
@ -587,6 +588,20 @@ 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) {
|
public boolean addPresenceEventListener(PresenceEventListener presenceEventListener) {
|
||||||
return presenceEventListeners.add(presenceEventListener);
|
return presenceEventListeners.add(presenceEventListener);
|
||||||
}
|
}
|
||||||
|
@ -1516,7 +1531,29 @@ public final class Roster extends Manager {
|
||||||
final Presence presence = (Presence) packet;
|
final Presence presence = (Presence) packet;
|
||||||
final Jid from = presence.getFrom();
|
final Jid from = presence.getFrom();
|
||||||
|
|
||||||
final BareJid key = from != null ? from.asBareJid() : null;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
asyncButOrdered.performAsyncButOrdered(key, new Runnable() {
|
asyncButOrdered.performAsyncButOrdered(key, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -151,18 +151,37 @@ public class XmppConnectionStressTest {
|
||||||
|
|
||||||
// Sanity check: All markers before must be true, all markers including the messageNumber marker must be false.
|
// 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++) {
|
for (int i = 0; i < fromMarkers.length; i++) {
|
||||||
if ((i < messageNumber && !fromMarkers[i])
|
final String inOrderViolation;
|
||||||
|| (i >= messageNumber && fromMarkers[i])) {
|
if (i < messageNumber && !fromMarkers[i]) {
|
||||||
// TODO: Better exception.
|
// A previous message was missing.
|
||||||
Exception exception = new Exception("out of order");
|
inOrderViolation = "not yet message #";
|
||||||
receiveExceptions.put(connection, exception);
|
} else if (i >= messageNumber && fromMarkers[i]) {
|
||||||
// TODO: Current Smack design does not guarantee that the listener won't be invoked again.
|
// We already received a new message.
|
||||||
// This is because the decission to invoke a sync listeners is done at a different place
|
// TODO: Can it ever happen that this is taken? Wouldn't we prior run into the "a previous
|
||||||
// then invoking the listener.
|
// message is missing" case?
|
||||||
connection.removeSyncStanzaListener(this);
|
inOrderViolation = "we already received a later (or the same) message #";
|
||||||
receivedSemaphore.release();
|
} else {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
fromMarkers[messageNumber] = true;
|
||||||
|
|
|
@ -240,6 +240,10 @@ public class XmppConnectionManager<DC extends AbstractXMPPConnection> {
|
||||||
}
|
}
|
||||||
|
|
||||||
connections.clear();
|
connections.clear();
|
||||||
|
|
||||||
|
if (accountRegistrationConnection != null) {
|
||||||
|
accountRegistrationConnection.disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ public class OmemoManagerSetupHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanUpPubSub(OmemoManager omemoManager) {
|
public static void cleanUpPubSub(OmemoManager omemoManager) {
|
||||||
PubSubManager pm = PubSubManager.getInstance(omemoManager.getConnection(),omemoManager.getOwnJid());
|
PubSubManager pm = PubSubManager.getInstanceFor(omemoManager.getConnection(),omemoManager.getOwnJid());
|
||||||
try {
|
try {
|
||||||
omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid());
|
omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid());
|
||||||
} catch (SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException e) {
|
} catch (SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException e) {
|
||||||
|
|
|
@ -17,12 +17,16 @@
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
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 java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
|
import org.jivesoftware.smack.packet.StanzaError;
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
|
@ -42,33 +46,70 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
if (pubSubService == null) {
|
if (pubSubService == null) {
|
||||||
throw new TestNotPossibleException("No PubSub service found");
|
throw new TestNotPossibleException("No PubSub service found");
|
||||||
}
|
}
|
||||||
pubSubManagerOne = PubSubManager.getInstance(conOne, pubSubService);
|
pubSubManagerOne = PubSubManager.getInstanceFor(conOne, pubSubService);
|
||||||
if (!pubSubManagerOne.canCreateNodesAndPublishItems()) {
|
if (!pubSubManagerOne.canCreateNodesAndPublishItems()) {
|
||||||
throw new TestNotPossibleException("PubSub service does not allow node creation");
|
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
|
@SmackIntegrationTest
|
||||||
public void simplePubSubNodeTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public void transientNotificationOnlyNodeWithoutItemTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
final String nodename = "sinttest-simple-nodename-" + testRunId;
|
final String nodename = "sinttest-transient-notificationonly-withoutitem-nodename-" + testRunId;
|
||||||
final String itemId = "sintest-simple-itemid-" + testRunId;
|
|
||||||
ConfigureForm defaultConfiguration = pubSubManagerOne.getDefaultConfiguration();
|
ConfigureForm defaultConfiguration = pubSubManagerOne.getDefaultConfiguration();
|
||||||
ConfigureForm config = new ConfigureForm(defaultConfiguration.createAnswerForm());
|
ConfigureForm config = new ConfigureForm(defaultConfiguration.createAnswerForm());
|
||||||
// Configure the node as "Notification-Only Node", which in turn means that
|
// Configure the node as "Notification-Only Node".
|
||||||
// items do not need payload, to prevent payload-required error responses when
|
|
||||||
// publishing the item.
|
|
||||||
config.setDeliverPayloads(false);
|
config.setDeliverPayloads(false);
|
||||||
// Set persistent_items to 'false' (was previously 'true') as workaround for ejabberd issue #2799
|
// Configure the node as "transient" (set persistent_items to 'false')
|
||||||
// (https://github.com/processone/ejabberd/issues/2799).
|
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 <bad-request/> error
|
||||||
|
* and a pubsub-specific error condition of <item-forbidden/>.
|
||||||
|
* </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')
|
||||||
config.setPersistentItems(false);
|
config.setPersistentItems(false);
|
||||||
Node node = pubSubManagerOne.createNode(nodename, config);
|
Node node = pubSubManagerOne.createNode(nodename, config);
|
||||||
try {
|
try {
|
||||||
LeafNode leafNode = (LeafNode) node;
|
LeafNode leafNode = (LeafNode) node;
|
||||||
leafNode.publish(new Item(itemId));
|
leafNode.publish(new Item(itemId));
|
||||||
List<Item> items = leafNode.getItems();
|
fail("An exception should have been thrown.");
|
||||||
assertEquals(1, items.size());
|
}
|
||||||
Item item = items.get(0);
|
catch (XMPPErrorException e) {
|
||||||
assertEquals(itemId, item.getId());
|
assertEquals(StanzaError.Type.MODIFY, e.getStanzaError().getType());
|
||||||
|
assertNotNull(e.getStanzaError().getExtension("item-forbidden", "http://jabber.org/protocol/pubsub#errors"));
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
pubSubManagerOne.deleteNode(nodename);
|
pubSubManagerOne.deleteNode(nodename);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,9 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.util.stringencoder.java7;
|
package org.jivesoftware.smack.util.stringencoder.java7;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.util.Base64;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Base 64 encoding implementation.
|
* A Base 64 encoding implementation.
|
||||||
|
@ -28,10 +26,15 @@ public final class Java7Base64Encoder implements org.jivesoftware.smack.util.str
|
||||||
|
|
||||||
private static final Java7Base64Encoder instance = new Java7Base64Encoder();
|
private static final Java7Base64Encoder instance = new Java7Base64Encoder();
|
||||||
|
|
||||||
private static final int BASE64_ENCODER_FLAGS = Base64.DONT_BREAK_LINES;
|
private final Base64.Encoder encoder;
|
||||||
|
private final Base64.Encoder encoderWithoutPadding;
|
||||||
|
|
||||||
|
private final Base64.Decoder decoder;
|
||||||
|
|
||||||
private Java7Base64Encoder() {
|
private Java7Base64Encoder() {
|
||||||
// Use getInstance()
|
encoder = Base64.getEncoder();
|
||||||
|
encoderWithoutPadding = encoder.withoutPadding();
|
||||||
|
decoder = Base64.getDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Java7Base64Encoder getInstance() {
|
public static Java7Base64Encoder getInstance() {
|
||||||
|
@ -40,27 +43,21 @@ public final class Java7Base64Encoder implements org.jivesoftware.smack.util.str
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] decode(String string) {
|
public byte[] decode(String string) {
|
||||||
return Base64.decode(string);
|
return decoder.decode(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] decode(byte[] input, int offset, int len) {
|
public String encodeToString(byte[] input) {
|
||||||
return Base64.decode(input, offset, len, 0);
|
return encoder.encodeToString(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeToString(byte[] input, int offset, int len) {
|
public String encodeToStringWithoutPadding(byte[] input) {
|
||||||
return Base64.encodeBytes(input, offset, len, BASE64_ENCODER_FLAGS);
|
return encoderWithoutPadding.encodeToString(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encode(byte[] input, int offset, int len) {
|
public byte[] encode(byte[] input) {
|
||||||
String string = encodeToString(input, offset, len);
|
return encoder.encode(input);
|
||||||
try {
|
|
||||||
return string.getBytes(StringUtils.USASCII);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.jivesoftware.smack.util.stringencoder.java7;
|
package org.jivesoftware.smack.util.stringencoder.java7;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.stringencoder.StringEncoder;
|
import org.jivesoftware.smack.util.stringencoder.StringEncoder;
|
||||||
|
@ -37,10 +38,12 @@ public final class Java7Base64UrlSafeEncoder implements StringEncoder<String> {
|
||||||
|
|
||||||
private static final Java7Base64UrlSafeEncoder instance = new Java7Base64UrlSafeEncoder();
|
private static final Java7Base64UrlSafeEncoder instance = new Java7Base64UrlSafeEncoder();
|
||||||
|
|
||||||
private static final int BASE64_ENCODER_FLAGS = Base64.URL_SAFE | Base64.DONT_BREAK_LINES;
|
private final Base64.Encoder encoder;
|
||||||
|
private final Base64.Decoder decoder;
|
||||||
|
|
||||||
private Java7Base64UrlSafeEncoder() {
|
private Java7Base64UrlSafeEncoder() {
|
||||||
// Use getInstance()
|
encoder = Base64.getUrlEncoder();
|
||||||
|
decoder = Base64.getUrlDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Java7Base64UrlSafeEncoder getInstance() {
|
public static Java7Base64UrlSafeEncoder getInstance() {
|
||||||
|
@ -56,13 +59,14 @@ public final class Java7Base64UrlSafeEncoder implements StringEncoder<String> {
|
||||||
catch (UnsupportedEncodingException e) {
|
catch (UnsupportedEncodingException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
return Base64.encodeBytes(bytes, BASE64_ENCODER_FLAGS);
|
return encoder.encodeToString(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String decode(String s) {
|
public String decode(String s) {
|
||||||
|
byte[] bytes = decoder.decode(s);
|
||||||
try {
|
try {
|
||||||
return new String(Base64.decode(s, BASE64_ENCODER_FLAGS), StringUtils.UTF8);
|
return new String(bytes, StringUtils.UTF8);
|
||||||
}
|
}
|
||||||
catch (UnsupportedEncodingException e) {
|
catch (UnsupportedEncodingException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
|
|
|
@ -620,7 +620,7 @@ public final class JingleSession extends JingleNegotiator implements MediaReceiv
|
||||||
* A XMPP connection
|
* A XMPP connection
|
||||||
* @return a Jingle session
|
* @return a Jingle session
|
||||||
*/
|
*/
|
||||||
public static JingleSession getInstanceFor(XMPPConnection con) {
|
public static synchronized JingleSession getInstanceFor(XMPPConnection con) {
|
||||||
if (con == null) {
|
if (con == null) {
|
||||||
throw new IllegalArgumentException("XMPPConnection cannot be null");
|
throw new IllegalArgumentException("XMPPConnection cannot be null");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ dependencies {
|
||||||
compile project(":smack-extensions")
|
compile project(":smack-extensions")
|
||||||
compile project(":smack-experimental")
|
compile project(":smack-experimental")
|
||||||
|
|
||||||
compile "org.bouncycastle:bcprov-jdk15on:1.60"
|
compile "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
|
||||||
|
|
||||||
testCompile project(path: ":smack-core", configuration: "testRuntime")
|
testCompile project(path: ":smack-core", configuration: "testRuntime")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Paul Schaub
|
* Copyright 2017 Paul Schaub, 2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -23,7 +23,6 @@ import java.io.UnsupportedEncodingException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -308,7 +307,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
try {
|
try {
|
||||||
builder = new OmemoMessageBuilder<>(userDevice, gullibleTrustCallback, getOmemoRatchet(manager),
|
builder = new OmemoMessageBuilder<>(userDevice, gullibleTrustCallback, getOmemoRatchet(manager),
|
||||||
messageKey, iv, null);
|
messageKey, iv, null);
|
||||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | UnsupportedEncodingException | NoSuchProviderException | IllegalBlockSizeException e) {
|
} catch (InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | UnsupportedEncodingException | IllegalBlockSizeException e) {
|
||||||
throw new CryptoFailedException(e);
|
throw new CryptoFailedException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +369,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
try {
|
try {
|
||||||
builder = new OmemoMessageBuilder<>(
|
builder = new OmemoMessageBuilder<>(
|
||||||
userDevice, manager.getTrustCallback(), getOmemoRatchet(managerGuard.get()), messageKey, iv, message);
|
userDevice, manager.getTrustCallback(), getOmemoRatchet(managerGuard.get()), messageKey, iv, message);
|
||||||
} catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException | NoSuchProviderException |
|
} catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException |
|
||||||
NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException e) {
|
NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException e) {
|
||||||
throw new CryptoFailedException(e);
|
throw new CryptoFailedException(e);
|
||||||
}
|
}
|
||||||
|
@ -557,7 +556,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException,
|
XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException,
|
||||||
PubSubException.NotAPubSubNodeException {
|
PubSubException.NotAPubSubNodeException {
|
||||||
|
|
||||||
PubSubManager pm = PubSubManager.getInstance(connection, contactsDevice.getJid());
|
PubSubManager pm = PubSubManager.getInstanceFor(connection, contactsDevice.getJid());
|
||||||
LeafNode node = pm.getLeafNode(contactsDevice.getBundleNodeName());
|
LeafNode node = pm.getLeafNode(contactsDevice.getBundleNodeName());
|
||||||
|
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
|
@ -585,7 +584,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
static void publishBundle(XMPPConnection connection, OmemoDevice userDevice, OmemoBundleElement bundle)
|
static void publishBundle(XMPPConnection connection, OmemoDevice userDevice, OmemoBundleElement bundle)
|
||||||
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||||
SmackException.NoResponseException {
|
SmackException.NoResponseException {
|
||||||
PubSubManager pm = PubSubManager.getInstance(connection, connection.getUser().asBareJid());
|
PubSubManager pm = PubSubManager.getInstanceFor(connection, connection.getUser().asBareJid());
|
||||||
pm.tryToPublishAndPossibleAutoCreate(userDevice.getBundleNodeName(), new PayloadItem<>(bundle));
|
pm.tryToPublishAndPossibleAutoCreate(userDevice.getBundleNodeName(), new PayloadItem<>(bundle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,7 +606,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
SmackException.NotConnectedException, XMPPException.XMPPErrorException,
|
SmackException.NotConnectedException, XMPPException.XMPPErrorException,
|
||||||
PubSubException.NotAPubSubNodeException {
|
PubSubException.NotAPubSubNodeException {
|
||||||
|
|
||||||
PubSubManager pm = PubSubManager.getInstance(connection, contact);
|
PubSubManager pm = PubSubManager.getInstanceFor(connection, contact);
|
||||||
String nodeName = OmemoConstants.PEP_NODE_DEVICE_LIST;
|
String nodeName = OmemoConstants.PEP_NODE_DEVICE_LIST;
|
||||||
LeafNode node = pm.getLeafNode(nodeName);
|
LeafNode node = pm.getLeafNode(nodeName);
|
||||||
|
|
||||||
|
@ -637,7 +636,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
SmackException.NoResponseException {
|
SmackException.NoResponseException {
|
||||||
|
|
||||||
PubSubManager.getInstance(connection, connection.getUser().asBareJid())
|
PubSubManager.getInstanceFor(connection, connection.getUser().asBareJid())
|
||||||
.tryToPublishAndPossibleAutoCreate(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList));
|
.tryToPublishAndPossibleAutoCreate(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,33 +785,6 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
processBundle(omemoManager, randomPreKeyBundle, contactsDevice);
|
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.
|
* 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.
|
* Return the set of all devices we have a session with afterwards.
|
||||||
|
@ -854,34 +826,6 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
return devicesWithSession;
|
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.
|
* 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.
|
* A device is also considered undecided, if its fingerprint cannot be loaded.
|
||||||
|
@ -914,37 +858,6 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
return undecidedDevices;
|
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.
|
* Return true, if the OmemoManager of userDevice has a session with the contactsDevice.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Paul Schaub
|
* Copyright 2017 Paul Schaub, 2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,11 +18,9 @@ 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.CIPHERMODE;
|
||||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE;
|
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.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
@ -50,13 +48,13 @@ public class CipherAndAuthTag {
|
||||||
|
|
||||||
Cipher cipher;
|
Cipher cipher;
|
||||||
try {
|
try {
|
||||||
cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
|
cipher = Cipher.getInstance(CIPHERMODE);
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
|
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
||||||
} catch (NoSuchAlgorithmException | java.security.InvalidKeyException |
|
} catch (NoSuchAlgorithmException | java.security.InvalidKeyException |
|
||||||
InvalidAlgorithmParameterException |
|
InvalidAlgorithmParameterException |
|
||||||
NoSuchPaddingException | NoSuchProviderException e) {
|
NoSuchPaddingException e) {
|
||||||
throw new CryptoFailedException(e);
|
throw new CryptoFailedException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,5 @@ public final class OmemoConstants {
|
||||||
public static final String KEYTYPE = "AES";
|
public static final String KEYTYPE = "AES";
|
||||||
public static final int KEYLENGTH = 128;
|
public static final int KEYLENGTH = 128;
|
||||||
public static final String CIPHERMODE = "AES/GCM/NoPadding";
|
public static final String CIPHERMODE = "AES/GCM/NoPadding";
|
||||||
public static final String PROVIDER = "BC";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Paul Schaub
|
* Copyright 2017 Paul Schaub, 2019 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,7 +19,6 @@ 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.CIPHERMODE;
|
||||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYLENGTH;
|
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.KEYTYPE;
|
||||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
@ -96,7 +95,6 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws IllegalBlockSizeException
|
* @throws IllegalBlockSizeException
|
||||||
* @throws UnsupportedEncodingException
|
* @throws UnsupportedEncodingException
|
||||||
* @throws NoSuchProviderException
|
|
||||||
* @throws InvalidAlgorithmParameterException
|
* @throws InvalidAlgorithmParameterException
|
||||||
*/
|
*/
|
||||||
public OmemoMessageBuilder(OmemoDevice userDevice,
|
public OmemoMessageBuilder(OmemoDevice userDevice,
|
||||||
|
@ -107,7 +105,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
String message)
|
String message)
|
||||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
|
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
|
||||||
IllegalBlockSizeException,
|
IllegalBlockSizeException,
|
||||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
UnsupportedEncodingException, InvalidAlgorithmParameterException {
|
||||||
this.userDevice = userDevice;
|
this.userDevice = userDevice;
|
||||||
this.trustCallback = callback;
|
this.trustCallback = callback;
|
||||||
this.ratchet = ratchet;
|
this.ratchet = ratchet;
|
||||||
|
@ -130,7 +128,6 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws IllegalBlockSizeException
|
* @throws IllegalBlockSizeException
|
||||||
* @throws UnsupportedEncodingException
|
* @throws UnsupportedEncodingException
|
||||||
* @throws NoSuchProviderException
|
|
||||||
* @throws InvalidAlgorithmParameterException
|
* @throws InvalidAlgorithmParameterException
|
||||||
*/
|
*/
|
||||||
public OmemoMessageBuilder(OmemoDevice userDevice,
|
public OmemoMessageBuilder(OmemoDevice userDevice,
|
||||||
|
@ -138,7 +135,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,
|
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet,
|
||||||
String message)
|
String message)
|
||||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
|
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
|
||||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
UnsupportedEncodingException, InvalidAlgorithmParameterException {
|
||||||
this(userDevice, callback, ratchet, generateKey(KEYTYPE, KEYLENGTH), generateIv(), message);
|
this(userDevice, callback, ratchet, generateKey(KEYTYPE, KEYLENGTH), generateIv(), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +147,6 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
*
|
*
|
||||||
* @param message plaintext message
|
* @param message plaintext message
|
||||||
* @throws NoSuchPaddingException
|
* @throws NoSuchPaddingException
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
* @throws InvalidAlgorithmParameterException
|
* @throws InvalidAlgorithmParameterException
|
||||||
* @throws InvalidKeyException
|
* @throws InvalidKeyException
|
||||||
|
@ -158,7 +154,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @throws BadPaddingException
|
* @throws BadPaddingException
|
||||||
* @throws IllegalBlockSizeException
|
* @throws IllegalBlockSizeException
|
||||||
*/
|
*/
|
||||||
private void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
|
private void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +162,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
// Encrypt message body
|
// Encrypt message body
|
||||||
SecretKey secretKey = new SecretKeySpec(messageKey, KEYTYPE);
|
SecretKey secretKey = new SecretKeySpec(messageKey, KEYTYPE);
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(initializationVector);
|
IvParameterSpec ivSpec = new IvParameterSpec(initializationVector);
|
||||||
Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
|
Cipher cipher = Cipher.getInstance(CIPHERMODE);
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
|
||||||
|
|
||||||
byte[] body;
|
byte[] body;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus, 2018 Paul Schaub.
|
* Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.
|
* @param connection xmpp connection.
|
||||||
* @return instance of the manager.
|
* @return instance of the manager.
|
||||||
*/
|
*/
|
||||||
public static OpenPgpManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized OpenPgpManager getInstanceFor(XMPPConnection connection) {
|
||||||
OpenPgpManager manager = INSTANCES.get(connection);
|
OpenPgpManager manager = INSTANCES.get(connection);
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
manager = new OpenPgpManager(connection);
|
manager = new OpenPgpManager(connection);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus, 2018 Paul Schaub.
|
* Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 static final String ELEMENT = "crypt";
|
||||||
|
|
||||||
public CryptElement(Set<Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
|
public CryptElement(Set<? extends Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
|
||||||
super(to, rpad, timestamp, payload);
|
super(to, rpad, timestamp, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptElement(Set<Jid> to, List<ExtensionElement> payload) {
|
public CryptElement(Set<? extends Jid> to, List<ExtensionElement> payload) {
|
||||||
super(to, payload);
|
super(to, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus, 2018 Paul Schaub.
|
* Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,13 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.element;
|
package org.jivesoftware.smackx.ox.element;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
import org.jivesoftware.smack.util.RandomUtil;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
@ -38,14 +38,14 @@ public abstract class EncryptedOpenPgpContentElement extends OpenPgpContentEleme
|
||||||
|
|
||||||
private final String rpad;
|
private final String rpad;
|
||||||
|
|
||||||
protected EncryptedOpenPgpContentElement(Set<Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
|
protected EncryptedOpenPgpContentElement(Set<? extends Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
|
||||||
super(Objects.requireNonNullNorEmpty(
|
super(Objects.requireNonNullNorEmpty(
|
||||||
to, "Encrypted OpenPGP content elements must have at least one 'to' attribute."),
|
to, "Encrypted OpenPGP content elements must have at least one 'to' attribute."),
|
||||||
timestamp, payload);
|
timestamp, payload);
|
||||||
this.rpad = Objects.requireNonNull(rpad);
|
this.rpad = Objects.requireNonNull(rpad);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected EncryptedOpenPgpContentElement(Set<Jid> to, List<ExtensionElement> payload) {
|
protected EncryptedOpenPgpContentElement(Set<? extends Jid> to, List<ExtensionElement> payload) {
|
||||||
super(Objects.requireNonNullNorEmpty(
|
super(Objects.requireNonNullNorEmpty(
|
||||||
to, "Encrypted OpenPGP content elements must have at least one 'to' attribute."),
|
to, "Encrypted OpenPGP content elements must have at least one 'to' attribute."),
|
||||||
new Date(), payload);
|
new Date(), payload);
|
||||||
|
@ -53,8 +53,7 @@ public abstract class EncryptedOpenPgpContentElement extends OpenPgpContentEleme
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String createRandomPadding() {
|
private static String createRandomPadding() {
|
||||||
SecureRandom secRan = new SecureRandom();
|
int len = RandomUtil.nextSecureRandomInt(256);
|
||||||
int len = secRan.nextInt(256);
|
|
||||||
return StringUtils.randomString(len);
|
return StringUtils.randomString(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus, 2018 Paul Schaub.
|
* Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 ATTR_STAMP = "stamp";
|
||||||
public static final String ELEM_PAYLOAD = "payload";
|
public static final String ELEM_PAYLOAD = "payload";
|
||||||
|
|
||||||
private final Set<Jid> to;
|
private final Set<? extends Jid> to;
|
||||||
private final Date timestamp;
|
private final Date timestamp;
|
||||||
private final MultiMap<String, ExtensionElement> payload;
|
private final MultiMap<String, ExtensionElement> payload;
|
||||||
|
|
||||||
private String timestampString;
|
private String timestampString;
|
||||||
|
|
||||||
protected OpenPgpContentElement(Set<Jid> to, Date timestamp, List<ExtensionElement> payload) {
|
protected OpenPgpContentElement(Set<? extends Jid> to, Date timestamp, List<ExtensionElement> payload) {
|
||||||
this.to = to;
|
this.to = to;
|
||||||
this.timestamp = Objects.requireNonNull(timestamp);
|
this.timestamp = Objects.requireNonNull(timestamp);
|
||||||
this.payload = new MultiMap<>();
|
this.payload = new MultiMap<>();
|
||||||
|
@ -68,7 +68,7 @@ public abstract class OpenPgpContentElement implements ExtensionElement {
|
||||||
*
|
*
|
||||||
* @return recipients.
|
* @return recipients.
|
||||||
*/
|
*/
|
||||||
public final Set<Jid> getTo() {
|
public final Set<? extends Jid> getTo() {
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus, 2018 Paul Schaub.
|
* Copyright 2017-2019 Florian Schmaus, 2018 Paul Schaub.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 static final String ELEMENT = "signcrypt";
|
||||||
|
|
||||||
public SigncryptElement(Set<Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
|
public SigncryptElement(Set<? extends Jid> to, String rpad, Date timestamp, List<ExtensionElement> payload) {
|
||||||
super(to, rpad, timestamp, payload);
|
super(to, rpad, timestamp, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SigncryptElement(Set<Jid> to, List<ExtensionElement> payload) {
|
public SigncryptElement(Set<? extends Jid> to, List<ExtensionElement> payload) {
|
||||||
super(to, payload);
|
super(to, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ public class OpenPgpPubSubUtil {
|
||||||
public static PublicKeysListElement fetchPubkeysList(XMPPConnection connection, BareJid contact)
|
public static PublicKeysListElement fetchPubkeysList(XMPPConnection connection, BareJid contact)
|
||||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException,
|
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NoResponseException,
|
||||||
PubSubException.NotALeafNodeException, SmackException.NotConnectedException, PubSubException.NotAPubSubNodeException {
|
PubSubException.NotALeafNodeException, SmackException.NotConnectedException, PubSubException.NotAPubSubNodeException {
|
||||||
PubSubManager pm = PubSubManager.getInstance(connection, contact);
|
PubSubManager pm = PubSubManager.getInstanceFor(connection, contact);
|
||||||
|
|
||||||
LeafNode node = getLeafNode(pm, PEP_NODE_PUBLIC_KEYS);
|
LeafNode node = getLeafNode(pm, PEP_NODE_PUBLIC_KEYS);
|
||||||
List<PayloadItem<PublicKeysListElement>> list = node.getItems(1);
|
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)
|
public static PubkeyElement fetchPubkey(XMPPConnection connection, BareJid contact, OpenPgpV4Fingerprint v4_fingerprint)
|
||||||
throws InterruptedException, XMPPException.XMPPErrorException, PubSubException.NotAPubSubNodeException,
|
throws InterruptedException, XMPPException.XMPPErrorException, PubSubException.NotAPubSubNodeException,
|
||||||
PubSubException.NotALeafNodeException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
PubSubException.NotALeafNodeException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
PubSubManager pm = PubSubManager.getInstance(connection, contact);
|
PubSubManager pm = PubSubManager.getInstanceFor(connection, contact);
|
||||||
String nodeName = PEP_NODE_PUBLIC_KEY(v4_fingerprint);
|
String nodeName = PEP_NODE_PUBLIC_KEY(v4_fingerprint);
|
||||||
|
|
||||||
LeafNode node = getLeafNode(pm, nodeName);
|
LeafNode node = getLeafNode(pm, nodeName);
|
||||||
|
|
|
@ -135,7 +135,7 @@ public final class OXInstantMessagingManager extends Manager {
|
||||||
* @param connection XMPP connection
|
* @param connection XMPP connection
|
||||||
* @return manager instance
|
* @return manager instance
|
||||||
*/
|
*/
|
||||||
public static OXInstantMessagingManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized OXInstantMessagingManager getInstanceFor(XMPPConnection connection) {
|
||||||
OXInstantMessagingManager manager = INSTANCES.get(connection);
|
OXInstantMessagingManager manager = INSTANCES.get(connection);
|
||||||
|
|
||||||
if (manager == null) {
|
if (manager == null) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ dependencies {
|
||||||
compile project(':smack-bosh')
|
compile project(':smack-bosh')
|
||||||
compile project(':smack-java7')
|
compile project(':smack-java7')
|
||||||
compile project(':smack-resolver-minidns')
|
compile project(':smack-resolver-minidns')
|
||||||
|
compile project(':smack-resolver-minidns-dox')
|
||||||
compile project(':smack-extensions')
|
compile project(':smack-extensions')
|
||||||
compile project(':smack-experimental')
|
compile project(':smack-experimental')
|
||||||
compile project(':smack-legacy')
|
compile project(':smack-legacy')
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
8
smack-resolver-minidns-dox/build.gradle
Normal file
8
smack-resolver-minidns-dox/build.gradle
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
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")
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Florian Schmaus
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* 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;
|
|
@ -0,0 +1,167 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,7 +121,6 @@ import org.jivesoftware.smack.util.dns.HostAddress;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
import org.jxmpp.jid.parts.Resourcepart;
|
import org.jxmpp.jid.parts.Resourcepart;
|
||||||
import org.jxmpp.stringprep.XmppStringprepException;
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
import org.jxmpp.util.XmppStringUtils;
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
@ -319,7 +318,7 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
* @throws XmppStringprepException
|
* @throws XmppStringprepException
|
||||||
*/
|
*/
|
||||||
public XMPPTCPConnection(CharSequence jid, String password) throws XmppStringprepException {
|
public XMPPTCPConnection(CharSequence jid, String password) throws XmppStringprepException {
|
||||||
this(XmppStringUtils.parseLocalpart(jid.toString()), password, XmppStringUtils.parseDomain(jid.toString()));
|
this(XMPPTCPConnectionConfiguration.builder().setXmppAddressAndPassword(jid, password).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -482,10 +481,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shutdown(boolean instant) {
|
private void shutdown(boolean instant) {
|
||||||
if (disconnectedButResumeable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First shutdown the writer, this will result in a closing stream element getting send to
|
// First shutdown the writer, this will result in a closing stream element getting send to
|
||||||
// the server
|
// the server
|
||||||
LOGGER.finer("PacketWriter shutdown()");
|
LOGGER.finer("PacketWriter shutdown()");
|
||||||
|
@ -503,6 +498,15 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
CloseableUtil.maybeClose(socket, LOGGER);
|
CloseableUtil.maybeClose(socket, LOGGER);
|
||||||
|
|
||||||
setWasAuthenticated();
|
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
|
// 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/authenticated/usingTLS to false since we like behave like we are still
|
||||||
// connected (e.g. sendStanza should not throw a NotConnectedException).
|
// connected (e.g. sendStanza should not throw a NotConnectedException).
|
||||||
|
@ -523,10 +527,6 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
writer = null;
|
writer = null;
|
||||||
|
|
||||||
initState();
|
initState();
|
||||||
|
|
||||||
// Wait for reader and writer threads to be terminated.
|
|
||||||
readerWriterSemaphore.acquireUninterruptibly(2);
|
|
||||||
readerWriterSemaphore.release(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -860,6 +860,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
|
|
||||||
protected class PacketReader {
|
protected class PacketReader {
|
||||||
|
|
||||||
|
private final String threadName = "Smack Reader (" + getConnectionCounter() + ')';
|
||||||
|
|
||||||
XmlPullParser parser;
|
XmlPullParser parser;
|
||||||
|
|
||||||
private volatile boolean done;
|
private volatile boolean done;
|
||||||
|
@ -874,13 +876,15 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
Async.go(new Runnable() {
|
Async.go(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
LOGGER.finer(threadName + " start");
|
||||||
try {
|
try {
|
||||||
parsePackets();
|
parsePackets();
|
||||||
} finally {
|
} finally {
|
||||||
|
LOGGER.finer(threadName + " exit");
|
||||||
XMPPTCPConnection.this.readerWriterSemaphore.release();
|
XMPPTCPConnection.this.readerWriterSemaphore.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, "Smack Reader (" + getConnectionCounter() + ")");
|
}, threadName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1128,6 +1132,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
protected class PacketWriter {
|
protected class PacketWriter {
|
||||||
public static final int QUEUE_SIZE = XMPPTCPConnection.QUEUE_SIZE;
|
public static final int QUEUE_SIZE = XMPPTCPConnection.QUEUE_SIZE;
|
||||||
|
|
||||||
|
private final String threadName = "Smack Writer (" + getConnectionCounter() + ')';
|
||||||
|
|
||||||
private final ArrayBlockingQueueWithShutdown<Element> queue = new ArrayBlockingQueueWithShutdown<>(
|
private final ArrayBlockingQueueWithShutdown<Element> queue = new ArrayBlockingQueueWithShutdown<>(
|
||||||
QUEUE_SIZE, true);
|
QUEUE_SIZE, true);
|
||||||
|
|
||||||
|
@ -1173,13 +1179,15 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
|
||||||
Async.go(new Runnable() {
|
Async.go(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
LOGGER.finer(threadName + " start");
|
||||||
try {
|
try {
|
||||||
writePackets();
|
writePackets();
|
||||||
} finally {
|
} finally {
|
||||||
|
LOGGER.finer(threadName + " exit");
|
||||||
XMPPTCPConnection.this.readerWriterSemaphore.release();
|
XMPPTCPConnection.this.readerWriterSemaphore.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, "Smack Writer (" + getConnectionCounter() + ")");
|
}, threadName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean done() {
|
private boolean done() {
|
||||||
|
|
|
@ -10,6 +10,7 @@ allprojects {
|
||||||
// - https://issues.igniterealtime.org/browse/SMACK-858
|
// - https://issues.igniterealtime.org/browse/SMACK-858
|
||||||
jxmppVersion = '0.7.0-alpha5'
|
jxmppVersion = '0.7.0-alpha5'
|
||||||
miniDnsVersion = '0.4.0-alpha3'
|
miniDnsVersion = '0.4.0-alpha3'
|
||||||
|
bouncyCastleVersion = '1.61'
|
||||||
smackMinAndroidSdk = 19
|
smackMinAndroidSdk = 19
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue