mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 06:12:05 +01:00
Merge branch '4.2'
This commit is contained in:
commit
7a5f9e6a03
187 changed files with 2284 additions and 588 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
# Mac OS X
|
# Mac OS X
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
@ -10,7 +10,7 @@ cache:
|
||||||
- $HOME/.m2
|
- $HOME/.m2
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- export GRADLE_VERSION=2.12
|
- export GRADLE_VERSION=3.5
|
||||||
- wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-all.zip
|
- wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-all.zip
|
||||||
- unzip -q gradle-${GRADLE_VERSION}-all.zip
|
- unzip -q gradle-${GRADLE_VERSION}-all.zip
|
||||||
- export PATH="$(pwd)/gradle-${GRADLE_VERSION}/bin:$PATH"
|
- export PATH="$(pwd)/gradle-${GRADLE_VERSION}/bin:$PATH"
|
||||||
|
@ -20,5 +20,5 @@ script: gradle check --stacktrace
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- JAVAC_VERSION=$((javac -version) 2>&1)
|
- JAVAC_VERSION=$((javac -version) 2>&1)
|
||||||
# Only run jacocoRootReport in the Java7 build
|
# Only run jacocoRootReport in the Java 8 build
|
||||||
- if [[ "$JAVAC_VERSION" = javac\ 1.7.* ]]; then gradle jacocoRootReport coveralls; fi
|
- if [[ "$JAVAC_VERSION" = javac\ 1.8.* ]]; then gradle jacocoRootReport coveralls; fi
|
||||||
|
|
|
@ -8,9 +8,9 @@ buildscript {
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'org.kordamp:markdown-gradle-plugin:1.0.0'
|
classpath 'org.kordamp:markdown-gradle-plugin:1.0.0'
|
||||||
classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.0'
|
classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2'
|
||||||
classpath "org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.3.1"
|
classpath "org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.3.1"
|
||||||
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.9'
|
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.10'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply plugin: 'org.kordamp.gradle.markdown'
|
apply plugin: 'org.kordamp.gradle.markdown'
|
||||||
|
@ -20,6 +20,7 @@ apply from: 'version.gradle'
|
||||||
allprojects {
|
allprojects {
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
|
apply plugin: 'idea'
|
||||||
apply plugin: 'jacoco'
|
apply plugin: 'jacoco'
|
||||||
apply plugin: 'net.ltgt.errorprone'
|
apply plugin: 'net.ltgt.errorprone'
|
||||||
|
|
||||||
|
@ -266,6 +267,7 @@ subprojects {
|
||||||
|
|
||||||
checkstyle {
|
checkstyle {
|
||||||
configFile = new File(rootConfigDir, 'checkstyle.xml')
|
configFile = new File(rootConfigDir, 'checkstyle.xml')
|
||||||
|
toolVersion = '7.7'
|
||||||
}
|
}
|
||||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||||
classifier = 'sources'
|
classifier = 'sources'
|
||||||
|
|
|
@ -117,5 +117,75 @@
|
||||||
, INDEX_OP
|
, INDEX_OP
|
||||||
"/>
|
"/>
|
||||||
</module>
|
</module>
|
||||||
|
<module name="WhitespaceAfter">
|
||||||
|
<property name="tokens" value="TYPECAST
|
||||||
|
, LITERAL_IF
|
||||||
|
, LITERAL_ELSE
|
||||||
|
, LITERAL_WHILE
|
||||||
|
, LITERAL_DO
|
||||||
|
, LITERAL_FOR
|
||||||
|
, DO_WHILE
|
||||||
|
"/>
|
||||||
|
</module>
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<property
|
||||||
|
name="ignoreEnhancedForColon"
|
||||||
|
value="false"
|
||||||
|
/>
|
||||||
|
<!-- Currently disabled tokens: LCURLY, RCURLY, WILDCARD_TYPE, GENERIC_START, GENERIC_END -->
|
||||||
|
<property
|
||||||
|
name="tokens"
|
||||||
|
value="ASSIGN
|
||||||
|
, ARRAY_INIT
|
||||||
|
, BAND
|
||||||
|
, BAND_ASSIGN
|
||||||
|
, BOR
|
||||||
|
, BOR_ASSIGN
|
||||||
|
, BSR
|
||||||
|
, BSR_ASSIGN
|
||||||
|
, BXOR
|
||||||
|
, BXOR_ASSIGN
|
||||||
|
, COLON
|
||||||
|
, DIV
|
||||||
|
, DIV_ASSIGN
|
||||||
|
, DO_WHILE
|
||||||
|
, EQUAL
|
||||||
|
, GE
|
||||||
|
, GT
|
||||||
|
, LAMBDA
|
||||||
|
, LAND
|
||||||
|
, LE
|
||||||
|
, LITERAL_CATCH
|
||||||
|
, LITERAL_DO
|
||||||
|
, LITERAL_ELSE
|
||||||
|
, LITERAL_FINALLY
|
||||||
|
, LITERAL_FOR
|
||||||
|
, LITERAL_IF
|
||||||
|
, LITERAL_RETURN
|
||||||
|
, LITERAL_SWITCH
|
||||||
|
, LITERAL_SYNCHRONIZED
|
||||||
|
, LITERAL_TRY
|
||||||
|
, LITERAL_WHILE
|
||||||
|
, LOR
|
||||||
|
, LT
|
||||||
|
, MINUS
|
||||||
|
, MINUS_ASSIGN
|
||||||
|
, MOD
|
||||||
|
, MOD_ASSIGN
|
||||||
|
, NOT_EQUAL
|
||||||
|
, PLUS
|
||||||
|
, PLUS_ASSIGN
|
||||||
|
, QUESTION
|
||||||
|
, SL
|
||||||
|
, SLIST
|
||||||
|
, SL_ASSIGN
|
||||||
|
, SR
|
||||||
|
, SR_ASSIGN
|
||||||
|
, STAR
|
||||||
|
, STAR_ASSIGN
|
||||||
|
, LITERAL_ASSERT
|
||||||
|
, TYPE_EXTENSION_AND
|
||||||
|
"/>
|
||||||
|
</module>
|
||||||
</module>
|
</module>
|
||||||
</module>
|
</module>
|
||||||
|
|
|
@ -87,6 +87,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
|
||||||
| [Internet of Things - Control](iot.md) | [XEP-0325](http://xmpp.org/extensions/xep-0325.html) | Describes how to control devices or actuators in an XMPP-based sensor netowrk. |
|
| [Internet of Things - Control](iot.md) | [XEP-0325](http://xmpp.org/extensions/xep-0325.html) | Describes how to control devices or actuators in an XMPP-based sensor netowrk. |
|
||||||
| [HTTP over XMPP transport](hoxt.md) | [XEP-0332](http://xmpp.org/extensions/xep-0332.html) | Allows to transport HTTP communication over XMPP peer-to-peer networks. |
|
| [HTTP over XMPP transport](hoxt.md) | [XEP-0332](http://xmpp.org/extensions/xep-0332.html) | Allows to transport HTTP communication over XMPP peer-to-peer networks. |
|
||||||
| Chat Markers | [XEP-0333](http://xmpp.org/extensions/xep-0333.html) | A solution of marking the last received, displayed and acknowledged message in a chat. |
|
| Chat Markers | [XEP-0333](http://xmpp.org/extensions/xep-0333.html) | A solution of marking the last received, displayed and acknowledged message in a chat. |
|
||||||
|
| Message Processing Hints | [XEP-0334](http://xmpp.org/extensions/xep-0334.html) | Hints to entities routing or receiving a message. |
|
||||||
| JSON Containers | [XEP-0335](http://xmpp.org/extensions/xep-0335.html) | Encapsulation of JSON data within XMPP Stanzas. |
|
| JSON Containers | [XEP-0335](http://xmpp.org/extensions/xep-0335.html) | Encapsulation of JSON data within XMPP Stanzas. |
|
||||||
| [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. |
|
| [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. |
|
||||||
| Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. |
|
| Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. |
|
||||||
|
|
|
@ -41,6 +41,7 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||||
|
import org.jivesoftware.smack.SmackConfiguration.UnknownIqRequestReplyMode;
|
||||||
import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
|
import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
|
||||||
import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
|
import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
|
@ -894,7 +895,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -991,20 +993,31 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
replyTimeout = timeout;
|
replyTimeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean replyToUnknownIqDefault = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the default value used to determine if new connection will reply to unknown IQ requests. The pre-configured
|
* Set the default value used to determine if new connection will reply to unknown IQ requests. The pre-configured
|
||||||
* default is 'true'.
|
* default is 'true'.
|
||||||
*
|
*
|
||||||
* @param replyToUnkownIqDefault
|
* @param replyToUnkownIqDefault
|
||||||
* @see #setReplyToUnknownIq(boolean)
|
* @see #setReplyToUnknownIq(boolean)
|
||||||
|
* @deprecated Use {@link SmackConfiguration#setUnknownIqRequestReplyMode(org.jivesoftware.smack.SmackConfiguration.UnknownIqRequestReplyMode)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
|
// TODO Remove in Smack 4.3
|
||||||
public static void setReplyToUnknownIqDefault(boolean replyToUnkownIqDefault) {
|
public static void setReplyToUnknownIqDefault(boolean replyToUnkownIqDefault) {
|
||||||
AbstractXMPPConnection.replyToUnknownIqDefault = replyToUnkownIqDefault;
|
SmackConfiguration.UnknownIqRequestReplyMode mode;
|
||||||
|
if (replyToUnkownIqDefault) {
|
||||||
|
mode = SmackConfiguration.UnknownIqRequestReplyMode.replyServiceUnavailable;
|
||||||
|
} else {
|
||||||
|
mode = SmackConfiguration.UnknownIqRequestReplyMode.doNotReply;
|
||||||
|
}
|
||||||
|
SmackConfiguration.setUnknownIqRequestReplyMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean replyToUnkownIq = replyToUnknownIqDefault;
|
private SmackConfiguration.UnknownIqRequestReplyMode unknownIqRequestReplyMode = SmackConfiguration.getUnknownIqRequestReplyMode();
|
||||||
|
|
||||||
|
public void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) {
|
||||||
|
this.unknownIqRequestReplyMode = Objects.requireNonNull(unknownIqRequestReplyMode, "Mode must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if Smack will automatically send
|
* Set if Smack will automatically send
|
||||||
|
@ -1012,9 +1025,18 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
* registered {@link IQRequestHandler} is received.
|
* registered {@link IQRequestHandler} is received.
|
||||||
*
|
*
|
||||||
* @param replyToUnknownIq
|
* @param replyToUnknownIq
|
||||||
|
* @deprecated use {@link #setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
|
// TODO Remove in Smack 4.3
|
||||||
public void setReplyToUnknownIq(boolean replyToUnknownIq) {
|
public void setReplyToUnknownIq(boolean replyToUnknownIq) {
|
||||||
this.replyToUnkownIq = replyToUnknownIq;
|
SmackConfiguration.UnknownIqRequestReplyMode mode;
|
||||||
|
if (replyToUnknownIq) {
|
||||||
|
mode = SmackConfiguration.UnknownIqRequestReplyMode.replyServiceUnavailable;
|
||||||
|
} else {
|
||||||
|
mode = SmackConfiguration.UnknownIqRequestReplyMode.doNotReply;
|
||||||
|
}
|
||||||
|
unknownIqRequestReplyMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void parseAndProcessStanza(XmlPullParser parser) throws Exception {
|
protected void parseAndProcessStanza(XmlPullParser parser) throws Exception {
|
||||||
|
@ -1089,13 +1111,24 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
throw new IllegalStateException("Should only encounter IQ type 'get' or 'set'");
|
throw new IllegalStateException("Should only encounter IQ type 'get' or 'set'");
|
||||||
}
|
}
|
||||||
if (iqRequestHandler == null) {
|
if (iqRequestHandler == null) {
|
||||||
if (!replyToUnkownIq) {
|
XMPPError.Condition replyCondition;
|
||||||
|
switch (unknownIqRequestReplyMode) {
|
||||||
|
case doNotReply:
|
||||||
return;
|
return;
|
||||||
|
case replyFeatureNotImplemented:
|
||||||
|
replyCondition = XMPPError.Condition.feature_not_implemented;
|
||||||
|
break;
|
||||||
|
case replyServiceUnavailable:
|
||||||
|
replyCondition = XMPPError.Condition.service_unavailable;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the IQ stanza is of type "get" or "set" with no registered IQ request handler, then answer an
|
// If the IQ stanza is of type "get" or "set" with no registered IQ request handler, then answer an
|
||||||
// IQ of type 'error' with condition 'service-unavailable'.
|
// IQ of type 'error' with condition 'service-unavailable'.
|
||||||
ErrorIQ errorIQ = IQ.createErrorResponse(iq, XMPPError.getBuilder((
|
ErrorIQ errorIQ = IQ.createErrorResponse(iq, XMPPError.getBuilder((
|
||||||
XMPPError.Condition.service_unavailable)));
|
replyCondition)));
|
||||||
try {
|
try {
|
||||||
sendStanza(errorIQ);
|
sendStanza(errorIQ);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.jivesoftware.smack.debugger.SmackDebugger;
|
||||||
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
|
import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
|
||||||
import org.jivesoftware.smack.parsing.ExceptionThrowingCallback;
|
import org.jivesoftware.smack.parsing.ExceptionThrowingCallback;
|
||||||
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
|
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the configuration of Smack. The configuration is used for:
|
* Represents the configuration of Smack. The configuration is used for:
|
||||||
|
@ -370,4 +371,20 @@ public final class SmackConfiguration {
|
||||||
return defaultHostnameVerififer;
|
return defaultHostnameVerififer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum UnknownIqRequestReplyMode {
|
||||||
|
doNotReply,
|
||||||
|
replyFeatureNotImplemented,
|
||||||
|
replyServiceUnavailable,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Change to replyFeatureNotImplemented in Smack 4.3
|
||||||
|
private static UnknownIqRequestReplyMode unknownIqRequestReplyMode = UnknownIqRequestReplyMode.replyServiceUnavailable;
|
||||||
|
|
||||||
|
public static UnknownIqRequestReplyMode getUnknownIqRequestReplyMode() {
|
||||||
|
return unknownIqRequestReplyMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) {
|
||||||
|
SmackConfiguration.unknownIqRequestReplyMode = Objects.requireNonNull(unknownIqRequestReplyMode, "Must set mode");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
170
smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java
Normal file
170
smack-core/src/main/java/org/jivesoftware/smack/SmackFuture.java
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
|
||||||
|
public abstract class SmackFuture<V> implements Future<V> {
|
||||||
|
|
||||||
|
private boolean cancelled;
|
||||||
|
|
||||||
|
private V result;
|
||||||
|
|
||||||
|
protected Exception exception;
|
||||||
|
|
||||||
|
private SuccessCallback<V> successCallback;
|
||||||
|
|
||||||
|
private ExceptionCallback exceptionCallback;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized final boolean cancel(boolean mayInterruptIfRunning) {
|
||||||
|
if (isDone()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized final boolean isCancelled() {
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized final boolean isDone() {
|
||||||
|
return result != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSuccessOrError(SuccessCallback<V> successCallback, ExceptionCallback exceptionCallback) {
|
||||||
|
this.successCallback = successCallback;
|
||||||
|
this.exceptionCallback = exceptionCallback;
|
||||||
|
|
||||||
|
maybeInvokeCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSuccess(SuccessCallback<V> successCallback) {
|
||||||
|
onSuccessOrError(successCallback, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onError(ExceptionCallback exceptionCallback) {
|
||||||
|
onSuccessOrError(null, exceptionCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final V getResultOrThrow() throws ExecutionException {
|
||||||
|
assert (result != null || exception != null);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ExecutionException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized final V get() throws InterruptedException, ExecutionException {
|
||||||
|
while (result == null && exception == null) {
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized final V get(long timeout, TimeUnit unit)
|
||||||
|
throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
final long deadline = System.currentTimeMillis() + unit.toMillis(timeout);
|
||||||
|
while (result != null && exception != null) {
|
||||||
|
final long waitTimeRemaining = deadline - System.currentTimeMillis();
|
||||||
|
if (waitTimeRemaining > 0) {
|
||||||
|
wait(waitTimeRemaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == null || exception == null) {
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getResultOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final synchronized void maybeInvokeCallbacks() {
|
||||||
|
if (result != null && successCallback != null) {
|
||||||
|
successCallback.onSuccess(result);
|
||||||
|
} else if (exception != null && exceptionCallback != null) {
|
||||||
|
exceptionCallback.processException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method checks if the given exception is <b>not</b> fatal. If this method returns <code>false</code>, then
|
||||||
|
* the future will automatically set the given exception as failure reason and notify potential waiting threads.
|
||||||
|
*
|
||||||
|
* @param exception the exception to check.
|
||||||
|
* @return <code>true</code> if the exception is not fatal, <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
protected abstract boolean isNonFatalException(Exception exception);
|
||||||
|
|
||||||
|
protected abstract void handleStanza(Stanza stanza) throws NotConnectedException, InterruptedException;
|
||||||
|
|
||||||
|
protected final void setResult(V result) {
|
||||||
|
assert (Thread.holdsLock(this));
|
||||||
|
|
||||||
|
this.result = result;
|
||||||
|
this.notifyAll();
|
||||||
|
|
||||||
|
maybeInvokeCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class InternalSmackFuture<V> extends SmackFuture<V> implements StanzaListener, ExceptionCallback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized final void processException(Exception exception) {
|
||||||
|
if (!isNonFatalException(exception)) {
|
||||||
|
this.exception = exception;
|
||||||
|
this.notifyAll();
|
||||||
|
|
||||||
|
maybeInvokeCallbacks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper method for {@link #handleStanza(Stanza)}. Note that this method is <code>synchronized</code>.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized final void processStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
|
||||||
|
handleStanza(stanza);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple version of InternalSmackFuture which implements {@link #isNonFatalException(Exception)} as always returning <code>false</code> method.
|
||||||
|
*
|
||||||
|
* @param <V>
|
||||||
|
*/
|
||||||
|
public static abstract class SimpleInternalSmackFuture<V> extends InternalSmackFuture<V> {
|
||||||
|
@Override
|
||||||
|
protected boolean isNonFatalException(Exception exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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;
|
||||||
|
|
||||||
|
public interface SuccessCallback<T> {
|
||||||
|
|
||||||
|
public void onSuccess(T result);
|
||||||
|
|
||||||
|
}
|
|
@ -413,6 +413,7 @@ public interface XMPPConnection {
|
||||||
* @deprecated use {@link #getReplyTimeout()} instead.
|
* @deprecated use {@link #getReplyTimeout()} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
// TODO Remove in Smack 4.3
|
||||||
public long getPacketReplyTimeout();
|
public long getPacketReplyTimeout();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -423,6 +424,7 @@ public interface XMPPConnection {
|
||||||
* @deprecated use {@link #setReplyTimeout(long)} instead.
|
* @deprecated use {@link #setReplyTimeout(long)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
// TODO Remove in Smack 4.3
|
||||||
public void setPacketReplyTimeout(long timeout);
|
public void setPacketReplyTimeout(long timeout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,7 +39,7 @@ public abstract class AbstractJidTypeFilter implements StanzaFilter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean accept(Stanza stanza) {
|
public final boolean accept(Stanza stanza) {
|
||||||
final Jid jid = stanza.getFrom();
|
final Jid jid = getJidToInspect(stanza);
|
||||||
|
|
||||||
if (jid == null) {
|
if (jid == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.filter;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters message stanzas which have at least one body.
|
||||||
|
*/
|
||||||
|
public final class MessageWithThreadFilter extends FlexibleStanzaTypeFilter<Message> {
|
||||||
|
|
||||||
|
public static final StanzaFilter INSTANCE = new MessageWithThreadFilter();
|
||||||
|
|
||||||
|
private MessageWithThreadFilter() {
|
||||||
|
super(Message.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean acceptSpecific(Message message) {
|
||||||
|
return StringUtils.isNotEmpty(message.getThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,9 +66,12 @@ public class AbstractError {
|
||||||
public String getDescriptiveText() {
|
public String getDescriptiveText() {
|
||||||
String defaultLocale = Locale.getDefault().getLanguage();
|
String defaultLocale = Locale.getDefault().getLanguage();
|
||||||
String descriptiveText = getDescriptiveText(defaultLocale);
|
String descriptiveText = getDescriptiveText(defaultLocale);
|
||||||
|
if (descriptiveText == null) {
|
||||||
|
descriptiveText = getDescriptiveText("en");
|
||||||
if (descriptiveText == null) {
|
if (descriptiveText == null) {
|
||||||
descriptiveText = getDescriptiveText("");
|
descriptiveText = getDescriptiveText("");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return descriptiveText;
|
return descriptiveText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2014 Florian Schmaus
|
* Copyright 2014-2017 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,9 +19,13 @@ package org.jivesoftware.smack.util;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class LazyStringBuilder implements Appendable, CharSequence {
|
public class LazyStringBuilder implements Appendable, CharSequence {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(LazyStringBuilder.class.getName());
|
||||||
|
|
||||||
private final List<CharSequence> list;
|
private final List<CharSequence> list;
|
||||||
|
|
||||||
private String cache;
|
private String cache;
|
||||||
|
@ -69,9 +73,16 @@ public class LazyStringBuilder implements Appendable, CharSequence {
|
||||||
return cache.length();
|
return cache.length();
|
||||||
}
|
}
|
||||||
int length = 0;
|
int length = 0;
|
||||||
|
try {
|
||||||
for (CharSequence csq : list) {
|
for (CharSequence csq : list) {
|
||||||
length += csq.length();
|
length += csq.length();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (NullPointerException npe) {
|
||||||
|
StringBuilder sb = safeToStringBuilder();
|
||||||
|
LOGGER.log(Level.SEVERE, "The following LazyStringBuilder threw a NullPointerException: " + sb, npe);
|
||||||
|
throw npe;
|
||||||
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +118,14 @@ public class LazyStringBuilder implements Appendable, CharSequence {
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StringBuilder safeToStringBuilder() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (CharSequence csq : list) {
|
||||||
|
sb.append(csq);
|
||||||
|
}
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the List of CharSequences representation of this instance. The list is unmodifiable. If
|
* Get the List of CharSequences representation of this instance. The list is unmodifiable. If
|
||||||
* the resulting String was already cached, a list with a single String entry will be returned.
|
* the resulting String was already cached, a list with a single String entry will be returned.
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
|
import org.jivesoftware.smack.SmackFuture.InternalSmackFuture;
|
||||||
|
import org.jivesoftware.smack.SmackFuture.SimpleInternalSmackFuture;
|
||||||
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SmackFutureTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void simpleSmackFutureSuccessTest() throws NotConnectedException, InterruptedException, ExecutionException {
|
||||||
|
InternalSmackFuture<Boolean> future = new SimpleInternalSmackFuture<Boolean>() {
|
||||||
|
@Override
|
||||||
|
protected void handleStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
|
||||||
|
setResult(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
future.processStanza(null);
|
||||||
|
|
||||||
|
assertTrue(future.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = TimeoutException.class)
|
||||||
|
public void simpleSmackFutureTimeoutTest() throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
InternalSmackFuture<Boolean> future = new SimpleInternalSmackFuture<Boolean>() {
|
||||||
|
@Override
|
||||||
|
protected void handleStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
future.get(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,9 +57,10 @@ public class ThreadedDummyConnection extends DummyConnection {
|
||||||
replyQ.add(replyPacket);
|
replyQ.add(replyPacket);
|
||||||
}
|
}
|
||||||
replyPacket.setStanzaId(packet.getStanzaId());
|
replyPacket.setStanzaId(packet.getStanzaId());
|
||||||
replyPacket.setFrom(packet.getTo());
|
|
||||||
replyPacket.setTo(packet.getFrom());
|
replyPacket.setTo(packet.getFrom());
|
||||||
|
if (replyPacket.getType() == null) {
|
||||||
replyPacket.setType(Type.result);
|
replyPacket.setType(Type.result);
|
||||||
|
}
|
||||||
|
|
||||||
new ProcessQueue(replyQ).start();
|
new ProcessQueue(replyQ).start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014 Florian Schmaus
|
* Copyright © 2014-2017 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.
|
||||||
|
@ -32,7 +32,7 @@ import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
public class DigestMd5SaslTest extends AbstractSaslTest {
|
public class DigestMd5SaslTest extends AbstractSaslTest {
|
||||||
|
|
||||||
protected static final String challenge = "realm=\"xmpp.org\",nonce=\"aTUr3GXqUtyy2B7HVDW6C+gQs+j+0EhWWjoBKkkg\",qop=\"auth\",charset=utf-8,algorithm=md5-sess";
|
protected static final String challenge = "realm=\"xmpp.org\",nonce=\"jgGgnz+cQcmyVaAs2n88kQ==\",qop=\"auth\",charset=utf-8,algorithm=md5-sess";
|
||||||
protected static final byte[] challengeBytes = StringUtils.toBytes(challenge);
|
protected static final byte[] challengeBytes = StringUtils.toBytes(challenge);
|
||||||
|
|
||||||
public DigestMd5SaslTest(SASLMechanism saslMechanism) {
|
public DigestMd5SaslTest(SASLMechanism saslMechanism) {
|
||||||
|
@ -50,8 +50,7 @@ public class DigestMd5SaslTest extends AbstractSaslTest {
|
||||||
String[] responseParts = responseString.split(",");
|
String[] responseParts = responseString.split(",");
|
||||||
Map<String, String> responsePairs = new HashMap<String, String>();
|
Map<String, String> responsePairs = new HashMap<String, String>();
|
||||||
for (String part : responseParts) {
|
for (String part : responseParts) {
|
||||||
String[] keyValue = part.split("=");
|
String[] keyValue = part.split("=", 2);
|
||||||
assertTrue(keyValue.length == 2);
|
|
||||||
String key = keyValue[0];
|
String key = keyValue[0];
|
||||||
String value = keyValue[1].replace("\"", "");
|
String value = keyValue[1].replace("\"", "");
|
||||||
responsePairs.put(key, value);
|
responsePairs.put(key, value);
|
||||||
|
@ -59,7 +58,7 @@ public class DigestMd5SaslTest extends AbstractSaslTest {
|
||||||
if (useAuthzid) {
|
if (useAuthzid) {
|
||||||
assertMapValue("authzid", "shazbat@xmpp.org", responsePairs);
|
assertMapValue("authzid", "shazbat@xmpp.org", responsePairs);
|
||||||
} else {
|
} else {
|
||||||
assert(!responsePairs.containsKey("authzid"));
|
assertTrue (!responsePairs.containsKey("authzid"));
|
||||||
}
|
}
|
||||||
assertMapValue("username", "florian", responsePairs);
|
assertMapValue("username", "florian", responsePairs);
|
||||||
assertMapValue("realm", "xmpp.org", responsePairs);
|
assertMapValue("realm", "xmpp.org", responsePairs);
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.eme;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
||||||
|
|
||||||
|
public final class ExplicitMessageEncryptionManager {
|
||||||
|
|
||||||
|
private static final Map<XMPPConnection, ExplicitMessageEncryptionManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
|
||||||
|
@Override
|
||||||
|
public void connectionCreated(XMPPConnection connection) {
|
||||||
|
getInstanceFor(connection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String NAMESPACE_V0 = ExplicitMessageEncryptionElement.NAMESPACE;
|
||||||
|
|
||||||
|
public static synchronized ExplicitMessageEncryptionManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
ExplicitMessageEncryptionManager manager = INSTANCES.get(connection);
|
||||||
|
if (manager == null) {
|
||||||
|
manager = new ExplicitMessageEncryptionManager(connection);
|
||||||
|
INSTANCES.put(connection, manager);
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExplicitMessageEncryptionManager(XMPPConnection connection) {
|
||||||
|
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
|
sdm.addFeature(NAMESPACE_V0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.eme.element;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
public class ExplicitMessageEncryptionElement implements ExtensionElement {
|
||||||
|
|
||||||
|
private static final Map<String, ExplicitMessageEncryptionProtocol> PROTOCOL_LUT = new HashMap<>();
|
||||||
|
|
||||||
|
public static final String ELEMENT = "encryption";
|
||||||
|
|
||||||
|
public static final String NAMESPACE = "urn:xmpp:eme:0";
|
||||||
|
|
||||||
|
public enum ExplicitMessageEncryptionProtocol {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The encryption method specified in <a href="https://xmpp.org/extensions/xep-0373.html">XEP-0373: OpenPGP for
|
||||||
|
* XMPP</a>.
|
||||||
|
*/
|
||||||
|
openpgpV0("urn:xmpp:openpgp:0", "OpenPGP for XMPP (XEP-0373)"),
|
||||||
|
|
||||||
|
otrV0("urn:xmpp:otr:0", "Off-the-Record Messaging (XEP-0364)"),
|
||||||
|
|
||||||
|
legacyOpenPGP("jabber:x:encrypted", "Legacy OpenPGP for XMPP [DANGEROUS, DO NOT USE!]"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String namespace;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private ExplicitMessageEncryptionProtocol(String namespace, String name) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
this.name = name;
|
||||||
|
PROTOCOL_LUT.put(namespace, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExplicitMessageEncryptionProtocol from(String namespace) {
|
||||||
|
return PROTOCOL_LUT.get(namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String encryptionNamespace;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private boolean isUnknownProtocol;
|
||||||
|
|
||||||
|
private ExplicitMessageEncryptionProtocol protocolCache;
|
||||||
|
|
||||||
|
public ExplicitMessageEncryptionElement(ExplicitMessageEncryptionProtocol protocol) {
|
||||||
|
this(protocol.getNamespace(), protocol.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExplicitMessageEncryptionElement(String encryptionNamespace) {
|
||||||
|
this(encryptionNamespace, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExplicitMessageEncryptionElement(String encryptionNamespace, String name) {
|
||||||
|
this.encryptionNamespace = StringUtils.requireNotNullOrEmpty(encryptionNamespace,
|
||||||
|
"encryptionNamespace must not be null");
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExplicitMessageEncryptionProtocol getProtocol() {
|
||||||
|
if (protocolCache != null) {
|
||||||
|
return protocolCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUnknownProtocol) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplicitMessageEncryptionProtocol protocol = PROTOCOL_LUT.get(encryptionNamespace);
|
||||||
|
if (protocol == null) {
|
||||||
|
isUnknownProtocol = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocolCache = protocol;
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncryptionNamespace() {
|
||||||
|
return encryptionNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the optional name of the encryption method.
|
||||||
|
*
|
||||||
|
* @return the name of the encryption method or <code>null</code>.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XmlStringBuilder toXML() {
|
||||||
|
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||||
|
xml.attribute("namespace", getEncryptionNamespace());
|
||||||
|
xml.optAttribute("name", getName());
|
||||||
|
xml.closeEmptyElement();
|
||||||
|
return xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExplicitMessageEncryptionElement from(Message message) {
|
||||||
|
return message.getExtension(ELEMENT, NAMESPACE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* XMPP extension elements for XEP-0380: Explicit Message Encryption.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.eme.element;
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015 Florian Schmaus
|
* Copyright 2017 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.
|
||||||
|
@ -14,8 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO describe me
|
* Smack's API for XEP-0380: Explicit Message Encryption.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.iqprivate;
|
package org.jivesoftware.smackx.eme;
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.eme.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
public class ExplicitMessageEncryptionProvider extends ExtensionElementProvider<ExplicitMessageEncryptionElement> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExplicitMessageEncryptionElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
|
String namespace = parser.getAttributeValue(null, "namespace");
|
||||||
|
String name = parser.getAttributeValue(null, "name");
|
||||||
|
return new ExplicitMessageEncryptionElement(namespace, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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 Provider for XEP-0380: Explicit Message Encryption.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.eme.provider;
|
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smackx.hints.element.MessageProcessingHintType;
|
||||||
|
import org.jivesoftware.smackx.hints.element.NoCopyHint;
|
||||||
|
import org.jivesoftware.smackx.hints.element.NoPermanentStoreHint;
|
||||||
|
import org.jivesoftware.smackx.hints.element.NoStoreHint;
|
||||||
|
import org.jivesoftware.smackx.hints.element.StoreHint;
|
||||||
|
|
||||||
|
public class MessageProcessingHintsManager {
|
||||||
|
|
||||||
|
public static Set<MessageProcessingHintType> getHintsFrom(Message message) {
|
||||||
|
Set<MessageProcessingHintType> hints = null;
|
||||||
|
|
||||||
|
boolean noCopyHint = NoCopyHint.hasHint(message);
|
||||||
|
if (noCopyHint) {
|
||||||
|
hints = new HashSet<>(MessageProcessingHintType.values().length);
|
||||||
|
hints.add(MessageProcessingHintType.no_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean noPermanentStoreHint = NoPermanentStoreHint.hasHint(message);
|
||||||
|
if (noPermanentStoreHint) {
|
||||||
|
if (hints == null) {
|
||||||
|
hints = new HashSet<>(MessageProcessingHintType.values().length);
|
||||||
|
}
|
||||||
|
hints.add(MessageProcessingHintType.no_permanent_store);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean noStoreHint = NoStoreHint.hasHint(message);
|
||||||
|
if (noStoreHint) {
|
||||||
|
if (hints == null) {
|
||||||
|
hints = new HashSet<>(MessageProcessingHintType.values().length);
|
||||||
|
}
|
||||||
|
hints.add(MessageProcessingHintType.no_store);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean storeHint = StoreHint.hasHint(message);
|
||||||
|
if (storeHint) {
|
||||||
|
if (hints == null) {
|
||||||
|
hints = new HashSet<>(MessageProcessingHintType.values().length);
|
||||||
|
}
|
||||||
|
hints.add(MessageProcessingHintType.store);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hints == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
return hints;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
|
||||||
|
public abstract class MessageProcessingHint implements ExtensionElement {
|
||||||
|
|
||||||
|
public static final String NAMESPACE = "urn:xmpp:hints";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract MessageProcessingHintType getHintType();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.element;
|
||||||
|
|
||||||
|
public enum MessageProcessingHintType {
|
||||||
|
|
||||||
|
no_permanent_store,
|
||||||
|
no_store,
|
||||||
|
no_copy,
|
||||||
|
store,
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "no copy" hint. Messages with this hint should not be copied to addresses other than the one to which it is addressed.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0334.html#no-copy">XEP-0344 § 4.3 No copies</a>
|
||||||
|
*/
|
||||||
|
public final class NoCopyHint extends MessageProcessingHint {
|
||||||
|
|
||||||
|
public static final NoCopyHint INSTANCE = new NoCopyHint();
|
||||||
|
|
||||||
|
public static final String ELEMENT = "no-copy";
|
||||||
|
|
||||||
|
private NoCopyHint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toXML() {
|
||||||
|
return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageProcessingHintType getHintType() {
|
||||||
|
return MessageProcessingHintType.no_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NoCopyHint from(Message message) {
|
||||||
|
return message.getExtension(ELEMENT, NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasHint(Message message) {
|
||||||
|
return from(message) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set(Message message) {
|
||||||
|
message.addExtension(INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "no permanent store" hint. Messages with this hint should not be stored in permanent stores or archives.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0334.html#no-permanent-store">XEP-0334 § 4.1 No permanent store</a>
|
||||||
|
*/
|
||||||
|
public final class NoPermanentStoreHint extends MessageProcessingHint {
|
||||||
|
|
||||||
|
public static final NoPermanentStoreHint INSTANCE = new NoPermanentStoreHint();
|
||||||
|
|
||||||
|
public static final String ELEMENT = "no-permanent-store";
|
||||||
|
|
||||||
|
private NoPermanentStoreHint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toXML() {
|
||||||
|
return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageProcessingHintType getHintType() {
|
||||||
|
return MessageProcessingHintType.no_permanent_store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NoPermanentStoreHint from(Message message) {
|
||||||
|
return message.getExtension(ELEMENT, NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasHint(Message message) {
|
||||||
|
return from(message) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set(Message message) {
|
||||||
|
if (StoreHint.hasHint(message)) {
|
||||||
|
// No need to set the no-permanent-store hint when a no-store hint is already set.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setExplicitly(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setExplicitly(Message message) {
|
||||||
|
message.addExtension(INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "no store" hint. Messages with this hint should not be stored in stores or archives.
|
||||||
|
*
|
||||||
|
* <a href="https://xmpp.org/extensions/xep-0334.html#no-store">XEP-0334 § 4.2 No store</a>
|
||||||
|
*/
|
||||||
|
public final class NoStoreHint extends MessageProcessingHint {
|
||||||
|
|
||||||
|
public static final NoStoreHint INSTANCE = new NoStoreHint();
|
||||||
|
|
||||||
|
public static final String ELEMENT = "no-store";
|
||||||
|
|
||||||
|
private NoStoreHint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toXML() {
|
||||||
|
return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageProcessingHintType getHintType() {
|
||||||
|
return MessageProcessingHintType.no_store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NoStoreHint from(Message message) {
|
||||||
|
return message.getExtension(ELEMENT, NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasHint(Message message) {
|
||||||
|
return from(message) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set(Message message) {
|
||||||
|
message.addExtension(INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "store" hint. Messages with this hint should be stored in permanent stores or archives.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0334.html#sect-idm140684698220992">XEP-0334 § 4.4 Store</a>
|
||||||
|
*/
|
||||||
|
public final class StoreHint extends MessageProcessingHint {
|
||||||
|
|
||||||
|
public static final StoreHint INSTANCE = new StoreHint();
|
||||||
|
|
||||||
|
public static final String ELEMENT = "store";
|
||||||
|
|
||||||
|
private StoreHint() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toXML() {
|
||||||
|
return '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageProcessingHintType getHintType() {
|
||||||
|
return MessageProcessingHintType.store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StoreHint from(Message message) {
|
||||||
|
return message.getExtension(ELEMENT, NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasHint(Message message) {
|
||||||
|
return from(message) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set(Message message) {
|
||||||
|
message.addExtension(INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* XMPP extension elements for XEP-0334: Message Processing Hints.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.hints.element;
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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-0334: Message Processing Hints.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.hints;
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smackx.hints.element.MessageProcessingHint;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
public abstract class MessageProcessingHintProvider<H extends MessageProcessingHint> extends ExtensionElementProvider<H> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public H parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
|
return getHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract H getHint();
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.hints.element.NoCopyHint;
|
||||||
|
|
||||||
|
public class NoCopyHintProvider extends MessageProcessingHintProvider<NoCopyHint> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NoCopyHint getHint() {
|
||||||
|
return NoCopyHint.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.hints.element.NoPermanentStoreHint;
|
||||||
|
|
||||||
|
public class NoPermanentStoreHintProvider extends MessageProcessingHintProvider<NoPermanentStoreHint> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NoPermanentStoreHint getHint() {
|
||||||
|
return NoPermanentStoreHint.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.hints.element.NoStoreHint;
|
||||||
|
|
||||||
|
public class NoStoreHintProvider extends MessageProcessingHintProvider<NoStoreHint> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected NoStoreHint getHint() {
|
||||||
|
return NoStoreHint.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.hints.provider;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.hints.element.StoreHint;
|
||||||
|
|
||||||
|
public class StoreHintProvider extends MessageProcessingHintProvider<StoreHint> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StoreHint getHint() {
|
||||||
|
return StoreHint.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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 Provider for XEP-0334: Message Processing Hints.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.hints.provider;
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2016 Florian Schmaus
|
* Copyright © 2016-2017 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.
|
||||||
|
@ -21,7 +21,7 @@ import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
public class IoTIsFriendResponse extends IQ {
|
public class IoTIsFriendResponse extends IQ {
|
||||||
|
|
||||||
public static final String ELEMENT = "isFriend";
|
public static final String ELEMENT = "isFriendResponse";
|
||||||
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
|
public static final String NAMESPACE = Constants.IOT_PROVISIONING_NAMESPACE;
|
||||||
|
|
||||||
private final BareJid jid;
|
private final BareJid jid;
|
||||||
|
@ -46,6 +46,7 @@ public class IoTIsFriendResponse extends IQ {
|
||||||
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
||||||
xml.attribute("jid", jid);
|
xml.attribute("jid", jid);
|
||||||
xml.attribute("result", result);
|
xml.attribute("result", result);
|
||||||
|
xml.setEmptyElement();
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class EnablePushNotificationsIQ extends IQ {
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
if (publishOptions != null) {
|
if (publishOptions != null) {
|
||||||
DataForm dataForm = new DataForm(DataForm.Type.form);
|
DataForm dataForm = new DataForm(DataForm.Type.submit);
|
||||||
|
|
||||||
FormField formTypeField = new FormField("FORM_TYPE");
|
FormField formTypeField = new FormField("FORM_TYPE");
|
||||||
formTypeField.addValue(PubSub.NAMESPACE + "#publish-options");
|
formTypeField.addValue(PubSub.NAMESPACE + "#publish-options");
|
||||||
|
|
|
@ -233,6 +233,28 @@
|
||||||
<className>org.jivesoftware.smackx.chat_markers.provider.AcknowledgedProvider</className>
|
<className>org.jivesoftware.smackx.chat_markers.provider.AcknowledgedProvider</className>
|
||||||
</extensionProvider>
|
</extensionProvider>
|
||||||
|
|
||||||
|
<!-- XEP-0334: Message Processing Hints -->
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>no-copy</elementName>
|
||||||
|
<namespace>urn:xmpp:hints</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.hints.provider.NoCopyHintProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>no-permanent-store</elementName>
|
||||||
|
<namespace>urn:xmpp:hints</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.hints.provider.NoPermanentStoreHintProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>no-store</elementName>
|
||||||
|
<namespace>urn:xmpp:hints</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.hints.provider.NoStoreHintProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>store</elementName>
|
||||||
|
<namespace>urn:xmpp:hints</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.hints.provider.StoreHintProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
g
|
||||||
<!-- XEP-0363: HTTP File Upload -->
|
<!-- XEP-0363: HTTP File Upload -->
|
||||||
<iqProvider>
|
<iqProvider>
|
||||||
<elementName>slot</elementName>
|
<elementName>slot</elementName>
|
||||||
|
@ -255,4 +277,11 @@
|
||||||
<className>org.jivesoftware.smackx.httpfileupload.provider.FileTooLargeErrorProvider</className>
|
<className>org.jivesoftware.smackx.httpfileupload.provider.FileTooLargeErrorProvider</className>
|
||||||
</extensionProvider>
|
</extensionProvider>
|
||||||
|
|
||||||
|
<!-- XEP-0380: Explicit Message Encryption -->
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>encryption</elementName>
|
||||||
|
<namespace>urn:xmpp:eme:0</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.eme.provider.ExplicitMessageEncryptionProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
|
||||||
</smackProviders>
|
</smackProviders>
|
||||||
|
|
|
@ -5,5 +5,6 @@
|
||||||
<className>org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager</className>
|
<className>org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager</className>
|
||||||
<className>org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager</className>
|
<className>org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager</className>
|
||||||
<className>org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager</className>
|
<className>org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager</className>
|
||||||
|
<className>org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager</className>
|
||||||
</startupClasses>
|
</startupClasses>
|
||||||
</smack>
|
</smack>
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.eme.provider;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
|
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
||||||
|
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ExplicitMessageEncryptionProviderTest {
|
||||||
|
|
||||||
|
private static final String OX_EME_ELEMENT = "<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:openpgp:0'/>";
|
||||||
|
|
||||||
|
private static final String UNKNOWN_NAMESPACE = "urn:xmpp:foobar:0";
|
||||||
|
private static final String UNKNOWN_NAME = "Foo Bar";
|
||||||
|
private static final String UNKNOWN_EME_ELEMENT = "<encryption xmlns='urn:xmpp:eme:0' namespace='" + UNKNOWN_NAMESPACE
|
||||||
|
+ "' name='" + UNKNOWN_NAME + "'/>";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseOxEmeElement() throws Exception {
|
||||||
|
ExplicitMessageEncryptionElement eme = TestUtils.parseExtensionElement(OX_EME_ELEMENT);
|
||||||
|
assertEquals(ExplicitMessageEncryptionProtocol.openpgpV0, eme.getProtocol());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseUnknownEmeElement() throws Exception {
|
||||||
|
ExplicitMessageEncryptionElement eme = TestUtils.parseExtensionElement(UNKNOWN_EME_ELEMENT);
|
||||||
|
assertEquals(UNKNOWN_NAMESPACE, eme.getEncryptionNamespace());
|
||||||
|
assertEquals(UNKNOWN_NAME, eme.getName());
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ public class EnablePushNotificationsIQTest {
|
||||||
|
|
||||||
String exampleEnableIQWithPublishOptions = "<iq id='x42' type='set'>"
|
String exampleEnableIQWithPublishOptions = "<iq id='x42' type='set'>"
|
||||||
+ "<enable xmlns='urn:xmpp:push:0' jid='push-5.client.example' node='yxs32uqsflafdk3iuqo'>"
|
+ "<enable xmlns='urn:xmpp:push:0' jid='push-5.client.example' node='yxs32uqsflafdk3iuqo'>"
|
||||||
+ "<x xmlns='jabber:x:data' type='form'>"
|
+ "<x xmlns='jabber:x:data' type='submit'>"
|
||||||
+ "<field var='FORM_TYPE'><value>http://jabber.org/protocol/pubsub#publish-options</value></field>"
|
+ "<field var='FORM_TYPE'><value>http://jabber.org/protocol/pubsub#publish-options</value></field>"
|
||||||
+ "<field var='secret'><value>eruio234vzxc2kla-91</value></field>" + "</x>" + "</enable>" + "</iq>";
|
+ "<field var='secret'><value>eruio234vzxc2kla-91</value></field>" + "</x>" + "</enable>" + "</iq>";
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,9 @@ import org.jivesoftware.smack.chat.ChatMessageListener;
|
||||||
import org.jivesoftware.smack.filter.AndFilter;
|
import org.jivesoftware.smack.filter.AndFilter;
|
||||||
import org.jivesoftware.smack.filter.FromMatchesFilter;
|
import org.jivesoftware.smack.filter.FromMatchesFilter;
|
||||||
import org.jivesoftware.smack.filter.MessageTypeFilter;
|
import org.jivesoftware.smack.filter.MessageTypeFilter;
|
||||||
|
import org.jivesoftware.smack.filter.MessageWithBodiesFilter;
|
||||||
import org.jivesoftware.smack.filter.MessageWithSubjectFilter;
|
import org.jivesoftware.smack.filter.MessageWithSubjectFilter;
|
||||||
|
import org.jivesoftware.smack.filter.MessageWithThreadFilter;
|
||||||
import org.jivesoftware.smack.filter.NotFilter;
|
import org.jivesoftware.smack.filter.NotFilter;
|
||||||
import org.jivesoftware.smack.filter.OrFilter;
|
import org.jivesoftware.smack.filter.OrFilter;
|
||||||
import org.jivesoftware.smack.filter.PresenceTypeFilter;
|
import org.jivesoftware.smack.filter.PresenceTypeFilter;
|
||||||
|
@ -170,10 +172,6 @@ public class MultiUserChat {
|
||||||
public void processStanza(Stanza packet) {
|
public void processStanza(Stanza packet) {
|
||||||
Message msg = (Message) packet;
|
Message msg = (Message) packet;
|
||||||
EntityFullJid from = msg.getFrom().asEntityFullJidIfPossible();
|
EntityFullJid from = msg.getFrom().asEntityFullJidIfPossible();
|
||||||
if (from == null) {
|
|
||||||
LOGGER.warning("Message subject not changed by a full JID: " + msg.getFrom());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update the room subject
|
// Update the room subject
|
||||||
subject = msg.getSubject();
|
subject = msg.getSubject();
|
||||||
// Fire event for subject updated listeners
|
// Fire event for subject updated listeners
|
||||||
|
@ -322,8 +320,17 @@ public class MultiUserChat {
|
||||||
connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
|
connection.addSyncStanzaListener(messageListener, fromRoomGroupchatFilter);
|
||||||
connection.addSyncStanzaListener(presenceListener, new AndFilter(fromRoomFilter,
|
connection.addSyncStanzaListener(presenceListener, new AndFilter(fromRoomFilter,
|
||||||
StanzaTypeFilter.PRESENCE));
|
StanzaTypeFilter.PRESENCE));
|
||||||
connection.addSyncStanzaListener(subjectListener, new AndFilter(fromRoomFilter,
|
// @formatter:off
|
||||||
MessageWithSubjectFilter.INSTANCE, new NotFilter(MessageTypeFilter.ERROR)));
|
connection.addSyncStanzaListener(subjectListener,
|
||||||
|
new AndFilter(fromRoomFilter,
|
||||||
|
MessageWithSubjectFilter.INSTANCE,
|
||||||
|
new NotFilter(MessageTypeFilter.ERROR),
|
||||||
|
// According to XEP-0045 § 8.1 "A message with a <subject/> and a <body/> or a <subject/> and a <thread/> is a
|
||||||
|
// legitimate message, but it SHALL NOT be interpreted as a subject change."
|
||||||
|
new NotFilter(MessageWithBodiesFilter.INSTANCE),
|
||||||
|
new NotFilter(MessageWithThreadFilter.INSTANCE))
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
connection.addSyncStanzaListener(declinesListener, DECLINE_FILTER);
|
connection.addSyncStanzaListener(declinesListener, DECLINE_FILTER);
|
||||||
connection.addPacketInterceptor(presenceInterceptor, new AndFilter(ToMatchesFilter.create(room),
|
connection.addPacketInterceptor(presenceInterceptor, new AndFilter(ToMatchesFilter.create(room),
|
||||||
StanzaTypeFilter.PRESENCE));
|
StanzaTypeFilter.PRESENCE));
|
||||||
|
|
|
@ -124,7 +124,7 @@ public class Occupant {
|
||||||
int result;
|
int result;
|
||||||
result = affiliation.hashCode();
|
result = affiliation.hashCode();
|
||||||
result = 17 * result + role.hashCode();
|
result = 17 * result + role.hashCode();
|
||||||
result = 17 * result + jid.hashCode();
|
result = 17 * result + (jid != null ? jid.hashCode() : 0);
|
||||||
result = 17 * result + (nick != null ? nick.hashCode() : 0);
|
result = 17 * result + (nick != null ? nick.hashCode() : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public interface SubjectUpdatedListener {
|
||||||
* Called when a MUC room has changed its subject.
|
* Called when a MUC room has changed its subject.
|
||||||
*
|
*
|
||||||
* @param subject the new room's subject.
|
* @param subject the new room's subject.
|
||||||
* @param from the user that changed the room's subject (e.g. room@conference.jabber.org/nick).
|
* @param from the user that changed the room's subject or <code>null</code> if the room itself changed the subject.
|
||||||
*/
|
*/
|
||||||
public abstract void subjectUpdated(String subject, EntityFullJid from);
|
public abstract void subjectUpdated(String subject, EntityFullJid from);
|
||||||
|
|
||||||
|
|
|
@ -31,16 +31,20 @@ import org.jivesoftware.smack.AbstractConnectionClosedListener;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
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.SmackException.NotLoggedInException;
|
||||||
|
import org.jivesoftware.smack.SmackFuture;
|
||||||
|
import org.jivesoftware.smack.SmackFuture.InternalSmackFuture;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
||||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
import org.jivesoftware.smack.packet.IQ.Type;
|
||||||
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.util.SmackExecutorThreadFactory;
|
import org.jivesoftware.smack.util.SmackExecutorThreadFactory;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.ping.packet.Ping;
|
import org.jivesoftware.smackx.ping.packet.Ping;
|
||||||
|
@ -142,6 +146,71 @@ public final class PingManager extends Manager {
|
||||||
maybeSchedulePingServerTask();
|
maybeSchedulePingServerTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isValidErrorPong(Jid destinationJid, XMPPErrorException xmppErrorException) {
|
||||||
|
// If it is an error error response and the destination was our own service, then this must mean that the
|
||||||
|
// service responded, i.e. is up and pingable.
|
||||||
|
if (destinationJid.equals(connection().getServiceName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final XMPPError xmppError = xmppErrorException.getXMPPError();
|
||||||
|
|
||||||
|
// We may received an error response from an intermediate service returning an error like
|
||||||
|
// 'remote-server-not-found' or 'remote-server-timeout' to us (which would fake the 'from' address,
|
||||||
|
// see RFC 6120 § 8.3.1 2.). Or the recipient could became unavailable.
|
||||||
|
|
||||||
|
// Sticking with the current rules of RFC 6120/6121, it is undecidable at this point whether we received an
|
||||||
|
// error response from the pinged entity or not. This is because a service-unavailable error condition is
|
||||||
|
// *required* (as per the RFCs) to be send back in both relevant cases:
|
||||||
|
// 1. When the receiving entity is unaware of the IQ request type. RFC 6120 § 8.4.:
|
||||||
|
// "If an intended recipient receives an IQ stanza of type "get" or
|
||||||
|
// "set" containing a child element qualified by a namespace it does
|
||||||
|
// not understand, then the entity MUST return an IQ stanza of type
|
||||||
|
// "error" with an error condition of <service-unavailable/>.
|
||||||
|
// 2. When the receiving resource is not available. RFC 6121 § 8.5.3.2.3.
|
||||||
|
|
||||||
|
// Some clients don't obey the first rule and instead send back a feature-not-implement condition with type 'cancel',
|
||||||
|
// which allows us to consider this response as valid "error response" pong.
|
||||||
|
XMPPError.Type type = xmppError.getType();
|
||||||
|
XMPPError.Condition condition = xmppError.getCondition();
|
||||||
|
return type == XMPPError.Type.CANCEL && condition == XMPPError.Condition.feature_not_implemented;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmackFuture<Boolean> pingAsync(Jid jid) {
|
||||||
|
return pingAsync(jid, connection().getReplyTimeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmackFuture<Boolean> pingAsync(final Jid jid, long pongTimeout) {
|
||||||
|
final InternalSmackFuture<Boolean> future = new InternalSmackFuture<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void handleStanza(Stanza packet) throws NotConnectedException, InterruptedException {
|
||||||
|
setResult(true);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isNonFatalException(Exception exception) {
|
||||||
|
if (exception instanceof XMPPErrorException) {
|
||||||
|
XMPPErrorException xmppErrorException = (XMPPErrorException) exception;
|
||||||
|
if (isValidErrorPong(jid, xmppErrorException)) {
|
||||||
|
setResult(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ping ping = new Ping(jid);
|
||||||
|
try {
|
||||||
|
XMPPConnection connection = getAuthenticatedConnectionOrThrow();
|
||||||
|
connection.sendIqWithResponseCallback(ping, future, future, pongTimeout);
|
||||||
|
}
|
||||||
|
catch (NotLoggedInException | NotConnectedException | InterruptedException e) {
|
||||||
|
future.processException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pings the given jid. This method will return false if an error occurs. The exception
|
* Pings the given jid. This method will return false if an error occurs. The exception
|
||||||
* to this, is a server ping, which will always return true if the server is reachable,
|
* to this, is a server ping, which will always return true if the server is reachable,
|
||||||
|
@ -168,8 +237,8 @@ public final class PingManager extends Manager {
|
||||||
try {
|
try {
|
||||||
connection.createStanzaCollectorAndSend(ping).nextResultOrThrow(pingTimeout);
|
connection.createStanzaCollectorAndSend(ping).nextResultOrThrow(pingTimeout);
|
||||||
}
|
}
|
||||||
catch (XMPPException exc) {
|
catch (XMPPErrorException e) {
|
||||||
return jid.equals(connection.getXMPPServiceDomain());
|
return isValidErrorPong(jid, e);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -608,9 +608,7 @@ abstract public class Node
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
public void processStanza(Stanza packet)
|
public void processStanza(Stanza packet)
|
||||||
{
|
{
|
||||||
// CHECKSTYLE:OFF
|
|
||||||
EventElement event = (EventElement) packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
|
EventElement event = (EventElement) packet.getExtension("event", PubSubNamespace.EVENT.getXmlns());
|
||||||
// CHECKSTYLE:ON
|
|
||||||
ItemsExtension itemsElem = (ItemsExtension) event.getEvent();
|
ItemsExtension itemsElem = (ItemsExtension) event.getEvent();
|
||||||
ItemPublishEvent eventItems = new ItemPublishEvent(itemsElem.getNode(), itemsElem.getItems(), getSubscriptionIds(packet), DelayInformationManager.getDelayTimestamp(packet));
|
ItemPublishEvent eventItems = new ItemPublishEvent(itemsElem.getNode(), itemsElem.getItems(), getSubscriptionIds(packet), DelayInformationManager.getDelayTimestamp(packet));
|
||||||
listener.handlePublishedItems(eventItems);
|
listener.handlePublishedItems(eventItems);
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 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.pubsub;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
|
public abstract class PubSubException extends SmackException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public static class NotALeafNodeException extends PubSubException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String nodeId;
|
||||||
|
private final BareJid pubSubService;
|
||||||
|
|
||||||
|
NotALeafNodeException(String nodeId, BareJid pubSubService) {
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
this.pubSubService = pubSubService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BareJid getPubSubService() {
|
||||||
|
return pubSubService;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,8 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -42,6 +40,7 @@ import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
|
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
|
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
|
||||||
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
||||||
|
@ -64,6 +63,8 @@ import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
*/
|
*/
|
||||||
public final class PubSubManager extends Manager {
|
public final class PubSubManager extends Manager {
|
||||||
|
|
||||||
|
public static final String AUTO_CREATE_FEATURE = "http://jabber.org/protocol/pubsub#auto-create";
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName());
|
||||||
private static final Map<XMPPConnection, Map<BareJid, PubSubManager>> INSTANCES = new WeakHashMap<>();
|
private static final Map<XMPPConnection, Map<BareJid, PubSubManager>> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
@ -267,10 +268,11 @@ public final class PubSubManager extends Manager {
|
||||||
* @throws NotConnectedException
|
* @throws NotConnectedException
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
* @throws XMPPErrorException
|
* @throws XMPPErrorException
|
||||||
|
* @throws NotALeafNodeException in case the node already exists as collection node.
|
||||||
* @since 4.2.1
|
* @since 4.2.1
|
||||||
*/
|
*/
|
||||||
public LeafNode getOrCreateLeafNode(final String id)
|
public LeafNode getOrCreateLeafNode(final String id)
|
||||||
throws NoResponseException, NotConnectedException, InterruptedException, XMPPErrorException {
|
throws NoResponseException, NotConnectedException, InterruptedException, XMPPErrorException, NotALeafNodeException {
|
||||||
try {
|
try {
|
||||||
return getNode(id);
|
return getNode(id);
|
||||||
}
|
}
|
||||||
|
@ -287,9 +289,7 @@ public final class PubSubManager extends Manager {
|
||||||
throw e2;
|
throw e2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw e1;
|
if (e1.getXMPPError().getCondition() == Condition.service_unavailable) {
|
||||||
}
|
|
||||||
catch (PubSubAssertionError.DiscoInfoNodeAssertionError e) {
|
|
||||||
// This could be caused by Prosody bug #805 (see https://prosody.im/issues/issue/805). Prosody does not
|
// This could be caused by Prosody bug #805 (see https://prosody.im/issues/issue/805). Prosody does not
|
||||||
// answer to disco#info requests on the node ID, which makes it undecidable if a node is a leaf or
|
// answer to disco#info requests on the node ID, which makes it undecidable if a node is a leaf or
|
||||||
// collection node.
|
// collection node.
|
||||||
|
@ -297,32 +297,107 @@ public final class PubSubManager extends Manager {
|
||||||
+ " threw an DiscoInfoNodeAssertionError, trying workaround for Prosody bug #805 (https://prosody.im/issues/issue/805)");
|
+ " threw an DiscoInfoNodeAssertionError, trying workaround for Prosody bug #805 (https://prosody.im/issues/issue/805)");
|
||||||
return getOrCreateLeafNodeProsodyWorkaround(id);
|
return getOrCreateLeafNodeProsodyWorkaround(id);
|
||||||
}
|
}
|
||||||
|
throw e1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to get a leaf node with the given node ID.
|
||||||
|
*
|
||||||
|
* @param id the node ID.
|
||||||
|
* @return the requested leaf node.
|
||||||
|
* @throws NotALeafNodeException in case the node exists but is a collection node.
|
||||||
|
* @throws NoResponseException
|
||||||
|
* @throws NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws XMPPErrorException
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public LeafNode getLeafNode(String id) throws NotALeafNodeException, NoResponseException, NotConnectedException,
|
||||||
|
InterruptedException, XMPPErrorException {
|
||||||
|
Node node;
|
||||||
|
try {
|
||||||
|
node = getNode(id);
|
||||||
|
}
|
||||||
|
catch (XMPPErrorException e) {
|
||||||
|
if (e.getXMPPError().getCondition() == Condition.service_unavailable) {
|
||||||
|
// This could be caused by Prosody bug #805 (see https://prosody.im/issues/issue/805). Prosody does not
|
||||||
|
// answer to disco#info requests on the node ID, which makes it undecidable if a node is a leaf or
|
||||||
|
// collection node.
|
||||||
|
return getLeafNodeProsodyWorkaround(id);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node instanceof LeafNode) {
|
||||||
|
return (LeafNode) node;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PubSubException.NotALeafNodeException(id, pubSubService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LeafNode getLeafNodeProsodyWorkaround(final String id) throws NoResponseException, NotConnectedException,
|
||||||
|
InterruptedException, NotALeafNodeException, XMPPErrorException {
|
||||||
|
LeafNode leafNode = new LeafNode(this, id);
|
||||||
|
try {
|
||||||
|
// Try to ensure that this is not a collection node by asking for one item form the node.
|
||||||
|
leafNode.getItems(1);
|
||||||
|
} catch (XMPPErrorException e) {
|
||||||
|
Condition condition = e.getXMPPError().getCondition();
|
||||||
|
if (condition == Condition.feature_not_implemented) {
|
||||||
|
// XEP-0060 § 6.5.9.5: Item retrieval not supported, e.g. because node is a collection node
|
||||||
|
throw new PubSubException.NotALeafNodeException(id, pubSubService);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMap.put(id, leafNode);
|
||||||
|
|
||||||
|
return leafNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LeafNode getOrCreateLeafNodeProsodyWorkaround(final String id)
|
private LeafNode getOrCreateLeafNodeProsodyWorkaround(final String id)
|
||||||
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException {
|
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotALeafNodeException {
|
||||||
try {
|
try {
|
||||||
return createNode(id);
|
return createNode(id);
|
||||||
}
|
}
|
||||||
catch (XMPPErrorException e1) {
|
catch (XMPPErrorException e1) {
|
||||||
if (e1.getXMPPError().getCondition() == Condition.conflict) {
|
if (e1.getXMPPError().getCondition() == Condition.conflict) {
|
||||||
Constructor<?> constructor = LeafNode.class.getDeclaredConstructors()[0];
|
return getLeafNodeProsodyWorkaround(id);
|
||||||
constructor.setAccessible(true);
|
|
||||||
LeafNode res;
|
|
||||||
try {
|
|
||||||
res = (LeafNode) constructor.newInstance(this, id);
|
|
||||||
}
|
|
||||||
catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
|
||||||
| InvocationTargetException e2) {
|
|
||||||
throw new AssertionError(e2);
|
|
||||||
}
|
|
||||||
// TODO: How to verify that this is actually a leafe node and not a conflict with a collection node?
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
throw e1;
|
throw e1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to publish an item and, if the node with the given ID does not exists, auto-create the node.
|
||||||
|
* <p>
|
||||||
|
* Not every PubSub service supports automatic node creation. You can discover if this service supports it by using
|
||||||
|
* {@link #supportsAutomaticNodeCreation()}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param id The unique id of the node.
|
||||||
|
* @param item The item to publish.
|
||||||
|
* @return the LeafNode on which the item was published.
|
||||||
|
* @throws NoResponseException
|
||||||
|
* @throws XMPPErrorException
|
||||||
|
* @throws NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public <I extends Item> LeafNode tryToPublishAndPossibleAutoCreate(String id, I item)
|
||||||
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
LeafNode leafNode = new LeafNode(this, id);
|
||||||
|
leafNode.publish(item);
|
||||||
|
|
||||||
|
// If LeafNode.publish() did not throw then we have successfully published an item and possible auto-created
|
||||||
|
// (XEP-0163 § 3., XEP-0060 § 7.1.4) the node. So we can put the node into the nodeMap.
|
||||||
|
nodeMap.put(id, leafNode);
|
||||||
|
|
||||||
|
return leafNode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the nodes that currently exist as a child of the specified
|
* Get all the nodes that currently exist as a child of the specified
|
||||||
* collection node. If the service does not support collection nodes
|
* collection node. If the service does not support collection nodes
|
||||||
|
@ -440,6 +515,23 @@ public final class PubSubManager extends Manager {
|
||||||
return mgr.discoverInfo(pubSubService);
|
return mgr.discoverInfo(pubSubService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the PubSub service supports automatic node creation.
|
||||||
|
*
|
||||||
|
* @return true if the PubSub service supports automatic node creation.
|
||||||
|
* @throws NoResponseException
|
||||||
|
* @throws XMPPErrorException
|
||||||
|
* @throws NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @since 4.2.1
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0060.html#publisher-publish-autocreate">XEP-0060 § 7.1.4 Automatic Node Creation</a>
|
||||||
|
*/
|
||||||
|
public boolean supportsAutomaticNodeCreation()
|
||||||
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
|
||||||
|
return sdm.supportsFeature(pubSubService, AUTO_CREATE_FEATURE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if it is possible to create PubSub nodes on this service. It could be possible that the
|
* Check if it is possible to create PubSub nodes on this service. It could be possible that the
|
||||||
* PubSub service allows only certain XMPP entities (clients) to create nodes and publish items
|
* PubSub service allows only certain XMPP entities (clients) to create nodes and publish items
|
||||||
|
|
|
@ -26,7 +26,9 @@ import org.jivesoftware.smack.ThreadedDummyConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.XMPPError;
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.packet.IQ.Type;
|
||||||
import org.jivesoftware.smack.packet.XMPPError.Condition;
|
import org.jivesoftware.smack.packet.XMPPError.Condition;
|
||||||
|
import org.jivesoftware.smackx.InitExtensions;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
|
@ -39,7 +41,7 @@ import org.junit.Test;
|
||||||
* @author Robin Collier
|
* @author Robin Collier
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ConfigureFormTest
|
public class ConfigureFormTest extends InitExtensions
|
||||||
{
|
{
|
||||||
@Test
|
@Test
|
||||||
public void checkChildrenAssocPolicy()
|
public void checkChildrenAssocPolicy()
|
||||||
|
@ -55,6 +57,8 @@ public class ConfigureFormTest
|
||||||
ThreadedDummyConnection con = ThreadedDummyConnection.newInstance();
|
ThreadedDummyConnection con = ThreadedDummyConnection.newInstance();
|
||||||
PubSubManager mgr = new PubSubManager(con, PubSubManagerTest.DUMMY_PUBSUB_SERVICE);
|
PubSubManager mgr = new PubSubManager(con, PubSubManagerTest.DUMMY_PUBSUB_SERVICE);
|
||||||
DiscoverInfo info = new DiscoverInfo();
|
DiscoverInfo info = new DiscoverInfo();
|
||||||
|
info.setType(Type.result);
|
||||||
|
info.setFrom(PubSubManagerTest.DUMMY_PUBSUB_SERVICE);
|
||||||
Identity ident = new Identity("pubsub", null, "leaf");
|
Identity ident = new Identity("pubsub", null, "leaf");
|
||||||
info.addIdentity(ident);
|
info.addIdentity(ident);
|
||||||
con.addIQReply(info);
|
con.addIQReply(info);
|
||||||
|
@ -62,6 +66,8 @@ public class ConfigureFormTest
|
||||||
Node node = mgr.getNode("princely_musings");
|
Node node = mgr.getNode("princely_musings");
|
||||||
|
|
||||||
PubSub errorIq = new PubSub();
|
PubSub errorIq = new PubSub();
|
||||||
|
errorIq.setType(Type.error);
|
||||||
|
errorIq.setFrom(PubSubManagerTest.DUMMY_PUBSUB_SERVICE);
|
||||||
XMPPError.Builder error = XMPPError.getBuilder(Condition.forbidden);
|
XMPPError.Builder error = XMPPError.getBuilder(Condition.forbidden);
|
||||||
errorIq.setError(error);
|
errorIq.setError(error);
|
||||||
con.addIQReply(errorIq);
|
con.addIQReply(errorIq);
|
||||||
|
|
|
@ -283,14 +283,46 @@ public final class RosterEntry extends Manager {
|
||||||
return other.item.equals(this.item);
|
return other.item.equals(this.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the RosterEntry to a Roster stanza <item/> element.
|
||||||
|
*
|
||||||
|
* @param entry the roster entry.
|
||||||
|
* @return the roster item.
|
||||||
|
*/
|
||||||
static RosterPacket.Item toRosterItem(RosterEntry entry) {
|
static RosterPacket.Item toRosterItem(RosterEntry entry) {
|
||||||
return toRosterItem(entry, entry.getName());
|
return toRosterItem(entry, entry.getName(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RosterPacket.Item toRosterItem(RosterEntry entry, String name) {
|
/**
|
||||||
|
* Convert the RosterEntry to a Roster stanza <item/> element.
|
||||||
|
*
|
||||||
|
* @param entry the roster entry
|
||||||
|
* @param name the name of the roster item.
|
||||||
|
* @return the roster item.
|
||||||
|
*/
|
||||||
|
static RosterPacket.Item toRosterItem(RosterEntry entry, String name) {
|
||||||
|
return toRosterItem(entry, name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RosterPacket.Item toRosterItem(RosterEntry entry, boolean includeAskAttribute) {
|
||||||
|
return toRosterItem(entry, entry.getName(), includeAskAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a roster entry with the given name to a roster item. As per RFC 6121 § 2.1.2.2., clients MUST NOT include
|
||||||
|
* the 'ask' attribute, thus set {@code includeAskAttribute} to {@code false}.
|
||||||
|
*
|
||||||
|
* @param entry the roster entry.
|
||||||
|
* @param name the name of the roster item.
|
||||||
|
* @param includeAskAttribute whether or not to include the 'ask' attribute.
|
||||||
|
* @return the roster item.
|
||||||
|
*/
|
||||||
|
private static RosterPacket.Item toRosterItem(RosterEntry entry, String name, boolean includeAskAttribute) {
|
||||||
RosterPacket.Item item = new RosterPacket.Item(entry.getJid(), name);
|
RosterPacket.Item item = new RosterPacket.Item(entry.getJid(), name);
|
||||||
item.setItemType(entry.getType());
|
item.setItemType(entry.getType());
|
||||||
|
if (includeAskAttribute) {
|
||||||
item.setSubscriptionPending(entry.isSubscriptionPending());
|
item.setSubscriptionPending(entry.isSubscriptionPending());
|
||||||
|
}
|
||||||
item.setApproved(entry.isApproved());
|
item.setApproved(entry.isApproved());
|
||||||
// Set the correct group names for the item.
|
// Set the correct group names for the item.
|
||||||
for (RosterGroup group : entry.getGroups()) {
|
for (RosterGroup group : entry.getGroups()) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015 Florian Schmaus
|
* Copyright 2015-2017 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,10 +18,23 @@ package org.jivesoftware.smackx.ping;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
public class PingIntegrationTest extends AbstractSmackIntegrationTest {
|
public class PingIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
|
||||||
|
@ -35,4 +48,52 @@ public class PingIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
assertTrue(pingManager.pingMyServer());
|
assertTrue(pingManager.pingMyServer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class Pinger implements Runnable {
|
||||||
|
private final List<Jid> toPing;
|
||||||
|
private final Collection<Future<Boolean>> pongFutures;
|
||||||
|
|
||||||
|
private final PingManager pingManager;
|
||||||
|
|
||||||
|
private Pinger(XMPPConnection connection, Collection<Future<Boolean>> pongFutures, Jid... toPing) {
|
||||||
|
this(connection, pongFutures, Arrays.asList(toPing));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pinger(XMPPConnection connection, Collection<Future<Boolean>> pongFutures, List<Jid> toPing) {
|
||||||
|
this.toPing = toPing;
|
||||||
|
this.pongFutures = pongFutures;
|
||||||
|
|
||||||
|
this.pingManager = PingManager.getInstanceFor(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
List<Future<Boolean>> futures = new ArrayList<>();
|
||||||
|
for (Jid jid : toPing) {
|
||||||
|
Future<Boolean> future = pingManager.pingAsync(jid);
|
||||||
|
futures.add(future);
|
||||||
|
}
|
||||||
|
pongFutures.addAll(futures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void pingAsync() throws InterruptedException, ExecutionException {
|
||||||
|
List<Future<Boolean>> pongFutures = Collections.synchronizedList(new ArrayList<Future<Boolean>>());
|
||||||
|
Runnable[] pinger = new Runnable[3];
|
||||||
|
pinger[0] = new Pinger(conOne, pongFutures, conTwo.getUser(), conThree.getUser());
|
||||||
|
pinger[1] = new Pinger(conTwo, pongFutures, conOne.getUser(), conThree.getUser());
|
||||||
|
pinger[2] = new Pinger(conThree, pongFutures, conOne.getUser(), conTwo.getUser());
|
||||||
|
|
||||||
|
ExecutorService executorService = Executors.newFixedThreadPool(pinger.length);
|
||||||
|
for (Runnable runnable : pinger) {
|
||||||
|
executorService.execute(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
executorService.shutdown();
|
||||||
|
executorService.awaitTermination(1, TimeUnit.MINUTES);
|
||||||
|
|
||||||
|
for (Future<Boolean> pongFuture : pongFutures) {
|
||||||
|
assertTrue(pongFuture.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2014 Florian Schmaus
|
* Copyright 2014-2017 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.
|
||||||
|
@ -115,8 +115,7 @@ public class SASLDigestMD5Mechanism extends SASLMechanism {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case INITIAL:
|
case INITIAL:
|
||||||
for (String part : challengeParts) {
|
for (String part : challengeParts) {
|
||||||
String[] keyValue = part.split("=");
|
String[] keyValue = part.split("=", 2);
|
||||||
assert (keyValue.length == 2);
|
|
||||||
String key = keyValue[0];
|
String key = keyValue[0];
|
||||||
// RFC 2831 § 7.1 about the formating of the digest-challenge:
|
// RFC 2831 § 7.1 about the formating of the digest-challenge:
|
||||||
// "The full form is "<n>#<m>element" indicating at least <n> and
|
// "The full form is "<n>#<m>element" indicating at least <n> and
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014 Florian Schmaus
|
* Copyright © 2014-2017 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.
|
||||||
|
|
Loading…
Reference in a new issue