mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-03-14 10:23:28 +01:00
Compare commits
133 commits
b9be45ae2c
...
01cd0507b6
Author | SHA1 | Date | |
---|---|---|---|
|
01cd0507b6 | ||
|
ea601d0f9f | ||
|
f07c8919cd | ||
|
cccf3ce5f3 | ||
|
229a6edb1b | ||
|
a15110694a | ||
|
4d026d8ae8 | ||
|
3885153ea1 | ||
|
ba2e36dbc3 | ||
|
4622d00d9e | ||
|
cd70e7aa5a | ||
|
aafc24a966 | ||
|
91a1825eb8 | ||
|
1b0e19f699 | ||
|
4a83d2957e | ||
|
4e715a35e5 | ||
|
67a5c3a41a | ||
|
ad9867ebdf | ||
|
5cd7a6c60e | ||
|
e25be8fea4 | ||
|
3ff553549a | ||
|
4cdb4acf26 | ||
|
7fad14ac0c | ||
|
c06cf72337 | ||
|
0e637068e6 | ||
|
1dae0c0c32 | ||
|
1a727c152e | ||
|
5f75d141ff | ||
|
5c46451cd2 | ||
|
940d7bf02a | ||
|
45a4987818 | ||
|
ae2394ea83 | ||
|
8be1568a5d | ||
|
178e82a3aa | ||
|
817dc0ed3a | ||
|
b3637101ce | ||
|
e530db2e6d | ||
|
56ca31b156 | ||
|
56c66f17d8 | ||
|
e51cf47b29 | ||
|
993a307222 | ||
|
182d01a4b0 | ||
|
e1845a52ca | ||
|
89ef46525e | ||
|
fc7fc10c69 | ||
|
e39adff40f | ||
|
d1273532ce | ||
|
10a2687ff1 | ||
|
69a55aaa84 | ||
|
def7c91e6a | ||
|
4e2d0035ac | ||
|
400a2b2564 | ||
|
33e8053258 | ||
|
819ed87a17 | ||
|
e3c50aeeb7 | ||
|
11cc2d8d77 | ||
|
cad63bc107 | ||
|
8736e3e434 | ||
|
61d56aa06a | ||
|
fdffd58d25 | ||
|
f611941dcf | ||
|
f2c2f45f18 | ||
|
7dc8cc176e | ||
|
2628dc328c | ||
|
403890d988 | ||
|
5731e61782 | ||
|
22cf7bace8 | ||
|
18b25902c4 | ||
53d385ab93 | |||
|
fa88f78232 | ||
|
22b2efc6a6 | ||
|
7fd300888e | ||
|
4ae3fbb073 | ||
|
447c1304cf | ||
|
d8ce2d335b | ||
|
8c87daa7b4 | ||
|
d0be6a6216 | ||
|
6d0a0ff2f4 | ||
|
f03f2c75f8 | ||
|
19270a2eca | ||
|
bd528d2c32 | ||
|
1956048811 | ||
|
e842195b71 | ||
|
ab92bc4b40 | ||
|
0fa6d88575 | ||
|
a0f97707d8 | ||
|
7199003f98 | ||
|
9fb7d6d97c | ||
|
ad6e285346 | ||
|
dac8b728b4 | ||
|
2ff53abef3 | ||
|
d25bd811dc | ||
|
6f67553fcf | ||
|
453ca6aeb0 | ||
|
a3840659aa | ||
|
b243a40e26 | ||
|
585bcb4dc8 | ||
|
27ff37fa98 | ||
|
b675aac81b | ||
|
8074ddd60a | ||
|
9b339efbc1 | ||
|
820adf8865 | ||
|
105c74b22b | ||
|
52a49769f9 | ||
|
ec456399b5 | ||
|
ae4ff244a3 | ||
|
49ad8c0954 | ||
|
b57cf8375f | ||
|
09710b3203 | ||
|
55299fb7e7 | ||
|
8ae5ef1f51 | ||
|
1564b98d60 | ||
|
f0a0796d33 | ||
|
bc281d84a7 | ||
|
5b857a1b82 | ||
|
3f2418dec7 | ||
|
c13f4ccac3 | ||
|
613f1afcab | ||
|
001985647a | ||
|
0d73c21945 | ||
|
32a38c4e77 | ||
|
a0b9279441 | ||
|
48c057ab10 | ||
|
98d530819f | ||
9e33fc56e1 | |||
9c5b0a2a16 | |||
|
f39e433121 | ||
|
d7c708b167 | ||
|
8f760eaeb3 | ||
|
e626580f68 | ||
|
58774ad05b | ||
|
00249f3a67 | ||
|
5e203086b3 |
118 changed files with 4989 additions and 2402 deletions
1855
CHANGELOG.md
Normal file
1855
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load diff
4
NOTICE
4
NOTICE
|
@ -29,6 +29,7 @@ Chris Deering
|
||||||
Christoph Fiehe
|
Christoph Fiehe
|
||||||
Craig Hesling
|
Craig Hesling
|
||||||
Damian Minkov
|
Damian Minkov
|
||||||
|
Dan Caseley
|
||||||
Daniele Ricci
|
Daniele Ricci
|
||||||
Daniel Henninger
|
Daniel Henninger
|
||||||
Daniel Hintze
|
Daniel Hintze
|
||||||
|
@ -44,6 +45,7 @@ Fernando Ramirez
|
||||||
Florian Kimmann
|
Florian Kimmann
|
||||||
Florian Schmaus
|
Florian Schmaus
|
||||||
Francisco Vives
|
Francisco Vives
|
||||||
|
Frank Matheron
|
||||||
Gaston Dombiak
|
Gaston Dombiak
|
||||||
Georg Lukas
|
Georg Lukas
|
||||||
Gilles Cornu
|
Gilles Cornu
|
||||||
|
@ -64,6 +66,7 @@ Jay Kline
|
||||||
Jeff Williams
|
Jeff Williams
|
||||||
Jesus Fuentes
|
Jesus Fuentes
|
||||||
John Haubrich
|
John Haubrich
|
||||||
|
Jonathan Lennox
|
||||||
Júlio Cesar Bueno Cotta
|
Júlio Cesar Bueno Cotta
|
||||||
Lars Noschinski
|
Lars Noschinski
|
||||||
Luca Stucchi
|
Luca Stucchi
|
||||||
|
@ -82,6 +85,7 @@ Pete Matern
|
||||||
Piotr Nosek
|
Piotr Nosek
|
||||||
Rajat Kumar Gupta
|
Rajat Kumar Gupta
|
||||||
Robin Collier
|
Robin Collier
|
||||||
|
Simon Abykov
|
||||||
Simon Schuster
|
Simon Schuster
|
||||||
Son Goku
|
Son Goku
|
||||||
Tairs Rzajevs
|
Tairs Rzajevs
|
||||||
|
|
14
build.gradle
14
build.gradle
|
@ -6,6 +6,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2'
|
classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2'
|
||||||
|
classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +148,7 @@ allprojects {
|
||||||
smackMinAndroidSdk = 19
|
smackMinAndroidSdk = 19
|
||||||
junitVersion = '5.7.1'
|
junitVersion = '5.7.1'
|
||||||
commonsIoVersion = '2.6'
|
commonsIoVersion = '2.6'
|
||||||
bouncyCastleVersion = '1.68'
|
bouncyCastleVersion = '1.69'
|
||||||
guavaVersion = '30.1-jre'
|
guavaVersion = '30.1-jre'
|
||||||
mockitoVersion = '3.7.7'
|
mockitoVersion = '3.7.7'
|
||||||
orgReflectionsVersion = '0.9.11'
|
orgReflectionsVersion = '0.9.11'
|
||||||
|
@ -196,10 +197,6 @@ allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
// Add OSS Sonatype Snapshot repository
|
|
||||||
maven {
|
|
||||||
url 'https://oss.sonatype.org/content/repositories/snapshots'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
tasks.withType(JavaCompile) {
|
||||||
|
@ -450,6 +447,7 @@ subprojects {
|
||||||
apply plugin: 'signing'
|
apply plugin: 'signing'
|
||||||
apply plugin: 'checkstyle'
|
apply plugin: 'checkstyle'
|
||||||
apply plugin: 'org.kordamp.gradle.clirr'
|
apply plugin: 'org.kordamp.gradle.clirr'
|
||||||
|
apply plugin: 'biz.aQute.bnd.builder'
|
||||||
|
|
||||||
checkstyle {
|
checkstyle {
|
||||||
toolVersion = '8.27'
|
toolVersion = '8.27'
|
||||||
|
@ -615,6 +613,12 @@ subprojects*.jar {
|
||||||
manifest {
|
manifest {
|
||||||
from sharedManifest
|
from sharedManifest
|
||||||
}
|
}
|
||||||
|
bundle {
|
||||||
|
bnd(
|
||||||
|
'-removeheaders': 'Tool, Bnd-*',
|
||||||
|
'-exportcontents': '*',
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(subprojects - gplLicensedProjects) {
|
configure(subprojects - gplLicensedProjects) {
|
||||||
|
|
|
@ -86,6 +86,27 @@ debugger=console
|
||||||
|
|
||||||
The framework will first load the properties file from `~/.config/smack-integration-test/properties`
|
The framework will first load the properties file from `~/.config/smack-integration-test/properties`
|
||||||
|
|
||||||
|
### Running selected tests only
|
||||||
|
|
||||||
|
Using `enabledTests` is is possible to run only selected tests. The
|
||||||
|
tests can be selected on a per class base or by specifying concrete
|
||||||
|
test methods. In the latter case, the methods must be qualified by a
|
||||||
|
(simple) class name.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ gradle integrationTest -Dsinttest.enabledTests=SoftwareInfoIntegrationTest.test
|
||||||
|
```
|
||||||
|
|
||||||
|
will only run the `test()` method of `SoftwareInfoIntegrationTest`, whereas
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ gradle integrationTest -Dsinttest.enabledTests=SoftwareInfoIntegrationTest
|
||||||
|
```
|
||||||
|
|
||||||
|
would run all tests defined in the `SoftwareInfoIntegrationTest` class.
|
||||||
|
|
||||||
Overview of the components
|
Overview of the components
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
|
38
resources/generate-notice-file
Executable file
38
resources/generate-notice-file
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
|
SMACK_DIR=$(realpath "${SCRIPT_DIR}/..")
|
||||||
|
|
||||||
|
cd "${SMACK_DIR}"
|
||||||
|
|
||||||
|
TEMPFILE=$(mktemp)
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm "${TEMPFILE}"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
git shortlog -s |\
|
||||||
|
cut -f2- |\
|
||||||
|
grep -v '(no author)' |\
|
||||||
|
grep '\w \w.*' |\
|
||||||
|
sort \
|
||||||
|
> "${TEMPFILE}"
|
||||||
|
|
||||||
|
readonly NOTICE_FILE="${SMACK_DIR}/NOTICE"
|
||||||
|
|
||||||
|
cat <<EOF > "${NOTICE_FILE}"
|
||||||
|
Smack
|
||||||
|
|
||||||
|
An open-source XMPP library
|
||||||
|
maintained by Florian Schmaus
|
||||||
|
|
||||||
|
https://igniterealtime.org/projects/smack
|
||||||
|
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat "${TEMPFILE}" >> "${NOTICE_FILE}"
|
|
@ -1,7 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
git shortlog -s |\
|
|
||||||
cut -f2- |\
|
|
||||||
grep -v '(no author)' |\
|
|
||||||
grep '\w \w.*' |\
|
|
||||||
sort
|
|
File diff suppressed because it is too large
Load diff
|
@ -43,6 +43,7 @@ import org.jivesoftware.smack.packet.StanzaError;
|
||||||
import org.jivesoftware.smack.util.CloseableUtil;
|
import org.jivesoftware.smack.util.CloseableUtil;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
import org.igniterealtime.jbosh.AbstractBody;
|
import org.igniterealtime.jbosh.AbstractBody;
|
||||||
import org.igniterealtime.jbosh.BOSHClient;
|
import org.igniterealtime.jbosh.BOSHClient;
|
||||||
|
@ -200,6 +201,14 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
||||||
+ getHost() + ":" + getPort() + ".";
|
+ getHost() + ":" + getPort() + ".";
|
||||||
throw new SmackException.SmackMessageException(errorMessage);
|
throw new SmackException.SmackMessageException(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
XmlPullParser parser = PacketParserUtils.getParserFor(
|
||||||
|
"<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'/>");
|
||||||
|
onStreamOpen(parser);
|
||||||
|
} catch (XmlPullParserException | IOException e) {
|
||||||
|
throw new AssertionError("Failed to setup stream environment", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -511,7 +520,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
|
||||||
parseAndProcessStanza(parser);
|
parseAndProcessStanza(parser);
|
||||||
break;
|
break;
|
||||||
case "features":
|
case "features":
|
||||||
parseFeatures(parser);
|
parseFeaturesAndNotify(parser);
|
||||||
break;
|
break;
|
||||||
case "error":
|
case "error":
|
||||||
// Some BOSH error isn't stream error.
|
// Some BOSH error isn't stream error.
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
// Note that this is also declared in the main build.gradle for
|
||||||
|
// subprojects, but since evaluationDependsOnChildren is enabled we
|
||||||
|
// need to declare it here too to have bundle{bnd{...}} available
|
||||||
|
apply plugin: 'biz.aQute.bnd.builder'
|
||||||
|
|
||||||
description = """\
|
description = """\
|
||||||
Smack core components."""
|
Smack core components."""
|
||||||
|
|
||||||
|
@ -55,3 +60,11 @@ task createVersionResource(type: CreateFileTask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
compileJava.dependsOn(createVersionResource)
|
compileJava.dependsOn(createVersionResource)
|
||||||
|
|
||||||
|
jar {
|
||||||
|
bundle {
|
||||||
|
bnd(
|
||||||
|
'DynamicImport-Package': '*',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -524,9 +524,6 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
closingStreamReceived = false;
|
closingStreamReceived = false;
|
||||||
streamId = null;
|
streamId = null;
|
||||||
|
|
||||||
// The connection should not be connected nor marked as such prior calling connectInternal().
|
|
||||||
assert !connected;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Perform the actual connection to the XMPP service
|
// Perform the actual connection to the XMPP service
|
||||||
connectInternal();
|
connectInternal();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2017-2020 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2017-2022 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,6 +32,7 @@ import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.security.UnrecoverableKeyException;
|
import java.security.UnrecoverableKeyException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
@ -189,7 +190,7 @@ public abstract class ConnectionConfiguration {
|
||||||
protected ConnectionConfiguration(Builder<?, ?> builder) {
|
protected ConnectionConfiguration(Builder<?, ?> builder) {
|
||||||
try {
|
try {
|
||||||
smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory,
|
smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory,
|
||||||
builder.customX509TrustManager, builder.keystoreType, builder.keystorePath,
|
builder.customX509TrustManager, builder.keyManagers, builder.sslContextSecureRandom, builder.keystoreType, builder.keystorePath,
|
||||||
builder.callbackHandler, builder.pkcs11Library);
|
builder.callbackHandler, builder.pkcs11Library);
|
||||||
} catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException
|
} catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException
|
||||||
| KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException
|
| KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException
|
||||||
|
@ -252,7 +253,7 @@ public abstract class ConnectionConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory,
|
private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory,
|
||||||
X509TrustManager trustManager, String keystoreType, String keystorePath,
|
X509TrustManager trustManager, KeyManager[] keyManagers, SecureRandom secureRandom, String keystoreType, String keystorePath,
|
||||||
CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException,
|
CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException,
|
||||||
CertificateException, IOException, KeyStoreException, NoSuchProviderException,
|
CertificateException, IOException, KeyStoreException, NoSuchProviderException,
|
||||||
UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException,
|
UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException,
|
||||||
|
@ -266,69 +267,10 @@ public abstract class ConnectionConfiguration {
|
||||||
context = SSLContext.getInstance("TLS");
|
context = SSLContext.getInstance("TLS");
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyStore ks = null;
|
// TODO: Remove the block below once we removed setKeystorePath(), setKeystoreType(), setCallbackHanlder() and
|
||||||
PasswordCallback pcb = null;
|
// setPKCS11Library() in the builder, and all related fields and the parameters of this function.
|
||||||
KeyManager[] kms = null;
|
if (keyManagers == null) {
|
||||||
|
keyManagers = Builder.getKeyManagersFrom(keystoreType, keystorePath, callbackHandler, pkcs11Library);
|
||||||
if ("PKCS11".equals(keystoreType)) {
|
|
||||||
Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class);
|
|
||||||
String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library;
|
|
||||||
ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8));
|
|
||||||
Provider p = (Provider) c.newInstance(config);
|
|
||||||
Security.addProvider(p);
|
|
||||||
ks = KeyStore.getInstance("PKCS11", p);
|
|
||||||
pcb = new PasswordCallback("PKCS11 Password: ", false);
|
|
||||||
callbackHandler.handle(new Callback[] { pcb });
|
|
||||||
ks.load(null, pcb.getPassword());
|
|
||||||
} else if ("Apple".equals(keystoreType)) {
|
|
||||||
ks = KeyStore.getInstance("KeychainStore", "Apple");
|
|
||||||
ks.load(null, null);
|
|
||||||
// pcb = new PasswordCallback("Apple Keychain",false);
|
|
||||||
// pcb.setPassword(null);
|
|
||||||
} else if (keystoreType != null) {
|
|
||||||
ks = KeyStore.getInstance(keystoreType);
|
|
||||||
if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) {
|
|
||||||
pcb = new PasswordCallback("Keystore Password: ", false);
|
|
||||||
callbackHandler.handle(new Callback[] { pcb });
|
|
||||||
ks.load(new FileInputStream(keystorePath), pcb.getPassword());
|
|
||||||
} else {
|
|
||||||
InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
|
||||||
try {
|
|
||||||
// Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous
|
|
||||||
// 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702
|
|
||||||
char[] password = "changeit".toCharArray();
|
|
||||||
try {
|
|
||||||
ks.load(stream, password);
|
|
||||||
} finally {
|
|
||||||
CloseableUtil.maybeClose(stream);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e);
|
|
||||||
|
|
||||||
ks = KeyStore.getInstance("jks");
|
|
||||||
// Open the stream again, so that we read it from the beginning.
|
|
||||||
stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
|
||||||
try {
|
|
||||||
ks.load(stream, null);
|
|
||||||
} finally {
|
|
||||||
CloseableUtil.maybeClose(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ks != null) {
|
|
||||||
String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
|
|
||||||
if (kmf != null) {
|
|
||||||
if (pcb == null) {
|
|
||||||
kmf.init(ks, null);
|
|
||||||
} else {
|
|
||||||
kmf.init(ks, pcb.getPassword());
|
|
||||||
pcb.clearPassword();
|
|
||||||
}
|
|
||||||
kms = kmf.getKeyManagers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SmackDaneVerifier daneVerifier = null;
|
SmackDaneVerifier daneVerifier = null;
|
||||||
|
@ -343,7 +285,7 @@ public abstract class ConnectionConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
// User requested DANE verification.
|
// User requested DANE verification.
|
||||||
daneVerifier.init(context, kms, trustManager, null);
|
daneVerifier.init(context, keyManagers, trustManager, secureRandom);
|
||||||
} else {
|
} else {
|
||||||
final TrustManager[] trustManagers;
|
final TrustManager[] trustManagers;
|
||||||
if (trustManager != null) {
|
if (trustManager != null) {
|
||||||
|
@ -354,7 +296,7 @@ public abstract class ConnectionConfiguration {
|
||||||
trustManagers = null;
|
trustManagers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.init(kms, trustManagers, null);
|
context.init(keyManagers, trustManagers, secureRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SmackTlsContext(context, daneVerifier);
|
return new SmackTlsContext(context, daneVerifier);
|
||||||
|
@ -405,7 +347,7 @@ public abstract class ConnectionConfiguration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the TLS security mode used when making the connection. By default,
|
* Returns the TLS security mode used when making the connection. By default,
|
||||||
* the mode is {@link SecurityMode#ifpossible}.
|
* the mode is {@link SecurityMode#required}.
|
||||||
*
|
*
|
||||||
* @return the security mode.
|
* @return the security mode.
|
||||||
*/
|
*/
|
||||||
|
@ -688,6 +630,8 @@ public abstract class ConnectionConfiguration {
|
||||||
public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
|
public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
|
||||||
private SecurityMode securityMode = SecurityMode.required;
|
private SecurityMode securityMode = SecurityMode.required;
|
||||||
private DnssecMode dnssecMode = DnssecMode.disabled;
|
private DnssecMode dnssecMode = DnssecMode.disabled;
|
||||||
|
private KeyManager[] keyManagers;
|
||||||
|
private SecureRandom sslContextSecureRandom;
|
||||||
private String keystorePath;
|
private String keystorePath;
|
||||||
private String keystoreType;
|
private String keystoreType;
|
||||||
private String pkcs11Library = "pkcs11.config";
|
private String pkcs11Library = "pkcs11.config";
|
||||||
|
@ -942,7 +886,12 @@ public abstract class ConnectionConfiguration {
|
||||||
* @param callbackHandler to obtain information, such as the password or
|
* @param callbackHandler to obtain information, such as the password or
|
||||||
* principal information during the SASL authentication.
|
* principal information during the SASL authentication.
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
|
* @deprecated set a callback-handler aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||||
|
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||||
|
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Remove in Smack 4.6.
|
||||||
|
@Deprecated
|
||||||
public B setCallbackHandler(CallbackHandler callbackHandler) {
|
public B setCallbackHandler(CallbackHandler callbackHandler) {
|
||||||
this.callbackHandler = callbackHandler;
|
this.callbackHandler = callbackHandler;
|
||||||
return getThis();
|
return getThis();
|
||||||
|
@ -960,7 +909,7 @@ public abstract class ConnectionConfiguration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the TLS security mode used when making the connection. By default,
|
* Sets the TLS security mode used when making the connection. By default,
|
||||||
* the mode is {@link SecurityMode#ifpossible}.
|
* the mode is {@link SecurityMode#required}.
|
||||||
*
|
*
|
||||||
* @param securityMode the security mode.
|
* @param securityMode the security mode.
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
|
@ -970,6 +919,47 @@ public abstract class ConnectionConfiguration {
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection.
|
||||||
|
*
|
||||||
|
* @param keyManagers an array of {@link KeyManager}s to initialize the {@link SSLContext} with.
|
||||||
|
* @return a reference to this builder.
|
||||||
|
* @since 4.4.5
|
||||||
|
*/
|
||||||
|
public B setKeyManagers(KeyManager[] keyManagers) {
|
||||||
|
this.keyManagers = keyManagers;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link KeyManager}s to initialize the {@link SSLContext} used by Smack to establish the XMPP connection.
|
||||||
|
*
|
||||||
|
* @param keyManager the {@link KeyManager}s to initialize the {@link SSLContext} with.
|
||||||
|
* @return a reference to this builder.
|
||||||
|
* @see #setKeyManagers(KeyManager[])
|
||||||
|
* @since 4.4.5
|
||||||
|
*/
|
||||||
|
public B setKeyManager(KeyManager keyManager) {
|
||||||
|
KeyManager[] keyManagers = new KeyManager[] { keyManager };
|
||||||
|
return setKeyManagers(keyManagers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link SecureRandom} used to initialize the {@link SSLContext} used by Smack to establish the XMPP
|
||||||
|
* connection. Note that you usually do not need (nor want) to set this. Because if the {@link SecureRandom} is
|
||||||
|
* not explicitly set, Smack will initialize the {@link SSLContext} with <code>null</code> as
|
||||||
|
* {@link SecureRandom} argument. And all sane {@link SSLContext} implementations will then select a safe secure
|
||||||
|
* random source by default.
|
||||||
|
*
|
||||||
|
* @param secureRandom the {@link SecureRandom} to initialize the {@link SSLContext} with.
|
||||||
|
* @return a reference to this builder.
|
||||||
|
* @since 4.4.5
|
||||||
|
*/
|
||||||
|
public B setSslContextSecureRandom(SecureRandom secureRandom) {
|
||||||
|
this.sslContextSecureRandom = secureRandom;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the path to the keystore file. The key store file contains the
|
* Sets the path to the keystore file. The key store file contains the
|
||||||
* certificates that may be used to authenticate the client to the server,
|
* certificates that may be used to authenticate the client to the server,
|
||||||
|
@ -977,7 +967,12 @@ public abstract class ConnectionConfiguration {
|
||||||
*
|
*
|
||||||
* @param keystorePath the path to the keystore file.
|
* @param keystorePath the path to the keystore file.
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
|
* @deprecated set a keystore-path aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||||
|
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||||
|
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Remove in Smack 4.6.
|
||||||
|
@Deprecated
|
||||||
public B setKeystorePath(String keystorePath) {
|
public B setKeystorePath(String keystorePath) {
|
||||||
this.keystorePath = keystorePath;
|
this.keystorePath = keystorePath;
|
||||||
return getThis();
|
return getThis();
|
||||||
|
@ -988,7 +983,12 @@ public abstract class ConnectionConfiguration {
|
||||||
*
|
*
|
||||||
* @param keystoreType the keystore type.
|
* @param keystoreType the keystore type.
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
|
* @deprecated set a key-type aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||||
|
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||||
|
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Remove in Smack 4.6.
|
||||||
|
@Deprecated
|
||||||
public B setKeystoreType(String keystoreType) {
|
public B setKeystoreType(String keystoreType) {
|
||||||
this.keystoreType = keystoreType;
|
this.keystoreType = keystoreType;
|
||||||
return getThis();
|
return getThis();
|
||||||
|
@ -1000,7 +1000,12 @@ public abstract class ConnectionConfiguration {
|
||||||
*
|
*
|
||||||
* @param pkcs11Library the path to the PKCS11 library file.
|
* @param pkcs11Library the path to the PKCS11 library file.
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
|
* @deprecated set a PKCS11-library aware {@link KeyManager} via {@link #setKeyManager(KeyManager)} or
|
||||||
|
* {@link #setKeyManagers(KeyManager[])}, created by
|
||||||
|
* {@link #getKeyManagersFrom(String, String, CallbackHandler, String)}, instead.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Remove in Smack 4.6.
|
||||||
|
@Deprecated
|
||||||
public B setPKCS11Library(String pkcs11Library) {
|
public B setPKCS11Library(String pkcs11Library) {
|
||||||
this.pkcs11Library = pkcs11Library;
|
this.pkcs11Library = pkcs11Library;
|
||||||
return getThis();
|
return getThis();
|
||||||
|
@ -1276,5 +1281,77 @@ public abstract class ConnectionConfiguration {
|
||||||
public abstract C build();
|
public abstract C build();
|
||||||
|
|
||||||
protected abstract B getThis();
|
protected abstract B getThis();
|
||||||
|
|
||||||
|
public static KeyManager[] getKeyManagersFrom(String keystoreType, String keystorePath,
|
||||||
|
CallbackHandler callbackHandler, String pkcs11Library)
|
||||||
|
throws NoSuchMethodException, SecurityException, ClassNotFoundException, KeyStoreException,
|
||||||
|
NoSuchProviderException, NoSuchAlgorithmException, CertificateException, IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, UnsupportedCallbackException, UnrecoverableKeyException {
|
||||||
|
KeyManager[] keyManagers = null;
|
||||||
|
KeyStore ks = null;
|
||||||
|
PasswordCallback pcb = null;
|
||||||
|
|
||||||
|
if ("PKCS11".equals(keystoreType)) {
|
||||||
|
Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class);
|
||||||
|
String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library;
|
||||||
|
ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8));
|
||||||
|
Provider p = (Provider) c.newInstance(config);
|
||||||
|
Security.addProvider(p);
|
||||||
|
ks = KeyStore.getInstance("PKCS11", p);
|
||||||
|
pcb = new PasswordCallback("PKCS11 Password: ", false);
|
||||||
|
callbackHandler.handle(new Callback[] { pcb });
|
||||||
|
ks.load(null, pcb.getPassword());
|
||||||
|
} else if ("Apple".equals(keystoreType)) {
|
||||||
|
ks = KeyStore.getInstance("KeychainStore", "Apple");
|
||||||
|
ks.load(null, null);
|
||||||
|
// pcb = new PasswordCallback("Apple Keychain",false);
|
||||||
|
// pcb.setPassword(null);
|
||||||
|
} else if (keystoreType != null) {
|
||||||
|
ks = KeyStore.getInstance(keystoreType);
|
||||||
|
if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) {
|
||||||
|
pcb = new PasswordCallback("Keystore Password: ", false);
|
||||||
|
callbackHandler.handle(new Callback[] { pcb });
|
||||||
|
ks.load(new FileInputStream(keystorePath), pcb.getPassword());
|
||||||
|
} else {
|
||||||
|
InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
||||||
|
try {
|
||||||
|
// Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous
|
||||||
|
// 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702
|
||||||
|
char[] password = "changeit".toCharArray();
|
||||||
|
try {
|
||||||
|
ks.load(stream, password);
|
||||||
|
} finally {
|
||||||
|
CloseableUtil.maybeClose(stream);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e);
|
||||||
|
|
||||||
|
ks = KeyStore.getInstance("jks");
|
||||||
|
// Open the stream again, so that we read it from the beginning.
|
||||||
|
stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
|
||||||
|
try {
|
||||||
|
ks.load(stream, null);
|
||||||
|
} finally {
|
||||||
|
CloseableUtil.maybeClose(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ks != null) {
|
||||||
|
String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
|
||||||
|
if (kmf != null) {
|
||||||
|
if (pcb == null) {
|
||||||
|
kmf.init(ks, null);
|
||||||
|
} else {
|
||||||
|
kmf.init(ks, pcb.getPassword());
|
||||||
|
pcb.clearPassword();
|
||||||
|
}
|
||||||
|
keyManagers = kmf.getKeyManagers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyManagers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,16 +170,20 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean callbacksInvoked;
|
||||||
|
|
||||||
protected final synchronized void maybeInvokeCallbacks() {
|
protected final synchronized void maybeInvokeCallbacks() {
|
||||||
if (cancelled) {
|
if (cancelled || callbacksInvoked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((result != null || exception != null) && completionCallback != null) {
|
if ((result != null || exception != null) && completionCallback != null) {
|
||||||
|
callbacksInvoked = true;
|
||||||
completionCallback.accept(this);
|
completionCallback.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != null && successCallback != null) {
|
if (result != null && successCallback != null) {
|
||||||
|
callbacksInvoked = true;
|
||||||
AbstractXMPPConnection.asyncGo(new Runnable() {
|
AbstractXMPPConnection.asyncGo(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -188,6 +192,7 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (exception != null && exceptionCallback != null) {
|
else if (exception != null && exceptionCallback != null) {
|
||||||
|
callbacksInvoked = true;
|
||||||
AbstractXMPPConnection.asyncGo(new Runnable() {
|
AbstractXMPPConnection.asyncGo(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -221,12 +221,11 @@ public class SmackReactor {
|
||||||
selectWait = 0;
|
selectWait = 0;
|
||||||
} else {
|
} else {
|
||||||
selectWait = nextScheduledAction.getTimeToDueMillis();
|
selectWait = nextScheduledAction.getTimeToDueMillis();
|
||||||
}
|
if (selectWait <= 0) {
|
||||||
|
|
||||||
if (selectWait < 0) {
|
|
||||||
// A scheduled action was just released and became ready to execute.
|
// A scheduled action was just released and became ready to execute.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Before we call select, we handle the pending the interest Ops. This will not block since no other
|
// Before we call select, we handle the pending the interest Ops. This will not block since no other
|
||||||
// thread is currently in select() at this time.
|
// thread is currently in select() at this time.
|
||||||
|
|
|
@ -120,6 +120,16 @@ public abstract class XMPPException extends Exception {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the stanza associated with this exception.
|
||||||
|
*
|
||||||
|
* @return the stanza from which this exception was created or {@code null} if the exception is not from a
|
||||||
|
* stanza.
|
||||||
|
*/
|
||||||
|
public Stanza getStanza() {
|
||||||
|
return stanza;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the request which triggered the error response causing this exception.
|
* Get the request which triggered the error response causing this exception.
|
||||||
*
|
*
|
||||||
|
|
|
@ -186,6 +186,10 @@ public class StateDescriptorGraph {
|
||||||
|
|
||||||
for (GraphVertex<Class<? extends StateDescriptor>> successor : sortedSuccessors) {
|
for (GraphVertex<Class<? extends StateDescriptor>> successor : sortedSuccessors) {
|
||||||
GraphVertex<StateDescriptor> successorVertex = successorStateDescriptors.get(successor.element);
|
GraphVertex<StateDescriptor> successorVertex = successorStateDescriptors.get(successor.element);
|
||||||
|
if (successorVertex == null) {
|
||||||
|
// The successor does not exist, probably because its module was not enabled.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
node.addOutgoingEdge(successorVertex);
|
node.addOutgoingEdge(successorVertex);
|
||||||
|
|
||||||
// Recurse further.
|
// Recurse further.
|
||||||
|
|
|
@ -202,7 +202,7 @@ public abstract class IQ extends Stanza implements IqView {
|
||||||
|
|
||||||
// Add the query section if there is one.
|
// Add the query section if there is one.
|
||||||
IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(
|
IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(
|
||||||
new IQChildElementXmlStringBuilder(this));
|
new IQChildElementXmlStringBuilder(getChildElementName(), getChildElementNamespace(), null, xml.getXmlEnvironment()));
|
||||||
// TOOD: Document the cases where iqChildElement is null but childElementName not. And if there are none, change
|
// TOOD: Document the cases where iqChildElement is null but childElementName not. And if there are none, change
|
||||||
// the logic.
|
// the logic.
|
||||||
if (iqChildElement == null) {
|
if (iqChildElement == null) {
|
||||||
|
@ -399,17 +399,16 @@ public abstract class IQ extends Stanza implements IqView {
|
||||||
|
|
||||||
private boolean isEmptyElement;
|
private boolean isEmptyElement;
|
||||||
|
|
||||||
private IQChildElementXmlStringBuilder(IQ iq) {
|
public IQChildElementXmlStringBuilder(ExtensionElement extensionElement,
|
||||||
this(iq.getChildElementName(), iq.getChildElementNamespace());
|
XmlEnvironment enclosingXmlEnvironment) {
|
||||||
|
this(extensionElement.getElementName(), extensionElement.getNamespace(), extensionElement.getLanguage(),
|
||||||
|
enclosingXmlEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQChildElementXmlStringBuilder(ExtensionElement pe) {
|
private IQChildElementXmlStringBuilder(String elementName, String xmlNs, String xmlLang,
|
||||||
this(pe.getElementName(), pe.getNamespace());
|
XmlEnvironment enclosingXmlEnvironment) {
|
||||||
}
|
super(elementName, xmlNs, xmlLang, enclosingXmlEnvironment);
|
||||||
|
this.element = elementName;
|
||||||
private IQChildElementXmlStringBuilder(String element, String namespace) {
|
|
||||||
prelude(element, namespace);
|
|
||||||
this.element = element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEmptyElement() {
|
public void setEmptyElement() {
|
||||||
|
|
|
@ -184,6 +184,19 @@ public abstract class StanzaBuilder<B extends StanzaBuilder<B>> implements Stanz
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final B removeExtension(String elementName, String namespace) {
|
||||||
|
QName key = new QName(namespace, elementName);
|
||||||
|
extensionElements.remove(key);
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final B removeExtension(ExtensionElement extension) {
|
||||||
|
QName key = extension.getQName();
|
||||||
|
List<XmlElement> list = extensionElements.getAll(key);
|
||||||
|
list.remove(extension);
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract Stanza build();
|
public abstract Stanza build();
|
||||||
|
|
||||||
public abstract B getThis();
|
public abstract B getThis();
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class ExceptionThrowingCallbackWithHint extends ExceptionThrowingCallback
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleUnparsableStanza(UnparseableStanza packetData) throws IOException {
|
public void handleUnparsableStanza(UnparseableStanza packetData) throws IOException {
|
||||||
LOGGER.warning("Parsing exception encountered."
|
LOGGER.warning("Parsing exception \"" + packetData.getParsingException().getMessage() + "\" encountered."
|
||||||
+ " This exception will be re-thrown, leading to a disconnect."
|
+ " This exception will be re-thrown, leading to a disconnect."
|
||||||
+ " You can change this behavior by setting a different ParsingExceptionCallback using setParsingExceptionCallback()."
|
+ " You can change this behavior by setting a different ParsingExceptionCallback using setParsingExceptionCallback()."
|
||||||
+ " More information an be found in AbstractXMPPConnection's javadoc.");
|
+ " More information an be found in AbstractXMPPConnection's javadoc.");
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Deprecation Notice:</b> This class is deprecated, use {@link IQProvider} instead.
|
* <b>Deprecation Notice:</b> This class is deprecated, use {@link IqProvider} instead.
|
||||||
* </p>
|
* </p>
|
||||||
* An abstract class for parsing custom IQ packets. Each IQProvider must be registered with
|
* An abstract class for parsing custom IQ packets. Each IQProvider must be registered with
|
||||||
* the ProviderManager class for it to be used. Every implementation of this
|
* the ProviderManager class for it to be used. Every implementation of this
|
||||||
|
|
|
@ -60,4 +60,8 @@ public class SASLAnonymous extends SASLMechanism {
|
||||||
// SASL Anonymous is always successful :)
|
// SASL Anonymous is always successful :)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresPassword() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,4 +89,11 @@ public class CollectionUtil {
|
||||||
}
|
}
|
||||||
return Collections.singletonList(element);
|
return Collections.singletonList(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Set<T> nullSafeUnmodifiableSet(Set<T> set) {
|
||||||
|
if (set == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(set);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2021 Florian Schmaus.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smack.util;
|
||||||
|
|
||||||
|
public final class DoOnce {
|
||||||
|
|
||||||
|
private boolean done;
|
||||||
|
|
||||||
|
public void once(Runnable run) {
|
||||||
|
if (done) return;
|
||||||
|
|
||||||
|
done = true;
|
||||||
|
run.run();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2019 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2019-2021 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.
|
||||||
|
@ -76,9 +76,9 @@ public class PacketParserUtils {
|
||||||
return getParserFor(new StringReader(stanza));
|
return getParserFor(new StringReader(stanza));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static XmlPullParser getParserFor(InputStream inputStream) throws XmlPullParserException {
|
public static XmlPullParser getParserFor(InputStream inputStream) throws XmlPullParserException, IOException {
|
||||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||||
return SmackXmlParser.newXmlParser(inputStreamReader);
|
return getParserFor(inputStreamReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static XmlPullParser getParserFor(Reader reader) throws XmlPullParserException, IOException {
|
public static XmlPullParser getParserFor(Reader reader) throws XmlPullParserException, IOException {
|
||||||
|
@ -501,6 +501,23 @@ public class PacketParserUtils {
|
||||||
return parseIQ(parser, null);
|
return parseIQ(parser, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IqData parseIqData(XmlPullParser parser) throws XmppStringprepException {
|
||||||
|
final String id = parser.getAttributeValue("", "id");
|
||||||
|
IqData iqData = StanzaBuilder.buildIqData(id);
|
||||||
|
|
||||||
|
final Jid to = ParserUtils.getJidAttribute(parser, "to");
|
||||||
|
iqData.to(to);
|
||||||
|
|
||||||
|
final Jid from = ParserUtils.getJidAttribute(parser, "from");
|
||||||
|
iqData.from(from);
|
||||||
|
|
||||||
|
String typeString = parser.getAttributeValue("", "type");
|
||||||
|
final IQ.Type type = IQ.Type.fromString(typeString);
|
||||||
|
iqData.ofType(type);
|
||||||
|
|
||||||
|
return iqData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an IQ packet.
|
* Parses an IQ packet.
|
||||||
*
|
*
|
||||||
|
@ -518,18 +535,7 @@ public class PacketParserUtils {
|
||||||
XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
|
XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
|
||||||
IQ iqPacket = null;
|
IQ iqPacket = null;
|
||||||
StanzaError error = null;
|
StanzaError error = null;
|
||||||
|
IqData iqData = parseIqData(parser);
|
||||||
final String id = parser.getAttributeValue("", "id");
|
|
||||||
IqData iqData = StanzaBuilder.buildIqData(id);
|
|
||||||
|
|
||||||
final Jid to = ParserUtils.getJidAttribute(parser, "to");
|
|
||||||
iqData.to(to);
|
|
||||||
|
|
||||||
final Jid from = ParserUtils.getJidAttribute(parser, "from");
|
|
||||||
iqData.from(from);
|
|
||||||
|
|
||||||
final IQ.Type type = IQ.Type.fromString(parser.getAttributeValue("", "type"));
|
|
||||||
iqData.ofType(type);
|
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event eventType = parser.next();
|
XmlPullParser.Event eventType = parser.next();
|
||||||
|
@ -549,8 +555,8 @@ public class PacketParserUtils {
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
iqPacket = provider.parse(parser, iqData, outerXmlEnvironment);
|
iqPacket = provider.parse(parser, iqData, outerXmlEnvironment);
|
||||||
}
|
}
|
||||||
// Note that if we reach this code, it is guranteed that the result IQ contained a child element
|
// Note that if we reach this code, it is guaranteed that the result IQ contained a child element
|
||||||
// (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_ELEMENT first.
|
// (RFC 6120 § 8.2.3 6) because otherwise we would have reached the END_ELEMENT first.
|
||||||
else {
|
else {
|
||||||
// No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
|
// No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
|
||||||
// so that the content of the IQ can be examined later on
|
// so that the content of the IQ can be examined later on
|
||||||
|
@ -571,7 +577,7 @@ public class PacketParserUtils {
|
||||||
}
|
}
|
||||||
// Decide what to do when an IQ packet was not understood
|
// Decide what to do when an IQ packet was not understood
|
||||||
if (iqPacket == null) {
|
if (iqPacket == null) {
|
||||||
switch (type) {
|
switch (iqData.getType()) {
|
||||||
case error:
|
case error:
|
||||||
// If an IQ packet wasn't created above, create an empty error IQ packet.
|
// If an IQ packet wasn't created above, create an empty error IQ packet.
|
||||||
iqPacket = new ErrorIQ(error);
|
iqPacket = new ErrorIQ(error);
|
||||||
|
@ -585,10 +591,10 @@ public class PacketParserUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set basic values on the iq packet.
|
// Set basic values on the iq packet.
|
||||||
iqPacket.setStanzaId(id);
|
iqPacket.setStanzaId(iqData.getStanzaId());
|
||||||
iqPacket.setTo(to);
|
iqPacket.setTo(iqData.getTo());
|
||||||
iqPacket.setFrom(from);
|
iqPacket.setFrom(iqData.getFrom());
|
||||||
iqPacket.setType(type);
|
iqPacket.setType(iqData.getType());
|
||||||
iqPacket.setError(error);
|
iqPacket.setError(error);
|
||||||
|
|
||||||
return iqPacket;
|
return iqPacket;
|
||||||
|
|
|
@ -78,6 +78,7 @@ public class ParserUtils {
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
XmlPullParser.Event event = parser.getEventType();
|
XmlPullParser.Event event = parser.getEventType();
|
||||||
while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) {
|
while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) {
|
||||||
|
assert event != XmlPullParser.Event.END_DOCUMENT;
|
||||||
event = parser.next();
|
event = parser.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2016-2020 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2016-2021 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.
|
||||||
|
@ -20,6 +20,7 @@ package org.jivesoftware.smack.util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -605,4 +606,13 @@ public class StringUtils {
|
||||||
String[] lines = input.split(PORTABLE_NEWLINE_REGEX);
|
String[] lines = input.split(PORTABLE_NEWLINE_REGEX);
|
||||||
return Arrays.asList(lines);
|
return Arrays.asList(lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> toStrings(Collection<? extends CharSequence> charSequences) {
|
||||||
|
List<String> res = new ArrayList<>(charSequences.size());
|
||||||
|
for (CharSequence cs : charSequences) {
|
||||||
|
String string = cs.toString();
|
||||||
|
res.add(string);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2014-2020 Florian Schmaus
|
* Copyright 2014-2021 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.
|
||||||
|
@ -52,11 +52,13 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmlStringBuilder(XmlElement element, XmlEnvironment enclosingXmlEnvironment) {
|
public XmlStringBuilder(XmlElement element, XmlEnvironment enclosingXmlEnvironment) {
|
||||||
sb = new LazyStringBuilder();
|
this(element.getElementName(), element.getNamespace(), element.getLanguage(), enclosingXmlEnvironment);
|
||||||
halfOpenElement(element);
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder(String elementName, String xmlNs, String xmlLang, XmlEnvironment enclosingXmlEnvironment) {
|
||||||
|
sb = new LazyStringBuilder();
|
||||||
|
halfOpenElement(elementName);
|
||||||
|
|
||||||
String xmlNs = element.getNamespace();
|
|
||||||
String xmlLang = element.getLanguage();
|
|
||||||
if (enclosingXmlEnvironment == null) {
|
if (enclosingXmlEnvironment == null) {
|
||||||
xmlnsAttribute(xmlNs);
|
xmlnsAttribute(xmlNs);
|
||||||
xmllangAttribute(xmlLang);
|
xmllangAttribute(xmlLang);
|
||||||
|
@ -286,8 +288,7 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
|
|
||||||
public XmlStringBuilder attribute(String name, Enum<?> value) {
|
public XmlStringBuilder attribute(String name, Enum<?> value) {
|
||||||
assert value != null;
|
assert value != null;
|
||||||
// TODO: Should use toString() instead of name().
|
attribute(name, value.toString());
|
||||||
attribute(name, value.name());
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2019-2021 Florian Schmaus
|
* Copyright 2019-2022 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.
|
||||||
|
@ -28,9 +28,13 @@ import java.util.function.Predicate;
|
||||||
import javax.xml.namespace.QName;
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Element;
|
import org.jivesoftware.smack.packet.Element;
|
||||||
|
import org.jivesoftware.smack.packet.IqData;
|
||||||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
|
import org.jivesoftware.smack.provider.AbstractProvider;
|
||||||
|
import org.jivesoftware.smack.provider.IqProvider;
|
||||||
import org.jivesoftware.smack.provider.Provider;
|
import org.jivesoftware.smack.provider.Provider;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserFactory;
|
import org.jivesoftware.smack.xml.XmlPullParserFactory;
|
||||||
|
@ -58,41 +62,56 @@ public class SmackTestUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Element, P extends Provider<E>> E parse(CharSequence xml, Class<P> providerClass, XmlPullParserKind parserKind)
|
public static <E extends Element, P extends AbstractProvider<E>> E parse(CharSequence xml, Class<P> providerClass, XmlPullParserKind parserKind)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
P provider = providerClassToProvider(providerClass);
|
P provider = providerClassToProvider(providerClass);
|
||||||
return parse(xml, provider, parserKind);
|
return parse(xml, provider, parserKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Element, P extends Provider<E>> E parse(InputStream inputStream, Class<P> providerClass, XmlPullParserKind parserKind)
|
public static <E extends Element, P extends AbstractProvider<E>> E parse(InputStream inputStream, Class<P> providerClass, XmlPullParserKind parserKind)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
P provider = providerClassToProvider(providerClass);
|
P provider = providerClassToProvider(providerClass);
|
||||||
return parse(inputStream, provider, parserKind);
|
return parse(inputStream, provider, parserKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Element, P extends Provider<E>> E parse(Reader reader, Class<P> providerClass, XmlPullParserKind parserKind)
|
public static <E extends Element, P extends AbstractProvider<E>> E parse(Reader reader, Class<P> providerClass, XmlPullParserKind parserKind)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
P provider = providerClassToProvider(providerClass);
|
P provider = providerClassToProvider(providerClass);
|
||||||
return parse(reader, provider, parserKind);
|
return parse(reader, provider, parserKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Element> E parse(CharSequence xml, Provider<E> provider, XmlPullParserKind parserKind)
|
public static <E extends Element> E parse(CharSequence xml, AbstractProvider<E> provider, XmlPullParserKind parserKind)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
String xmlString = xml.toString();
|
String xmlString = xml.toString();
|
||||||
Reader reader = new StringReader(xmlString);
|
Reader reader = new StringReader(xmlString);
|
||||||
return parse(reader, provider, parserKind);
|
return parse(reader, provider, parserKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Element> E parse(InputStream inputStream, Provider<E> provider, XmlPullParserKind parserKind)
|
public static <E extends Element> E parse(InputStream inputStream, AbstractProvider<E> provider, XmlPullParserKind parserKind)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||||
return parse(inputStreamReader, provider, parserKind);
|
return parse(inputStreamReader, provider, parserKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends Element> E parse(Reader reader, Provider<E> provider, XmlPullParserKind parserKind)
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <E extends Element> E parse(Reader reader, AbstractProvider<E> abstractProvider, XmlPullParserKind parserKind)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
XmlPullParser parser = getParserFor(reader, parserKind);
|
XmlPullParser parser = getParserFor(reader, parserKind);
|
||||||
E element = provider.parse(parser);
|
|
||||||
|
final E element;
|
||||||
|
if (abstractProvider instanceof Provider) {
|
||||||
|
Provider<E> provider = (Provider<E>) abstractProvider;
|
||||||
|
element = provider.parse(parser);
|
||||||
|
} else if (abstractProvider instanceof IqProvider) {
|
||||||
|
IqData iqData = PacketParserUtils.parseIqData(parser);
|
||||||
|
parser.next();
|
||||||
|
ParserUtils.forwardToStartElement(parser);
|
||||||
|
IqProvider<?> iqProvider = (IqProvider<?>) abstractProvider;
|
||||||
|
element = (E) iqProvider.parse(parser, iqData);
|
||||||
|
} else {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +151,7 @@ public class SmackTestUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <E extends Element, P extends Provider<E>> P providerClassToProvider(Class<P> providerClass) {
|
private static <E extends Element, P extends AbstractProvider<E>> P providerClassToProvider(Class<P> providerClass) {
|
||||||
P provider;
|
P provider;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -34,7 +34,13 @@ import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -419,38 +425,11 @@ public class EnhancedDebugger extends SmackDebugger {
|
||||||
// Create a special Reader that wraps the main Reader and logs data to the GUI.
|
// Create a special Reader that wraps the main Reader and logs data to the GUI.
|
||||||
ObservableReader debugReader = new ObservableReader(reader);
|
ObservableReader debugReader = new ObservableReader(reader);
|
||||||
readerListener = new ReaderListener() {
|
readerListener = new ReaderListener() {
|
||||||
@Override
|
private final PriorityBlockingQueue<String> buffer = new PriorityBlockingQueue<>();
|
||||||
public void read(final String str) {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER &&
|
|
||||||
!EnhancedDebuggerWindow.getInstance().isVisible()) {
|
|
||||||
// Do not add content if the parent is not visible
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = str.lastIndexOf(">");
|
@Override
|
||||||
if (index != -1) {
|
public void read(final String string) {
|
||||||
if (receivedText.getLineCount() >= EnhancedDebuggerWindow.MAX_TABLE_ROWS) {
|
addBatched(string, buffer, receivedText);
|
||||||
try {
|
|
||||||
receivedText.replaceRange("", 0, receivedText.getLineEndOffset(0));
|
|
||||||
}
|
|
||||||
catch (BadLocationException e) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
receivedText.append(str.substring(0, index + 1));
|
|
||||||
receivedText.append(NEWLINE);
|
|
||||||
if (str.length() > index) {
|
|
||||||
receivedText.append(str.substring(index + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
receivedText.append(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
debugReader.addReaderListener(readerListener);
|
debugReader.addReaderListener(readerListener);
|
||||||
|
@ -458,34 +437,11 @@ public class EnhancedDebugger extends SmackDebugger {
|
||||||
// Create a special Writer that wraps the main Writer and logs data to the GUI.
|
// Create a special Writer that wraps the main Writer and logs data to the GUI.
|
||||||
ObservableWriter debugWriter = new ObservableWriter(writer);
|
ObservableWriter debugWriter = new ObservableWriter(writer);
|
||||||
writerListener = new WriterListener() {
|
writerListener = new WriterListener() {
|
||||||
|
private final PriorityBlockingQueue<String> buffer = new PriorityBlockingQueue<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final String str) {
|
public void write(final String string) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
addBatched(string, buffer, sentText);
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER &&
|
|
||||||
!EnhancedDebuggerWindow.getInstance().isVisible()) {
|
|
||||||
// Do not add content if the parent is not visible
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sentText.getLineCount() >= EnhancedDebuggerWindow.MAX_TABLE_ROWS) {
|
|
||||||
try {
|
|
||||||
sentText.replaceRange("", 0, sentText.getLineEndOffset(0));
|
|
||||||
}
|
|
||||||
catch (BadLocationException e) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: " + EnhancedDebuggerWindow.MAX_TABLE_ROWS, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sentText.append(str);
|
|
||||||
if (str.endsWith(">")) {
|
|
||||||
sentText.append(NEWLINE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
debugWriter.addWriterListener(writerListener);
|
debugWriter.addWriterListener(writerListener);
|
||||||
|
@ -497,6 +453,50 @@ public class EnhancedDebugger extends SmackDebugger {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addBatched(String string, PriorityBlockingQueue<String> buffer, JTextArea jTextArea) {
|
||||||
|
buffer.add(string);
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
List<String> linesToAdd = new ArrayList<>();
|
||||||
|
String data;
|
||||||
|
Instant start = Instant.now();
|
||||||
|
try {
|
||||||
|
// To reduce overhead/increase performance, try to process up to a certain amount of lines at the
|
||||||
|
// same time, when they arrive in rapid succession.
|
||||||
|
while (linesToAdd.size() < 50
|
||||||
|
&& Duration.between(start, Instant.now()).compareTo(Duration.ofMillis(100)) < 0
|
||||||
|
&& (data = buffer.poll(10, TimeUnit.MILLISECONDS)) != null) {
|
||||||
|
linesToAdd.add(data);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOGGER.log(Level.FINER, "Interrupted wait-for-poll in addBatched(). Process all data now.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linesToAdd.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnhancedDebuggerWindow.PERSISTED_DEBUGGER && !EnhancedDebuggerWindow.getInstance().isVisible()) {
|
||||||
|
// Do not add content if the parent is not visible
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete lines from the top, if lines to be added will exceed the maximum.
|
||||||
|
int linesToDelete = jTextArea.getLineCount() + linesToAdd.size() - EnhancedDebuggerWindow.MAX_TABLE_ROWS;
|
||||||
|
if (linesToDelete > 0) {
|
||||||
|
try {
|
||||||
|
jTextArea.replaceRange("", 0, jTextArea.getLineEndOffset(linesToDelete - 1));
|
||||||
|
} catch (BadLocationException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error with line offset, MAX_TABLE_ROWS is set too low: "
|
||||||
|
+ EnhancedDebuggerWindow.MAX_TABLE_ROWS, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new content.
|
||||||
|
jTextArea.append(String.join(NEWLINE, linesToAdd));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void addAdhocPacketPanel() {
|
private void addAdhocPacketPanel() {
|
||||||
// Create UI elements for sending ad-hoc messages.
|
// Create UI elements for sending ad-hoc messages.
|
||||||
final JTextArea adhocMessages = new JTextArea();
|
final JTextArea adhocMessages = new JTextArea();
|
||||||
|
|
|
@ -154,8 +154,7 @@ public final class CarbonManager extends Manager {
|
||||||
// because we also reset in authenticated() if the stream got not resumed, but for maximum correctness,
|
// because we also reset in authenticated() if the stream got not resumed, but for maximum correctness,
|
||||||
// also reset here.
|
// also reset here.
|
||||||
enabled_state = false;
|
enabled_state = false;
|
||||||
boolean removed = connection().removeSyncStanzaListener(carbonsListener);
|
connection().removeSyncStanzaListener(carbonsListener);
|
||||||
assert removed;
|
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void authenticated(XMPPConnection connection, boolean resumed) {
|
public void authenticated(XMPPConnection connection, boolean resumed) {
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2022 Micha Kurvers
|
||||||
|
*
|
||||||
|
* 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.httpfileupload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.httpfileupload.element.Slot;
|
||||||
|
/**
|
||||||
|
* An exception class to provide additional information in case of exceptions during file uploading.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AbstractHttpUploadException extends IOException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final long fileSize;
|
||||||
|
private final Slot slot;
|
||||||
|
|
||||||
|
protected AbstractHttpUploadException(long fileSize, Slot slot, String message) {
|
||||||
|
this(fileSize, slot, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractHttpUploadException(long fileSize, Slot slot, String message, Throwable wrappedThrowable) {
|
||||||
|
super(message, wrappedThrowable);
|
||||||
|
this.fileSize = fileSize;
|
||||||
|
this.slot = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFileSize() {
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getPutUrl() {
|
||||||
|
return slot.getPutUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Slot getSlot() {
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when http response returned after upload is not 200.
|
||||||
|
*/
|
||||||
|
public static class HttpUploadErrorException extends AbstractHttpUploadException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8494356028399474995L;
|
||||||
|
private final int httpStatus;
|
||||||
|
private final String responseMsg;
|
||||||
|
|
||||||
|
public HttpUploadErrorException(int httpStatus, String responseMsg, long fileSize, Slot slot) {
|
||||||
|
super(fileSize, slot, "Error response " + httpStatus + " from server during file upload: "
|
||||||
|
+ responseMsg + ", file size: " + fileSize + ", put URL: "
|
||||||
|
+ slot.getPutUrl());
|
||||||
|
this.httpStatus = httpStatus;
|
||||||
|
this.responseMsg = responseMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHttpStatus() {
|
||||||
|
return httpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResponseMsg() {
|
||||||
|
return responseMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when an unexpected exception occurred during the upload.
|
||||||
|
*/
|
||||||
|
public static class HttpUploadIOException extends AbstractHttpUploadException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 5940866318073349451L;
|
||||||
|
private final IOException wrappedIOException;
|
||||||
|
|
||||||
|
public HttpUploadIOException(long fileSize, Slot slot, IOException cause) {
|
||||||
|
super(fileSize, slot, "Unexpected error occurred during file upload, file size: " + fileSize
|
||||||
|
+ ", put Url: " + slot.getPutUrl(), cause);
|
||||||
|
this.wrappedIOException = cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOException getCausingIOException() {
|
||||||
|
return this.wrappedIOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,6 +49,8 @@ import org.jivesoftware.smack.proxy.ProxyInfo;
|
||||||
|
|
||||||
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.httpfileupload.AbstractHttpUploadException.HttpUploadErrorException;
|
||||||
|
import org.jivesoftware.smackx.httpfileupload.AbstractHttpUploadException.HttpUploadIOException;
|
||||||
import org.jivesoftware.smackx.httpfileupload.UploadService.Version;
|
import org.jivesoftware.smackx.httpfileupload.UploadService.Version;
|
||||||
import org.jivesoftware.smackx.httpfileupload.element.Slot;
|
import org.jivesoftware.smackx.httpfileupload.element.Slot;
|
||||||
import org.jivesoftware.smackx.httpfileupload.element.SlotRequest;
|
import org.jivesoftware.smackx.httpfileupload.element.SlotRequest;
|
||||||
|
@ -494,11 +496,12 @@ public final class HttpFileUploadManager extends Manager {
|
||||||
case HttpURLConnection.HTTP_NO_CONTENT:
|
case HttpURLConnection.HTTP_NO_CONTENT:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IOException("Error response " + status + " from server during file upload: "
|
throw new HttpUploadErrorException(status, urlConnection.getResponseMessage(), fileSize, slot);
|
||||||
+ urlConnection.getResponseMessage() + ", file size: " + fileSize + ", put URL: "
|
|
||||||
+ putUrl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new HttpUploadIOException(fileSize, slot, e);
|
||||||
|
}
|
||||||
finally {
|
finally {
|
||||||
urlConnection.disconnect();
|
urlConnection.disconnect();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,15 +47,17 @@ import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.commands.AdHocCommandManager;
|
import org.jivesoftware.smackx.commands.AdHocCommandManager;
|
||||||
import org.jivesoftware.smackx.commands.RemoteCommand;
|
import org.jivesoftware.smackx.commands.RemoteCommand;
|
||||||
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.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
||||||
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
|
import org.jivesoftware.smackx.mam.element.MamElementFactory;
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements;
|
import org.jivesoftware.smackx.mam.element.MamElements;
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
|
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
|
||||||
import org.jivesoftware.smackx.mam.element.MamFinIQ;
|
import org.jivesoftware.smackx.mam.element.MamFinIQ;
|
||||||
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
||||||
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
|
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
|
||||||
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
import org.jivesoftware.smackx.mam.filter.MamResultFilter;
|
import org.jivesoftware.smackx.mam.filter.MamResultFilter;
|
||||||
import org.jivesoftware.smackx.muc.MultiUserChat;
|
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||||
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
||||||
|
@ -225,6 +227,8 @@ public final class MamManager extends Manager {
|
||||||
|
|
||||||
private final AdHocCommandManager adHocCommandManager;
|
private final AdHocCommandManager adHocCommandManager;
|
||||||
|
|
||||||
|
private MamVersion mamVersion = null;
|
||||||
|
|
||||||
private MamManager(XMPPConnection connection, Jid archiveAddress) {
|
private MamManager(XMPPConnection connection, Jid archiveAddress) {
|
||||||
super(connection);
|
super(connection);
|
||||||
this.archiveAddress = archiveAddress;
|
this.archiveAddress = archiveAddress;
|
||||||
|
@ -250,6 +254,52 @@ public final class MamManager extends Manager {
|
||||||
return archiveAddress;
|
return archiveAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the MAM namespace used by this {@link MamManager}. If the archive does not support any MAM namespace
|
||||||
|
* supported by Smack, null is returned.
|
||||||
|
*
|
||||||
|
* @return the MAM namespace used by this manager, null if MAM is not supported
|
||||||
|
* @throws NoResponseException if there was no response from the remote entity.
|
||||||
|
* @throws XMPPErrorException if there was an XMPP error returned.
|
||||||
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
|
*/
|
||||||
|
public String getMamNamespace() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
|
||||||
|
MamVersion mamVersion = getSupportedMamVersionOrNull();
|
||||||
|
return mamVersion == null ? null : mamVersion.getNamespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MamVersion getSupportedMamVersionOrNull() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
|
||||||
|
if (mamVersion != null) {
|
||||||
|
return mamVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiscoverInfo info = serviceDiscoveryManager.discoverInfo(getArchiveAddress());
|
||||||
|
|
||||||
|
// Enum values are always returned the order they are declared (see https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3).
|
||||||
|
// We pick the first version supported by the server.
|
||||||
|
for (MamVersion v : MamVersion.values()) {
|
||||||
|
if (info.containsFeature(v.getNamespace())) {
|
||||||
|
mamVersion = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mamVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MamVersion getSupportedMamVersionOrThrow() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
|
||||||
|
MamVersion mamVersion = getSupportedMamVersionOrNull();
|
||||||
|
if (mamVersion == null) {
|
||||||
|
throw new UnsupportedOperationException("Message Archive Management is not supported by " + getArchiveAddress());
|
||||||
|
}
|
||||||
|
return mamVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MamElementFactory getElementFactory() throws XMPPErrorException, NotConnectedException, NoResponseException, InterruptedException {
|
||||||
|
return getSupportedMamVersionOrThrow().newElementFactory();
|
||||||
|
}
|
||||||
|
|
||||||
public static final class MamQueryArgs {
|
public static final class MamQueryArgs {
|
||||||
private final String node;
|
private final String node;
|
||||||
|
|
||||||
|
@ -275,11 +325,11 @@ public final class MamManager extends Manager {
|
||||||
|
|
||||||
private DataForm dataForm;
|
private DataForm dataForm;
|
||||||
|
|
||||||
DataForm getDataForm() {
|
DataForm getDataForm(MamVersion version) {
|
||||||
if (dataForm != null) {
|
if (dataForm != null) {
|
||||||
return dataForm;
|
return dataForm;
|
||||||
}
|
}
|
||||||
DataForm.Builder dataFormBuilder = getNewMamForm();
|
DataForm.Builder dataFormBuilder = getNewMamForm(version);
|
||||||
dataFormBuilder.addFields(formFields.values());
|
dataFormBuilder.addFields(formFields.values());
|
||||||
dataForm = dataFormBuilder.build();
|
dataForm = dataFormBuilder.build();
|
||||||
return dataForm;
|
return dataForm;
|
||||||
|
@ -472,9 +522,9 @@ public final class MamManager extends Manager {
|
||||||
NotConnectedException, NotLoggedInException, InterruptedException {
|
NotConnectedException, NotLoggedInException, InterruptedException {
|
||||||
String queryId = StringUtils.secureUniqueRandomString();
|
String queryId = StringUtils.secureUniqueRandomString();
|
||||||
String node = mamQueryArgs.node;
|
String node = mamQueryArgs.node;
|
||||||
DataForm dataForm = mamQueryArgs.getDataForm();
|
DataForm dataForm = mamQueryArgs.getDataForm(mamVersion);
|
||||||
|
|
||||||
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, dataForm);
|
MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, dataForm);
|
||||||
mamQueryIQ.setType(IQ.Type.set);
|
mamQueryIQ.setType(IQ.Type.set);
|
||||||
mamQueryIQ.setTo(archiveAddress);
|
mamQueryIQ.setTo(archiveAddress);
|
||||||
|
|
||||||
|
@ -530,7 +580,7 @@ public final class MamManager extends Manager {
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException,
|
throws NoResponseException, XMPPErrorException, NotConnectedException,
|
||||||
InterruptedException, NotLoggedInException {
|
InterruptedException, NotLoggedInException {
|
||||||
String queryId = StringUtils.secureUniqueRandomString();
|
String queryId = StringUtils.secureUniqueRandomString();
|
||||||
MamQueryIQ mamQueryIq = new MamQueryIQ(queryId, node, null);
|
MamQueryIQ mamQueryIq = getElementFactory().newQueryIQ(queryId, node, null);
|
||||||
mamQueryIq.setTo(archiveAddress);
|
mamQueryIq.setTo(archiveAddress);
|
||||||
|
|
||||||
MamQueryIQ mamResponseQueryIq = connection().sendIqRequestAndWaitForResponse(mamQueryIq);
|
MamQueryIQ mamResponseQueryIq = connection().sendIqRequestAndWaitForResponse(mamQueryIq);
|
||||||
|
@ -592,7 +642,7 @@ public final class MamManager extends Manager {
|
||||||
private List<Message> page(RSMSet requestRsmSet) throws NoResponseException, XMPPErrorException,
|
private List<Message> page(RSMSet requestRsmSet) throws NoResponseException, XMPPErrorException,
|
||||||
NotConnectedException, NotLoggedInException, InterruptedException {
|
NotConnectedException, NotLoggedInException, InterruptedException {
|
||||||
String queryId = StringUtils.secureUniqueRandomString();
|
String queryId = StringUtils.secureUniqueRandomString();
|
||||||
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, form);
|
MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, form);
|
||||||
mamQueryIQ.setType(IQ.Type.set);
|
mamQueryIQ.setType(IQ.Type.set);
|
||||||
mamQueryIQ.setTo(archiveAddress);
|
mamQueryIQ.setTo(archiveAddress);
|
||||||
mamQueryIQ.addExtension(requestRsmSet);
|
mamQueryIQ.addExtension(requestRsmSet);
|
||||||
|
@ -696,9 +746,7 @@ public final class MamManager extends Manager {
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0313.html#support">XEP-0313 § 7. Determining support</a>
|
* @see <a href="https://xmpp.org/extensions/xep-0313.html#support">XEP-0313 § 7. Determining support</a>
|
||||||
*/
|
*/
|
||||||
public boolean isSupported() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public boolean isSupported() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
// Note that this may return 'null' but SDM's supportsFeature() does the right thing™ then.
|
return getSupportedMamVersionOrNull() != null;
|
||||||
Jid archiveAddress = getArchiveAddress();
|
|
||||||
return serviceDiscoveryManager.supportsFeature(archiveAddress, MamElements.NAMESPACE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdvancedConfigurationSupported() throws InterruptedException, XMPPException, SmackException {
|
public boolean isAdvancedConfigurationSupported() throws InterruptedException, XMPPException, SmackException {
|
||||||
|
@ -720,8 +768,8 @@ public final class MamManager extends Manager {
|
||||||
throw new SmackException.FeatureNotSupportedException(ADVANCED_CONFIG_NODE, archiveAddress);
|
throw new SmackException.FeatureNotSupportedException(ADVANCED_CONFIG_NODE, archiveAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataForm.Builder getNewMamForm() {
|
private static DataForm.Builder getNewMamForm(MamVersion version) {
|
||||||
FormField field = FormField.buildHiddenFormType(MamElements.NAMESPACE);
|
FormField field = FormField.buildHiddenFormType(version.getNamespace());
|
||||||
DataForm.Builder form = DataForm.builder();
|
DataForm.Builder form = DataForm.builder();
|
||||||
form.addField(field);
|
form.addField(field);
|
||||||
return form;
|
return form;
|
||||||
|
@ -765,7 +813,7 @@ public final class MamManager extends Manager {
|
||||||
*/
|
*/
|
||||||
public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException,
|
public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException,
|
||||||
NotConnectedException, InterruptedException, NotLoggedInException {
|
NotConnectedException, InterruptedException, NotLoggedInException {
|
||||||
MamPrefsIQ mamPrefIQ = new MamPrefsIQ();
|
MamPrefsIQ mamPrefIQ = getElementFactory().newPrefsIQ();
|
||||||
return queryMamPrefs(mamPrefIQ);
|
return queryMamPrefs(mamPrefIQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,6 +878,7 @@ public final class MamManager extends Manager {
|
||||||
public static final class MamPrefs {
|
public static final class MamPrefs {
|
||||||
private final List<Jid> alwaysJids;
|
private final List<Jid> alwaysJids;
|
||||||
private final List<Jid> neverJids;
|
private final List<Jid> neverJids;
|
||||||
|
private final MamVersion mamVersion;
|
||||||
private DefaultBehavior defaultBehavior;
|
private DefaultBehavior defaultBehavior;
|
||||||
|
|
||||||
private MamPrefs(MamPrefsResult mamPrefsResult) {
|
private MamPrefs(MamPrefsResult mamPrefsResult) {
|
||||||
|
@ -837,6 +886,7 @@ public final class MamManager extends Manager {
|
||||||
this.alwaysJids = new ArrayList<>(mamPrefsIq.getAlwaysJids());
|
this.alwaysJids = new ArrayList<>(mamPrefsIq.getAlwaysJids());
|
||||||
this.neverJids = new ArrayList<>(mamPrefsIq.getNeverJids());
|
this.neverJids = new ArrayList<>(mamPrefsIq.getNeverJids());
|
||||||
this.defaultBehavior = mamPrefsIq.getDefault();
|
this.defaultBehavior = mamPrefsIq.getDefault();
|
||||||
|
this.mamVersion = MamVersion.fromNamespace(mamPrefsIq.getNamespace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultBehavior(DefaultBehavior defaultBehavior) {
|
public void setDefaultBehavior(DefaultBehavior defaultBehavior) {
|
||||||
|
@ -856,7 +906,7 @@ public final class MamManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MamPrefsIQ constructMamPrefsIq() {
|
private MamPrefsIQ constructMamPrefsIq() {
|
||||||
return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior);
|
return mamVersion.newElementFactory().newPrefsIQ(alwaysJids, neverJids, defaultBehavior);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
|
||||||
|
*
|
||||||
|
* 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.mam.element;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
|
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
||||||
|
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory that creates MAM objects.
|
||||||
|
*
|
||||||
|
* @since 4.5.0
|
||||||
|
*/
|
||||||
|
public interface MamElementFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link MamElementFactory} for the parser based on the namespace of the parser.
|
||||||
|
* @param parser the XML parser to retrieve the MAM namespace from
|
||||||
|
* @return the factory suitable for the MAM namespace
|
||||||
|
*/
|
||||||
|
static MamElementFactory forParser(XmlPullParser parser) {
|
||||||
|
String namespace = parser.getNamespace();
|
||||||
|
return MamVersion.fromNamespace(namespace).newElementFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a MAM result extension class.
|
||||||
|
*
|
||||||
|
* @param queryId id of the query
|
||||||
|
* @param id the message's archive UID
|
||||||
|
* @param forwarded the original message as it was received
|
||||||
|
* @return the result extension
|
||||||
|
*/
|
||||||
|
MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded<Message> forwarded);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a MAM fin IQ class.
|
||||||
|
*
|
||||||
|
* @param queryId id of the query
|
||||||
|
* @param rsmSet the RSM set included in the {@code <fin/>}
|
||||||
|
* @param complete true if the results returned by the server are complete (no further paging in needed)
|
||||||
|
* @param stable false if the results returned by the sever are unstable (e.g. they might later change in sequence or content)
|
||||||
|
* @return the fin IQ
|
||||||
|
*/
|
||||||
|
MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new MAM preferences IQ.
|
||||||
|
*
|
||||||
|
* @param alwaysJids JIDs for which all messages are archived by default
|
||||||
|
* @param neverJids JIDs for which messages are never archived
|
||||||
|
* @param defaultBehavior default archive behavior
|
||||||
|
* @return the prefs IQ
|
||||||
|
*/
|
||||||
|
MamPrefsIQ newPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new MAM {@code <prefs/>} IQ retrieval request (IQ type 'get').
|
||||||
|
*
|
||||||
|
* @return the prefs IQ
|
||||||
|
*/
|
||||||
|
MamPrefsIQ newPrefsIQ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new MAM Query IQ.
|
||||||
|
*
|
||||||
|
* @param queryId id of the query
|
||||||
|
* @param node pubsub node id when querying a pubsub node, null when not querying a pubsub node
|
||||||
|
* @param dataForm the dataform containing the query parameters
|
||||||
|
* @return the query IQ
|
||||||
|
*/
|
||||||
|
MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm);
|
||||||
|
|
||||||
|
}
|
|
@ -18,15 +18,13 @@ package org.jivesoftware.smackx.mam.element;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.xml.namespace.QName;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Element;
|
import org.jivesoftware.smack.packet.Element;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.MessageView;
|
import org.jivesoftware.smack.packet.MessageView;
|
||||||
|
import org.jivesoftware.smack.packet.XmlElement;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
@ -41,8 +39,6 @@ import org.jxmpp.jid.Jid;
|
||||||
*/
|
*/
|
||||||
public class MamElements {
|
public class MamElements {
|
||||||
|
|
||||||
public static final String NAMESPACE = "urn:xmpp:mam:2";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAM result extension class.
|
* MAM result extension class.
|
||||||
*
|
*
|
||||||
|
@ -50,18 +46,13 @@ public class MamElements {
|
||||||
* Archive Management</a>
|
* Archive Management</a>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static class MamResultExtension implements ExtensionElement {
|
public abstract static class MamResultExtension implements ExtensionElement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* result element.
|
* result element.
|
||||||
*/
|
*/
|
||||||
public static final String ELEMENT = "result";
|
public static final String ELEMENT = "result";
|
||||||
|
|
||||||
/**
|
|
||||||
* The qualified name of the MAM result extension element.
|
|
||||||
*/
|
|
||||||
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* id of the result.
|
* id of the result.
|
||||||
*/
|
*/
|
||||||
|
@ -77,20 +68,27 @@ public class MamElements {
|
||||||
*/
|
*/
|
||||||
private String queryId;
|
private String queryId;
|
||||||
|
|
||||||
|
protected final MamVersion version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAM result extension constructor.
|
* MAM result extension constructor.
|
||||||
*
|
*
|
||||||
|
* @param version TODO javadoc me please
|
||||||
* @param queryId TODO javadoc me please
|
* @param queryId TODO javadoc me please
|
||||||
* @param id TODO javadoc me please
|
* @param id TODO javadoc me please
|
||||||
* @param forwarded TODO javadoc me please
|
* @param forwarded TODO javadoc me please
|
||||||
*/
|
*/
|
||||||
public MamResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
|
public MamResultExtension(MamVersion version, String queryId, String id, Forwarded<Message> forwarded) {
|
||||||
if (StringUtils.isEmpty(id)) {
|
if (StringUtils.isEmpty(id)) {
|
||||||
throw new IllegalArgumentException("id must not be null or empty");
|
throw new IllegalArgumentException("id must not be null or empty");
|
||||||
}
|
}
|
||||||
if (forwarded == null) {
|
if (forwarded == null) {
|
||||||
throw new IllegalArgumentException("forwarded must no be null");
|
throw new IllegalArgumentException("forwarded must no be null");
|
||||||
}
|
}
|
||||||
|
if (version == null) {
|
||||||
|
throw new IllegalArgumentException("version must not be null");
|
||||||
|
}
|
||||||
|
this.version = version;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.forwarded = forwarded;
|
this.forwarded = forwarded;
|
||||||
this.queryId = queryId;
|
this.queryId = queryId;
|
||||||
|
@ -130,7 +128,7 @@ public class MamElements {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String getNamespace() {
|
public final String getNamespace() {
|
||||||
return NAMESPACE;
|
return version.getNamespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -148,7 +146,13 @@ public class MamElements {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MamResultExtension from(MessageView message) {
|
public static MamResultExtension from(MessageView message) {
|
||||||
return message.getExtension(MamResultExtension.class);
|
for (XmlElement extension : message.getExtensions()) {
|
||||||
|
if (extension instanceof MamResultExtension) {
|
||||||
|
return (MamResultExtension) extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,6 @@ public class MamFinIQ extends IQ {
|
||||||
*/
|
*/
|
||||||
public static final String ELEMENT = "fin";
|
public static final String ELEMENT = "fin";
|
||||||
|
|
||||||
/**
|
|
||||||
* the IQ NAMESPACE.
|
|
||||||
*/
|
|
||||||
public static final String NAMESPACE = MamElements.NAMESPACE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSM set.
|
* RSM set.
|
||||||
*/
|
*/
|
||||||
|
@ -63,13 +58,14 @@ public class MamFinIQ extends IQ {
|
||||||
/**
|
/**
|
||||||
* MamFinIQ constructor.
|
* MamFinIQ constructor.
|
||||||
*
|
*
|
||||||
|
* @param version TODO javadoc me please
|
||||||
* @param queryId TODO javadoc me please
|
* @param queryId TODO javadoc me please
|
||||||
* @param rsmSet TODO javadoc me please
|
* @param rsmSet TODO javadoc me please
|
||||||
* @param complete TODO javadoc me please
|
* @param complete TODO javadoc me please
|
||||||
* @param stable TODO javadoc me please
|
* @param stable TODO javadoc me please
|
||||||
*/
|
*/
|
||||||
public MamFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
|
public MamFinIQ(MamVersion version, String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
|
||||||
super(ELEMENT, NAMESPACE);
|
super(ELEMENT, version.getNamespace());
|
||||||
if (rsmSet == null) {
|
if (rsmSet == null) {
|
||||||
throw new IllegalArgumentException("rsmSet must not be null");
|
throw new IllegalArgumentException("rsmSet must not be null");
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,11 +46,6 @@ public class MamPrefsIQ extends IQ {
|
||||||
*/
|
*/
|
||||||
public static final String ELEMENT = "prefs";
|
public static final String ELEMENT = "prefs";
|
||||||
|
|
||||||
/**
|
|
||||||
* the IQ NAMESPACE.
|
|
||||||
*/
|
|
||||||
public static final String NAMESPACE = MamElements.NAMESPACE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* list of always.
|
* list of always.
|
||||||
*/
|
*/
|
||||||
|
@ -68,9 +63,11 @@ public class MamPrefsIQ extends IQ {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new MAM {@code <prefs/>} IQ retrieval request (IQ type 'get').
|
* Construct a new MAM {@code <prefs/>} IQ retrieval request (IQ type 'get').
|
||||||
|
*
|
||||||
|
* @param version TODO javadoc me please *
|
||||||
*/
|
*/
|
||||||
public MamPrefsIQ() {
|
public MamPrefsIQ(MamVersion version) {
|
||||||
super(ELEMENT, NAMESPACE);
|
super(ELEMENT, version.getNamespace());
|
||||||
alwaysJids = null;
|
alwaysJids = null;
|
||||||
neverJids = null;
|
neverJids = null;
|
||||||
defaultBehavior = null;
|
defaultBehavior = null;
|
||||||
|
@ -79,12 +76,13 @@ public class MamPrefsIQ extends IQ {
|
||||||
/**
|
/**
|
||||||
* MAM preferences IQ constructor.
|
* MAM preferences IQ constructor.
|
||||||
*
|
*
|
||||||
|
* @param version TODO javadoc me please
|
||||||
* @param alwaysJids TODO javadoc me please
|
* @param alwaysJids TODO javadoc me please
|
||||||
* @param neverJids TODO javadoc me please
|
* @param neverJids TODO javadoc me please
|
||||||
* @param defaultBehavior TODO javadoc me please
|
* @param defaultBehavior TODO javadoc me please
|
||||||
*/
|
*/
|
||||||
public MamPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, DefaultBehavior defaultBehavior) {
|
public MamPrefsIQ(MamVersion version, List<Jid> alwaysJids, List<Jid> neverJids, DefaultBehavior defaultBehavior) {
|
||||||
super(ELEMENT, NAMESPACE);
|
super(ELEMENT, version.getNamespace());
|
||||||
setType(Type.set);
|
setType(Type.set);
|
||||||
this.alwaysJids = alwaysJids;
|
this.alwaysJids = alwaysJids;
|
||||||
this.neverJids = neverJids;
|
this.neverJids = neverJids;
|
||||||
|
|
|
@ -35,11 +35,6 @@ public class MamQueryIQ extends IQ {
|
||||||
*/
|
*/
|
||||||
public static final String ELEMENT = QUERY_ELEMENT;
|
public static final String ELEMENT = QUERY_ELEMENT;
|
||||||
|
|
||||||
/**
|
|
||||||
* the MAM query IQ NAMESPACE.
|
|
||||||
*/
|
|
||||||
public static final String NAMESPACE = MamElements.NAMESPACE;
|
|
||||||
|
|
||||||
private final String queryId;
|
private final String queryId;
|
||||||
private final String node;
|
private final String node;
|
||||||
private final DataForm dataForm;
|
private final DataForm dataForm;
|
||||||
|
@ -47,41 +42,45 @@ public class MamQueryIQ extends IQ {
|
||||||
/**
|
/**
|
||||||
* MAM query IQ constructor.
|
* MAM query IQ constructor.
|
||||||
*
|
*
|
||||||
|
* @param version TODO javadoc me please
|
||||||
* @param queryId TODO javadoc me please
|
* @param queryId TODO javadoc me please
|
||||||
*/
|
*/
|
||||||
public MamQueryIQ(String queryId) {
|
public MamQueryIQ(MamVersion version, String queryId) {
|
||||||
this(queryId, null, null);
|
this(version, queryId, null, null);
|
||||||
setType(IQ.Type.get);
|
setType(IQ.Type.get);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAM query IQ constructor.
|
* MAM query IQ constructor.
|
||||||
*
|
*
|
||||||
|
* @param version TODO javadoc me please
|
||||||
* @param form TODO javadoc me please
|
* @param form TODO javadoc me please
|
||||||
*/
|
*/
|
||||||
public MamQueryIQ(DataForm form) {
|
public MamQueryIQ(MamVersion version, DataForm form) {
|
||||||
this(null, null, form);
|
this(version, null, null, form);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAM query IQ constructor.
|
* MAM query IQ constructor.
|
||||||
*
|
*
|
||||||
|
* @param version TODO javadoc me please
|
||||||
* @param queryId TODO javadoc me please
|
* @param queryId TODO javadoc me please
|
||||||
* @param form TODO javadoc me please
|
* @param form TODO javadoc me please
|
||||||
*/
|
*/
|
||||||
public MamQueryIQ(String queryId, DataForm form) {
|
public MamQueryIQ(MamVersion version, String queryId, DataForm form) {
|
||||||
this(queryId, null, form);
|
this(version, queryId, null, form);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MAM query IQ constructor.
|
* MAM query IQ constructor.
|
||||||
*
|
*
|
||||||
|
* @param version TODO javadoc me please
|
||||||
* @param queryId TODO javadoc me please
|
* @param queryId TODO javadoc me please
|
||||||
* @param node TODO javadoc me please
|
* @param node TODO javadoc me please
|
||||||
* @param dataForm TODO javadoc me please
|
* @param dataForm TODO javadoc me please
|
||||||
*/
|
*/
|
||||||
public MamQueryIQ(String queryId, String node, DataForm dataForm) {
|
public MamQueryIQ(MamVersion version, String queryId, String node, DataForm dataForm) {
|
||||||
super(ELEMENT, NAMESPACE);
|
super(ELEMENT, version.getNamespace());
|
||||||
this.queryId = queryId;
|
this.queryId = queryId;
|
||||||
this.node = node;
|
this.node = node;
|
||||||
this.dataForm = dataForm;
|
this.dataForm = dataForm;
|
||||||
|
@ -91,9 +90,9 @@ public class MamQueryIQ extends IQ {
|
||||||
if (formType == null) {
|
if (formType == null) {
|
||||||
throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field");
|
throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field");
|
||||||
}
|
}
|
||||||
if (!formType.equals(MamElements.NAMESPACE)) {
|
if (!formType.equals(version.getNamespace())) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'");
|
"Value of the hidden form type field must be '" + version.getNamespace() + "'");
|
||||||
}
|
}
|
||||||
addExtension(dataForm);
|
addExtension(dataForm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
|
||||||
|
*
|
||||||
|
* 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.mam.element;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
||||||
|
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
class MamV1ElementFactory implements MamElementFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
|
||||||
|
return new MamV1ResultExtension(queryId, id, forwarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
|
||||||
|
return new MamFinIQ(MamVersion.MAM1, queryId, rsmSet, complete, stable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamPrefsIQ newPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior) {
|
||||||
|
return new MamPrefsIQ(MamVersion.MAM1, alwaysJids, neverJids, defaultBehavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamPrefsIQ newPrefsIQ() {
|
||||||
|
return new MamPrefsIQ(MamVersion.MAM1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm) {
|
||||||
|
return new MamQueryIQ(MamVersion.MAM1, queryId, node, dataForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MamV1ResultExtension extends MamElements.MamResultExtension {
|
||||||
|
/**
|
||||||
|
* The qualified name of the MAM result extension element.
|
||||||
|
*/
|
||||||
|
public static final QName QNAME = new QName(MamVersion.MAM1.getNamespace(), ELEMENT);
|
||||||
|
|
||||||
|
MamV1ResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
|
||||||
|
super(MamVersion.MAM1, queryId, id, forwarded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
|
||||||
|
*
|
||||||
|
* 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.mam.element;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
||||||
|
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
class MamV2ElementFactory implements MamElementFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamElements.MamResultExtension newResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
|
||||||
|
return new MamV2ResultExtension(queryId, id, forwarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamFinIQ newFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
|
||||||
|
return new MamFinIQ(MamVersion.MAM2, queryId, rsmSet, complete, stable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamPrefsIQ newPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, MamPrefsIQ.DefaultBehavior defaultBehavior) {
|
||||||
|
return new MamPrefsIQ(MamVersion.MAM2, alwaysJids, neverJids, defaultBehavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamPrefsIQ newPrefsIQ() {
|
||||||
|
return new MamPrefsIQ(MamVersion.MAM2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MamQueryIQ newQueryIQ(String queryId, String node, DataForm dataForm) {
|
||||||
|
return new MamQueryIQ(MamVersion.MAM2, queryId, node, dataForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MamV2ResultExtension extends MamElements.MamResultExtension {
|
||||||
|
/**
|
||||||
|
* The qualified name of the MAM result extension element.
|
||||||
|
*/
|
||||||
|
public static final QName QNAME = new QName(MamVersion.MAM2.getNamespace(), ELEMENT);
|
||||||
|
|
||||||
|
MamV2ResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
|
||||||
|
super(MamVersion.MAM2, queryId, id, forwarded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright © 2016-2021 Florian Schmaus and Frank Matheron
|
||||||
|
*
|
||||||
|
* 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.mam.element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MAM versions supported by Smack.
|
||||||
|
*
|
||||||
|
* @since 4.5.0
|
||||||
|
*/
|
||||||
|
public enum MamVersion {
|
||||||
|
// Note that the order in which the enum values are defined, is also the order in which we attempt to find a
|
||||||
|
// supported version. The versions should therefore be listed in order of newest to oldest, so that Smack prefers
|
||||||
|
// using a newer version over an older version.
|
||||||
|
MAM2("urn:xmpp:mam:2") {
|
||||||
|
@Override
|
||||||
|
public MamElementFactory newElementFactory() {
|
||||||
|
return new MamV2ElementFactory();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MAM1("urn:xmpp:mam:1") {
|
||||||
|
@Override
|
||||||
|
public MamElementFactory newElementFactory() {
|
||||||
|
return new MamV1ElementFactory();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String namespace;
|
||||||
|
|
||||||
|
MamVersion(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each MAM version is identified by its namespace. Returns the namespace for this MAM version.
|
||||||
|
* @return the namespace of the MAM version
|
||||||
|
*/
|
||||||
|
public String getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new factory that creates IQ's and extension objects for this MAM version.
|
||||||
|
* @return the factory
|
||||||
|
*/
|
||||||
|
public abstract MamElementFactory newElementFactory();
|
||||||
|
|
||||||
|
public static MamVersion fromNamespace(String namespace) {
|
||||||
|
for (MamVersion v : MamVersion.values()) {
|
||||||
|
if (v.namespace.equals(namespace)) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unsupported namespace: " + namespace);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import org.jivesoftware.smack.util.ParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamElementFactory;
|
||||||
import org.jivesoftware.smackx.mam.element.MamFinIQ;
|
import org.jivesoftware.smackx.mam.element.MamFinIQ;
|
||||||
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
||||||
import org.jivesoftware.smackx.rsm.provider.RSMSetProvider;
|
import org.jivesoftware.smackx.rsm.provider.RSMSetProvider;
|
||||||
|
@ -41,6 +42,7 @@ public class MamFinIQProvider extends IQProvider<MamFinIQ> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MamFinIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
public MamFinIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
|
||||||
String queryId = parser.getAttributeValue("", "queryid");
|
String queryId = parser.getAttributeValue("", "queryid");
|
||||||
boolean complete = ParserUtils.getBooleanAttribute(parser, "complete", false);
|
boolean complete = ParserUtils.getBooleanAttribute(parser, "complete", false);
|
||||||
boolean stable = ParserUtils.getBooleanAttribute(parser, "stable", true);
|
boolean stable = ParserUtils.getBooleanAttribute(parser, "stable", true);
|
||||||
|
@ -65,7 +67,7 @@ public class MamFinIQProvider extends IQProvider<MamFinIQ> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MamFinIQ(queryId, rsmSet, complete, stable);
|
return elementFactory.newFinIQ(queryId, rsmSet, complete, stable);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.jivesoftware.smack.provider.IQProvider;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamElementFactory;
|
||||||
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
||||||
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
|
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
|
||||||
|
|
||||||
|
@ -41,19 +42,17 @@ import org.jxmpp.jid.impl.JidCreate;
|
||||||
*/
|
*/
|
||||||
public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
|
public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
|
||||||
|
|
||||||
|
public static final MamPrefsIQProvider INSTANCE = new MamPrefsIQProvider();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MamPrefsIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
public MamPrefsIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
|
||||||
String iqType = parser.getAttributeValue("", "type");
|
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
|
||||||
String defaultBehaviorString = parser.getAttributeValue("", "default");
|
String defaultBehaviorString = parser.getAttributeValue("", "default");
|
||||||
DefaultBehavior defaultBehavior = null;
|
DefaultBehavior defaultBehavior = null;
|
||||||
if (defaultBehaviorString != null) {
|
if (defaultBehaviorString != null) {
|
||||||
defaultBehavior = DefaultBehavior.valueOf(defaultBehaviorString);
|
defaultBehavior = DefaultBehavior.valueOf(defaultBehaviorString);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iqType == null) {
|
|
||||||
iqType = "result";
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Jid> alwaysJids = null;
|
List<Jid> alwaysJids = null;
|
||||||
List<Jid> neverJids = null;
|
List<Jid> neverJids = null;
|
||||||
|
|
||||||
|
@ -82,7 +81,7 @@ public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior);
|
return elementFactory.newPrefsIQ(alwaysJids, neverJids, defaultBehavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Jid> iterateJids(XmlPullParser parser) throws XmlPullParserException, IOException {
|
private static List<Jid> iterateJids(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.jivesoftware.smack.provider.IQProvider;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamElementFactory;
|
||||||
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
import org.jivesoftware.smackx.xdata.provider.DataFormProvider;
|
import org.jivesoftware.smackx.xdata.provider.DataFormProvider;
|
||||||
|
@ -41,6 +42,7 @@ public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
|
||||||
@Override
|
@Override
|
||||||
public MamQueryIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
|
public MamQueryIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
|
||||||
DataForm dataForm = null;
|
DataForm dataForm = null;
|
||||||
String queryId = parser.getAttributeValue("", "queryid");
|
String queryId = parser.getAttributeValue("", "queryid");
|
||||||
String node = parser.getAttributeValue("", "node");
|
String node = parser.getAttributeValue("", "node");
|
||||||
|
@ -68,7 +70,7 @@ public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MamQueryIQ(queryId, node, dataForm);
|
return elementFactory.newQueryIQ(queryId, node, dataForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
||||||
import org.jivesoftware.smackx.forward.provider.ForwardedProvider;
|
import org.jivesoftware.smackx.forward.provider.ForwardedProvider;
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamElementFactory;
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
|
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +44,7 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
|
||||||
@Override
|
@Override
|
||||||
public MamResultExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
|
public MamResultExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException, ParseException {
|
throws XmlPullParserException, IOException, SmackParsingException, ParseException {
|
||||||
|
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
|
||||||
Forwarded<Message> forwarded = null;
|
Forwarded<Message> forwarded = null;
|
||||||
String queryId = parser.getAttributeValue("", "queryid");
|
String queryId = parser.getAttributeValue("", "queryid");
|
||||||
String id = parser.getAttributeValue("", "id");
|
String id = parser.getAttributeValue("", "id");
|
||||||
|
@ -69,7 +71,7 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MamResultExtension(queryId, id, forwarded);
|
return elementFactory.newResultExtension(queryId, id, forwarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,26 @@
|
||||||
<namespace>urn:xmpp:mam:2</namespace>
|
<namespace>urn:xmpp:mam:2</namespace>
|
||||||
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
|
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
|
||||||
</extensionProvider>
|
</extensionProvider>
|
||||||
|
<iqProvider>
|
||||||
|
<elementName>prefs</elementName>
|
||||||
|
<namespace>urn:xmpp:mam:1</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider</className>
|
||||||
|
</iqProvider>
|
||||||
|
<iqProvider>
|
||||||
|
<elementName>query</elementName>
|
||||||
|
<namespace>urn:xmpp:mam:1</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.mam.provider.MamQueryIQProvider</className>
|
||||||
|
</iqProvider>
|
||||||
|
<iqProvider>
|
||||||
|
<elementName>fin</elementName>
|
||||||
|
<namespace>urn:xmpp:mam:1</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.mam.provider.MamFinIQProvider</className>
|
||||||
|
</iqProvider>
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>result</elementName>
|
||||||
|
<namespace>urn:xmpp:mam:1</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
|
||||||
<!-- XEP-0323: Internet of Things - Data -->
|
<!-- XEP-0323: Internet of Things - Data -->
|
||||||
<iqProvider>
|
<iqProvider>
|
||||||
|
|
|
@ -23,7 +23,7 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
|
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements;
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -35,7 +35,7 @@ public class FiltersTest extends MamTest {
|
||||||
|
|
||||||
private static String getMamXMemberWith(List<String> fieldsNames, List<? extends CharSequence> fieldsValues) {
|
private static String getMamXMemberWith(List<String> fieldsNames, List<? extends CharSequence> fieldsValues) {
|
||||||
String xml = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
String xml = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
||||||
+ MamElements.NAMESPACE + "</value>" + "</field>";
|
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>";
|
||||||
|
|
||||||
for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) {
|
for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) {
|
||||||
xml += "<field var='" + fieldsNames.get(i) + "'>" + "<value>" + fieldsValues.get(i) + "</value>"
|
xml += "<field var='" + fieldsNames.get(i) + "'>" + "<value>" + fieldsValues.get(i) + "</value>"
|
||||||
|
@ -51,7 +51,7 @@ public class FiltersTest extends MamTest {
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
|
|
||||||
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsSince(date).build();
|
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsSince(date).build();
|
||||||
DataForm dataForm = mamQueryArgs.getDataForm();
|
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
|
||||||
|
|
||||||
List<String> fields = new ArrayList<>();
|
List<String> fields = new ArrayList<>();
|
||||||
fields.add("start");
|
fields.add("start");
|
||||||
|
@ -66,7 +66,7 @@ public class FiltersTest extends MamTest {
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
|
|
||||||
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsBefore(date).build();
|
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsBefore(date).build();
|
||||||
DataForm dataForm = mamQueryArgs.getDataForm();
|
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
|
||||||
|
|
||||||
List<String> fields = new ArrayList<>();
|
List<String> fields = new ArrayList<>();
|
||||||
fields.add("end");
|
fields.add("end");
|
||||||
|
@ -81,7 +81,7 @@ public class FiltersTest extends MamTest {
|
||||||
Jid jid = JidTestUtil.BARE_JID_1;
|
Jid jid = JidTestUtil.BARE_JID_1;
|
||||||
|
|
||||||
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsToJid(jid).build();
|
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsToJid(jid).build();
|
||||||
DataForm dataForm = mamQueryArgs.getDataForm();
|
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
|
||||||
|
|
||||||
List<String> fields = new ArrayList<>();
|
List<String> fields = new ArrayList<>();
|
||||||
fields.add("with");
|
fields.add("with");
|
||||||
|
|
|
@ -19,54 +19,62 @@ package org.jivesoftware.smackx.mam;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestUtil;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestUtil.XmlPullParserKind;
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
||||||
import org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider;
|
import org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
public class MamPrefIQProviderTest extends MamTest {
|
public class MamPrefIQProviderTest extends MamTest {
|
||||||
|
|
||||||
private static final String exampleMamPrefsIQ1 = "<iq type='set' id='juliet3'>" + "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
|
private static final String exampleMamPrefsIQ1 = "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
|
||||||
+ "<always>" + "<jid>romeo@montague.lit</jid>" + "</always>" + "<never>"
|
+ "<always>" + "<jid>romeo@montague.lit</jid>" + "</always>" + "<never>"
|
||||||
+ "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>" + "</iq>";
|
+ "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>";
|
||||||
|
|
||||||
private static final String exampleMamPrefsIQ2 = "<iq type='set' id='juliet3'>" + "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
|
private static final String exampleMamPrefsIQ2 = "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
|
||||||
+ "<always>" + "<jid>romeo@montague.lit</jid>" + "<jid>montague@montague.lit</jid>" + "</always>"
|
+ "<always>" + "<jid>romeo@montague.lit</jid>" + "<jid>montague@montague.lit</jid>" + "</always>"
|
||||||
+ "<never>" + "</never>" + "</prefs>" + "</iq>";
|
+ "<never>" + "</never>" + "</prefs>";
|
||||||
|
|
||||||
private static final String exampleMamPrefsIQ3 = "<iq type='get' id='juliet3'>" + "<prefs xmlns='urn:xmpp:mam:2'>" + "</prefs>"
|
private static final String exampleMamPrefsIQ3 = "<prefs xmlns='urn:xmpp:mam:2'>" + "</prefs>";
|
||||||
+ "</iq>";
|
|
||||||
|
|
||||||
private static final String exampleMamPrefsResultIQ = "<iq type='result' id='juliet3'>"
|
private static final String exampleMamPrefsResultIQ = "<iq type='result' id='juliet3'>"
|
||||||
+ "<prefs xmlns='urn:xmpp:mam:2' default='roster'>" + "<always>" + "<jid>romeo@montague.lit</jid>"
|
+ "<prefs xmlns='urn:xmpp:mam:2' default='roster'>" + "<always>" + "<jid>romeo@montague.lit</jid>"
|
||||||
+ "</always>" + "<never>" + "<jid>sarasa@montague.lit</jid>" + "<jid>montague@montague.lit</jid>"
|
+ "</always>" + "<never>" + "<jid>sarasa@montague.lit</jid>" + "<jid>montague@montague.lit</jid>"
|
||||||
+ "</never>" + "</prefs>" + "</iq>";
|
+ "</never>" + "</prefs>" + "</iq>";
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void checkMamPrefsIQProvider() throws Exception {
|
@EnumSource(value = SmackTestUtil.XmlPullParserKind.class)
|
||||||
XmlPullParser parser1 = PacketParserUtils.getParserFor(exampleMamPrefsIQ1);
|
public void checkMamPrefsIQProvider(XmlPullParserKind parserKind)
|
||||||
MamPrefsIQ mamPrefIQ1 = new MamPrefsIQProvider().parse(parser1);
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
XmlPullParser parser1 = SmackTestUtil.getParserFor(exampleMamPrefsIQ1, parserKind);
|
||||||
|
MamPrefsIQ mamPrefIQ1 = MamPrefsIQProvider.INSTANCE.parse(parser1);
|
||||||
|
|
||||||
assertEquals(IQ.Type.set, mamPrefIQ1.getType());
|
assertEquals(IQ.Type.set, mamPrefIQ1.getType());
|
||||||
assertEquals(mamPrefIQ1.getAlwaysJids().get(0).toString(), "romeo@montague.lit");
|
assertEquals(mamPrefIQ1.getAlwaysJids().get(0).toString(), "romeo@montague.lit");
|
||||||
assertEquals(mamPrefIQ1.getNeverJids().get(0).toString(), "montague@montague.lit");
|
assertEquals(mamPrefIQ1.getNeverJids().get(0).toString(), "montague@montague.lit");
|
||||||
|
|
||||||
XmlPullParser parser2 = PacketParserUtils.getParserFor(exampleMamPrefsIQ2);
|
XmlPullParser parser2 = SmackTestUtil.getParserFor(exampleMamPrefsIQ2, parserKind);
|
||||||
MamPrefsIQ mamPrefIQ2 = new MamPrefsIQProvider().parse(parser2);
|
MamPrefsIQ mamPrefIQ2 = MamPrefsIQProvider.INSTANCE.parse(parser2);
|
||||||
assertEquals(IQ.Type.set, mamPrefIQ2.getType());
|
assertEquals(IQ.Type.set, mamPrefIQ2.getType());
|
||||||
assertEquals(mamPrefIQ2.getAlwaysJids().get(0).toString(), "romeo@montague.lit");
|
assertEquals(mamPrefIQ2.getAlwaysJids().get(0).toString(), "romeo@montague.lit");
|
||||||
assertEquals(mamPrefIQ2.getAlwaysJids().get(1).toString(), "montague@montague.lit");
|
assertEquals(mamPrefIQ2.getAlwaysJids().get(1).toString(), "montague@montague.lit");
|
||||||
assertTrue(mamPrefIQ2.getNeverJids().isEmpty());
|
assertTrue(mamPrefIQ2.getNeverJids().isEmpty());
|
||||||
|
|
||||||
XmlPullParser parser3 = PacketParserUtils.getParserFor(exampleMamPrefsIQ3);
|
XmlPullParser parser3 = SmackTestUtil.getParserFor(exampleMamPrefsIQ3, parserKind);
|
||||||
MamPrefsIQ mamPrefIQ3 = new MamPrefsIQProvider().parse(parser3);
|
MamPrefsIQ mamPrefIQ3 = MamPrefsIQProvider.INSTANCE.parse(parser3);
|
||||||
assertEquals(IQ.Type.set, mamPrefIQ3.getType());
|
assertEquals(IQ.Type.set, mamPrefIQ3.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.jivesoftware.smack.DummyConnection;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
@ -47,9 +48,9 @@ public class MamTest extends SmackTestSuite {
|
||||||
|
|
||||||
protected DataForm getNewMamForm() throws NoSuchMethodException, SecurityException, IllegalAccessException,
|
protected DataForm getNewMamForm() throws NoSuchMethodException, SecurityException, IllegalAccessException,
|
||||||
IllegalArgumentException, InvocationTargetException {
|
IllegalArgumentException, InvocationTargetException {
|
||||||
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm");
|
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm", MamVersion.class);
|
||||||
methodGetNewMamForm.setAccessible(true);
|
methodGetNewMamForm.setAccessible(true);
|
||||||
DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager);
|
DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager, MamVersion.MAM2);
|
||||||
return dataFormBuilder.build();
|
return dataFormBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.StreamOpen;
|
import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
import org.jivesoftware.smackx.rsm.packet.RSMSet;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ public class PagingTest extends MamTest {
|
||||||
int max = 10;
|
int max = 10;
|
||||||
RSMSet rsmSet = new RSMSet(max);
|
RSMSet rsmSet = new RSMSet(max);
|
||||||
|
|
||||||
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
|
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
|
||||||
mamQueryIQ.setStanzaId("sarasa");
|
mamQueryIQ.setStanzaId("sarasa");
|
||||||
mamQueryIQ.setType(IQ.Type.set);
|
mamQueryIQ.setType(IQ.Type.set);
|
||||||
mamQueryIQ.addExtension(rsmSet);
|
mamQueryIQ.addExtension(rsmSet);
|
||||||
|
|
|
@ -23,9 +23,9 @@ import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.StreamOpen;
|
import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements;
|
|
||||||
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
|
||||||
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
|
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
@ -33,16 +33,16 @@ import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
|
||||||
public class PreferencesTest {
|
public class PreferencesTest {
|
||||||
|
|
||||||
private static final String retrievePrefsStanzaExample = "<iq id='sarasa' type='get'>" + "<prefs xmlns='" + MamElements.NAMESPACE
|
private static final String retrievePrefsStanzaExample = "<iq id='sarasa' type='get'>" + "<prefs xmlns='" + MamVersion.MAM2.getNamespace()
|
||||||
+ "'/>" + "</iq>";
|
+ "'/>" + "</iq>";
|
||||||
|
|
||||||
private static final String updatePrefsStanzaExample = "<iq id='sarasa' type='set'>" + "<prefs xmlns='" + MamElements.NAMESPACE
|
private static final String updatePrefsStanzaExample = "<iq id='sarasa' type='set'>" + "<prefs xmlns='" + MamVersion.MAM2.getNamespace()
|
||||||
+ "' default='roster'>" + "<always>" + "<jid>romeo@montague.lit</jid>" + "<jid>other@montague.lit</jid>"
|
+ "' default='roster'>" + "<always>" + "<jid>romeo@montague.lit</jid>" + "<jid>other@montague.lit</jid>"
|
||||||
+ "</always>" + "<never>" + "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>" + "</iq>";
|
+ "</always>" + "<never>" + "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>" + "</iq>";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkRetrievePrefsStanza() throws Exception {
|
public void checkRetrievePrefsStanza() throws Exception {
|
||||||
MamPrefsIQ mamPrefIQ = new MamPrefsIQ();
|
MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ();
|
||||||
mamPrefIQ.setStanzaId("sarasa");
|
mamPrefIQ.setStanzaId("sarasa");
|
||||||
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), retrievePrefsStanzaExample);
|
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), retrievePrefsStanzaExample);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class PreferencesTest {
|
||||||
List<Jid> neverJids = new ArrayList<>();
|
List<Jid> neverJids = new ArrayList<>();
|
||||||
neverJids.add(JidCreate.from("montague@montague.lit"));
|
neverJids.add(JidCreate.from("montague@montague.lit"));
|
||||||
|
|
||||||
MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster);
|
MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster);
|
||||||
mamPrefIQ.setStanzaId("sarasa");
|
mamPrefIQ.setStanzaId("sarasa");
|
||||||
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), updatePrefsStanzaExample);
|
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), updatePrefsStanzaExample);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,9 @@ import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.delay.packet.DelayInformation;
|
import org.jivesoftware.smackx.delay.packet.DelayInformation;
|
||||||
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
import org.jivesoftware.smackx.forward.packet.Forwarded;
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements;
|
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
|
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
|
||||||
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -41,7 +41,7 @@ public class QueryArchiveTest extends MamTest {
|
||||||
|
|
||||||
private static final String mamSimpleQueryIQ = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:2' queryid='testid'>"
|
private static final String mamSimpleQueryIQ = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:2' queryid='testid'>"
|
||||||
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
||||||
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "</query>" + "</iq>";
|
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>" + "</x>" + "</query>" + "</iq>";
|
||||||
|
|
||||||
private static final String mamQueryResultExample = "<message to='hag66@shakespeare.lit/pda' from='coven@chat.shakespeare.lit' id='iasd207'>"
|
private static final String mamQueryResultExample = "<message to='hag66@shakespeare.lit/pda' from='coven@chat.shakespeare.lit' id='iasd207'>"
|
||||||
+ "<result xmlns='urn:xmpp:mam:2' queryid='g27' id='34482-21985-73620'>"
|
+ "<result xmlns='urn:xmpp:mam:2' queryid='g27' id='34482-21985-73620'>"
|
||||||
|
@ -54,7 +54,7 @@ public class QueryArchiveTest extends MamTest {
|
||||||
@Test
|
@Test
|
||||||
public void checkMamQueryIQ() throws Exception {
|
public void checkMamQueryIQ() throws Exception {
|
||||||
DataForm dataForm = getNewMamForm();
|
DataForm dataForm = getNewMamForm();
|
||||||
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
|
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
|
||||||
mamQueryIQ.setType(IQ.Type.set);
|
mamQueryIQ.setType(IQ.Type.set);
|
||||||
mamQueryIQ.setStanzaId("sarasa");
|
mamQueryIQ.setStanzaId("sarasa");
|
||||||
assertEquals(mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), mamSimpleQueryIQ);
|
assertEquals(mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), mamSimpleQueryIQ);
|
||||||
|
@ -80,7 +80,7 @@ public class QueryArchiveTest extends MamTest {
|
||||||
|
|
||||||
Forwarded<Message> forwarded = new Forwarded<>(forwardedMessage, delay);
|
Forwarded<Message> forwarded = new Forwarded<>(forwardedMessage, delay);
|
||||||
|
|
||||||
message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded));
|
message.addExtension(MamVersion.MAM2.newElementFactory().newResultExtension("g27", "34482-21985-73620", forwarded));
|
||||||
|
|
||||||
assertEquals(mamQueryResultExample, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
assertEquals(mamQueryResultExample, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.StreamOpen;
|
import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
|
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements;
|
|
||||||
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -32,13 +32,13 @@ public class ResultsLimitTest extends MamTest {
|
||||||
|
|
||||||
private static final String resultsLimitStanza = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:2' queryid='testid'>"
|
private static final String resultsLimitStanza = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:2' queryid='testid'>"
|
||||||
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
||||||
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
|
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
|
||||||
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
|
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkResultsLimit() throws Exception {
|
public void checkResultsLimit() throws Exception {
|
||||||
DataForm dataForm = getNewMamForm();
|
DataForm dataForm = getNewMamForm();
|
||||||
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
|
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
|
||||||
mamQueryIQ.setType(IQ.Type.set);
|
mamQueryIQ.setType(IQ.Type.set);
|
||||||
mamQueryIQ.setStanzaId("sarasa");
|
mamQueryIQ.setStanzaId("sarasa");
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import org.jivesoftware.smack.packet.StreamOpen;
|
import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
|
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
|
||||||
import org.jivesoftware.smackx.mam.element.MamElements;
|
|
||||||
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
|
||||||
|
import org.jivesoftware.smackx.mam.element.MamVersion;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
@ -32,18 +32,18 @@ import org.jxmpp.jid.JidTestUtil;
|
||||||
|
|
||||||
public class RetrieveFormFieldsTest extends MamTest {
|
public class RetrieveFormFieldsTest extends MamTest {
|
||||||
|
|
||||||
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamElements.NAMESPACE
|
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamVersion.MAM2.getNamespace()
|
||||||
+ "' queryid='testid'></query>" + "</iq>";
|
+ "' queryid='testid'></query>" + "</iq>";
|
||||||
|
|
||||||
private static final String additionalFieldsStanza = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>"
|
private static final String additionalFieldsStanza = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>"
|
||||||
+ "<value>" + MamElements.NAMESPACE + "</value>" + "</field>"
|
+ "<value>" + MamVersion.MAM2.getNamespace() + "</value>" + "</field>"
|
||||||
+ "<field var='urn:example:xmpp:free-text-search'>" + "<value>Hi</value>" + "</field>"
|
+ "<field var='urn:example:xmpp:free-text-search'>" + "<value>Hi</value>" + "</field>"
|
||||||
+ "<field var='urn:example:xmpp:stanza-content'>" + "<value>one@exampleone.org</value>" + "</field>"
|
+ "<field var='urn:example:xmpp:stanza-content'>" + "<value>one@exampleone.org</value>" + "</field>"
|
||||||
+ "</x>";
|
+ "</x>";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkRetrieveFormFieldsStanza() throws Exception {
|
public void checkRetrieveFormFieldsStanza() throws Exception {
|
||||||
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId);
|
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId);
|
||||||
mamQueryIQ.setStanzaId("sarasa");
|
mamQueryIQ.setStanzaId("sarasa");
|
||||||
|
|
||||||
assertEquals(retrieveFormFieldStanza, mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
assertEquals(retrieveFormFieldStanza, mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
|
||||||
|
@ -63,7 +63,7 @@ public class RetrieveFormFieldsTest extends MamTest {
|
||||||
.withAdditionalFormField(field1)
|
.withAdditionalFormField(field1)
|
||||||
.withAdditionalFormField(field2)
|
.withAdditionalFormField(field2)
|
||||||
.build();
|
.build();
|
||||||
DataForm dataForm = mamQueryArgs.getDataForm();
|
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
|
||||||
|
|
||||||
String dataFormResult = dataForm.toXML().toString();
|
String dataFormResult = dataForm.toXML().toString();
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ public class DataPacketExtension implements ExtensionElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
|
||||||
XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this));
|
XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace));
|
||||||
xml.closeElement(this);
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
|
@ -615,10 +615,8 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
|
||||||
if (annouceLocalStreamHost) {
|
if (annouceLocalStreamHost) {
|
||||||
// add local proxy on first position if exists
|
// add local proxy on first position if exists
|
||||||
List<StreamHost> localProxies = getLocalStreamHost();
|
List<StreamHost> localProxies = getLocalStreamHost();
|
||||||
if (localProxies != null) {
|
|
||||||
streamHosts.addAll(localProxies);
|
streamHosts.addAll(localProxies);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// query SOCKS5 proxies for network settings
|
// query SOCKS5 proxies for network settings
|
||||||
for (Jid proxy : proxies) {
|
for (Jid proxy : proxies) {
|
||||||
|
@ -652,12 +650,14 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the stream host information of the local SOCKS5 proxy containing the IP address and
|
* Returns the stream host information of the local SOCKS5 proxy containing the IP address and
|
||||||
* the port or null if local SOCKS5 proxy is not running.
|
* the port. The returned list may be empty if the local SOCKS5 proxy is not running.
|
||||||
*
|
*
|
||||||
* @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy
|
* @return the stream host information of the local SOCKS5 proxy
|
||||||
* is not running
|
|
||||||
*/
|
*/
|
||||||
public List<StreamHost> getLocalStreamHost() {
|
public List<StreamHost> getLocalStreamHost() {
|
||||||
|
// Ensure that the local SOCKS5 proxy is running (if enabled).
|
||||||
|
Socks5Proxy.getSocks5Proxy();
|
||||||
|
|
||||||
List<StreamHost> streamHosts = new ArrayList<>();
|
List<StreamHost> streamHosts = new ArrayList<>();
|
||||||
|
|
||||||
XMPPConnection connection = connection();
|
XMPPConnection connection = connection();
|
||||||
|
|
|
@ -700,7 +700,7 @@ public final class EntityCapsManager extends Manager {
|
||||||
for (FormField f : fs) {
|
for (FormField f : fs) {
|
||||||
sb.append(f.getFieldName());
|
sb.append(f.getFieldName());
|
||||||
sb.append('<');
|
sb.append('<');
|
||||||
formFieldValuesToCaps(f.getRawValues(), sb);
|
formFieldValuesToCaps(f.getRawValueCharSequences(), sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -921,6 +921,10 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
return entityCapabilitiesChangedListeners.add(entityCapabilitiesChangedListener);
|
return entityCapabilitiesChangedListeners.add(entityCapabilitiesChangedListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean removeEntityCapabilitiesChangedListener(EntityCapabilitiesChangedListener entityCapabilitiesChangedListener) {
|
||||||
|
return entityCapabilitiesChangedListeners.remove(entityCapabilitiesChangedListener);
|
||||||
|
}
|
||||||
|
|
||||||
private static final int RENEW_ENTITY_CAPS_DELAY_MILLIS = 25;
|
private static final int RENEW_ENTITY_CAPS_DELAY_MILLIS = 25;
|
||||||
|
|
||||||
private ScheduledAction renewEntityCapsScheduledAction;
|
private ScheduledAction renewEntityCapsScheduledAction;
|
||||||
|
|
|
@ -258,6 +258,14 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView {
|
||||||
return features.contains(new Feature(feature));
|
return features.contains(new Feature(feature));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean nullSafeContainsFeature(DiscoverInfo discoverInfo, CharSequence feature) {
|
||||||
|
if (discoverInfo == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return discoverInfo.containsFeature(feature);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
|
||||||
xml.optAttribute("node", getNode());
|
xml.optAttribute("node", getNode());
|
||||||
|
|
|
@ -68,6 +68,12 @@ public class FormFieldRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void register(String formType, FormField.Type fieldType, String... fieldNames) {
|
||||||
|
for (String fieldName : fieldNames) {
|
||||||
|
register(formType, fieldName, fieldType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void register(String formType, String fieldName, FormField.Type fieldType) {
|
public static void register(String formType, String fieldName, FormField.Type fieldType) {
|
||||||
StringUtils.requireNotNullNorEmpty(fieldName, "fieldName must be provided");
|
StringUtils.requireNotNullNorEmpty(fieldName, "fieldName must be provided");
|
||||||
Objects.requireNonNull(fieldType);
|
Objects.requireNonNull(fieldType);
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class JingleUtil {
|
||||||
JingleContentDescription description,
|
JingleContentDescription description,
|
||||||
JingleContentTransport transport) {
|
JingleContentTransport transport) {
|
||||||
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setAction(JingleAction.session_initiate)
|
jb.setAction(JingleAction.session_initiate)
|
||||||
.setSessionId(sessionId)
|
.setSessionId(sessionId)
|
||||||
.setInitiator(connection.getUser());
|
.setInitiator(connection.getUser());
|
||||||
|
@ -118,7 +118,7 @@ public class JingleUtil {
|
||||||
JingleContentDescription description,
|
JingleContentDescription description,
|
||||||
JingleContentTransport transport) {
|
JingleContentTransport transport) {
|
||||||
|
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setResponder(connection.getUser())
|
jb.setResponder(connection.getUser())
|
||||||
.setAction(JingleAction.session_accept)
|
.setAction(JingleAction.session_accept)
|
||||||
.setSessionId(sessionId);
|
.setSessionId(sessionId);
|
||||||
|
@ -153,7 +153,7 @@ public class JingleUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) {
|
public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setAction(JingleAction.session_terminate)
|
jb.setAction(JingleAction.session_terminate)
|
||||||
.setSessionId(sessionId)
|
.setSessionId(sessionId)
|
||||||
.setReason(reason);
|
.setReason(reason);
|
||||||
|
@ -232,7 +232,7 @@ public class JingleUtil {
|
||||||
|
|
||||||
public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId,
|
public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId,
|
||||||
JingleContent.Creator contentCreator, String contentName) {
|
JingleContent.Creator contentCreator, String contentName) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setAction(JingleAction.session_terminate)
|
jb.setAction(JingleAction.session_terminate)
|
||||||
.setSessionId(sessionId);
|
.setSessionId(sessionId);
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ public class JingleUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Jingle createSessionPing(FullJid recipient, String sessionId) {
|
public Jingle createSessionPing(FullJid recipient, String sessionId) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setSessionId(sessionId)
|
jb.setSessionId(sessionId)
|
||||||
.setAction(JingleAction.session_info);
|
.setAction(JingleAction.session_info);
|
||||||
|
|
||||||
|
@ -343,7 +343,7 @@ public class JingleUtil {
|
||||||
public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
|
public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
JingleContent.Creator contentCreator, String contentName,
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
JingleContentTransport transport) {
|
JingleContentTransport transport) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setInitiator(initiator)
|
jb.setInitiator(initiator)
|
||||||
.setSessionId(sessionId)
|
.setSessionId(sessionId)
|
||||||
.setAction(JingleAction.transport_replace);
|
.setAction(JingleAction.transport_replace);
|
||||||
|
@ -370,7 +370,7 @@ public class JingleUtil {
|
||||||
public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
|
public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
JingleContent.Creator contentCreator, String contentName,
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
JingleContentTransport transport) {
|
JingleContentTransport transport) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setAction(JingleAction.transport_accept)
|
jb.setAction(JingleAction.transport_accept)
|
||||||
.setInitiator(initiator)
|
.setInitiator(initiator)
|
||||||
.setSessionId(sessionId);
|
.setSessionId(sessionId);
|
||||||
|
@ -397,7 +397,7 @@ public class JingleUtil {
|
||||||
public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId,
|
public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId,
|
||||||
JingleContent.Creator contentCreator, String contentName,
|
JingleContent.Creator contentCreator, String contentName,
|
||||||
JingleContentTransport transport) {
|
JingleContentTransport transport) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection);
|
||||||
jb.setAction(JingleAction.transport_reject)
|
jb.setAction(JingleAction.transport_reject)
|
||||||
.setInitiator(initiator)
|
.setInitiator(initiator)
|
||||||
.setSessionId(sessionId);
|
.setSessionId(sessionId);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus
|
* Copyright 2003-2007 Jive Software, 2014-2021 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,11 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.IqBuilder;
|
||||||
|
import org.jivesoftware.smack.packet.IqData;
|
||||||
|
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
|
@ -65,9 +69,9 @@ public final class Jingle extends IQ {
|
||||||
|
|
||||||
private final List<JingleContent> contents;
|
private final List<JingleContent> contents;
|
||||||
|
|
||||||
private Jingle(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
|
private Jingle(Builder builder, String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
|
||||||
List<JingleContent> contents) {
|
List<JingleContent> contents) {
|
||||||
super(ELEMENT, NAMESPACE);
|
super(builder, ELEMENT, NAMESPACE);
|
||||||
this.sessionId = StringUtils.requireNotNullNorEmpty(sessionId, "Jingle session ID must not be null");
|
this.sessionId = StringUtils.requireNotNullNorEmpty(sessionId, "Jingle session ID must not be null");
|
||||||
this.action = Objects.requireNonNull(action, "Jingle action must not be null");
|
this.action = Objects.requireNonNull(action, "Jingle action must not be null");
|
||||||
this.initiator = initiator;
|
this.initiator = initiator;
|
||||||
|
@ -169,11 +173,31 @@ public final class Jingle extends IQ {
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated, do not use.
|
||||||
|
*
|
||||||
|
* @return a builder.
|
||||||
|
* @deprecated use {@link #builder(XMPPConnection)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
// TODO: Remove in Smack 4.6.
|
||||||
public static Builder getBuilder() {
|
public static Builder getBuilder() {
|
||||||
return new Builder();
|
return builder(StandardStanzaIdSource.DEFAULT.getNewStanzaId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static Builder builder(XMPPConnection connection) {
|
||||||
|
return new Builder(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder(IqData iqData) {
|
||||||
|
return new Builder(iqData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder(String stanzaId) {
|
||||||
|
return new Builder(stanzaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Builder extends IqBuilder<Builder, Jingle> {
|
||||||
private String sid;
|
private String sid;
|
||||||
|
|
||||||
private JingleAction action;
|
private JingleAction action;
|
||||||
|
@ -186,7 +210,16 @@ public final class Jingle extends IQ {
|
||||||
|
|
||||||
private List<JingleContent> contents;
|
private List<JingleContent> contents;
|
||||||
|
|
||||||
private Builder() {
|
Builder(IqData iqCommon) {
|
||||||
|
super(iqCommon);
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder(XMPPConnection connection) {
|
||||||
|
super(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder(String stanzaId) {
|
||||||
|
super(stanzaId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setSessionId(String sessionId) {
|
public Builder setSessionId(String sessionId) {
|
||||||
|
@ -228,8 +261,14 @@ public final class Jingle extends IQ {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Jingle build() {
|
public Jingle build() {
|
||||||
return new Jingle(sid, action, initiator, responder, reason, contents);
|
return new Jingle(this, sid, action, initiator, responder, reason, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder getThis() {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,6 +145,11 @@ public final class JingleContent implements XmlElement {
|
||||||
xml.optAttribute(DISPOSITION_ATTRIBUTE_NAME, disposition);
|
xml.optAttribute(DISPOSITION_ATTRIBUTE_NAME, disposition);
|
||||||
xml.attribute(NAME_ATTRIBUTE_NAME, name);
|
xml.attribute(NAME_ATTRIBUTE_NAME, name);
|
||||||
xml.optAttribute(SENDERS_ATTRIBUTE_NAME, senders);
|
xml.optAttribute(SENDERS_ATTRIBUTE_NAME, senders);
|
||||||
|
|
||||||
|
if (description == null && transport == null) {
|
||||||
|
return xml.closeEmptyElement();
|
||||||
|
}
|
||||||
|
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
xml.optAppend(description);
|
xml.optAppend(description);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017-2021 Florian Schmaus
|
* Copyright 2017-2022 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.
|
||||||
|
@ -34,6 +34,7 @@ public class JingleReason implements XmlElement {
|
||||||
|
|
||||||
public static final String ELEMENT = "reason";
|
public static final String ELEMENT = "reason";
|
||||||
public static final String NAMESPACE = Jingle.NAMESPACE;
|
public static final String NAMESPACE = Jingle.NAMESPACE;
|
||||||
|
public static final String TEXT_ELEMENT = "text";
|
||||||
|
|
||||||
public static AlternativeSession AlternativeSession(String sessionId) {
|
public static AlternativeSession AlternativeSession(String sessionId) {
|
||||||
return new AlternativeSession(sessionId);
|
return new AlternativeSession(sessionId);
|
||||||
|
@ -105,9 +106,17 @@ public class JingleReason implements XmlElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Reason reason;
|
protected final Reason reason;
|
||||||
|
private final String text;
|
||||||
|
private final XmlElement element;
|
||||||
|
|
||||||
public JingleReason(Reason reason) {
|
public JingleReason(Reason reason) {
|
||||||
|
this(reason, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JingleReason(Reason reason, String text, XmlElement element) {
|
||||||
this.reason = reason;
|
this.reason = reason;
|
||||||
|
this.text = text;
|
||||||
|
this.element = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -120,12 +129,34 @@ public class JingleReason implements XmlElement {
|
||||||
return NAMESPACE;
|
return NAMESPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional text that provides human-readable information about the reason for the action.
|
||||||
|
*
|
||||||
|
* @return a human-readable text with information regarding this reason or <code>null</code>.
|
||||||
|
* @since 4.4.5
|
||||||
|
*/
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional element that provides more detailed machine-readable information about the reason for the action.
|
||||||
|
*
|
||||||
|
* @return an element with machine-readable information about this reason or <code>null</code>.
|
||||||
|
* @since 4.4.5
|
||||||
|
*/
|
||||||
|
public XmlElement getElement() {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) {
|
public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingXmlEnvironment);
|
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingXmlEnvironment);
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
xml.emptyElement(reason);
|
xml.emptyElement(reason);
|
||||||
|
xml.optElement(TEXT_ELEMENT, text);
|
||||||
|
xml.optAppend(element);
|
||||||
|
|
||||||
xml.closeElement(this);
|
xml.closeElement(this);
|
||||||
return xml;
|
return xml;
|
||||||
|
@ -142,7 +173,11 @@ public class JingleReason implements XmlElement {
|
||||||
private final String sessionId;
|
private final String sessionId;
|
||||||
|
|
||||||
public AlternativeSession(String sessionId) {
|
public AlternativeSession(String sessionId) {
|
||||||
super(Reason.alternative_session);
|
this(sessionId, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlternativeSession(String sessionId, String text, XmlElement element) {
|
||||||
|
super(Reason.alternative_session, text, element);
|
||||||
if (StringUtils.isNullOrEmpty(sessionId)) {
|
if (StringUtils.isNullOrEmpty(sessionId)) {
|
||||||
throw new NullPointerException("SessionID must not be null or empty.");
|
throw new NullPointerException("SessionID must not be null or empty.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017-2019 Florian Schmaus
|
* Copyright 2017-2022 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,11 +19,14 @@ package org.jivesoftware.smackx.jingle.provider;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.IqData;
|
||||||
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.XmlElement;
|
||||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
import org.jivesoftware.smack.parsing.SmackParsingException;
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
|
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.IQProvider;
|
import org.jivesoftware.smack.provider.IqProvider;
|
||||||
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
@ -40,13 +43,13 @@ import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransport;
|
||||||
|
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
|
|
||||||
public class JingleProvider extends IQProvider<Jingle> {
|
public class JingleProvider extends IqProvider<Jingle> {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Jingle parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
public Jingle parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
Jingle.Builder builder = Jingle.builder(iqData);
|
||||||
|
|
||||||
String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
|
String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
|
||||||
if (actionString != null) {
|
if (actionString != null) {
|
||||||
|
@ -75,16 +78,7 @@ public class JingleProvider extends IQProvider<Jingle> {
|
||||||
builder.addJingleContent(content);
|
builder.addJingleContent(content);
|
||||||
break;
|
break;
|
||||||
case JingleReason.ELEMENT:
|
case JingleReason.ELEMENT:
|
||||||
parser.next();
|
JingleReason reason = parseJingleReason(parser);
|
||||||
String reasonString = parser.getName();
|
|
||||||
JingleReason reason;
|
|
||||||
if (reasonString.equals("alternative-session")) {
|
|
||||||
parser.next();
|
|
||||||
String sid = parser.nextText();
|
|
||||||
reason = new JingleReason.AlternativeSession(sid);
|
|
||||||
} else {
|
|
||||||
reason = new JingleReason(Reason.fromString(reasonString));
|
|
||||||
}
|
|
||||||
builder.setReason(reason);
|
builder.setReason(reason);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -177,4 +171,57 @@ public class JingleProvider extends IQProvider<Jingle> {
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JingleReason parseJingleReason(XmlPullParser parser)
|
||||||
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
ParserUtils.assertAtStartTag(parser);
|
||||||
|
final int initialDepth = parser.getDepth();
|
||||||
|
final String jingleNamespace = parser.getNamespace();
|
||||||
|
|
||||||
|
JingleReason.Reason reason = null;
|
||||||
|
XmlElement element = null;
|
||||||
|
String text = null;
|
||||||
|
|
||||||
|
// 'sid' is only set if the reason is 'alternative-session'.
|
||||||
|
String sid = null;
|
||||||
|
|
||||||
|
outerloop: while (true) {
|
||||||
|
XmlPullParser.TagEvent event = parser.nextTag();
|
||||||
|
switch (event) {
|
||||||
|
case START_ELEMENT:
|
||||||
|
String elementName = parser.getName();
|
||||||
|
String namespace = parser.getNamespace();
|
||||||
|
if (namespace.equals(jingleNamespace)) {
|
||||||
|
switch (elementName) {
|
||||||
|
case "text":
|
||||||
|
text = parser.nextText();
|
||||||
|
break;
|
||||||
|
case "alternative-session":
|
||||||
|
parser.next();
|
||||||
|
sid = parser.nextText();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reason = Reason.fromString(elementName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
element = PacketParserUtils.parseExtensionElement(elementName, namespace, parser, null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case END_ELEMENT:
|
||||||
|
if (parser.getDepth() == initialDepth) {
|
||||||
|
break outerloop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JingleReason res;
|
||||||
|
if (sid != null) {
|
||||||
|
res = new JingleReason.AlternativeSession(sid, text, element);
|
||||||
|
} else {
|
||||||
|
res = new JingleReason(reason, text, element);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,10 @@ public abstract class JingleTransportManager<D extends JingleContentTransport> i
|
||||||
}
|
}
|
||||||
|
|
||||||
public XMPPConnection getConnection() {
|
public XMPPConnection getConnection() {
|
||||||
|
return connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public XMPPConnection connection() {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
|
||||||
public Jingle createCandidateUsed(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Senders contentSenders,
|
public Jingle createCandidateUsed(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Senders contentSenders,
|
||||||
JingleContent.Creator contentCreator, String contentName, String streamId,
|
JingleContent.Creator contentCreator, String contentName, String streamId,
|
||||||
String candidateId) {
|
String candidateId) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection());
|
||||||
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
|
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
|
||||||
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
@ -165,7 +165,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
|
||||||
}
|
}
|
||||||
|
|
||||||
public Jingle createCandidateError(FullJid remote, FullJid initiator, String sessionId, JingleContent.Senders senders, JingleContent.Creator creator, String name, String streamId) {
|
public Jingle createCandidateError(FullJid remote, FullJid initiator, String sessionId, JingleContent.Senders senders, JingleContent.Creator creator, String name, String streamId) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection());
|
||||||
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
|
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
|
||||||
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
@ -184,7 +184,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
|
||||||
public Jingle createProxyError(FullJid remote, FullJid initiator, String sessionId,
|
public Jingle createProxyError(FullJid remote, FullJid initiator, String sessionId,
|
||||||
JingleContent.Senders senders, JingleContent.Creator creator,
|
JingleContent.Senders senders, JingleContent.Creator creator,
|
||||||
String name, String streamId) {
|
String name, String streamId) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection());
|
||||||
jb.setSessionId(sessionId).setAction(JingleAction.transport_info).setInitiator(initiator);
|
jb.setSessionId(sessionId).setAction(JingleAction.transport_info).setInitiator(initiator);
|
||||||
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
@ -202,7 +202,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
|
||||||
public Jingle createCandidateActivated(FullJid remote, FullJid initiator, String sessionId,
|
public Jingle createCandidateActivated(FullJid remote, FullJid initiator, String sessionId,
|
||||||
JingleContent.Senders senders, JingleContent.Creator creator,
|
JingleContent.Senders senders, JingleContent.Creator creator,
|
||||||
String name, String streamId, String candidateId) {
|
String name, String streamId, String candidateId) {
|
||||||
Jingle.Builder jb = Jingle.getBuilder();
|
Jingle.Builder jb = Jingle.builder(connection());
|
||||||
jb.setInitiator(initiator).setSessionId(sessionId).setAction(JingleAction.transport_info);
|
jb.setInitiator(initiator).setSessionId(sessionId).setAction(JingleAction.transport_info);
|
||||||
|
|
||||||
JingleContent.Builder cb = JingleContent.getBuilder();
|
JingleContent.Builder cb = JingleContent.getBuilder();
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.XmlEnvironment;
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smack.util.DoOnce;
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
@ -35,6 +36,8 @@ import org.jivesoftware.smackx.jiveproperties.packet.JivePropertiesExtension;
|
||||||
|
|
||||||
public class JivePropertiesExtensionProvider extends ExtensionElementProvider<JivePropertiesExtension> {
|
public class JivePropertiesExtensionProvider extends ExtensionElementProvider<JivePropertiesExtension> {
|
||||||
|
|
||||||
|
private static final DoOnce LOG_OBJECT_NOT_ENABLED = new DoOnce();
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(JivePropertiesExtensionProvider.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(JivePropertiesExtensionProvider.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,7 +116,10 @@ public class JivePropertiesExtensionProvider extends ExtensionElementProvider<Ji
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOGGER.severe("JavaObject is not enabled. Enable with JivePropertiesManager.setJavaObjectEnabled(true)");
|
LOG_OBJECT_NOT_ENABLED.once(
|
||||||
|
() -> LOGGER.severe(
|
||||||
|
"JavaObject is not enabled. Enable with JivePropertiesManager.setJavaObjectEnabled(true)")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (name != null && value != null) {
|
if (name != null && value != null) {
|
||||||
|
|
|
@ -78,6 +78,17 @@ public class MucConfigFormManager {
|
||||||
*/
|
*/
|
||||||
public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret";
|
public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constant String {@value}.
|
||||||
|
*/
|
||||||
|
public static final String MUC_ROOMCONFIG_MODERATEDROOM = "muc#roomconfig_moderatedroom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constant String {@value}.
|
||||||
|
*/
|
||||||
|
public static final String MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM = "muc#roomconfig_publicroom";
|
||||||
|
|
||||||
|
|
||||||
private final MultiUserChat multiUserChat;
|
private final MultiUserChat multiUserChat;
|
||||||
private final FillableForm answerForm;
|
private final FillableForm answerForm;
|
||||||
private final List<Jid> owners;
|
private final List<Jid> owners;
|
||||||
|
@ -151,6 +162,15 @@ public class MucConfigFormManager {
|
||||||
return answerForm.hasField(MUC_ROOMCONFIG_MEMBERSONLY);
|
return answerForm.hasField(MUC_ROOMCONFIG_MEMBERSONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the room supports being moderated in the configuration.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if supported, <code>false</code> if not.
|
||||||
|
*/
|
||||||
|
public boolean supportsModeration() {
|
||||||
|
return answerForm.hasField(MUC_ROOMCONFIG_MODERATEDROOM);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make the room for members only.
|
* Make the room for members only.
|
||||||
*
|
*
|
||||||
|
@ -176,6 +196,68 @@ public class MucConfigFormManager {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the room moderated.
|
||||||
|
*
|
||||||
|
* @return a reference to this object.
|
||||||
|
* @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
|
||||||
|
*/
|
||||||
|
public MucConfigFormManager makeModerated() throws MucConfigurationNotSupportedException {
|
||||||
|
return setModerated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if the room is members only. Rooms are not members only per default.
|
||||||
|
*
|
||||||
|
* @param isModerated if the room should be moderated.
|
||||||
|
* @return a reference to this object.
|
||||||
|
* @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
|
||||||
|
*/
|
||||||
|
public MucConfigFormManager setModerated(boolean isModerated) throws MucConfigurationNotSupportedException {
|
||||||
|
if (!supportsModeration()) {
|
||||||
|
throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_MODERATEDROOM);
|
||||||
|
}
|
||||||
|
answerForm.setAnswer(MUC_ROOMCONFIG_MODERATEDROOM, isModerated);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the room publicly searchable.
|
||||||
|
*
|
||||||
|
* @return a reference to this object.
|
||||||
|
* @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
|
||||||
|
*/
|
||||||
|
public MucConfigFormManager makePublic() throws MucConfigurationNotSupportedException {
|
||||||
|
return setPublic(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the room hidden (not publicly searchable).
|
||||||
|
*
|
||||||
|
* @return a reference to this object.
|
||||||
|
* @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
|
||||||
|
*/
|
||||||
|
public MucConfigFormManager makeHidden() throws MucConfigurationNotSupportedException {
|
||||||
|
return setPublic(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if the room is publicly searchable (i.e. visible via discovery requests to the MUC service).
|
||||||
|
*
|
||||||
|
* @param isPublic if the room should be publicly searchable.
|
||||||
|
* @return a reference to this object.
|
||||||
|
* @throws MucConfigurationNotSupportedException if the requested MUC configuration is not supported by the MUC service.
|
||||||
|
*/
|
||||||
|
public MucConfigFormManager setPublic(boolean isPublic) throws MucConfigurationNotSupportedException {
|
||||||
|
if (!supportsModeration()) {
|
||||||
|
throw new MucConfigurationNotSupportedException(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM);
|
||||||
|
}
|
||||||
|
answerForm.setAnswer(MUC_ROOMCONFIG_PUBLICLYSEARCHABLEROOM, isPublic);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the room supports password protection.
|
* Check if the room supports password protection.
|
||||||
*
|
*
|
||||||
|
|
|
@ -205,12 +205,20 @@ public class MultiUserChat {
|
||||||
if (from == null) {
|
if (from == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final EntityFullJid myRoomJID = myRoomJid;
|
final EntityFullJid myRoomJID = getMyRoomJid();
|
||||||
final boolean isUserStatusModification = presence.getFrom().equals(myRoomJID);
|
final boolean isUserStatusModification = presence.getFrom().equals(myRoomJID);
|
||||||
final MUCUser mucUser = MUCUser.from(packet);
|
final MUCUser mucUser = MUCUser.from(packet);
|
||||||
|
|
||||||
switch (presence.getType()) {
|
switch (presence.getType()) {
|
||||||
case available:
|
case available:
|
||||||
|
if (!processedReflectedSelfPresence
|
||||||
|
&& mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) {
|
||||||
|
processedReflectedSelfPresence = true;
|
||||||
|
synchronized (this) {
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Presence oldPresence = occupantsMap.put(from, presence);
|
Presence oldPresence = occupantsMap.put(from, presence);
|
||||||
if (oldPresence != null) {
|
if (oldPresence != null) {
|
||||||
// Get the previous occupant's affiliation & role
|
// Get the previous occupant's affiliation & role
|
||||||
|
@ -228,11 +236,6 @@ public class MultiUserChat {
|
||||||
newAffiliation,
|
newAffiliation,
|
||||||
isUserStatusModification,
|
isUserStatusModification,
|
||||||
from);
|
from);
|
||||||
} else if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) {
|
|
||||||
processedReflectedSelfPresence = true;
|
|
||||||
synchronized (this) {
|
|
||||||
notify();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// A new occupant has joined the room
|
// A new occupant has joined the room
|
||||||
for (ParticipantStatusListener listener : participantStatusListeners) {
|
for (ParticipantStatusListener listener : participantStatusListeners) {
|
||||||
|
@ -259,6 +262,7 @@ public class MultiUserChat {
|
||||||
listener.left(from);
|
listener.left(from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Destroy destroy = mucUser.getDestroy();
|
Destroy destroy = mucUser.getDestroy();
|
||||||
// The room has been destroyed.
|
// The room has been destroyed.
|
||||||
|
@ -275,7 +279,7 @@ public class MultiUserChat {
|
||||||
listener.roomDestroyed(alternateMuc, destroy.getReason());
|
listener.roomDestroyed(alternateMuc, destroy.getReason());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (isUserStatusModification) {
|
if (isUserStatusModification) {
|
||||||
for (UserStatusListener listener : userStatusListeners) {
|
for (UserStatusListener listener : userStatusListeners) {
|
||||||
listener.removed(mucUser, presence);
|
listener.removed(mucUser, presence);
|
||||||
|
@ -728,7 +732,7 @@ public class MultiUserChat {
|
||||||
* @return true if currently in the multi user chat room.
|
* @return true if currently in the multi user chat room.
|
||||||
*/
|
*/
|
||||||
public boolean isJoined() {
|
public boolean isJoined() {
|
||||||
return myRoomJid != null;
|
return getMyRoomJid() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -764,7 +768,7 @@ public class MultiUserChat {
|
||||||
// "if (!joined) return" because it should be always be possible to leave the room in case the instance's
|
// "if (!joined) return" because it should be always be possible to leave the room in case the instance's
|
||||||
// state does not reflect the actual state.
|
// state does not reflect the actual state.
|
||||||
|
|
||||||
final EntityFullJid myRoomJid = this.myRoomJid;
|
final EntityFullJid myRoomJid = getMyRoomJid();
|
||||||
if (myRoomJid == null) {
|
if (myRoomJid == null) {
|
||||||
throw new MucNotJoinedException(this);
|
throw new MucNotJoinedException(this);
|
||||||
}
|
}
|
||||||
|
@ -793,11 +797,14 @@ public class MultiUserChat {
|
||||||
|
|
||||||
StanzaFilter reflectedLeavePresenceFilter = new AndFilter(reflectedLeavePresenceFilters);
|
StanzaFilter reflectedLeavePresenceFilter = new AndFilter(reflectedLeavePresenceFilters);
|
||||||
|
|
||||||
// Reset occupant information first so that we are assume that we left the room even if sendStanza() would
|
Presence reflectedLeavePresence;
|
||||||
// throw.
|
try {
|
||||||
|
reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow();
|
||||||
|
} finally {
|
||||||
|
// Reset occupant information after we send the leave presence. This ensures that we only call userHasLeft()
|
||||||
|
// and reset the local MUC state after we successfully left the MUC (or if an exception occurred).
|
||||||
userHasLeft();
|
userHasLeft();
|
||||||
|
}
|
||||||
Presence reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow();
|
|
||||||
|
|
||||||
return reflectedLeavePresence;
|
return reflectedLeavePresence;
|
||||||
}
|
}
|
||||||
|
@ -1193,13 +1200,23 @@ public class MultiUserChat {
|
||||||
* @return the nickname currently being used.
|
* @return the nickname currently being used.
|
||||||
*/
|
*/
|
||||||
public Resourcepart getNickname() {
|
public Resourcepart getNickname() {
|
||||||
final EntityFullJid myRoomJid = this.myRoomJid;
|
final EntityFullJid myRoomJid = getMyRoomJid();
|
||||||
if (myRoomJid == null) {
|
if (myRoomJid == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return myRoomJid.getResourcepart();
|
return myRoomJid.getResourcepart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the full JID of the user in the room, or <code>null</code> if the room is not joined.
|
||||||
|
*
|
||||||
|
* @return the full JID of the user in the room, or <code>null</code>.
|
||||||
|
* @since 4.5.0
|
||||||
|
*/
|
||||||
|
public EntityFullJid getMyRoomJid() {
|
||||||
|
return myRoomJid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the occupant's nickname to a new nickname within the room. Each room occupant
|
* Changes the occupant's nickname to a new nickname within the room. Each room occupant
|
||||||
* will receive two presence packets. One of type "unavailable" for the old nickname and one
|
* will receive two presence packets. One of type "unavailable" for the old nickname and one
|
||||||
|
@ -1256,7 +1273,7 @@ public class MultiUserChat {
|
||||||
* @throws MucNotJoinedException if not joined to the Multi-User Chat.
|
* @throws MucNotJoinedException if not joined to the Multi-User Chat.
|
||||||
*/
|
*/
|
||||||
public void changeAvailabilityStatus(String status, Presence.Mode mode) throws NotConnectedException, InterruptedException, MucNotJoinedException {
|
public void changeAvailabilityStatus(String status, Presence.Mode mode) throws NotConnectedException, InterruptedException, MucNotJoinedException {
|
||||||
final EntityFullJid myRoomJid = this.myRoomJid;
|
final EntityFullJid myRoomJid = getMyRoomJid();
|
||||||
if (myRoomJid == null) {
|
if (myRoomJid == null) {
|
||||||
throw new MucNotJoinedException(this);
|
throw new MucNotJoinedException(this);
|
||||||
}
|
}
|
||||||
|
@ -2579,7 +2596,7 @@ public class MultiUserChat {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean serviceSupportsStableIds() {
|
public boolean serviceSupportsStableIds() {
|
||||||
return mucServiceDiscoInfo.containsFeature(MultiUserChatConstants.STABLE_ID_FEATURE);
|
return DiscoverInfo.nullSafeContainsFeature(mucServiceDiscoInfo, MultiUserChatConstants.STABLE_ID_FEATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -38,11 +38,7 @@ public class FormNode extends NodeExtension {
|
||||||
* @param submitForm The form
|
* @param submitForm The form
|
||||||
*/
|
*/
|
||||||
public FormNode(FormNodeType formType, DataForm submitForm) {
|
public FormNode(FormNodeType formType, DataForm submitForm) {
|
||||||
super(formType.getNodeElement());
|
this(formType, null, submitForm);
|
||||||
|
|
||||||
if (submitForm == null)
|
|
||||||
throw new IllegalArgumentException("Submit form cannot be null");
|
|
||||||
configForm = submitForm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,9 +51,6 @@ public class FormNode extends NodeExtension {
|
||||||
*/
|
*/
|
||||||
public FormNode(FormNodeType formType, String nodeId, DataForm submitForm) {
|
public FormNode(FormNodeType formType, String nodeId, DataForm submitForm) {
|
||||||
super(formType.getNodeElement(), nodeId);
|
super(formType.getNodeElement(), nodeId);
|
||||||
|
|
||||||
if (submitForm == null)
|
|
||||||
throw new IllegalArgumentException("Submit form cannot be null");
|
|
||||||
configForm = submitForm;
|
configForm = submitForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,11 @@ import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
public class FormNodeProvider extends EmbeddedExtensionProvider<FormNode> {
|
public class FormNodeProvider extends EmbeddedExtensionProvider<FormNode> {
|
||||||
@Override
|
@Override
|
||||||
protected FormNode createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends XmlElement> content) {
|
protected FormNode createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends XmlElement> content) {
|
||||||
return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), (DataForm) content.iterator().next());
|
DataForm dataForm = null;
|
||||||
|
if (!content.isEmpty()) {
|
||||||
|
dataForm = (DataForm) content.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), dataForm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,9 @@ public class ItemProvider extends ExtensionElementProvider<Item> {
|
||||||
String xmlns = parser.getNamespace();
|
String xmlns = parser.getNamespace();
|
||||||
ItemNamespace itemNamespace = ItemNamespace.fromXmlns(xmlns);
|
ItemNamespace itemNamespace = ItemNamespace.fromXmlns(xmlns);
|
||||||
|
|
||||||
XmlPullParser.Event tag = parser.next();
|
XmlPullParser.TagEvent event = parser.nextTag();
|
||||||
|
switch (event) {
|
||||||
if (tag == XmlPullParser.Event.END_ELEMENT) {
|
case START_ELEMENT:
|
||||||
return new Item(itemNamespace, id, node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
String payloadElemName = parser.getName();
|
String payloadElemName = parser.getName();
|
||||||
String payloadNS = parser.getNamespace();
|
String payloadNS = parser.getNamespace();
|
||||||
|
|
||||||
|
@ -68,6 +65,10 @@ public class ItemProvider extends ExtensionElementProvider<Item> {
|
||||||
else {
|
else {
|
||||||
return new PayloadItem<>(itemNamespace, id, node, extensionProvider.parse(parser));
|
return new PayloadItem<>(itemNamespace, id, node, extensionProvider.parse(parser));
|
||||||
}
|
}
|
||||||
|
case END_ELEMENT:
|
||||||
|
return new Item(itemNamespace, id, node);
|
||||||
|
default:
|
||||||
|
throw new AssertionError("unknown: " + event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Aditya Borikar
|
* Copyright 2020 Aditya Borikar, 2021 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.
|
||||||
|
@ -20,7 +20,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.EqualsUtil;
|
import org.jivesoftware.smack.util.EqualsUtil;
|
||||||
import org.jivesoftware.smack.util.HashCode;
|
import org.jivesoftware.smack.util.HashCode;
|
||||||
|
import org.jivesoftware.smackx.formtypes.FormFieldRegistry;
|
||||||
import org.jivesoftware.smackx.mediaelement.element.MediaElement;
|
import org.jivesoftware.smackx.mediaelement.element.MediaElement;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
import org.jivesoftware.smackx.xdata.FormFieldChildElement;
|
import org.jivesoftware.smackx.xdata.FormFieldChildElement;
|
||||||
|
@ -47,6 +47,11 @@ public final class SoftwareInfoForm extends FilledForm {
|
||||||
public static final String SOFTWARE_VERSION = "software_version";
|
public static final String SOFTWARE_VERSION = "software_version";
|
||||||
public static final String ICON = "icon";
|
public static final String ICON = "icon";
|
||||||
|
|
||||||
|
static {
|
||||||
|
FormFieldRegistry.register(FORM_TYPE, FormField.Type.text_single,
|
||||||
|
OS, OS_VERSION, SOFTWARE, SOFTWARE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
private SoftwareInfoForm(DataForm dataForm) {
|
private SoftwareInfoForm(DataForm dataForm) {
|
||||||
super(dataForm);
|
super(dataForm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,35 +27,26 @@ import org.jxmpp.util.XmppDateTime;
|
||||||
|
|
||||||
public class AbstractMultiFormField extends FormField {
|
public class AbstractMultiFormField extends FormField {
|
||||||
|
|
||||||
private final List<String> values;
|
private final List<Value> values;
|
||||||
|
|
||||||
private final List<String> rawValues;
|
|
||||||
|
|
||||||
protected AbstractMultiFormField(Builder<?, ?> builder) {
|
protected AbstractMultiFormField(Builder<?, ?> builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
values = CollectionUtil.cloneAndSeal(builder.values);
|
values = CollectionUtil.cloneAndSeal(builder.values);
|
||||||
rawValues = CollectionUtil.cloneAndSeal(builder.rawValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final List<String> getValues() {
|
public final List<Value> getRawValues() {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final List<String> getRawValues() {
|
|
||||||
return rawValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class Builder<F extends AbstractMultiFormField, B extends FormField.Builder<F, B>>
|
public abstract static class Builder<F extends AbstractMultiFormField, B extends FormField.Builder<F, B>>
|
||||||
extends FormField.Builder<F, B> {
|
extends FormField.Builder<F, B> {
|
||||||
|
|
||||||
private List<String> values;
|
private List<Value> values;
|
||||||
private List<String> rawValues;
|
|
||||||
|
|
||||||
protected Builder(AbstractMultiFormField formField) {
|
protected Builder(AbstractMultiFormField formField) {
|
||||||
super(formField);
|
super(formField);
|
||||||
values = CollectionUtil.newListWith(formField.getValues());
|
values = CollectionUtil.newListWith(formField.getRawValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Builder(String fieldName, FormField.Type type) {
|
protected Builder(String fieldName, FormField.Type type) {
|
||||||
|
@ -65,7 +56,6 @@ public class AbstractMultiFormField extends FormField {
|
||||||
private void ensureValuesAreInitialized() {
|
private void ensureValuesAreInitialized() {
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
values = new ArrayList<>();
|
values = new ArrayList<>();
|
||||||
rawValues = new ArrayList<>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,11 +67,13 @@ public class AbstractMultiFormField extends FormField {
|
||||||
public abstract B addValue(CharSequence value);
|
public abstract B addValue(CharSequence value);
|
||||||
|
|
||||||
public B addValueVerbatim(CharSequence value) {
|
public B addValueVerbatim(CharSequence value) {
|
||||||
|
return addValueVerbatim(new Value(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public B addValueVerbatim(Value value) {
|
||||||
ensureValuesAreInitialized();
|
ensureValuesAreInitialized();
|
||||||
|
|
||||||
String valueString = value.toString();
|
values.add(value);
|
||||||
values.add(valueString);
|
|
||||||
rawValues.add(valueString);
|
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,8 +74,15 @@ public class AbstractSingleStringValueFormField extends SingleValueFormField {
|
||||||
return setValue(value);
|
return setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public B setValue(Value value) {
|
||||||
|
this.value = value.getValue().toString();
|
||||||
|
this.rawValue = value;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
public B setValue(CharSequence value) {
|
public B setValue(CharSequence value) {
|
||||||
this.rawValue = this.value = value.toString();
|
this.value = value.toString();
|
||||||
|
rawValue = new Value(this.value);
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,31 @@ public class BooleanFormField extends SingleValueFormField {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getValueAsBoolean() {
|
/**
|
||||||
|
* Get the value of the booelan field. Note that, if no explicit boolean value is provided, in the form of "true",
|
||||||
|
* "false", "0", or "1", then the default value of a boolean field is <code>false</code>, according to
|
||||||
|
* XEP-0004 § 3.3.
|
||||||
|
*
|
||||||
|
* @return the boolean value of this form field.
|
||||||
|
*/
|
||||||
|
public boolean getValueAsBoolean() {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the boolean field or maybe <code>null</code>. Note that you usually want to use
|
||||||
|
* {@link #getValueAsBoolean()} instead of this method, as {@link #getValueAsBoolean()} considers the default value
|
||||||
|
* of boolean fields. That is, boolean form fields have the value <code>false</code> if not explicitly set to
|
||||||
|
* something else.
|
||||||
|
*
|
||||||
|
* @return the boolean value of this form field or <code>null</code> if no value was explicitly provided.
|
||||||
|
* @see #getValueAsBoolean()
|
||||||
|
* @since 4.4.5
|
||||||
|
*/
|
||||||
|
public Boolean getValueAsBooleanOrNull() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,17 +95,21 @@ public class BooleanFormField extends SingleValueFormField {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
// TODO: Remove in Smack 4.6.
|
// TODO: Remove in Smack 4.6.
|
||||||
public Builder addValue(CharSequence value) {
|
public Builder addValue(CharSequence value) {
|
||||||
return setValue(value);
|
return setValue(new Value(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setValue(CharSequence value) {
|
public Builder setValue(CharSequence value) {
|
||||||
rawValue = value.toString();
|
return setValue(new Value(value));
|
||||||
boolean valueBoolean = ParserUtils.parseXmlBoolean(rawValue);
|
}
|
||||||
return setValue(valueBoolean);
|
|
||||||
|
public Builder setValue(Value value) {
|
||||||
|
this.value = ParserUtils.parseXmlBoolean(value.getValue().toString());
|
||||||
|
rawValue = value;
|
||||||
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setValue(boolean value) {
|
public Builder setValue(boolean value) {
|
||||||
rawValue = Boolean.toString(value);
|
rawValue = new Value(Boolean.toString(value));
|
||||||
this.value = value;
|
this.value = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
import org.jivesoftware.smack.util.EqualsUtil;
|
import org.jivesoftware.smack.util.EqualsUtil;
|
||||||
import org.jivesoftware.smack.util.HashCode;
|
import org.jivesoftware.smack.util.HashCode;
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
import org.jivesoftware.smack.util.MultiMap;
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
@ -269,9 +270,25 @@ public abstract class FormField implements XmlElement {
|
||||||
*
|
*
|
||||||
* @return a List of the default values or answered values of the question.
|
* @return a List of the default values or answered values of the question.
|
||||||
*/
|
*/
|
||||||
public abstract List<? extends CharSequence> getValues();
|
public List<? extends CharSequence> getValues() {
|
||||||
|
return getRawValueCharSequences();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract List<String> getRawValues();
|
public abstract List<Value> getRawValues();
|
||||||
|
|
||||||
|
private transient List<CharSequence> rawValueCharSequences;
|
||||||
|
|
||||||
|
public final List<CharSequence> getRawValueCharSequences() {
|
||||||
|
if (rawValueCharSequences == null) {
|
||||||
|
List<Value> rawValues = getRawValues();
|
||||||
|
rawValueCharSequences = new ArrayList<>(rawValues.size());
|
||||||
|
for (Value value : rawValues) {
|
||||||
|
rawValueCharSequences.add(value.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawValueCharSequences;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasValueSet() {
|
public boolean hasValueSet() {
|
||||||
List<?> values = getValues();
|
List<?> values = getValues();
|
||||||
|
@ -385,12 +402,15 @@ public abstract class FormField implements XmlElement {
|
||||||
|
|
||||||
protected transient List<XmlElement> extraXmlChildElements;
|
protected transient List<XmlElement> extraXmlChildElements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate @{link {@link #extraXmlChildElements}}. Note that this method may be overridden by subclasses.
|
||||||
|
*/
|
||||||
protected void populateExtraXmlChildElements() {
|
protected void populateExtraXmlChildElements() {
|
||||||
List<? extends CharSequence> values = getValues();
|
List<Value> values = getRawValues();
|
||||||
|
// Note that we need to create a new ArrayList here, since subclasses may add to it by overriding
|
||||||
|
// populateExtraXmlChildElements.
|
||||||
extraXmlChildElements = new ArrayList<>(values.size());
|
extraXmlChildElements = new ArrayList<>(values.size());
|
||||||
for (CharSequence value : values) {
|
extraXmlChildElements.addAll(values);
|
||||||
extraXmlChildElements.add(new Value(value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -414,7 +434,8 @@ public abstract class FormField implements XmlElement {
|
||||||
populateExtraXmlChildElements();
|
populateExtraXmlChildElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formFieldChildElements.isEmpty() && extraXmlChildElements == null) {
|
if (formFieldChildElements.isEmpty()
|
||||||
|
&& (extraXmlChildElements == null || extraXmlChildElements.isEmpty())) {
|
||||||
buf.closeEmptyElement();
|
buf.closeEmptyElement();
|
||||||
} else {
|
} else {
|
||||||
buf.rightAngleBracket();
|
buf.rightAngleBracket();
|
||||||
|
@ -580,7 +601,7 @@ public abstract class FormField implements XmlElement {
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
*/
|
*/
|
||||||
public B setLabel(String label) {
|
public B setLabel(String label) {
|
||||||
this.label = StringUtils.requireNotNullNorEmpty(label, "label must not be null or empty");
|
this.label = Objects.requireNonNull(label, "label must not be null");
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,14 @@ import java.util.List;
|
||||||
import org.jivesoftware.smack.util.CollectionUtil;
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
import org.jxmpp.jid.util.JidUtil;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
public final class JidMultiFormField extends FormField {
|
public final class JidMultiFormField extends FormField {
|
||||||
|
|
||||||
private final List<Jid> values;
|
private final List<Jid> values;
|
||||||
|
|
||||||
private final List<String> rawValues;
|
private final List<Value> rawValues;
|
||||||
|
|
||||||
JidMultiFormField(Builder builder) {
|
JidMultiFormField(Builder builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
|
@ -43,7 +44,7 @@ public final class JidMultiFormField extends FormField {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getRawValues() {
|
public List<Value> getRawValues() {
|
||||||
return rawValues;
|
return rawValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ public final class JidMultiFormField extends FormField {
|
||||||
public static final class Builder extends FormField.Builder<JidMultiFormField, JidMultiFormField.Builder> {
|
public static final class Builder extends FormField.Builder<JidMultiFormField, JidMultiFormField.Builder> {
|
||||||
private List<Jid> values;
|
private List<Jid> values;
|
||||||
|
|
||||||
private List<String> rawValues;
|
private List<Value> rawValues;
|
||||||
|
|
||||||
private Builder(JidMultiFormField jidMultiFormField) {
|
private Builder(JidMultiFormField jidMultiFormField) {
|
||||||
super(jidMultiFormField);
|
super(jidMultiFormField);
|
||||||
|
@ -79,27 +80,29 @@ public final class JidMultiFormField extends FormField {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addValue(Jid jid) {
|
public Builder addValue(Jid jid) {
|
||||||
return addValue(jid, null);
|
Value value = new Value(jid);
|
||||||
}
|
|
||||||
|
|
||||||
public Builder addValue(Jid jid, String rawValue) {
|
|
||||||
if (rawValue == null) {
|
|
||||||
rawValue = jid.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureValuesAreInitialized();
|
ensureValuesAreInitialized();
|
||||||
|
|
||||||
values.add(jid);
|
values.add(jid);
|
||||||
rawValues.add(rawValue);
|
rawValues.add(value);
|
||||||
|
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder addValue(Value value) throws XmppStringprepException {
|
||||||
|
Jid jid = JidCreate.from(value.getValue());
|
||||||
|
|
||||||
|
ensureValuesAreInitialized();
|
||||||
|
values.add(jid);
|
||||||
|
rawValues.add(value);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addValues(Collection<? extends Jid> jids) {
|
public Builder addValues(Collection<? extends Jid> jids) {
|
||||||
ensureValuesAreInitialized();
|
for (Jid jid : jids) {
|
||||||
|
addValue(jid);
|
||||||
values.addAll(jids);
|
}
|
||||||
rawValues.addAll(JidUtil.toStringList(jids));
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package org.jivesoftware.smackx.xdata;
|
package org.jivesoftware.smackx.xdata;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
public class JidSingleFormField extends SingleValueFormField {
|
public class JidSingleFormField extends SingleValueFormField {
|
||||||
|
|
||||||
|
@ -60,11 +62,13 @@ public class JidSingleFormField extends SingleValueFormField {
|
||||||
|
|
||||||
public Builder setValue(Jid value, String rawValue) {
|
public Builder setValue(Jid value, String rawValue) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
if (rawValue != null) {
|
this.rawValue = new Value(value);
|
||||||
this.rawValue = rawValue;
|
return getThis();
|
||||||
} else {
|
|
||||||
this.rawValue = value.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setValue(Value value) throws XmppStringprepException {
|
||||||
|
this.value = JidCreate.from(value.getValue());
|
||||||
|
this.rawValue = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
|
|
||||||
public abstract class SingleValueFormField extends FormField {
|
public abstract class SingleValueFormField extends FormField {
|
||||||
|
|
||||||
private final String rawValue;
|
private final Value rawValue;
|
||||||
|
|
||||||
protected SingleValueFormField(Builder<?, ?> builder) {
|
protected SingleValueFormField(Builder<?, ?> builder) {
|
||||||
super(builder);
|
super(builder);
|
||||||
|
@ -38,24 +38,23 @@ public abstract class SingleValueFormField extends FormField {
|
||||||
|
|
||||||
public abstract CharSequence getValue();
|
public abstract CharSequence getValue();
|
||||||
|
|
||||||
public final String getRawValue() {
|
public final Value getRawValue() {
|
||||||
return rawValue;
|
return rawValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final List<String> getRawValues() {
|
public final List<Value> getRawValues() {
|
||||||
String rawValue = getRawValue();
|
Value rawValue = getRawValue();
|
||||||
return CollectionUtil.emptyOrSingletonListFrom(rawValue);
|
return CollectionUtil.emptyOrSingletonListFrom(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void populateExtraXmlChildElements() {
|
protected void populateExtraXmlChildElements() {
|
||||||
CharSequence value = getValue();
|
if (rawValue == null) {
|
||||||
if (value == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
extraXmlChildElements = Collections.singletonList(new Value(value));
|
extraXmlChildElements = Collections.singletonList(rawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class Builder<F extends SingleValueFormField, B extends Builder<F, B>>
|
public abstract static class Builder<F extends SingleValueFormField, B extends Builder<F, B>>
|
||||||
|
@ -65,11 +64,12 @@ public abstract class SingleValueFormField extends FormField {
|
||||||
super(fieldName, type);
|
super(fieldName, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Builder(FormField formField) {
|
protected Builder(SingleValueFormField formField) {
|
||||||
super(formField);
|
super(formField);
|
||||||
|
rawValue = formField.getRawValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String rawValue;
|
protected Value rawValue;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetInternal() {
|
protected void resetInternal() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus
|
* Copyright 2020-2021 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,6 +21,8 @@ import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.xdata.AbstractMultiFormField;
|
import org.jivesoftware.smackx.xdata.AbstractMultiFormField;
|
||||||
import org.jivesoftware.smackx.xdata.AbstractSingleStringValueFormField;
|
import org.jivesoftware.smackx.xdata.AbstractSingleStringValueFormField;
|
||||||
import org.jivesoftware.smackx.xdata.BooleanFormField;
|
import org.jivesoftware.smackx.xdata.BooleanFormField;
|
||||||
|
@ -54,7 +56,8 @@ public interface FormReader {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
AbstractMultiFormField multiFormField = formField.ifPossibleAs(AbstractMultiFormField.class);
|
AbstractMultiFormField multiFormField = formField.ifPossibleAs(AbstractMultiFormField.class);
|
||||||
return multiFormField.getValues();
|
List<? extends CharSequence> charSequences = multiFormField.getValues();
|
||||||
|
return StringUtils.toStrings(charSequences);
|
||||||
}
|
}
|
||||||
|
|
||||||
default Boolean readBoolean(String fieldName) {
|
default Boolean readBoolean(String fieldName) {
|
||||||
|
|
|
@ -49,9 +49,6 @@ import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
import org.jivesoftware.smackx.xdatalayout.packet.DataLayout;
|
import org.jivesoftware.smackx.xdatalayout.packet.DataLayout;
|
||||||
import org.jivesoftware.smackx.xdatalayout.provider.DataLayoutProvider;
|
import org.jivesoftware.smackx.xdatalayout.provider.DataLayoutProvider;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DataFormProvider parses DataForm packets.
|
* The DataFormProvider parses DataForm packets.
|
||||||
*
|
*
|
||||||
|
@ -237,9 +234,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
case jid_multi:
|
case jid_multi:
|
||||||
JidMultiFormField.Builder jidMultiBuilder = FormField.jidMultiBuilder(fieldName);
|
JidMultiFormField.Builder jidMultiBuilder = FormField.jidMultiBuilder(fieldName);
|
||||||
for (FormField.Value value : values) {
|
for (FormField.Value value : values) {
|
||||||
String rawValue = value.getValue().toString();
|
jidMultiBuilder.addValue(value);
|
||||||
Jid jid = JidCreate.from(rawValue);
|
|
||||||
jidMultiBuilder.addValue(jid, rawValue);
|
|
||||||
}
|
}
|
||||||
builder = jidMultiBuilder;
|
builder = jidMultiBuilder;
|
||||||
break;
|
break;
|
||||||
|
@ -247,9 +242,8 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
ensureAtMostSingleValue(type, values);
|
ensureAtMostSingleValue(type, values);
|
||||||
JidSingleFormField.Builder jidSingleBuilder = FormField.jidSingleBuilder(fieldName);
|
JidSingleFormField.Builder jidSingleBuilder = FormField.jidSingleBuilder(fieldName);
|
||||||
if (!values.isEmpty()) {
|
if (!values.isEmpty()) {
|
||||||
String rawValue = values.get(0).getValue().toString();
|
FormField.Value value = values.get(0);
|
||||||
Jid jid = JidCreate.from(rawValue);
|
jidSingleBuilder.setValue(value);
|
||||||
jidSingleBuilder.setValue(jid, rawValue);
|
|
||||||
}
|
}
|
||||||
builder = jidSingleBuilder;
|
builder = jidSingleBuilder;
|
||||||
break;
|
break;
|
||||||
|
@ -303,7 +297,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
BooleanFormField.Builder builder = FormField.booleanBuilder(fieldName);
|
BooleanFormField.Builder builder = FormField.booleanBuilder(fieldName);
|
||||||
ensureAtMostSingleValue(builder.getType(), values);
|
ensureAtMostSingleValue(builder.getType(), values);
|
||||||
if (values.size() == 1) {
|
if (values.size() == 1) {
|
||||||
String value = values.get(0).getValue().toString();
|
FormField.Value value = values.get(0);
|
||||||
builder.setValue(value);
|
builder.setValue(value);
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
|
|
|
@ -20,5 +20,6 @@
|
||||||
<className>org.jivesoftware.smackx.receipts.DeliveryReceiptManager</className>
|
<className>org.jivesoftware.smackx.receipts.DeliveryReceiptManager</className>
|
||||||
<className>org.jivesoftware.smackx.iqversion.VersionManager</className>
|
<className>org.jivesoftware.smackx.iqversion.VersionManager</className>
|
||||||
<className>org.jivesoftware.smackx.caps.EntityCapsManager</className>
|
<className>org.jivesoftware.smackx.caps.EntityCapsManager</className>
|
||||||
|
<className>org.jivesoftware.smackx.softwareinfo.form.SoftwareInfoForm</className>
|
||||||
</startupClasses>
|
</startupClasses>
|
||||||
</smack>
|
</smack>
|
||||||
|
|
|
@ -67,7 +67,6 @@ public class AMPExtensionTest {
|
||||||
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
|
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
|
||||||
XmlPullParser parser = PacketParserUtils.getParserFor(INCORRECT_RECEIVING_STANZA_STREAM);
|
XmlPullParser parser = PacketParserUtils.getParserFor(INCORRECT_RECEIVING_STANZA_STREAM);
|
||||||
|
|
||||||
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
|
|
||||||
assertEquals(AMPExtension.ELEMENT, parser.getName());
|
assertEquals(AMPExtension.ELEMENT, parser.getName());
|
||||||
|
|
||||||
ExtensionElement extension = ampProvider.parse(parser);
|
ExtensionElement extension = ampProvider.parse(parser);
|
||||||
|
@ -85,7 +84,6 @@ public class AMPExtensionTest {
|
||||||
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
|
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
|
||||||
XmlPullParser parser = PacketParserUtils.getParserFor(CORRECT_SENDING_STANZA_STREAM);
|
XmlPullParser parser = PacketParserUtils.getParserFor(CORRECT_SENDING_STANZA_STREAM);
|
||||||
|
|
||||||
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
|
|
||||||
assertEquals(AMPExtension.ELEMENT, parser.getName());
|
assertEquals(AMPExtension.ELEMENT, parser.getName());
|
||||||
ExtensionElement extension = ampProvider.parse(parser);
|
ExtensionElement extension = ampProvider.parse(parser);
|
||||||
assertTrue(extension instanceof AMPExtension);
|
assertTrue(extension instanceof AMPExtension);
|
||||||
|
|
|
@ -75,8 +75,7 @@ public class JingleContentTest extends SmackTestSuite {
|
||||||
assertEquals(content1.toXML().toString(), builder.build().toXML().toString());
|
assertEquals(content1.toXML().toString(), builder.build().toXML().toString());
|
||||||
|
|
||||||
String xml =
|
String xml =
|
||||||
"<content xmlns='urn:xmpp:jingle:1' creator='initiator' disposition='session' name='A name' senders='both'>" +
|
"<content xmlns='urn:xmpp:jingle:1' creator='initiator' disposition='session' name='A name' senders='both'/>";
|
||||||
"</content>";
|
|
||||||
assertEquals(xml, content1.toXML().toString());
|
assertEquals(xml, content1.toXML().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class JingleTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyBuilderTest() {
|
public void emptyBuilderTest() {
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
Jingle.Builder builder = Jingle.builder("id");
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
builder.build();
|
builder.build();
|
||||||
});
|
});
|
||||||
|
@ -48,7 +48,7 @@ public class JingleTest extends SmackTestSuite {
|
||||||
public void onlySessionIdBuilderTest() {
|
public void onlySessionIdBuilderTest() {
|
||||||
String sessionId = "testSessionId";
|
String sessionId = "testSessionId";
|
||||||
|
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
Jingle.Builder builder = Jingle.builder("id");
|
||||||
builder.setSessionId(sessionId);
|
builder.setSessionId(sessionId);
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
builder.build();
|
builder.build();
|
||||||
|
@ -59,7 +59,7 @@ public class JingleTest extends SmackTestSuite {
|
||||||
public void parserTest() throws XmppStringprepException {
|
public void parserTest() throws XmppStringprepException {
|
||||||
String sessionId = "testSessionId";
|
String sessionId = "testSessionId";
|
||||||
|
|
||||||
Jingle.Builder builder = Jingle.getBuilder();
|
Jingle.Builder builder = Jingle.builder("id");
|
||||||
builder.setSessionId(sessionId);
|
builder.setSessionId(sessionId);
|
||||||
builder.setAction(JingleAction.session_initiate);
|
builder.setAction(JingleAction.session_initiate);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.jingle.element;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.StreamOpen;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class JingleTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noRedundantNamespaceTest() {
|
||||||
|
Jingle.Builder jingleBuilder = Jingle.builder("test-id");
|
||||||
|
jingleBuilder.setSessionId("MySession");
|
||||||
|
jingleBuilder.setAction(JingleAction.content_accept);
|
||||||
|
|
||||||
|
JingleContent.Builder jingleContentBuilder = JingleContent.getBuilder();
|
||||||
|
jingleContentBuilder.setName("Hello world");
|
||||||
|
jingleContentBuilder.setCreator(JingleContent.Creator.initiator);
|
||||||
|
|
||||||
|
jingleBuilder.addJingleContent(jingleContentBuilder.build());
|
||||||
|
Jingle iq = jingleBuilder.build();
|
||||||
|
|
||||||
|
String actualXml = iq.toXML(StreamOpen.CLIENT_NAMESPACE).toString();
|
||||||
|
String expectedXml
|
||||||
|
= "<iq id='test-id' type='set'>"
|
||||||
|
+ "<jingle xmlns='urn:xmpp:jingle:1' action='content-accept' sid='MySession'>"
|
||||||
|
+ "<content creator='initiator' name='Hello world'/>"
|
||||||
|
+ "</jingle></iq>";
|
||||||
|
assertEquals(expectedXml, actualXml);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2017 Florian Schmaus
|
* Copyright 2017-2022 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.
|
||||||
|
@ -17,23 +17,30 @@
|
||||||
package org.jivesoftware.smackx.jingle.provider;
|
package org.jivesoftware.smackx.jingle.provider;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
import org.jivesoftware.smack.packet.XmlElement;
|
||||||
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestUtil;
|
||||||
import org.jivesoftware.smack.xml.XmlPullParserException;
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.jingle.element.Jingle;
|
import org.jivesoftware.smackx.jingle.element.Jingle;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
|
||||||
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
|
||||||
|
import org.jivesoftware.smackx.jingle.element.JingleReason;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
|
|
||||||
public class JingleProviderTest {
|
public class JingleProviderTest {
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testParseUnknownJingleContentDescrption() throws Exception {
|
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
|
||||||
|
public void testParseUnknownJingleContentDescrption(SmackTestUtil.XmlPullParserKind parserKind)
|
||||||
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
final String unknownJingleContentDescriptionNamespace = "urn:xmpp:jingle:unknown-description:5";
|
final String unknownJingleContentDescriptionNamespace = "urn:xmpp:jingle:unknown-description:5";
|
||||||
final String unknownJingleContentDescription =
|
final String unknownJingleContentDescription =
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
@ -50,8 +57,8 @@ public class JingleProviderTest {
|
||||||
"</file>" +
|
"</file>" +
|
||||||
"</description>";
|
"</description>";
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
XmlPullParser parser = createTestJingle(unknownJingleContentDescription);
|
CharSequence xml = createTestJingle(unknownJingleContentDescription);
|
||||||
Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser);
|
Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind);
|
||||||
|
|
||||||
JingleContentDescription jingleContentDescription = jingle.getSoleContentOrThrow().getDescription();
|
JingleContentDescription jingleContentDescription = jingle.getSoleContentOrThrow().getDescription();
|
||||||
|
|
||||||
|
@ -59,8 +66,10 @@ public class JingleProviderTest {
|
||||||
assertEquals(unknownJingleContentDescriptionNamespace, parsedUnknownJingleContentDescriptionNamespace);
|
assertEquals(unknownJingleContentDescriptionNamespace, parsedUnknownJingleContentDescriptionNamespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testParseUnknownJingleContentTransport() throws Exception {
|
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
|
||||||
|
public void testParseUnknownJingleContentTransport(SmackTestUtil.XmlPullParserKind parserKind)
|
||||||
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
final String unknownJingleContentTransportNamespace = "urn:xmpp:jingle:unknown-transport:foo:1";
|
final String unknownJingleContentTransportNamespace = "urn:xmpp:jingle:unknown-transport:foo:1";
|
||||||
final String unknownJingleContentTransport =
|
final String unknownJingleContentTransport =
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
@ -81,8 +90,8 @@ public class JingleProviderTest {
|
||||||
" type='direct'/>" +
|
" type='direct'/>" +
|
||||||
"</transport>";
|
"</transport>";
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
XmlPullParser parser = createTestJingle(unknownJingleContentTransport);
|
CharSequence xml = createTestJingle(unknownJingleContentTransport);
|
||||||
Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser);
|
Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind);
|
||||||
|
|
||||||
JingleContentTransport jingleContentTransport = jingle.getSoleContentOrThrow().getTransport();
|
JingleContentTransport jingleContentTransport = jingle.getSoleContentOrThrow().getTransport();
|
||||||
|
|
||||||
|
@ -90,7 +99,38 @@ public class JingleProviderTest {
|
||||||
assertEquals(unknownJingleContentTransportNamespace, parsedUnknownJingleContentTransportNamespace);
|
assertEquals(unknownJingleContentTransportNamespace, parsedUnknownJingleContentTransportNamespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static XmlPullParser createTestJingle(String... childs) throws XmlPullParserException, IOException {
|
@ParameterizedTest
|
||||||
|
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
|
||||||
|
public void testReasonElementWithExtraElement(SmackTestUtil.XmlPullParserKind parserKind)
|
||||||
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
String xml = "<iq from='juliet@capulet.lit/balcony'"
|
||||||
|
+ " id='le71fa63'"
|
||||||
|
+ " to='romeo@montague.lit/orchard'"
|
||||||
|
+ " type='set'>"
|
||||||
|
+ "<jingle xmlns='urn:xmpp:jingle:1'"
|
||||||
|
+ " action='session-terminate'"
|
||||||
|
+ " sid='a73sjjvkla37jfea'>"
|
||||||
|
+ "<reason>"
|
||||||
|
+ "<success/>"
|
||||||
|
+ "<my-element xmlns='https://example.org' foo='bar'/>"
|
||||||
|
+ "</reason>"
|
||||||
|
+ "</jingle>"
|
||||||
|
+ "</iq>";
|
||||||
|
Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind);
|
||||||
|
JingleReason jingleReason = jingle.getReason();
|
||||||
|
|
||||||
|
assertEquals(JingleReason.Reason.success, jingleReason.asEnum());
|
||||||
|
|
||||||
|
XmlElement element = jingleReason.getElement();
|
||||||
|
// TODO: Use JUnit 5.8's assertInstanceOf when possible
|
||||||
|
// assertInstanceOf(StandardExtesionElement.class, extraElement);
|
||||||
|
assertTrue(element instanceof StandardExtensionElement);
|
||||||
|
StandardExtensionElement extraElement = (StandardExtensionElement) element;
|
||||||
|
assertEquals("https://example.org", extraElement.getNamespace());
|
||||||
|
assertEquals("bar", extraElement.getAttributes().get("foo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CharSequence createTestJingle(String... childs) throws XmlPullParserException, IOException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(// @formatter:off
|
sb.append(// @formatter:off
|
||||||
"<iq from='romeo@montague.example/dr4hcr0st3lup4c'" +
|
"<iq from='romeo@montague.example/dr4hcr0st3lup4c'" +
|
||||||
|
@ -98,9 +138,9 @@ public class JingleProviderTest {
|
||||||
" to='juliet@capulet.example/yn0cl4bnw0yr3vym'" +
|
" to='juliet@capulet.example/yn0cl4bnw0yr3vym'" +
|
||||||
" type='set'>" +
|
" type='set'>" +
|
||||||
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
"<jingle xmlns='urn:xmpp:jingle:1' " +
|
||||||
" action='session-initiate' " +
|
"action='session-initiate' " +
|
||||||
" initiator='romeo@montague.example/dr4hcr0st3lup4c' " +
|
"initiator='romeo@montague.example/dr4hcr0st3lup4c' " +
|
||||||
" sid='851ba2'>" +
|
"sid='851ba2'>" +
|
||||||
"<content creator='initiator' name='a-file-offer' senders='initiator'>"
|
"<content creator='initiator' name='a-file-offer' senders='initiator'>"
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
|
@ -114,9 +154,6 @@ public class JingleProviderTest {
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
|
|
||||||
String jingleStanza = sb.toString();
|
return sb;
|
||||||
|
|
||||||
XmlPullParser parser = PacketParserUtils.getParserFor(jingleStanza);
|
|
||||||
return parser;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2021 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.provider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestUtil;
|
||||||
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
|
|
||||||
|
public class ItemProviderTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that {@link ItemProvider} is able to parse items which have whitespace before their Payload.
|
||||||
|
*
|
||||||
|
* @param parserKind the used parser Kind
|
||||||
|
* @throws XmlPullParserException if an XML pull parser exception occurs.
|
||||||
|
* @throws IOException if an IO exception occurs.
|
||||||
|
* @throws SmackParsingException if an Smack parsing exception occurs.
|
||||||
|
* @see <a href="https://igniterealtime.atlassian.net/jira/software/c/projects/SMACK/issues/SMACK-918">SMACK-918</a>
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
|
||||||
|
public void whitespaceBeforeItemPayload(SmackTestUtil.XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
String item = "<item id='13a3710c-68c3-4da2-8484-d8d9c77af91e' xmlns='http://jabber.org/protocol/pubsub#event'>"
|
||||||
|
+ "\n <geoloc xmlns='http://jabber.org/protocol/geoloc' xml:lang='en'/>"
|
||||||
|
+ "</item>";
|
||||||
|
SmackTestUtil.parse(item, ItemProvider.class, parserKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,8 @@
|
||||||
package org.jivesoftware.smackx.xdata;
|
package org.jivesoftware.smackx.xdata;
|
||||||
|
|
||||||
import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar;
|
import static org.jivesoftware.smack.test.util.XmlAssertUtil.assertXmlSimilar;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.jxmpp.jid.JidTestUtil;
|
import org.jxmpp.jid.JidTestUtil;
|
||||||
|
@ -35,4 +37,18 @@ class FormFieldTest {
|
||||||
assertXmlSimilar(expectedXml, xml);
|
assertXmlSimilar(expectedXml, xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyLabel() {
|
||||||
|
TextSingleFormField.Builder builder = FormField.textSingleBuilder("type");
|
||||||
|
builder.setLabel("");
|
||||||
|
TextSingleFormField formField = builder.build();
|
||||||
|
|
||||||
|
assertEquals("", formField.getLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testThrowExceptionWhenNullLabel() {
|
||||||
|
TextSingleFormField.Builder builder = FormField.textSingleBuilder("type");
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> builder.setLabel(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,4 +115,33 @@ public class DataFormProviderTest {
|
||||||
assertEquals(2, items.size());
|
assertEquals(2, items.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRetrieveFieldWithEmptyLabel() throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
|
||||||
|
String form =
|
||||||
|
"<x xmlns='jabber:x:data' type='form'>" +
|
||||||
|
" <title>Advanced User Search</title>" +
|
||||||
|
" <instructions>The following fields are available for searching. Wildcard (*) characters are allowed as part of the query.</instructions>" +
|
||||||
|
" <field var='FORM_TYPE' label='' type='hidden'>" +
|
||||||
|
" <value>jabber:iq:search</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Search' var='search'>" +
|
||||||
|
" <required/>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Username' var='Username' type='boolean'>" +
|
||||||
|
" <value>true</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Name' var='Name' type='boolean'>" +
|
||||||
|
" <value>true</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Email' var='Email' type='boolean'>" +
|
||||||
|
" <value>true</value>" +
|
||||||
|
" </field>" +
|
||||||
|
"</x>";
|
||||||
|
XmlPullParser parser = PacketParserUtils.getParserFor(form);
|
||||||
|
DataForm dataForm = DataFormProvider.INSTANCE.parse(parser);
|
||||||
|
FormField usernameFormField = dataForm.getField("FORM_TYPE");
|
||||||
|
assertEquals(FormField.Type.hidden, usernameFormField.getType());
|
||||||
|
assertEquals("", usernameFormField.getLabel());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,6 @@ public class XHTMLExtensionProviderTest {
|
||||||
public void parsesWell() throws IOException, XmlPullParserException {
|
public void parsesWell() throws IOException, XmlPullParserException {
|
||||||
InputStream inputStream = getClass().getResourceAsStream(XHTML_EXTENSION_SAMPLE_RESOURCE_NAME);
|
InputStream inputStream = getClass().getResourceAsStream(XHTML_EXTENSION_SAMPLE_RESOURCE_NAME);
|
||||||
XmlPullParser parser = PacketParserUtils.getParserFor(inputStream);
|
XmlPullParser parser = PacketParserUtils.getParserFor(inputStream);
|
||||||
parser.next();
|
|
||||||
|
|
||||||
XHTMLExtensionProvider provider = new XHTMLExtensionProvider();
|
XHTMLExtensionProvider provider = new XHTMLExtensionProvider();
|
||||||
ExtensionElement extension = provider.parse(parser, parser.getDepth(), null);
|
ExtensionElement extension = provider.parse(parser, parser.getDepth(), null);
|
||||||
|
|
|
@ -469,11 +469,14 @@ public final class Roster extends Manager {
|
||||||
@Override
|
@Override
|
||||||
public void processException(Exception exception) {
|
public void processException(Exception exception) {
|
||||||
rosterState = RosterState.uninitialized;
|
rosterState = RosterState.uninitialized;
|
||||||
Level logLevel;
|
Level logLevel = Level.SEVERE;
|
||||||
if (exception instanceof NotConnectedException) {
|
if (exception instanceof NotConnectedException) {
|
||||||
logLevel = Level.FINE;
|
logLevel = Level.FINE;
|
||||||
} else {
|
} else if (exception instanceof XMPPErrorException) {
|
||||||
logLevel = Level.SEVERE;
|
Condition condition = ((XMPPErrorException) exception).getStanzaError().getCondition();
|
||||||
|
if (condition == Condition.feature_not_implemented || condition == Condition.service_unavailable) {
|
||||||
|
logLevel = Level.FINE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LOGGER.log(logLevel, "Exception reloading roster", exception);
|
LOGGER.log(logLevel, "Exception reloading roster", exception);
|
||||||
for (RosterLoadedListener listener : rosterLoadedListeners) {
|
for (RosterLoadedListener listener : rosterLoadedListeners) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015-2020 Florian Schmaus
|
* Copyright 2015-2021 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.
|
||||||
|
@ -85,6 +85,7 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
|
||||||
* @param action the action to perform.
|
* @param action the action to perform.
|
||||||
* @throws Exception in case of an exception.
|
* @throws Exception in case of an exception.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("ThreadPriorityCheck")
|
||||||
protected void performActionAndWaitForPresence(XMPPConnection conA, XMPPConnection conB, ThrowingRunnable action)
|
protected void performActionAndWaitForPresence(XMPPConnection conA, XMPPConnection conB, ThrowingRunnable action)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
final SimpleResultSyncPoint presenceReceivedSyncPoint = new SimpleResultSyncPoint();
|
final SimpleResultSyncPoint presenceReceivedSyncPoint = new SimpleResultSyncPoint();
|
||||||
|
@ -109,5 +110,8 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
|
||||||
} finally {
|
} finally {
|
||||||
conA.removeAsyncStanzaListener(presenceListener);
|
conA.removeAsyncStanzaListener(presenceListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Ugly hack to make tests using this method more reliable. Ideally no test would use this method.
|
||||||
|
Thread.yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015-2020 Florian Schmaus
|
* Copyright 2015-2021 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,8 +19,11 @@ package org.igniterealtime.smack.inttest;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -33,6 +36,7 @@ import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
|
||||||
import org.jivesoftware.smack.debugger.ConsoleDebugger;
|
import org.jivesoftware.smack.debugger.ConsoleDebugger;
|
||||||
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
import org.jivesoftware.smack.util.Function;
|
import org.jivesoftware.smack.util.Function;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
|
@ -101,8 +105,12 @@ public final class Configuration {
|
||||||
|
|
||||||
public final Set<String> enabledTests;
|
public final Set<String> enabledTests;
|
||||||
|
|
||||||
|
private final Map<String, Set<String>> enabledTestsMap;
|
||||||
|
|
||||||
public final Set<String> disabledTests;
|
public final Set<String> disabledTests;
|
||||||
|
|
||||||
|
private final Map<String, Set<String>> disabledTestsMap;
|
||||||
|
|
||||||
public final String defaultConnectionNickname;
|
public final String defaultConnectionNickname;
|
||||||
|
|
||||||
public final Set<String> enabledConnections;
|
public final Set<String> enabledConnections;
|
||||||
|
@ -117,6 +125,13 @@ public final class Configuration {
|
||||||
|
|
||||||
public final DnsResolver dnsResolver;
|
public final DnsResolver dnsResolver;
|
||||||
|
|
||||||
|
public enum CompatibilityMode {
|
||||||
|
standardsCompliant,
|
||||||
|
ejabberd,
|
||||||
|
}
|
||||||
|
|
||||||
|
public final CompatibilityMode compatibilityMode;
|
||||||
|
|
||||||
private Configuration(Configuration.Builder builder) throws KeyManagementException, NoSuchAlgorithmException {
|
private Configuration(Configuration.Builder builder) throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
service = Objects.requireNonNull(builder.service,
|
service = Objects.requireNonNull(builder.service,
|
||||||
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
|
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
|
||||||
|
@ -162,8 +177,10 @@ public final class Configuration {
|
||||||
this.accountTwoPassword = builder.accountTwoPassword;
|
this.accountTwoPassword = builder.accountTwoPassword;
|
||||||
this.accountThreeUsername = builder.accountThreeUsername;
|
this.accountThreeUsername = builder.accountThreeUsername;
|
||||||
this.accountThreePassword = builder.accountThreePassword;
|
this.accountThreePassword = builder.accountThreePassword;
|
||||||
this.enabledTests = builder.enabledTests;
|
this.enabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.enabledTests);
|
||||||
this.disabledTests = builder.disabledTests;
|
this.enabledTestsMap = convertTestsToMap(enabledTests);
|
||||||
|
this.disabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.disabledTests);
|
||||||
|
this.disabledTestsMap = convertTestsToMap(disabledTests);
|
||||||
this.defaultConnectionNickname = builder.defaultConnectionNickname;
|
this.defaultConnectionNickname = builder.defaultConnectionNickname;
|
||||||
this.enabledConnections = builder.enabledConnections;
|
this.enabledConnections = builder.enabledConnections;
|
||||||
this.disabledConnections = builder.disabledConnections;
|
this.disabledConnections = builder.disabledConnections;
|
||||||
|
@ -192,6 +209,7 @@ public final class Configuration {
|
||||||
this.verbose = builder.verbose;
|
this.verbose = builder.verbose;
|
||||||
|
|
||||||
this.dnsResolver = builder.dnsResolver;
|
this.dnsResolver = builder.dnsResolver;
|
||||||
|
this.compatibilityMode = builder.compatibilityMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAccountRegistrationPossible() {
|
public boolean isAccountRegistrationPossible() {
|
||||||
|
@ -246,6 +264,8 @@ public final class Configuration {
|
||||||
|
|
||||||
private DnsResolver dnsResolver = DnsResolver.minidns;
|
private DnsResolver dnsResolver = DnsResolver.minidns;
|
||||||
|
|
||||||
|
private CompatibilityMode compatibilityMode = CompatibilityMode.standardsCompliant;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +447,20 @@ public final class Configuration {
|
||||||
return setDnsResolver(dnsResolver);
|
return setDnsResolver(dnsResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setCompatibilityMode(CompatibilityMode compatibilityMode) {
|
||||||
|
this.compatibilityMode = compatibilityMode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCompatibilityMode(String compatibilityModeString) {
|
||||||
|
if (compatibilityModeString == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompatibilityMode compatibilityMode = CompatibilityMode.valueOf(compatibilityModeString);
|
||||||
|
return setCompatibilityMode(compatibilityMode);
|
||||||
|
}
|
||||||
|
|
||||||
public Configuration build() throws KeyManagementException, NoSuchAlgorithmException {
|
public Configuration build() throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
return new Configuration(this);
|
return new Configuration(this);
|
||||||
}
|
}
|
||||||
|
@ -500,6 +534,8 @@ public final class Configuration {
|
||||||
|
|
||||||
builder.setDnsResolver(properties.getProperty("dnsResolver"));
|
builder.setDnsResolver(properties.getProperty("dnsResolver"));
|
||||||
|
|
||||||
|
builder.setCompatibilityMode(properties.getProperty("compatibilityMode"));
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,4 +587,112 @@ public final class Configuration {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, Set<String>> convertTestsToMap(Set<String> tests) {
|
||||||
|
Map<String, Set<String>> res = new HashMap<>();
|
||||||
|
for (String test : tests) {
|
||||||
|
String[] testParts = test.split("\\.");
|
||||||
|
if (testParts.length == 1) {
|
||||||
|
// The whole test specification does not contain a dot, assume it is a test class specification.
|
||||||
|
res.put(test, Collections.emptySet());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String lastTestPart = testParts[testParts.length - 1];
|
||||||
|
if (lastTestPart.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Invalid test specifier: " + test);
|
||||||
|
}
|
||||||
|
|
||||||
|
char firstCharOfLastTestPart = lastTestPart.charAt(0);
|
||||||
|
if (!Character.isLowerCase(firstCharOfLastTestPart)) {
|
||||||
|
// The first character of the last test part is not lowercase, assume this is a fully qualified test
|
||||||
|
// class specification, e.g. org.foo.bar.TestClass.
|
||||||
|
res.put(test, Collections.emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first character of the last test part is lowercase, assume this is a test class *and* method name
|
||||||
|
// specification.
|
||||||
|
String testMethodName = lastTestPart;
|
||||||
|
int classPartsCount = testParts.length - 1;
|
||||||
|
String[] classParts = new String[classPartsCount];
|
||||||
|
System.arraycopy(testParts, 0, classParts, 0, classPartsCount);
|
||||||
|
String testClass = String.join(".", classParts);
|
||||||
|
|
||||||
|
res.compute(testClass, (k, v) -> {
|
||||||
|
if (v == null) {
|
||||||
|
v = new HashSet<>();
|
||||||
|
}
|
||||||
|
v.add(testMethodName);
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> getKey(Class<?> testClass, Map<String, Set<String>> testsMap) {
|
||||||
|
String className = testClass.getName();
|
||||||
|
if (testsMap.containsKey(className)) {
|
||||||
|
return testsMap.get(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
String unqualifiedClassName = testClass.getSimpleName();
|
||||||
|
if (testsMap.containsKey(unqualifiedClassName)) {
|
||||||
|
return testsMap.get(unqualifiedClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean contains(Class<? extends AbstractSmackIntTest> testClass, Map<String, Set<String>> testsMap) {
|
||||||
|
Set<String> enabledMethods = getKey(testClass, testsMap);
|
||||||
|
return enabledMethods != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClassEnabled(Class<? extends AbstractSmackIntTest> testClass) {
|
||||||
|
if (enabledTestsMap.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contains(testClass, enabledTestsMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClassDisabled(Class<? extends AbstractSmackIntTest> testClass) {
|
||||||
|
if (disabledTestsMap.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contains(testClass, disabledTestsMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean contains(Method method, Map<String, Set<String>> testsMap) {
|
||||||
|
Class<?> testClass = method.getDeclaringClass();
|
||||||
|
Set<String> methods = getKey(testClass, testsMap);
|
||||||
|
|
||||||
|
if (methods == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String methodName = method.getName();
|
||||||
|
return methods.contains(methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMethodEnabled(Method method) {
|
||||||
|
if (enabledTestsMap.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contains(method, enabledTestsMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMethodDisabled(Method method) {
|
||||||
|
if (disabledTestsMap.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contains(method, disabledTestsMap);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,13 +282,13 @@ public class SmackIntegrationTestFramework {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.enabledTests != null && !isInSet(testClass, config.enabledTests)) {
|
if (!config.isClassEnabled(testClass)) {
|
||||||
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is not enabled");
|
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is not enabled");
|
||||||
testRunResult.disabledTestClasses.add(disabledTestClass);
|
testRunResult.disabledTestClasses.add(disabledTestClass);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInSet(testClass, config.disabledTests)) {
|
if (config.isClassDisabled(testClass)) {
|
||||||
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disalbed");
|
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disalbed");
|
||||||
testRunResult.disabledTestClasses.add(disabledTestClass);
|
testRunResult.disabledTestClasses.add(disabledTestClass);
|
||||||
continue;
|
continue;
|
||||||
|
@ -377,14 +377,13 @@ public class SmackIntegrationTestFramework {
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
final Method method = it.next();
|
final Method method = it.next();
|
||||||
final String methodName = method.getName();
|
final String methodName = method.getName();
|
||||||
if (config.enabledTests != null && !(config.enabledTests.contains(methodName)
|
if (!config.isMethodEnabled(method)) {
|
||||||
|| isInSet(testClass, config.enabledTests))) {
|
|
||||||
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is not enabled");
|
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is not enabled");
|
||||||
testRunResult.disabledTests.add(disabledTest);
|
testRunResult.disabledTests.add(disabledTest);
|
||||||
it.remove();
|
it.remove();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (config.disabledTests != null && config.disabledTests.contains(methodName)) {
|
if (config.isMethodDisabled(method)) {
|
||||||
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is disabled");
|
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is disabled");
|
||||||
testRunResult.disabledTests.add(disabledTest);
|
testRunResult.disabledTests.add(disabledTest);
|
||||||
it.remove();
|
it.remove();
|
||||||
|
@ -607,15 +606,6 @@ public class SmackIntegrationTestFramework {
|
||||||
return (Exception) e;
|
return (Exception) e;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isInSet(Class<?> clz, Set<String> classes) {
|
|
||||||
if (classes == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final String className = clz.getName();
|
|
||||||
final String unqualifiedClassName = clz.getSimpleName();
|
|
||||||
return classes.contains(className) || classes.contains(unqualifiedClassName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class TestRunResult {
|
public static final class TestRunResult {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.jivesoftware.smack.SmackException.NotLoggedInException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
|
import org.jivesoftware.smack.packet.StanzaError;
|
||||||
import org.jivesoftware.smack.roster.AbstractPresenceEventListener;
|
import org.jivesoftware.smack.roster.AbstractPresenceEventListener;
|
||||||
import org.jivesoftware.smack.roster.PresenceEventListener;
|
import org.jivesoftware.smack.roster.PresenceEventListener;
|
||||||
import org.jivesoftware.smack.roster.Roster;
|
import org.jivesoftware.smack.roster.Roster;
|
||||||
|
@ -99,7 +100,16 @@ public class IntegrationTestRosterUtil {
|
||||||
if (c2Entry == null) {
|
if (c2Entry == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
roster.removeEntry(c2Entry);
|
roster.removeEntry(c2Entry);
|
||||||
|
} catch (XMPPErrorException e) {
|
||||||
|
// Account for race conditions: server-sided, the item might already have been removed.
|
||||||
|
if (e.getStanzaError().getCondition() == StanzaError.Condition.item_not_found) {
|
||||||
|
// Trying to remove non-existing item. As it needs to be gone, this is fine.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,9 @@ 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.SmackException.NotLoggedInException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.disco.EntityCapabilitiesChangedListener;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.geoloc.GeoLocationManager;
|
import org.jivesoftware.smackx.geoloc.GeoLocationManager;
|
||||||
import org.jivesoftware.smackx.geoloc.packet.GeoLocation;
|
import org.jivesoftware.smackx.geoloc.packet.GeoLocation;
|
||||||
import org.jivesoftware.smackx.pep.PepEventListener;
|
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||||
|
@ -35,7 +36,7 @@ import org.igniterealtime.smack.inttest.annotations.AfterClass;
|
||||||
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
|
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
|
||||||
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.jxmpp.util.XmppDateTime;
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
|
||||||
public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest {
|
public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
@ -49,10 +50,21 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
glm2 = GeoLocationManager.getInstanceFor(conTwo);
|
glm2 = GeoLocationManager.getInstanceFor(conTwo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public void unsubscribe() throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that a notification is sent when a publication is received, assuming that notification filtering
|
||||||
|
* has been adjusted to allow for the notification to be delivered.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
@SmackIntegrationTest
|
@SmackIntegrationTest
|
||||||
public void test() throws TimeoutException, Exception {
|
public void testNotification() throws Exception {
|
||||||
GeoLocation.Builder builder = GeoLocation.builder();
|
GeoLocation.Builder builder = GeoLocation.builder();
|
||||||
GeoLocation geoLocation1 = builder.setAccuracy(23d)
|
GeoLocation data = builder.setAccuracy(23d)
|
||||||
.setAlt(1000d)
|
.setAlt(1000d)
|
||||||
.setAltAccuracy(10d)
|
.setAltAccuracy(10d)
|
||||||
.setArea("Delhi")
|
.setArea("Delhi")
|
||||||
|
@ -77,31 +89,163 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
|
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
|
||||||
final SimpleResultSyncPoint geoLocationReceived = new SimpleResultSyncPoint();
|
|
||||||
final PepEventListener<GeoLocation> geoLocationListener = new PepEventListener<GeoLocation>() {
|
|
||||||
|
|
||||||
@Override
|
final SimpleResultSyncPoint geoLocationReceived = new SimpleResultSyncPoint();
|
||||||
public void onPepEvent(EntityBareJid jid, GeoLocation geoLocation, String id, Message message) {
|
|
||||||
if (geoLocation.equals(geoLocation1)) {
|
final PepEventListener<GeoLocation> geoLocationListener = (jid, geoLocation, id, message) -> {
|
||||||
|
if (geoLocation.equals(data)) {
|
||||||
geoLocationReceived.signal();
|
geoLocationReceived.signal();
|
||||||
} else {
|
|
||||||
geoLocationReceived.signalFailure("Received non matching GeoLocation");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
glm2.addGeoLocationListener(geoLocationListener);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
glm1.publishGeoLocation(geoLocation1);
|
// Register ConTwo's interest in receiving geolocation notifications, and wait for that interest to have been propagated.
|
||||||
geoLocationReceived.waitForResult(timeout);
|
registerListenerAndWait(glm2, ServiceDiscoveryManager.getInstanceFor(conTwo), geoLocationListener);
|
||||||
|
|
||||||
|
// Publish the data.
|
||||||
|
glm1.publishGeoLocation(data); // for the purpose of this test, this needs not be blocking/use publishAndWait();
|
||||||
|
|
||||||
|
// Wait for the data to be received.
|
||||||
|
try {
|
||||||
|
Object result = geoLocationReceived.waitForResult(timeout);
|
||||||
|
|
||||||
|
// Explicitly assert the success case.
|
||||||
|
Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not.");
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Assertions.fail("Expected to receive a PEP notification, but did not.");
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
glm2.removeGeoLocationListener(geoLocationListener);
|
unregisterListener(glm2, geoLocationListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
/**
|
||||||
public void unsubscribe() throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
* Verifies that a notification for a previously sent publication is received as soon as notification filtering
|
||||||
IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
|
* has been adjusted to allow for the notification to be delivered.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void testNotificationAfterFilterChange() throws Exception {
|
||||||
|
GeoLocation.Builder builder = GeoLocation.builder();
|
||||||
|
GeoLocation data = builder.setAccuracy(12d)
|
||||||
|
.setAlt(999d)
|
||||||
|
.setAltAccuracy(9d)
|
||||||
|
.setArea("Amsterdam")
|
||||||
|
.setBearing(9d)
|
||||||
|
.setBuilding("Test Building")
|
||||||
|
.setCountry("Netherlands")
|
||||||
|
.setCountryCode("NL")
|
||||||
|
.setDescription("My Description")
|
||||||
|
.setFloor("middle")
|
||||||
|
.setLat(25.098345d)
|
||||||
|
.setLocality("brilliant")
|
||||||
|
.setLon(77.992034)
|
||||||
|
.setPostalcode("110085")
|
||||||
|
.setRegion("North")
|
||||||
|
.setRoom("small")
|
||||||
|
.setSpeed(250.0d)
|
||||||
|
.setStreet("Wall Street")
|
||||||
|
.setText("Unit Testing GeoLocation 2")
|
||||||
|
.setTimestamp(XmppDateTime.parseDate("2007-02-19"))
|
||||||
|
.setTzo("+5:30")
|
||||||
|
.setUri(new URI("http://xmpp.org"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
|
||||||
|
|
||||||
|
final SimpleResultSyncPoint geoLocationReceived = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
|
final PepEventListener<GeoLocation> geoLocationListener = (jid, geoLocation, id, message) -> {
|
||||||
|
if (geoLocation.equals(data)) {
|
||||||
|
geoLocationReceived.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO Ensure that pre-existing filtering notification excludes geolocation.
|
||||||
|
try {
|
||||||
|
// Publish the data
|
||||||
|
publishAndWait(glm1, ServiceDiscoveryManager.getInstanceFor(conOne), data);
|
||||||
|
|
||||||
|
// Adds listener, which implicitly publishes a disco/info filter for geolocation notification.
|
||||||
|
registerListenerAndWait(glm2, ServiceDiscoveryManager.getInstanceFor(conTwo), geoLocationListener);
|
||||||
|
|
||||||
|
// Wait for the data to be received.
|
||||||
|
try {
|
||||||
|
Object result = geoLocationReceived.waitForResult(timeout);
|
||||||
|
|
||||||
|
// Explicitly assert the success case.
|
||||||
|
Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not.");
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Assertions.fail("Expected to receive a PEP notification, but did not.");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
unregisterListener(glm2, geoLocationListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener for GeoLocation data. This implicitly publishes a CAPS update to include a notification
|
||||||
|
* filter for the geolocation node. This method blocks until the server has indicated that this update has been
|
||||||
|
* received.
|
||||||
|
*
|
||||||
|
* @param geoManager The GeoLocationManager instance for the connection that is expected to receive data.
|
||||||
|
* @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data.
|
||||||
|
* @param listener A listener instance for GeoLocation data that is to be registered.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
|
public void registerListenerAndWait(GeoLocationManager geoManager, ServiceDiscoveryManager discoManager, PepEventListener<GeoLocation> listener) throws Exception {
|
||||||
|
final SimpleResultSyncPoint notificationFilterReceived = new SimpleResultSyncPoint();
|
||||||
|
final EntityCapabilitiesChangedListener notificationFilterReceivedListener = info -> {
|
||||||
|
if (info.containsFeature(GeoLocationManager.GEOLOCATION_NODE + "+notify")) {
|
||||||
|
notificationFilterReceived.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
discoManager.addEntityCapabilitiesChangedListener(notificationFilterReceivedListener);
|
||||||
|
try {
|
||||||
|
geoManager.addGeoLocationListener(listener);
|
||||||
|
notificationFilterReceived.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
discoManager.removeEntityCapabilitiesChangedListener(notificationFilterReceivedListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The functionally reverse of {@link #registerListenerAndWait(GeoLocationManager, ServiceDiscoveryManager, PepEventListener)}
|
||||||
|
* with the difference of not being a blocking operation.
|
||||||
|
*
|
||||||
|
* @param geoManager The GeoLocationManager instance for the connection that was expected to receive data.
|
||||||
|
* @param listener A listener instance for GeoLocation data that is to be removed.
|
||||||
|
*/
|
||||||
|
public void unregisterListener(GeoLocationManager geoManager, PepEventListener<GeoLocation> listener) {
|
||||||
|
// Does it make sense to have a method implementation that's one line? This is provided to allow for symmetry in the API.
|
||||||
|
geoManager.removeGeoLocationListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish data using PEP, and block until the server has echoed the publication back to the publishing user.
|
||||||
|
*
|
||||||
|
* @param geoManager The GeoLocationManager instance for the connection that is expected to publish data.
|
||||||
|
* @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data.
|
||||||
|
* @param data The data to be published.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
|
public void publishAndWait(GeoLocationManager geoManager, ServiceDiscoveryManager discoManager, GeoLocation data) throws Exception {
|
||||||
|
final SimpleResultSyncPoint publicationEchoReceived = new SimpleResultSyncPoint();
|
||||||
|
final PepEventListener<GeoLocation> publicationEchoListener = (jid, geoLocation, id, message) -> {
|
||||||
|
if (geoLocation.equals(data)) {
|
||||||
|
publicationEchoReceived.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
registerListenerAndWait(geoManager, discoManager, publicationEchoListener);
|
||||||
|
geoManager.addGeoLocationListener(publicationEchoListener);
|
||||||
|
geoManager.publishGeoLocation(data);
|
||||||
|
} finally {
|
||||||
|
geoManager.removeGeoLocationListener(publicationEchoListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.mood;
|
package org.jivesoftware.smackx.mood;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.disco.EntityCapabilitiesChangedListener;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.mood.element.MoodElement;
|
import org.jivesoftware.smackx.mood.element.MoodElement;
|
||||||
import org.jivesoftware.smackx.pep.PepEventListener;
|
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||||
|
|
||||||
|
@ -28,6 +32,7 @@ import org.igniterealtime.smack.inttest.annotations.AfterClass;
|
||||||
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
|
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
|
||||||
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
|
||||||
public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
|
public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
|
|
||||||
|
@ -40,32 +45,155 @@ public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
|
||||||
mm2 = MoodManager.getInstanceFor(conTwo);
|
mm2 = MoodManager.getInstanceFor(conTwo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SmackIntegrationTest
|
|
||||||
public void test() throws Exception {
|
|
||||||
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
|
|
||||||
|
|
||||||
final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint();
|
|
||||||
|
|
||||||
final PepEventListener<MoodElement> moodListener = (jid, moodElement, id, message) -> {
|
|
||||||
if (moodElement.getMood() == Mood.satisfied) {
|
|
||||||
moodReceived.signal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mm2.addMoodListener(moodListener);
|
|
||||||
|
|
||||||
try {
|
|
||||||
mm1.setMood(Mood.satisfied);
|
|
||||||
|
|
||||||
moodReceived.waitForResult(timeout);
|
|
||||||
} finally {
|
|
||||||
mm2.removeMoodListener(moodListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public void unsubscribe()
|
public void unsubscribe()
|
||||||
throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
||||||
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
|
||||||
IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
|
IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that a notification is sent when a publication is received, assuming that notification filtering
|
||||||
|
* has been adjusted to allow for the notification to be delivered.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void testNotification() throws Exception {
|
||||||
|
Mood data = Mood.satisfied;
|
||||||
|
|
||||||
|
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
|
||||||
|
|
||||||
|
final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
|
final PepEventListener<MoodElement> moodListener = (jid, moodElement, id, message) -> {
|
||||||
|
if (moodElement.getMood().equals(data)) {
|
||||||
|
moodReceived.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Register ConTwo's interest in receiving mood notifications, and wait for that interest to have been propagated.
|
||||||
|
registerListenerAndWait(mm2, ServiceDiscoveryManager.getInstanceFor(conTwo), moodListener);
|
||||||
|
|
||||||
|
// Publish the data.
|
||||||
|
mm1.setMood(data); // for the purpose of this test, this needs not be blocking/use publishAndWait();
|
||||||
|
|
||||||
|
// Wait for the data to be received.
|
||||||
|
try {
|
||||||
|
moodReceived.waitForResult(timeout);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Assertions.fail("Expected to receive a PEP notification, but did not.");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
unregisterListener(mm2, moodListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that a notification for a previously sent publication is received as soon as notification filtering
|
||||||
|
* has been adjusted to allow for the notification to be delivered.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
|
@SmackIntegrationTest
|
||||||
|
public void testNotificationAfterFilterChange() throws Exception {
|
||||||
|
Mood data = Mood.cautious;
|
||||||
|
|
||||||
|
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
|
||||||
|
|
||||||
|
final SimpleResultSyncPoint moodReceived = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
|
final PepEventListener<MoodElement> moodListener = (jid, moodElement, id, message) -> {
|
||||||
|
if (moodElement.getMood().equals(data)) {
|
||||||
|
moodReceived.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO Ensure that pre-existing filtering notification excludes mood.
|
||||||
|
try {
|
||||||
|
// Publish the data
|
||||||
|
publishAndWait(mm1, ServiceDiscoveryManager.getInstanceFor(conOne), data);
|
||||||
|
|
||||||
|
// Adds listener, which implicitly publishes a disco/info filter for mood notification.
|
||||||
|
registerListenerAndWait(mm2, ServiceDiscoveryManager.getInstanceFor(conTwo), moodListener);
|
||||||
|
|
||||||
|
// Wait for the data to be received.
|
||||||
|
try {
|
||||||
|
Object result = moodReceived.waitForResult(timeout);
|
||||||
|
|
||||||
|
// Explicitly assert the success case.
|
||||||
|
Assertions.assertNotNull(result, "Expected to receive a PEP notification, but did not.");
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
Assertions.fail("Expected to receive a PEP notification, but did not.");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
unregisterListener(mm2, moodListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a listener for User Tune data. This implicitly publishes a CAPS update to include a notification
|
||||||
|
* filter for the mood node. This method blocks until the server has indicated that this update has been
|
||||||
|
* received.
|
||||||
|
*
|
||||||
|
* @param moodManager The MoodManager instance for the connection that is expected to receive data.
|
||||||
|
* @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data.
|
||||||
|
* @param listener A listener instance for Mood data that is to be registered.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
|
public void registerListenerAndWait(MoodManager moodManager, ServiceDiscoveryManager discoManager, PepEventListener<MoodElement> listener) throws Exception {
|
||||||
|
final SimpleResultSyncPoint notificationFilterReceived = new SimpleResultSyncPoint();
|
||||||
|
final EntityCapabilitiesChangedListener notificationFilterReceivedListener = info -> {
|
||||||
|
if (info.containsFeature(MoodManager.MOOD_NODE + "+notify")) {
|
||||||
|
notificationFilterReceived.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
discoManager.addEntityCapabilitiesChangedListener(notificationFilterReceivedListener);
|
||||||
|
try {
|
||||||
|
moodManager.addMoodListener(listener);
|
||||||
|
notificationFilterReceived.waitForResult(timeout);
|
||||||
|
} finally {
|
||||||
|
discoManager.removeEntityCapabilitiesChangedListener(notificationFilterReceivedListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The functionally reverse of {@link #registerListenerAndWait(MoodManager, ServiceDiscoveryManager, PepEventListener)}
|
||||||
|
* with the difference of not being a blocking operation.
|
||||||
|
*
|
||||||
|
* @param moodManager The MoodManager instance for the connection that was expected to receive data.
|
||||||
|
* @param listener A listener instance for Mood data that is to be removed.
|
||||||
|
*/
|
||||||
|
public void unregisterListener(MoodManager moodManager, PepEventListener<MoodElement> listener) {
|
||||||
|
// Does it make sense to have a method implementation that's one line? This is provided to allow for symmetry in the API.
|
||||||
|
moodManager.removeMoodListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish data using PEP, and block until the server has echoed the publication back to the publishing user.
|
||||||
|
*
|
||||||
|
* @param moodManager The MoodManager instance for the connection that is expected to publish data.
|
||||||
|
* @param discoManager The ServiceDiscoveryManager instance for the connection that is expected to publish data.
|
||||||
|
* @param data The data to be published.
|
||||||
|
*
|
||||||
|
* @throws Exception if the test fails
|
||||||
|
*/
|
||||||
|
public void publishAndWait(MoodManager moodManager, ServiceDiscoveryManager discoManager, Mood data) throws Exception {
|
||||||
|
final SimpleResultSyncPoint publicationEchoReceived = new SimpleResultSyncPoint();
|
||||||
|
final PepEventListener<MoodElement> publicationEchoListener = (jid, moodElement, id, message) -> {
|
||||||
|
if (moodElement.getMood().equals(data)) {
|
||||||
|
publicationEchoReceived.signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
registerListenerAndWait(moodManager, discoManager, publicationEchoListener);
|
||||||
|
moodManager.addMoodListener(publicationEchoListener);
|
||||||
|
moodManager.setMood(data);
|
||||||
|
} finally {
|
||||||
|
moodManager.removeMoodListener(publicationEchoListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,6 @@ import java.util.List;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
|
||||||
import org.jivesoftware.smackx.xdata.form.Form;
|
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
|
@ -57,10 +55,9 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati
|
||||||
if (services.isEmpty()) {
|
if (services.isEmpty()) {
|
||||||
throw new TestNotPossibleException("No MUC (XEP-45) service found");
|
throw new TestNotPossibleException("No MUC (XEP-45) service found");
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
mucService = services.get(0);
|
mucService = services.get(0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a random room name.
|
* Gets a random room name.
|
||||||
|
@ -90,18 +87,57 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati
|
||||||
muc.destroy("test fixture teardown", null);
|
muc.destroy("test fixture teardown", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void createMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException {
|
static void createMuc(MultiUserChat muc, Resourcepart resourceName) throws
|
||||||
MultiUserChat.MucCreateConfigFormHandle handle = muc.create(Resourcepart.from(resourceName));
|
SmackException.NoResponseException, XMPPException.XMPPErrorException,
|
||||||
if (handle != null) {
|
InterruptedException, MultiUserChatException.MucAlreadyJoinedException,
|
||||||
handle.makeInstant();
|
SmackException.NotConnectedException,
|
||||||
}
|
MultiUserChatException.MissingMucCreationAcknowledgeException,
|
||||||
|
MultiUserChatException.NotAMucServiceException {
|
||||||
|
muc.create(resourceName).makeInstant();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void createModeratedMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException {
|
static void createMuc(MultiUserChat muc, String nickname) throws
|
||||||
muc.create(Resourcepart.from(resourceName));
|
XmppStringprepException, MultiUserChatException.MucAlreadyJoinedException,
|
||||||
Form configForm = muc.getConfigurationForm();
|
XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
FillableForm answerForm = configForm.getFillableForm();
|
MultiUserChatException.MissingMucCreationAcknowledgeException,
|
||||||
answerForm.setAnswer("muc#roomconfig_moderatedroom", true); //TODO Add this to the MucConfigFormManager?
|
SmackException.NoResponseException, InterruptedException,
|
||||||
muc.sendConfigurationForm(answerForm);
|
MultiUserChatException.NotAMucServiceException {
|
||||||
|
createMuc(muc, Resourcepart.from(nickname));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createMembersOnlyMuc(MultiUserChat muc, Resourcepart resourceName) throws
|
||||||
|
SmackException.NoResponseException, XMPPException.XMPPErrorException,
|
||||||
|
InterruptedException, MultiUserChatException.MucAlreadyJoinedException,
|
||||||
|
SmackException.NotConnectedException,
|
||||||
|
MultiUserChatException.MissingMucCreationAcknowledgeException,
|
||||||
|
MultiUserChatException.MucConfigurationNotSupportedException,
|
||||||
|
MultiUserChatException.NotAMucServiceException {
|
||||||
|
muc.create(resourceName)
|
||||||
|
.getConfigFormManager()
|
||||||
|
.makeMembersOnly()
|
||||||
|
.submitConfigurationForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createModeratedMuc(MultiUserChat muc, Resourcepart resourceName)
|
||||||
|
throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException,
|
||||||
|
MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException,
|
||||||
|
MultiUserChatException.MissingMucCreationAcknowledgeException,
|
||||||
|
MultiUserChatException.NotAMucServiceException,
|
||||||
|
MultiUserChatException.MucConfigurationNotSupportedException {
|
||||||
|
muc.create(resourceName)
|
||||||
|
.getConfigFormManager()
|
||||||
|
.makeModerated()
|
||||||
|
.submitConfigurationForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createHiddenMuc(MultiUserChat muc, Resourcepart resourceName)
|
||||||
|
throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException,
|
||||||
|
MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException,
|
||||||
|
MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException,
|
||||||
|
MultiUserChatException.MucConfigurationNotSupportedException {
|
||||||
|
muc.create(resourceName)
|
||||||
|
.getConfigFormManager()
|
||||||
|
.makeHidden()
|
||||||
|
.submitConfigurationForm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue