Compare commits

..

No commits in common. "01cd0507b6ca1473a3ec3d30bd56dfbd6bb19e55" and "b9be45ae2ccf860c62642092c61f5847c312f1f0" have entirely different histories.

118 changed files with 2398 additions and 4985 deletions

File diff suppressed because it is too large Load Diff

6
NOTICE
View File

@ -29,7 +29,6 @@ Chris Deering
Christoph Fiehe
Craig Hesling
Damian Minkov
Dan Caseley
Daniele Ricci
Daniel Henninger
Daniel Hintze
@ -45,7 +44,6 @@ Fernando Ramirez
Florian Kimmann
Florian Schmaus
Francisco Vives
Frank Matheron
Gaston Dombiak
Georg Lukas
Gilles Cornu
@ -66,7 +64,6 @@ Jay Kline
Jeff Williams
Jesus Fuentes
John Haubrich
Jonathan Lennox
Júlio Cesar Bueno Cotta
Lars Noschinski
Luca Stucchi
@ -85,7 +82,6 @@ Pete Matern
Piotr Nosek
Rajat Kumar Gupta
Robin Collier
Simon Abykov
Simon Schuster
Son Goku
Tairs Rzajevs
@ -102,4 +98,4 @@ V Lau
Vyacheslav Blinov
Wolf Posdorfer
Xiaowei YAN
Yash Thakkar
Yash Thakkar

View File

@ -6,7 +6,6 @@ buildscript {
}
dependencies {
classpath 'org.kordamp.gradle:clirr-gradle-plugin:0.2.2'
classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:6.0.0"
}
}
@ -148,7 +147,7 @@ allprojects {
smackMinAndroidSdk = 19
junitVersion = '5.7.1'
commonsIoVersion = '2.6'
bouncyCastleVersion = '1.69'
bouncyCastleVersion = '1.68'
guavaVersion = '30.1-jre'
mockitoVersion = '3.7.7'
orgReflectionsVersion = '0.9.11'
@ -197,6 +196,10 @@ allprojects {
repositories {
mavenLocal()
mavenCentral()
// Add OSS Sonatype Snapshot repository
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
}
tasks.withType(JavaCompile) {
@ -447,7 +450,6 @@ subprojects {
apply plugin: 'signing'
apply plugin: 'checkstyle'
apply plugin: 'org.kordamp.gradle.clirr'
apply plugin: 'biz.aQute.bnd.builder'
checkstyle {
toolVersion = '8.27'
@ -610,15 +612,9 @@ project(':smack-omemo').clirr.enabled = false
project(':smack-omemo-signal').clirr.enabled = false
subprojects*.jar {
manifest {
from sharedManifest
}
bundle {
bnd(
'-removeheaders': 'Tool, Bnd-*',
'-exportcontents': '*',
)
}
manifest {
from sharedManifest
}
}
configure(subprojects - gplLicensedProjects) {

View File

@ -86,27 +86,6 @@ debugger=console
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
--------------------------

View File

@ -1,38 +0,0 @@
#!/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}"

7
resources/get-contributors.sh Executable file
View File

@ -0,0 +1,7 @@
#!/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

View File

@ -43,7 +43,6 @@ import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.igniterealtime.jbosh.AbstractBody;
import org.igniterealtime.jbosh.BOSHClient;
@ -201,14 +200,6 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
+ getHost() + ":" + getPort() + ".";
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
@ -520,7 +511,7 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
parseAndProcessStanza(parser);
break;
case "features":
parseFeaturesAndNotify(parser);
parseFeatures(parser);
break;
case "error":
// Some BOSH error isn't stream error.

View File

@ -1,8 +1,3 @@
// 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 = """\
Smack core components."""
@ -60,11 +55,3 @@ task createVersionResource(type: CreateFileTask) {
}
compileJava.dependsOn(createVersionResource)
jar {
bundle {
bnd(
'DynamicImport-Package': '*',
)
}
}

View File

@ -524,6 +524,9 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
closingStreamReceived = false;
streamId = null;
// The connection should not be connected nor marked as such prior calling connectInternal().
assert !connected;
try {
// Perform the actual connection to the XMPP service
connectInternal();

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software, 2017-2022 Florian Schmaus.
* Copyright 2003-2007 Jive Software, 2017-2020 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,7 +32,6 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
@ -190,7 +189,7 @@ public abstract class ConnectionConfiguration {
protected ConnectionConfiguration(Builder<?, ?> builder) {
try {
smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory,
builder.customX509TrustManager, builder.keyManagers, builder.sslContextSecureRandom, builder.keystoreType, builder.keystorePath,
builder.customX509TrustManager, builder.keystoreType, builder.keystorePath,
builder.callbackHandler, builder.pkcs11Library);
} catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException
| KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException
@ -253,7 +252,7 @@ public abstract class ConnectionConfiguration {
}
private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory,
X509TrustManager trustManager, KeyManager[] keyManagers, SecureRandom secureRandom, String keystoreType, String keystorePath,
X509TrustManager trustManager, String keystoreType, String keystorePath,
CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException,
CertificateException, IOException, KeyStoreException, NoSuchProviderException,
UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException,
@ -267,10 +266,69 @@ public abstract class ConnectionConfiguration {
context = SSLContext.getInstance("TLS");
}
// TODO: Remove the block below once we removed setKeystorePath(), setKeystoreType(), setCallbackHanlder() and
// setPKCS11Library() in the builder, and all related fields and the parameters of this function.
if (keyManagers == null) {
keyManagers = Builder.getKeyManagersFrom(keystoreType, keystorePath, callbackHandler, pkcs11Library);
KeyStore ks = null;
PasswordCallback pcb = null;
KeyManager[] kms = 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();
}
kms = kmf.getKeyManagers();
}
}
SmackDaneVerifier daneVerifier = null;
@ -285,7 +343,7 @@ public abstract class ConnectionConfiguration {
}
// User requested DANE verification.
daneVerifier.init(context, keyManagers, trustManager, secureRandom);
daneVerifier.init(context, kms, trustManager, null);
} else {
final TrustManager[] trustManagers;
if (trustManager != null) {
@ -296,7 +354,7 @@ public abstract class ConnectionConfiguration {
trustManagers = null;
}
context.init(keyManagers, trustManagers, secureRandom);
context.init(kms, trustManagers, null);
}
return new SmackTlsContext(context, daneVerifier);
@ -347,7 +405,7 @@ public abstract class ConnectionConfiguration {
/**
* Returns the TLS security mode used when making the connection. By default,
* the mode is {@link SecurityMode#required}.
* the mode is {@link SecurityMode#ifpossible}.
*
* @return the security mode.
*/
@ -630,8 +688,6 @@ public abstract class ConnectionConfiguration {
public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
private SecurityMode securityMode = SecurityMode.required;
private DnssecMode dnssecMode = DnssecMode.disabled;
private KeyManager[] keyManagers;
private SecureRandom sslContextSecureRandom;
private String keystorePath;
private String keystoreType;
private String pkcs11Library = "pkcs11.config";
@ -886,12 +942,7 @@ public abstract class ConnectionConfiguration {
* @param callbackHandler to obtain information, such as the password or
* principal information during the SASL authentication.
* @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) {
this.callbackHandler = callbackHandler;
return getThis();
@ -909,7 +960,7 @@ public abstract class ConnectionConfiguration {
/**
* Sets the TLS security mode used when making the connection. By default,
* the mode is {@link SecurityMode#required}.
* the mode is {@link SecurityMode#ifpossible}.
*
* @param securityMode the security mode.
* @return a reference to this builder.
@ -919,47 +970,6 @@ public abstract class ConnectionConfiguration {
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
* certificates that may be used to authenticate the client to the server,
@ -967,12 +977,7 @@ public abstract class ConnectionConfiguration {
*
* @param keystorePath the path to the keystore file.
* @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) {
this.keystorePath = keystorePath;
return getThis();
@ -983,12 +988,7 @@ public abstract class ConnectionConfiguration {
*
* @param keystoreType the keystore type.
* @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) {
this.keystoreType = keystoreType;
return getThis();
@ -1000,12 +1000,7 @@ public abstract class ConnectionConfiguration {
*
* @param pkcs11Library the path to the PKCS11 library file.
* @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) {
this.pkcs11Library = pkcs11Library;
return getThis();
@ -1281,77 +1276,5 @@ public abstract class ConnectionConfiguration {
public abstract C build();
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;
}
}
}

View File

@ -170,20 +170,16 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
return exception;
}
private boolean callbacksInvoked;
protected final synchronized void maybeInvokeCallbacks() {
if (cancelled || callbacksInvoked) {
if (cancelled) {
return;
}
if ((result != null || exception != null) && completionCallback != null) {
callbacksInvoked = true;
completionCallback.accept(this);
}
if (result != null && successCallback != null) {
callbacksInvoked = true;
AbstractXMPPConnection.asyncGo(new Runnable() {
@Override
public void run() {
@ -192,7 +188,6 @@ public abstract class SmackFuture<V, E extends Exception> implements Future<V>,
});
}
else if (exception != null && exceptionCallback != null) {
callbacksInvoked = true;
AbstractXMPPConnection.asyncGo(new Runnable() {
@Override
public void run() {

View File

@ -221,10 +221,11 @@ public class SmackReactor {
selectWait = 0;
} else {
selectWait = nextScheduledAction.getTimeToDueMillis();
if (selectWait <= 0) {
// A scheduled action was just released and became ready to execute.
return;
}
}
if (selectWait < 0) {
// A scheduled action was just released and became ready to execute.
return;
}
// Before we call select, we handle the pending the interest Ops. This will not block since no other

View File

@ -120,16 +120,6 @@ public abstract class XMPPException extends Exception {
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.
*

View File

@ -186,10 +186,6 @@ public class StateDescriptorGraph {
for (GraphVertex<Class<? extends StateDescriptor>> successor : sortedSuccessors) {
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);
// Recurse further.

View File

@ -202,7 +202,7 @@ public abstract class IQ extends Stanza implements IqView {
// Add the query section if there is one.
IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(
new IQChildElementXmlStringBuilder(getChildElementName(), getChildElementNamespace(), null, xml.getXmlEnvironment()));
new IQChildElementXmlStringBuilder(this));
// TOOD: Document the cases where iqChildElement is null but childElementName not. And if there are none, change
// the logic.
if (iqChildElement == null) {
@ -399,16 +399,17 @@ public abstract class IQ extends Stanza implements IqView {
private boolean isEmptyElement;
public IQChildElementXmlStringBuilder(ExtensionElement extensionElement,
XmlEnvironment enclosingXmlEnvironment) {
this(extensionElement.getElementName(), extensionElement.getNamespace(), extensionElement.getLanguage(),
enclosingXmlEnvironment);
private IQChildElementXmlStringBuilder(IQ iq) {
this(iq.getChildElementName(), iq.getChildElementNamespace());
}
private IQChildElementXmlStringBuilder(String elementName, String xmlNs, String xmlLang,
XmlEnvironment enclosingXmlEnvironment) {
super(elementName, xmlNs, xmlLang, enclosingXmlEnvironment);
this.element = elementName;
public IQChildElementXmlStringBuilder(ExtensionElement pe) {
this(pe.getElementName(), pe.getNamespace());
}
private IQChildElementXmlStringBuilder(String element, String namespace) {
prelude(element, namespace);
this.element = element;
}
public void setEmptyElement() {

View File

@ -184,19 +184,6 @@ public abstract class StanzaBuilder<B extends StanzaBuilder<B>> implements Stanz
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 B getThis();

View File

@ -34,7 +34,7 @@ public class ExceptionThrowingCallbackWithHint extends ExceptionThrowingCallback
@Override
public void handleUnparsableStanza(UnparseableStanza packetData) throws IOException {
LOGGER.warning("Parsing exception \"" + packetData.getParsingException().getMessage() + "\" encountered."
LOGGER.warning("Parsing exception encountered."
+ " This exception will be re-thrown, leading to a disconnect."
+ " You can change this behavior by setting a different ParsingExceptionCallback using setParsingExceptionCallback()."
+ " More information an be found in AbstractXMPPConnection's javadoc.");

View File

@ -30,7 +30,7 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
/**
* <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>
* 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

View File

@ -60,8 +60,4 @@ public class SASLAnonymous extends SASLMechanism {
// SASL Anonymous is always successful :)
}
@Override
public boolean requiresPassword() {
return false;
}
}

View File

@ -89,11 +89,4 @@ public class CollectionUtil {
}
return Collections.singletonList(element);
}
public static <T> Set<T> nullSafeUnmodifiableSet(Set<T> set) {
if (set == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(set);
}
}

View File

@ -1,29 +0,0 @@
/**
*
* 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();
}
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software, 2019-2021 Florian Schmaus.
* Copyright 2003-2007 Jive Software, 2019 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -76,9 +76,9 @@ public class PacketParserUtils {
return getParserFor(new StringReader(stanza));
}
public static XmlPullParser getParserFor(InputStream inputStream) throws XmlPullParserException, IOException {
public static XmlPullParser getParserFor(InputStream inputStream) throws XmlPullParserException {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
return getParserFor(inputStreamReader);
return SmackXmlParser.newXmlParser(inputStreamReader);
}
public static XmlPullParser getParserFor(Reader reader) throws XmlPullParserException, IOException {
@ -501,23 +501,6 @@ public class PacketParserUtils {
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.
*
@ -535,7 +518,18 @@ public class PacketParserUtils {
XmlEnvironment iqXmlEnvironment = XmlEnvironment.from(parser, outerXmlEnvironment);
IQ iqPacket = 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) {
XmlPullParser.Event eventType = parser.next();
@ -555,8 +549,8 @@ public class PacketParserUtils {
if (provider != null) {
iqPacket = provider.parse(parser, iqData, outerXmlEnvironment);
}
// 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 otherwise we would have reached the END_ELEMENT first.
// Note that if we reach this code, it is guranteed that the result IQ contained a child element
// (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_ELEMENT first.
else {
// 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
@ -577,7 +571,7 @@ public class PacketParserUtils {
}
// Decide what to do when an IQ packet was not understood
if (iqPacket == null) {
switch (iqData.getType()) {
switch (type) {
case error:
// If an IQ packet wasn't created above, create an empty error IQ packet.
iqPacket = new ErrorIQ(error);
@ -591,10 +585,10 @@ public class PacketParserUtils {
}
// Set basic values on the iq packet.
iqPacket.setStanzaId(iqData.getStanzaId());
iqPacket.setTo(iqData.getTo());
iqPacket.setFrom(iqData.getFrom());
iqPacket.setType(iqData.getType());
iqPacket.setStanzaId(id);
iqPacket.setTo(to);
iqPacket.setFrom(from);
iqPacket.setType(type);
iqPacket.setError(error);
return iqPacket;

View File

@ -78,7 +78,6 @@ public class ParserUtils {
throws XmlPullParserException, IOException {
XmlPullParser.Event event = parser.getEventType();
while (!(event == XmlPullParser.Event.END_ELEMENT && parser.getDepth() == depth)) {
assert event != XmlPullParser.Event.END_DOCUMENT;
event = parser.next();
}
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software, 2016-2021 Florian Schmaus.
* Copyright 2003-2007 Jive Software, 2016-2020 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,7 +20,6 @@ package org.jivesoftware.smack.util;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@ -606,13 +605,4 @@ public class StringUtils {
String[] lines = input.split(PORTABLE_NEWLINE_REGEX);
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;
}
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2014-2021 Florian Schmaus
* Copyright 2014-2020 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -52,13 +52,11 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
}
public XmlStringBuilder(XmlElement element, XmlEnvironment enclosingXmlEnvironment) {
this(element.getElementName(), element.getNamespace(), element.getLanguage(), enclosingXmlEnvironment);
}
public XmlStringBuilder(String elementName, String xmlNs, String xmlLang, XmlEnvironment enclosingXmlEnvironment) {
sb = new LazyStringBuilder();
halfOpenElement(elementName);
halfOpenElement(element);
String xmlNs = element.getNamespace();
String xmlLang = element.getLanguage();
if (enclosingXmlEnvironment == null) {
xmlnsAttribute(xmlNs);
xmllangAttribute(xmlLang);
@ -288,7 +286,8 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
public XmlStringBuilder attribute(String name, Enum<?> value) {
assert value != null;
attribute(name, value.toString());
// TODO: Should use toString() instead of name().
attribute(name, value.name());
return this;
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2019-2022 Florian Schmaus
* Copyright 2019-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.
@ -28,13 +28,9 @@ import java.util.function.Predicate;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.IqData;
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.util.PacketParserUtils;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smack.xml.XmlPullParserFactory;
@ -62,56 +58,41 @@ public class SmackTestUtil {
}
}
public static <E extends Element, P extends AbstractProvider<E>> E parse(CharSequence xml, Class<P> providerClass, XmlPullParserKind parserKind)
public static <E extends Element, P extends Provider<E>> E parse(CharSequence xml, Class<P> providerClass, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
P provider = providerClassToProvider(providerClass);
return parse(xml, provider, parserKind);
}
public static <E extends Element, P extends AbstractProvider<E>> E parse(InputStream inputStream, Class<P> providerClass, XmlPullParserKind parserKind)
public static <E extends Element, P extends Provider<E>> E parse(InputStream inputStream, Class<P> providerClass, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
P provider = providerClassToProvider(providerClass);
return parse(inputStream, provider, parserKind);
}
public static <E extends Element, P extends AbstractProvider<E>> E parse(Reader reader, Class<P> providerClass, XmlPullParserKind parserKind)
public static <E extends Element, P extends Provider<E>> E parse(Reader reader, Class<P> providerClass, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
P provider = providerClassToProvider(providerClass);
return parse(reader, provider, parserKind);
}
public static <E extends Element> E parse(CharSequence xml, AbstractProvider<E> provider, XmlPullParserKind parserKind)
public static <E extends Element> E parse(CharSequence xml, Provider<E> provider, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
String xmlString = xml.toString();
Reader reader = new StringReader(xmlString);
return parse(reader, provider, parserKind);
}
public static <E extends Element> E parse(InputStream inputStream, AbstractProvider<E> provider, XmlPullParserKind parserKind)
public static <E extends Element> E parse(InputStream inputStream, Provider<E> provider, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
return parse(inputStreamReader, provider, parserKind);
}
@SuppressWarnings("unchecked")
public static <E extends Element> E parse(Reader reader, AbstractProvider<E> abstractProvider, XmlPullParserKind parserKind)
public static <E extends Element> E parse(Reader reader, Provider<E> provider, XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
XmlPullParser parser = getParserFor(reader, parserKind);
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();
}
E element = provider.parse(parser);
return element;
}
@ -151,7 +132,7 @@ public class SmackTestUtil {
}
@SuppressWarnings("unchecked")
private static <E extends Element, P extends AbstractProvider<E>> P providerClassToProvider(Class<P> providerClass) {
private static <E extends Element, P extends Provider<E>> P providerClassToProvider(Class<P> providerClass) {
P provider;
try {

View File

@ -34,13 +34,7 @@ import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
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.Logger;
@ -425,11 +419,38 @@ public class EnhancedDebugger extends SmackDebugger {
// Create a special Reader that wraps the main Reader and logs data to the GUI.
ObservableReader debugReader = new ObservableReader(reader);
readerListener = new ReaderListener() {
private final PriorityBlockingQueue<String> buffer = new PriorityBlockingQueue<>();
@Override
public void read(final String string) {
addBatched(string, buffer, receivedText);
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(">");
if (index != -1) {
if (receivedText.getLineCount() >= EnhancedDebuggerWindow.MAX_TABLE_ROWS) {
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);
@ -437,11 +458,34 @@ public class EnhancedDebugger extends SmackDebugger {
// Create a special Writer that wraps the main Writer and logs data to the GUI.
ObservableWriter debugWriter = new ObservableWriter(writer);
writerListener = new WriterListener() {
private final PriorityBlockingQueue<String> buffer = new PriorityBlockingQueue<>();
@Override
public void write(final String string) {
addBatched(string, buffer, sentText);
public void write(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;
}
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);
@ -453,50 +497,6 @@ 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() {
// Create UI elements for sending ad-hoc messages.
final JTextArea adhocMessages = new JTextArea();

View File

@ -154,7 +154,8 @@ public final class CarbonManager extends Manager {
// because we also reset in authenticated() if the stream got not resumed, but for maximum correctness,
// also reset here.
enabled_state = false;
connection().removeSyncStanzaListener(carbonsListener);
boolean removed = connection().removeSyncStanzaListener(carbonsListener);
assert removed;
}
@Override
public void authenticated(XMPPConnection connection, boolean resumed) {

View File

@ -1,101 +0,0 @@
/**
*
* 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;
}
}
}

View File

@ -49,8 +49,6 @@ import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
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.element.Slot;
import org.jivesoftware.smackx.httpfileupload.element.SlotRequest;
@ -496,12 +494,11 @@ public final class HttpFileUploadManager extends Manager {
case HttpURLConnection.HTTP_NO_CONTENT:
break;
default:
throw new HttpUploadErrorException(status, urlConnection.getResponseMessage(), fileSize, slot);
throw new IOException("Error response " + status + " from server during file upload: "
+ urlConnection.getResponseMessage() + ", file size: " + fileSize + ", put URL: "
+ putUrl);
}
}
catch (IOException e) {
throw new HttpUploadIOException(fileSize, slot, e);
}
finally {
urlConnection.disconnect();
}

View File

@ -47,17 +47,15 @@ import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.commands.AdHocCommandManager;
import org.jivesoftware.smackx.commands.RemoteCommand;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
import org.jivesoftware.smackx.mam.element.MamElements;
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
import org.jivesoftware.smackx.mam.element.MamFinIQ;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior;
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.muc.MultiUserChat;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
@ -227,8 +225,6 @@ public final class MamManager extends Manager {
private final AdHocCommandManager adHocCommandManager;
private MamVersion mamVersion = null;
private MamManager(XMPPConnection connection, Jid archiveAddress) {
super(connection);
this.archiveAddress = archiveAddress;
@ -254,52 +250,6 @@ public final class MamManager extends Manager {
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 {
private final String node;
@ -325,11 +275,11 @@ public final class MamManager extends Manager {
private DataForm dataForm;
DataForm getDataForm(MamVersion version) {
DataForm getDataForm() {
if (dataForm != null) {
return dataForm;
}
DataForm.Builder dataFormBuilder = getNewMamForm(version);
DataForm.Builder dataFormBuilder = getNewMamForm();
dataFormBuilder.addFields(formFields.values());
dataForm = dataFormBuilder.build();
return dataForm;
@ -522,9 +472,9 @@ public final class MamManager extends Manager {
NotConnectedException, NotLoggedInException, InterruptedException {
String queryId = StringUtils.secureUniqueRandomString();
String node = mamQueryArgs.node;
DataForm dataForm = mamQueryArgs.getDataForm(mamVersion);
DataForm dataForm = mamQueryArgs.getDataForm();
MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, dataForm);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, dataForm);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setTo(archiveAddress);
@ -580,7 +530,7 @@ public final class MamManager extends Manager {
throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException, NotLoggedInException {
String queryId = StringUtils.secureUniqueRandomString();
MamQueryIQ mamQueryIq = getElementFactory().newQueryIQ(queryId, node, null);
MamQueryIQ mamQueryIq = new MamQueryIQ(queryId, node, null);
mamQueryIq.setTo(archiveAddress);
MamQueryIQ mamResponseQueryIq = connection().sendIqRequestAndWaitForResponse(mamQueryIq);
@ -642,7 +592,7 @@ public final class MamManager extends Manager {
private List<Message> page(RSMSet requestRsmSet) throws NoResponseException, XMPPErrorException,
NotConnectedException, NotLoggedInException, InterruptedException {
String queryId = StringUtils.secureUniqueRandomString();
MamQueryIQ mamQueryIQ = getElementFactory().newQueryIQ(queryId, node, form);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, form);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setTo(archiveAddress);
mamQueryIQ.addExtension(requestRsmSet);
@ -746,7 +696,9 @@ public final class MamManager extends Manager {
* @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 {
return getSupportedMamVersionOrNull() != null;
// Note that this may return 'null' but SDM's supportsFeature() does the right thing then.
Jid archiveAddress = getArchiveAddress();
return serviceDiscoveryManager.supportsFeature(archiveAddress, MamElements.NAMESPACE);
}
public boolean isAdvancedConfigurationSupported() throws InterruptedException, XMPPException, SmackException {
@ -768,8 +720,8 @@ public final class MamManager extends Manager {
throw new SmackException.FeatureNotSupportedException(ADVANCED_CONFIG_NODE, archiveAddress);
}
private static DataForm.Builder getNewMamForm(MamVersion version) {
FormField field = FormField.buildHiddenFormType(version.getNamespace());
private static DataForm.Builder getNewMamForm() {
FormField field = FormField.buildHiddenFormType(MamElements.NAMESPACE);
DataForm.Builder form = DataForm.builder();
form.addField(field);
return form;
@ -813,7 +765,7 @@ public final class MamManager extends Manager {
*/
public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
MamPrefsIQ mamPrefIQ = getElementFactory().newPrefsIQ();
MamPrefsIQ mamPrefIQ = new MamPrefsIQ();
return queryMamPrefs(mamPrefIQ);
}
@ -878,7 +830,6 @@ public final class MamManager extends Manager {
public static final class MamPrefs {
private final List<Jid> alwaysJids;
private final List<Jid> neverJids;
private final MamVersion mamVersion;
private DefaultBehavior defaultBehavior;
private MamPrefs(MamPrefsResult mamPrefsResult) {
@ -886,7 +837,6 @@ public final class MamManager extends Manager {
this.alwaysJids = new ArrayList<>(mamPrefsIq.getAlwaysJids());
this.neverJids = new ArrayList<>(mamPrefsIq.getNeverJids());
this.defaultBehavior = mamPrefsIq.getDefault();
this.mamVersion = MamVersion.fromNamespace(mamPrefsIq.getNamespace());
}
public void setDefaultBehavior(DefaultBehavior defaultBehavior) {
@ -906,7 +856,7 @@ public final class MamManager extends Manager {
}
private MamPrefsIQ constructMamPrefsIq() {
return mamVersion.newElementFactory().newPrefsIQ(alwaysJids, neverJids, defaultBehavior);
return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior);
}
}

View File

@ -1,94 +0,0 @@
/**
*
* 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);
}

View File

@ -18,13 +18,15 @@ package org.jivesoftware.smackx.mam.element;
import java.util.List;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageView;
import org.jivesoftware.smack.packet.XmlElement;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jxmpp.jid.Jid;
@ -39,6 +41,8 @@ import org.jxmpp.jid.Jid;
*/
public class MamElements {
public static final String NAMESPACE = "urn:xmpp:mam:2";
/**
* MAM result extension class.
*
@ -46,13 +50,18 @@ public class MamElements {
* Archive Management</a>
*
*/
public abstract static class MamResultExtension implements ExtensionElement {
public static class MamResultExtension implements ExtensionElement {
/**
* result element.
*/
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.
*/
@ -68,27 +77,20 @@ public class MamElements {
*/
private String queryId;
protected final MamVersion version;
/**
* MAM result extension constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param id TODO javadoc me please
* @param forwarded TODO javadoc me please
*/
public MamResultExtension(MamVersion version, String queryId, String id, Forwarded<Message> forwarded) {
public MamResultExtension(String queryId, String id, Forwarded<Message> forwarded) {
if (StringUtils.isEmpty(id)) {
throw new IllegalArgumentException("id must not be null or empty");
}
if (forwarded == 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.forwarded = forwarded;
this.queryId = queryId;
@ -128,7 +130,7 @@ public class MamElements {
@Override
public final String getNamespace() {
return version.getNamespace();
return NAMESPACE;
}
@Override
@ -146,13 +148,7 @@ public class MamElements {
}
public static MamResultExtension from(MessageView message) {
for (XmlElement extension : message.getExtensions()) {
if (extension instanceof MamResultExtension) {
return (MamResultExtension) extension;
}
}
return null;
return message.getExtension(MamResultExtension.class);
}
}

View File

@ -35,6 +35,11 @@ public class MamFinIQ extends IQ {
*/
public static final String ELEMENT = "fin";
/**
* the IQ NAMESPACE.
*/
public static final String NAMESPACE = MamElements.NAMESPACE;
/**
* RSM set.
*/
@ -58,14 +63,13 @@ public class MamFinIQ extends IQ {
/**
* MamFinIQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param rsmSet TODO javadoc me please
* @param complete TODO javadoc me please
* @param stable TODO javadoc me please
*/
public MamFinIQ(MamVersion version, String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
super(ELEMENT, version.getNamespace());
public MamFinIQ(String queryId, RSMSet rsmSet, boolean complete, boolean stable) {
super(ELEMENT, NAMESPACE);
if (rsmSet == null) {
throw new IllegalArgumentException("rsmSet must not be null");
}

View File

@ -46,6 +46,11 @@ public class MamPrefsIQ extends IQ {
*/
public static final String ELEMENT = "prefs";
/**
* the IQ NAMESPACE.
*/
public static final String NAMESPACE = MamElements.NAMESPACE;
/**
* list of always.
*/
@ -63,11 +68,9 @@ public class MamPrefsIQ extends IQ {
/**
* Construct a new MAM {@code <prefs/>} IQ retrieval request (IQ type 'get').
*
* @param version TODO javadoc me please *
*/
public MamPrefsIQ(MamVersion version) {
super(ELEMENT, version.getNamespace());
public MamPrefsIQ() {
super(ELEMENT, NAMESPACE);
alwaysJids = null;
neverJids = null;
defaultBehavior = null;
@ -76,13 +79,12 @@ public class MamPrefsIQ extends IQ {
/**
* MAM preferences IQ constructor.
*
* @param version TODO javadoc me please
* @param alwaysJids TODO javadoc me please
* @param neverJids TODO javadoc me please
* @param defaultBehavior TODO javadoc me please
*/
public MamPrefsIQ(MamVersion version, List<Jid> alwaysJids, List<Jid> neverJids, DefaultBehavior defaultBehavior) {
super(ELEMENT, version.getNamespace());
public MamPrefsIQ(List<Jid> alwaysJids, List<Jid> neverJids, DefaultBehavior defaultBehavior) {
super(ELEMENT, NAMESPACE);
setType(Type.set);
this.alwaysJids = alwaysJids;
this.neverJids = neverJids;

View File

@ -35,6 +35,11 @@ public class MamQueryIQ extends IQ {
*/
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 node;
private final DataForm dataForm;
@ -42,45 +47,41 @@ public class MamQueryIQ extends IQ {
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
*/
public MamQueryIQ(MamVersion version, String queryId) {
this(version, queryId, null, null);
public MamQueryIQ(String queryId) {
this(queryId, null, null);
setType(IQ.Type.get);
}
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param form TODO javadoc me please
*/
public MamQueryIQ(MamVersion version, DataForm form) {
this(version, null, null, form);
public MamQueryIQ(DataForm form) {
this(null, null, form);
}
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param form TODO javadoc me please
*/
public MamQueryIQ(MamVersion version, String queryId, DataForm form) {
this(version, queryId, null, form);
public MamQueryIQ(String queryId, DataForm form) {
this(queryId, null, form);
}
/**
* MAM query IQ constructor.
*
* @param version TODO javadoc me please
* @param queryId TODO javadoc me please
* @param node TODO javadoc me please
* @param dataForm TODO javadoc me please
*/
public MamQueryIQ(MamVersion version, String queryId, String node, DataForm dataForm) {
super(ELEMENT, version.getNamespace());
public MamQueryIQ(String queryId, String node, DataForm dataForm) {
super(ELEMENT, NAMESPACE);
this.queryId = queryId;
this.node = node;
this.dataForm = dataForm;
@ -90,9 +91,9 @@ public class MamQueryIQ extends IQ {
if (formType == null) {
throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field");
}
if (!formType.equals(version.getNamespace())) {
if (!formType.equals(MamElements.NAMESPACE)) {
throw new IllegalArgumentException(
"Value of the hidden form type field must be '" + version.getNamespace() + "'");
"Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'");
}
addExtension(dataForm);
}

View File

@ -1,66 +0,0 @@
/**
*
* 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);
}
}
}

View File

@ -1,66 +0,0 @@
/**
*
* 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);
}
}
}

View File

@ -1,69 +0,0 @@
/**
*
* 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);
}
}

View File

@ -25,7 +25,6 @@ import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamFinIQ;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
import org.jivesoftware.smackx.rsm.provider.RSMSetProvider;
@ -42,7 +41,6 @@ public class MamFinIQProvider extends IQProvider<MamFinIQ> {
@Override
public MamFinIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
String queryId = parser.getAttributeValue("", "queryid");
boolean complete = ParserUtils.getBooleanAttribute(parser, "complete", false);
boolean stable = ParserUtils.getBooleanAttribute(parser, "stable", true);
@ -67,7 +65,7 @@ public class MamFinIQProvider extends IQProvider<MamFinIQ> {
}
}
return elementFactory.newFinIQ(queryId, rsmSet, complete, stable);
return new MamFinIQ(queryId, rsmSet, complete, stable);
}
}

View File

@ -25,7 +25,6 @@ import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.xml.XmlPullParser;
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.DefaultBehavior;
@ -42,17 +41,19 @@ import org.jxmpp.jid.impl.JidCreate;
*/
public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
public static final MamPrefsIQProvider INSTANCE = new MamPrefsIQProvider();
@Override
public MamPrefsIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
String iqType = parser.getAttributeValue("", "type");
String defaultBehaviorString = parser.getAttributeValue("", "default");
DefaultBehavior defaultBehavior = null;
if (defaultBehaviorString != null) {
defaultBehavior = DefaultBehavior.valueOf(defaultBehaviorString);
}
if (iqType == null) {
iqType = "result";
}
List<Jid> alwaysJids = null;
List<Jid> neverJids = null;
@ -81,7 +82,7 @@ public class MamPrefsIQProvider extends IQProvider<MamPrefsIQ> {
}
}
return elementFactory.newPrefsIQ(alwaysJids, neverJids, defaultBehavior);
return new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior);
}
private static List<Jid> iterateJids(XmlPullParser parser) throws XmlPullParserException, IOException {

View File

@ -24,7 +24,6 @@ import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamQueryIQ;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jivesoftware.smackx.xdata.provider.DataFormProvider;
@ -42,7 +41,6 @@ public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
@Override
public MamQueryIQ parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
DataForm dataForm = null;
String queryId = parser.getAttributeValue("", "queryid");
String node = parser.getAttributeValue("", "node");
@ -70,7 +68,7 @@ public class MamQueryIQProvider extends IQProvider<MamQueryIQ> {
}
}
return elementFactory.newQueryIQ(queryId, node, dataForm);
return new MamQueryIQ(queryId, node, dataForm);
}
}

View File

@ -28,7 +28,6 @@ import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.forward.packet.Forwarded;
import org.jivesoftware.smackx.forward.provider.ForwardedProvider;
import org.jivesoftware.smackx.mam.element.MamElementFactory;
import org.jivesoftware.smackx.mam.element.MamElements.MamResultExtension;
/**
@ -44,7 +43,6 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
@Override
public MamResultExtension parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws XmlPullParserException, IOException, SmackParsingException, ParseException {
MamElementFactory elementFactory = MamElementFactory.forParser(parser);
Forwarded<Message> forwarded = null;
String queryId = parser.getAttributeValue("", "queryid");
String id = parser.getAttributeValue("", "id");
@ -71,7 +69,7 @@ public class MamResultProvider extends ExtensionElementProvider<MamResultExtensi
}
}
return elementFactory.newResultExtension(queryId, id, forwarded);
return new MamResultExtension(queryId, id, forwarded);
}
}

View File

@ -35,26 +35,6 @@
<namespace>urn:xmpp:mam:2</namespace>
<className>org.jivesoftware.smackx.mam.provider.MamResultProvider</className>
</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 -->
<iqProvider>

View File

@ -23,7 +23,7 @@ import java.util.Date;
import java.util.List;
import org.jivesoftware.smackx.mam.MamManager.MamQueryArgs;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.mam.element.MamElements;
import org.jivesoftware.smackx.xdata.packet.DataForm;
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) {
String xml = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>";
+ MamElements.NAMESPACE + "</value>" + "</field>";
for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) {
xml += "<field var='" + fieldsNames.get(i) + "'>" + "<value>" + fieldsValues.get(i) + "</value>"
@ -51,7 +51,7 @@ public class FiltersTest extends MamTest {
Date date = new Date();
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsSince(date).build();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
DataForm dataForm = mamQueryArgs.getDataForm();
List<String> fields = new ArrayList<>();
fields.add("start");
@ -66,7 +66,7 @@ public class FiltersTest extends MamTest {
Date date = new Date();
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsBefore(date).build();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
DataForm dataForm = mamQueryArgs.getDataForm();
List<String> fields = new ArrayList<>();
fields.add("end");
@ -81,7 +81,7 @@ public class FiltersTest extends MamTest {
Jid jid = JidTestUtil.BARE_JID_1;
MamQueryArgs mamQueryArgs = MamQueryArgs.builder().limitResultsToJid(jid).build();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
DataForm dataForm = mamQueryArgs.getDataForm();
List<String> fields = new ArrayList<>();
fields.add("with");

View File

@ -19,62 +19,54 @@ package org.jivesoftware.smackx.mam;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.List;
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.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.mam.element.MamPrefsIQ;
import org.jivesoftware.smackx.mam.provider.MamPrefsIQProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.jxmpp.jid.Jid;
public class MamPrefIQProviderTest extends MamTest {
private static final String exampleMamPrefsIQ1 = "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
private static final String exampleMamPrefsIQ1 = "<iq type='set' id='juliet3'>" + "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
+ "<always>" + "<jid>romeo@montague.lit</jid>" + "</always>" + "<never>"
+ "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>";
+ "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>" + "</iq>";
private static final String exampleMamPrefsIQ2 = "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
private static final String exampleMamPrefsIQ2 = "<iq type='set' id='juliet3'>" + "<prefs xmlns='urn:xmpp:mam:2' default='roster'>"
+ "<always>" + "<jid>romeo@montague.lit</jid>" + "<jid>montague@montague.lit</jid>" + "</always>"
+ "<never>" + "</never>" + "</prefs>";
+ "<never>" + "</never>" + "</prefs>" + "</iq>";
private static final String exampleMamPrefsIQ3 = "<prefs xmlns='urn:xmpp:mam:2'>" + "</prefs>";
private static final String exampleMamPrefsIQ3 = "<iq type='get' id='juliet3'>" + "<prefs xmlns='urn:xmpp:mam:2'>" + "</prefs>"
+ "</iq>";
private static final String exampleMamPrefsResultIQ = "<iq type='result' id='juliet3'>"
+ "<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>"
+ "</never>" + "</prefs>" + "</iq>";
@ParameterizedTest
@EnumSource(value = SmackTestUtil.XmlPullParserKind.class)
public void checkMamPrefsIQProvider(XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
XmlPullParser parser1 = SmackTestUtil.getParserFor(exampleMamPrefsIQ1, parserKind);
MamPrefsIQ mamPrefIQ1 = MamPrefsIQProvider.INSTANCE.parse(parser1);
@Test
public void checkMamPrefsIQProvider() throws Exception {
XmlPullParser parser1 = PacketParserUtils.getParserFor(exampleMamPrefsIQ1);
MamPrefsIQ mamPrefIQ1 = new MamPrefsIQProvider().parse(parser1);
assertEquals(IQ.Type.set, mamPrefIQ1.getType());
assertEquals(mamPrefIQ1.getAlwaysJids().get(0).toString(), "romeo@montague.lit");
assertEquals(mamPrefIQ1.getNeverJids().get(0).toString(), "montague@montague.lit");
XmlPullParser parser2 = SmackTestUtil.getParserFor(exampleMamPrefsIQ2, parserKind);
MamPrefsIQ mamPrefIQ2 = MamPrefsIQProvider.INSTANCE.parse(parser2);
XmlPullParser parser2 = PacketParserUtils.getParserFor(exampleMamPrefsIQ2);
MamPrefsIQ mamPrefIQ2 = new MamPrefsIQProvider().parse(parser2);
assertEquals(IQ.Type.set, mamPrefIQ2.getType());
assertEquals(mamPrefIQ2.getAlwaysJids().get(0).toString(), "romeo@montague.lit");
assertEquals(mamPrefIQ2.getAlwaysJids().get(1).toString(), "montague@montague.lit");
assertTrue(mamPrefIQ2.getNeverJids().isEmpty());
XmlPullParser parser3 = SmackTestUtil.getParserFor(exampleMamPrefsIQ3, parserKind);
MamPrefsIQ mamPrefIQ3 = MamPrefsIQProvider.INSTANCE.parse(parser3);
XmlPullParser parser3 = PacketParserUtils.getParserFor(exampleMamPrefsIQ3);
MamPrefsIQ mamPrefIQ3 = new MamPrefsIQProvider().parse(parser3);
assertEquals(IQ.Type.set, mamPrefIQ3.getType());
}

View File

@ -23,7 +23,6 @@ import org.jivesoftware.smack.DummyConnection;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.junit.jupiter.api.BeforeAll;
@ -48,9 +47,9 @@ public class MamTest extends SmackTestSuite {
protected DataForm getNewMamForm() throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm", MamVersion.class);
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm");
methodGetNewMamForm.setAccessible(true);
DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager, MamVersion.MAM2);
DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager);
return dataFormBuilder.build();
}

View File

@ -22,7 +22,6 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.StreamOpen;
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.xdata.packet.DataForm;
@ -41,7 +40,7 @@ public class PagingTest extends MamTest {
int max = 10;
RSMSet rsmSet = new RSMSet(max);
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
mamQueryIQ.setStanzaId("sarasa");
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.addExtension(rsmSet);

View File

@ -23,9 +23,9 @@ import java.util.List;
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.DefaultBehavior;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.junit.jupiter.api.Test;
import org.jxmpp.jid.Jid;
@ -33,16 +33,16 @@ import org.jxmpp.jid.impl.JidCreate;
public class PreferencesTest {
private static final String retrievePrefsStanzaExample = "<iq id='sarasa' type='get'>" + "<prefs xmlns='" + MamVersion.MAM2.getNamespace()
private static final String retrievePrefsStanzaExample = "<iq id='sarasa' type='get'>" + "<prefs xmlns='" + MamElements.NAMESPACE
+ "'/>" + "</iq>";
private static final String updatePrefsStanzaExample = "<iq id='sarasa' type='set'>" + "<prefs xmlns='" + MamVersion.MAM2.getNamespace()
private static final String updatePrefsStanzaExample = "<iq id='sarasa' type='set'>" + "<prefs xmlns='" + MamElements.NAMESPACE
+ "' default='roster'>" + "<always>" + "<jid>romeo@montague.lit</jid>" + "<jid>other@montague.lit</jid>"
+ "</always>" + "<never>" + "<jid>montague@montague.lit</jid>" + "</never>" + "</prefs>" + "</iq>";
@Test
public void checkRetrievePrefsStanza() throws Exception {
MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ();
MamPrefsIQ mamPrefIQ = new MamPrefsIQ();
mamPrefIQ.setStanzaId("sarasa");
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), retrievePrefsStanzaExample);
}
@ -56,7 +56,7 @@ public class PreferencesTest {
List<Jid> neverJids = new ArrayList<>();
neverJids.add(JidCreate.from("montague@montague.lit"));
MamPrefsIQ mamPrefIQ = MamVersion.MAM2.newElementFactory().newPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster);
MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, DefaultBehavior.roster);
mamPrefIQ.setStanzaId("sarasa");
assertEquals(mamPrefIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), updatePrefsStanzaExample);
}

View File

@ -29,9 +29,9 @@ import org.jivesoftware.smack.packet.StreamOpen;
import org.jivesoftware.smackx.delay.packet.DelayInformation;
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.MamQueryIQ;
import org.jivesoftware.smackx.mam.element.MamVersion;
import org.jivesoftware.smackx.xdata.packet.DataForm;
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'>"
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>" + "</x>" + "</query>" + "</iq>";
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "</query>" + "</iq>";
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'>"
@ -54,7 +54,7 @@ public class QueryArchiveTest extends MamTest {
@Test
public void checkMamQueryIQ() throws Exception {
DataForm dataForm = getNewMamForm();
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setStanzaId("sarasa");
assertEquals(mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString(), mamSimpleQueryIQ);
@ -80,7 +80,7 @@ public class QueryArchiveTest extends MamTest {
Forwarded<Message> forwarded = new Forwarded<>(forwardedMessage, delay);
message.addExtension(MamVersion.MAM2.newElementFactory().newResultExtension("g27", "34482-21985-73620", forwarded));
message.addExtension(new MamResultExtension("g27", "34482-21985-73620", forwarded));
assertEquals(mamQueryResultExample, message.toXML(StreamOpen.CLIENT_NAMESPACE).toString());

View File

@ -22,8 +22,8 @@ import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.StreamOpen;
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.MamVersion;
import org.jivesoftware.smackx.xdata.packet.DataForm;
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'>"
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
+ MamVersion.MAM2.getNamespace() + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
@Test
public void checkResultsLimit() throws Exception {
DataForm dataForm = getNewMamForm();
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId, dataForm);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
mamQueryIQ.setType(IQ.Type.set);
mamQueryIQ.setStanzaId("sarasa");

View File

@ -22,8 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import org.jivesoftware.smack.packet.StreamOpen;
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.MamVersion;
import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.packet.DataForm;
@ -32,18 +32,18 @@ import org.jxmpp.jid.JidTestUtil;
public class RetrieveFormFieldsTest extends MamTest {
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamVersion.MAM2.getNamespace()
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamElements.NAMESPACE
+ "' queryid='testid'></query>" + "</iq>";
private static final String additionalFieldsStanza = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>"
+ "<value>" + MamVersion.MAM2.getNamespace() + "</value>" + "</field>"
+ "<value>" + MamElements.NAMESPACE + "</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>"
+ "</x>";
@Test
public void checkRetrieveFormFieldsStanza() throws Exception {
MamQueryIQ mamQueryIQ = new MamQueryIQ(MamVersion.MAM2, queryId);
MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId);
mamQueryIQ.setStanzaId("sarasa");
assertEquals(retrieveFormFieldStanza, mamQueryIQ.toXML(StreamOpen.CLIENT_NAMESPACE).toString());
@ -63,7 +63,7 @@ public class RetrieveFormFieldsTest extends MamTest {
.withAdditionalFormField(field1)
.withAdditionalFormField(field2)
.build();
DataForm dataForm = mamQueryArgs.getDataForm(MamVersion.MAM2);
DataForm dataForm = mamQueryArgs.getDataForm();
String dataFormResult = dataForm.toXML().toString();

View File

@ -153,7 +153,7 @@ public class DataPacketExtension implements ExtensionElement {
@Override
public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this, enclosingNamespace));
XmlStringBuilder xml = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this));
xml.closeElement(this);
return xml;
}

View File

@ -615,7 +615,9 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
if (annouceLocalStreamHost) {
// add local proxy on first position if exists
List<StreamHost> localProxies = getLocalStreamHost();
streamHosts.addAll(localProxies);
if (localProxies != null) {
streamHosts.addAll(localProxies);
}
}
// query SOCKS5 proxies for network settings
@ -650,14 +652,12 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
/**
* Returns the stream host information of the local SOCKS5 proxy containing the IP address and
* the port. The returned list may be empty if the local SOCKS5 proxy is not running.
* the port or null if local SOCKS5 proxy is not running.
*
* @return the stream host information of the local SOCKS5 proxy
* @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy
* is not running
*/
public List<StreamHost> getLocalStreamHost() {
// Ensure that the local SOCKS5 proxy is running (if enabled).
Socks5Proxy.getSocks5Proxy();
List<StreamHost> streamHosts = new ArrayList<>();
XMPPConnection connection = connection();

View File

@ -700,7 +700,7 @@ public final class EntityCapsManager extends Manager {
for (FormField f : fs) {
sb.append(f.getFieldName());
sb.append('<');
formFieldValuesToCaps(f.getRawValueCharSequences(), sb);
formFieldValuesToCaps(f.getRawValues(), sb);
}
}

View File

@ -921,10 +921,6 @@ public final class ServiceDiscoveryManager extends Manager {
return entityCapabilitiesChangedListeners.add(entityCapabilitiesChangedListener);
}
public boolean removeEntityCapabilitiesChangedListener(EntityCapabilitiesChangedListener entityCapabilitiesChangedListener) {
return entityCapabilitiesChangedListeners.remove(entityCapabilitiesChangedListener);
}
private static final int RENEW_ENTITY_CAPS_DELAY_MILLIS = 25;
private ScheduledAction renewEntityCapsScheduledAction;

View File

@ -258,14 +258,6 @@ public class DiscoverInfo extends IQ implements DiscoverInfoView {
return features.contains(new Feature(feature));
}
public static boolean nullSafeContainsFeature(DiscoverInfo discoverInfo, CharSequence feature) {
if (discoverInfo == null) {
return false;
}
return discoverInfo.containsFeature(feature);
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.optAttribute("node", getNode());

View File

@ -68,12 +68,6 @@ 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) {
StringUtils.requireNotNullNorEmpty(fieldName, "fieldName must be provided");
Objects.requireNonNull(fieldType);

View File

@ -53,7 +53,7 @@ public class JingleUtil {
JingleContentDescription description,
JingleContentTransport transport) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setAction(JingleAction.session_initiate)
.setSessionId(sessionId)
.setInitiator(connection.getUser());
@ -118,7 +118,7 @@ public class JingleUtil {
JingleContentDescription description,
JingleContentTransport transport) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setResponder(connection.getUser())
.setAction(JingleAction.session_accept)
.setSessionId(sessionId);
@ -153,7 +153,7 @@ public class JingleUtil {
}
public Jingle createSessionTerminate(FullJid recipient, String sessionId, JingleReason reason) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setAction(JingleAction.session_terminate)
.setSessionId(sessionId)
.setReason(reason);
@ -232,7 +232,7 @@ public class JingleUtil {
public Jingle createSessionTerminateContentCancel(FullJid recipient, String sessionId,
JingleContent.Creator contentCreator, String contentName) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setAction(JingleAction.session_terminate)
.setSessionId(sessionId);
@ -314,7 +314,7 @@ public class JingleUtil {
}
public Jingle createSessionPing(FullJid recipient, String sessionId) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setSessionId(sessionId)
.setAction(JingleAction.session_info);
@ -343,7 +343,7 @@ public class JingleUtil {
public Jingle createTransportReplace(FullJid recipient, FullJid initiator, String sessionId,
JingleContent.Creator contentCreator, String contentName,
JingleContentTransport transport) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setInitiator(initiator)
.setSessionId(sessionId)
.setAction(JingleAction.transport_replace);
@ -370,7 +370,7 @@ public class JingleUtil {
public Jingle createTransportAccept(FullJid recipient, FullJid initiator, String sessionId,
JingleContent.Creator contentCreator, String contentName,
JingleContentTransport transport) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setAction(JingleAction.transport_accept)
.setInitiator(initiator)
.setSessionId(sessionId);
@ -397,7 +397,7 @@ public class JingleUtil {
public Jingle createTransportReject(FullJid recipient, FullJid initiator, String sessionId,
JingleContent.Creator contentCreator, String contentName,
JingleContentTransport transport) {
Jingle.Builder jb = Jingle.builder(connection);
Jingle.Builder jb = Jingle.getBuilder();
jb.setAction(JingleAction.transport_reject)
.setInitiator(initiator)
.setSessionId(sessionId);

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software, 2014-2021 Florian Schmaus
* Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,11 +21,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jivesoftware.smack.XMPPConnection;
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.StringUtils;
@ -69,9 +65,9 @@ public final class Jingle extends IQ {
private final List<JingleContent> contents;
private Jingle(Builder builder, String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
private Jingle(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
List<JingleContent> contents) {
super(builder, ELEMENT, NAMESPACE);
super(ELEMENT, NAMESPACE);
this.sessionId = StringUtils.requireNotNullNorEmpty(sessionId, "Jingle session ID must not be null");
this.action = Objects.requireNonNull(action, "Jingle action must not be null");
this.initiator = initiator;
@ -173,31 +169,11 @@ public final class Jingle extends IQ {
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() {
return builder(StandardStanzaIdSource.DEFAULT.getNewStanzaId());
return new 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> {
public static final class Builder {
private String sid;
private JingleAction action;
@ -210,16 +186,7 @@ public final class Jingle extends IQ {
private List<JingleContent> contents;
Builder(IqData iqCommon) {
super(iqCommon);
}
Builder(XMPPConnection connection) {
super(connection);
}
Builder(String stanzaId) {
super(stanzaId);
private Builder() {
}
public Builder setSessionId(String sessionId) {
@ -261,14 +228,8 @@ public final class Jingle extends IQ {
return this;
}
@Override
public Jingle build() {
return new Jingle(this, sid, action, initiator, responder, reason, contents);
}
@Override
public Builder getThis() {
return this;
return new Jingle(sid, action, initiator, responder, reason, contents);
}
}
}

View File

@ -145,11 +145,6 @@ public final class JingleContent implements XmlElement {
xml.optAttribute(DISPOSITION_ATTRIBUTE_NAME, disposition);
xml.attribute(NAME_ATTRIBUTE_NAME, name);
xml.optAttribute(SENDERS_ATTRIBUTE_NAME, senders);
if (description == null && transport == null) {
return xml.closeEmptyElement();
}
xml.rightAngleBracket();
xml.optAppend(description);

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2017-2022 Florian Schmaus
* Copyright 2017-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.
@ -34,7 +34,6 @@ public class JingleReason implements XmlElement {
public static final String ELEMENT = "reason";
public static final String NAMESPACE = Jingle.NAMESPACE;
public static final String TEXT_ELEMENT = "text";
public static AlternativeSession AlternativeSession(String sessionId) {
return new AlternativeSession(sessionId);
@ -106,17 +105,9 @@ public class JingleReason implements XmlElement {
}
protected final Reason reason;
private final String text;
private final XmlElement element;
public JingleReason(Reason reason) {
this(reason, null, null);
}
public JingleReason(Reason reason, String text, XmlElement element) {
this.reason = reason;
this.text = text;
this.element = element;
}
@Override
@ -129,34 +120,12 @@ public class JingleReason implements XmlElement {
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
public XmlStringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingXmlEnvironment);
xml.rightAngleBracket();
xml.emptyElement(reason);
xml.optElement(TEXT_ELEMENT, text);
xml.optAppend(element);
xml.closeElement(this);
return xml;
@ -173,11 +142,7 @@ public class JingleReason implements XmlElement {
private final String sessionId;
public AlternativeSession(String sessionId) {
this(sessionId, null, null);
}
public AlternativeSession(String sessionId, String text, XmlElement element) {
super(Reason.alternative_session, text, element);
super(Reason.alternative_session);
if (StringUtils.isNullOrEmpty(sessionId)) {
throw new NullPointerException("SessionID must not be null or empty.");
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2017-2022 Florian Schmaus
* Copyright 2017-2019 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,14 +19,11 @@ package org.jivesoftware.smackx.jingle.provider;
import java.io.IOException;
import java.util.logging.Logger;
import org.jivesoftware.smack.packet.IqData;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.XmlElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
import org.jivesoftware.smack.provider.IqProvider;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
@ -43,13 +40,13 @@ import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransport;
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());
@Override
public Jingle parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
Jingle.Builder builder = Jingle.builder(iqData);
public Jingle parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
Jingle.Builder builder = Jingle.getBuilder();
String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
if (actionString != null) {
@ -78,7 +75,16 @@ public class JingleProvider extends IqProvider<Jingle> {
builder.addJingleContent(content);
break;
case JingleReason.ELEMENT:
JingleReason reason = parseJingleReason(parser);
parser.next();
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);
break;
default:
@ -171,57 +177,4 @@ public class JingleProvider extends IqProvider<Jingle> {
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;
}
}

View File

@ -36,10 +36,6 @@ public abstract class JingleTransportManager<D extends JingleContentTransport> i
}
public XMPPConnection getConnection() {
return connection();
}
public XMPPConnection connection() {
return connection;
}

View File

@ -148,7 +148,7 @@ public final class JingleS5BTransportManager extends JingleTransportManager<Jing
public Jingle createCandidateUsed(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Senders contentSenders,
JingleContent.Creator contentCreator, String contentName, String streamId,
String candidateId) {
Jingle.Builder jb = Jingle.builder(connection());
Jingle.Builder jb = Jingle.getBuilder();
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
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) {
Jingle.Builder jb = Jingle.builder(connection());
Jingle.Builder jb = Jingle.getBuilder();
jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);
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,
JingleContent.Senders senders, JingleContent.Creator creator,
String name, String streamId) {
Jingle.Builder jb = Jingle.builder(connection());
Jingle.Builder jb = Jingle.getBuilder();
jb.setSessionId(sessionId).setAction(JingleAction.transport_info).setInitiator(initiator);
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,
JingleContent.Senders senders, JingleContent.Creator creator,
String name, String streamId, String candidateId) {
Jingle.Builder jb = Jingle.builder(connection());
Jingle.Builder jb = Jingle.getBuilder();
jb.setInitiator(initiator).setSessionId(sessionId).setAction(JingleAction.transport_info);
JingleContent.Builder cb = JingleContent.getBuilder();

View File

@ -26,7 +26,6 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.DoOnce;
import org.jivesoftware.smack.util.stringencoder.Base64;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
@ -36,8 +35,6 @@ import org.jivesoftware.smackx.jiveproperties.packet.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());
/**
@ -116,10 +113,7 @@ public class JivePropertiesExtensionProvider extends ExtensionElementProvider<Ji
}
}
else {
LOG_OBJECT_NOT_ENABLED.once(
() -> LOGGER.severe(
"JavaObject is not enabled. Enable with JivePropertiesManager.setJavaObjectEnabled(true)")
);
LOGGER.severe("JavaObject is not enabled. Enable with JivePropertiesManager.setJavaObjectEnabled(true)");
}
}
if (name != null && value != null) {

View File

@ -78,17 +78,6 @@ public class MucConfigFormManager {
*/
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 FillableForm answerForm;
private final List<Jid> owners;
@ -162,15 +151,6 @@ public class MucConfigFormManager {
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.
*
@ -196,68 +176,6 @@ public class MucConfigFormManager {
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.
*

View File

@ -205,20 +205,12 @@ public class MultiUserChat {
if (from == null) {
return;
}
final EntityFullJid myRoomJID = getMyRoomJid();
final EntityFullJid myRoomJID = myRoomJid;
final boolean isUserStatusModification = presence.getFrom().equals(myRoomJID);
final MUCUser mucUser = MUCUser.from(packet);
switch (presence.getType()) {
case available:
if (!processedReflectedSelfPresence
&& mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) {
processedReflectedSelfPresence = true;
synchronized (this) {
notify();
}
}
Presence oldPresence = occupantsMap.put(from, presence);
if (oldPresence != null) {
// Get the previous occupant's affiliation & role
@ -236,6 +228,11 @@ public class MultiUserChat {
newAffiliation,
isUserStatusModification,
from);
} else if (mucUser.getStatus().contains(MUCUser.Status.PRESENCE_TO_SELF_110)) {
processedReflectedSelfPresence = true;
synchronized (this) {
notify();
}
} else {
// A new occupant has joined the room
for (ParticipantStatusListener listener : participantStatusListeners) {
@ -262,24 +259,23 @@ public class MultiUserChat {
listener.left(from);
}
}
}
Destroy destroy = mucUser.getDestroy();
// The room has been destroyed.
if (destroy != null) {
EntityBareJid alternateMucJid = destroy.getJid();
final MultiUserChat alternateMuc;
if (alternateMucJid == null) {
alternateMuc = null;
} else {
alternateMuc = multiUserChatManager.getMultiUserChat(alternateMucJid);
}
Destroy destroy = mucUser.getDestroy();
// The room has been destroyed.
if (destroy != null) {
EntityBareJid alternateMucJid = destroy.getJid();
final MultiUserChat alternateMuc;
if (alternateMucJid == null) {
alternateMuc = null;
} else {
alternateMuc = multiUserChatManager.getMultiUserChat(alternateMucJid);
}
for (UserStatusListener listener : userStatusListeners) {
listener.roomDestroyed(alternateMuc, destroy.getReason());
for (UserStatusListener listener : userStatusListeners) {
listener.roomDestroyed(alternateMuc, destroy.getReason());
}
}
}
if (isUserStatusModification) {
for (UserStatusListener listener : userStatusListeners) {
listener.removed(mucUser, presence);
@ -732,7 +728,7 @@ public class MultiUserChat {
* @return true if currently in the multi user chat room.
*/
public boolean isJoined() {
return getMyRoomJid() != null;
return myRoomJid != null;
}
/**
@ -768,7 +764,7 @@ public class MultiUserChat {
// "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.
final EntityFullJid myRoomJid = getMyRoomJid();
final EntityFullJid myRoomJid = this.myRoomJid;
if (myRoomJid == null) {
throw new MucNotJoinedException(this);
}
@ -797,14 +793,11 @@ public class MultiUserChat {
StanzaFilter reflectedLeavePresenceFilter = new AndFilter(reflectedLeavePresenceFilters);
Presence reflectedLeavePresence;
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();
}
// Reset occupant information first so that we are assume that we left the room even if sendStanza() would
// throw.
userHasLeft();
Presence reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow();
return reflectedLeavePresence;
}
@ -1200,23 +1193,13 @@ public class MultiUserChat {
* @return the nickname currently being used.
*/
public Resourcepart getNickname() {
final EntityFullJid myRoomJid = getMyRoomJid();
final EntityFullJid myRoomJid = this.myRoomJid;
if (myRoomJid == null) {
return null;
}
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
* will receive two presence packets. One of type "unavailable" for the old nickname and one
@ -1273,7 +1256,7 @@ public class MultiUserChat {
* @throws MucNotJoinedException if not joined to the Multi-User Chat.
*/
public void changeAvailabilityStatus(String status, Presence.Mode mode) throws NotConnectedException, InterruptedException, MucNotJoinedException {
final EntityFullJid myRoomJid = getMyRoomJid();
final EntityFullJid myRoomJid = this.myRoomJid;
if (myRoomJid == null) {
throw new MucNotJoinedException(this);
}
@ -2596,7 +2579,7 @@ public class MultiUserChat {
}
public boolean serviceSupportsStableIds() {
return DiscoverInfo.nullSafeContainsFeature(mucServiceDiscoInfo, MultiUserChatConstants.STABLE_ID_FEATURE);
return mucServiceDiscoInfo.containsFeature(MultiUserChatConstants.STABLE_ID_FEATURE);
}
@Override

View File

@ -38,7 +38,11 @@ public class FormNode extends NodeExtension {
* @param submitForm The form
*/
public FormNode(FormNodeType formType, DataForm submitForm) {
this(formType, null, submitForm);
super(formType.getNodeElement());
if (submitForm == null)
throw new IllegalArgumentException("Submit form cannot be null");
configForm = submitForm;
}
/**
@ -51,6 +55,9 @@ public class FormNode extends NodeExtension {
*/
public FormNode(FormNodeType formType, String nodeId, DataForm submitForm) {
super(formType.getNodeElement(), nodeId);
if (submitForm == null)
throw new IllegalArgumentException("Submit form cannot be null");
configForm = submitForm;
}

View File

@ -35,11 +35,6 @@ import org.jivesoftware.smackx.xdata.packet.DataForm;
public class FormNodeProvider extends EmbeddedExtensionProvider<FormNode> {
@Override
protected FormNode createReturnExtension(String currentElement, String currentNamespace, Map<String, String> attributeMap, List<? extends XmlElement> content) {
DataForm dataForm = null;
if (!content.isEmpty()) {
dataForm = (DataForm) content.get(0);
}
return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), dataForm);
return new FormNode(FormNodeType.valueOfFromElementName(currentElement, currentNamespace), attributeMap.get("node"), (DataForm) content.iterator().next());
}
}

View File

@ -50,25 +50,24 @@ public class ItemProvider extends ExtensionElementProvider<Item> {
String xmlns = parser.getNamespace();
ItemNamespace itemNamespace = ItemNamespace.fromXmlns(xmlns);
XmlPullParser.TagEvent event = parser.nextTag();
switch (event) {
case START_ELEMENT:
String payloadElemName = parser.getName();
String payloadNS = parser.getNamespace();
XmlPullParser.Event tag = parser.next();
final ExtensionElementProvider<ExtensionElement> extensionProvider = ProviderManager.getExtensionProvider(payloadElemName, payloadNS);
if (extensionProvider == null) {
// TODO: Should we use StandardExtensionElement in this case? And probably remove SimplePayload all together.
CharSequence payloadText = PacketParserUtils.parseElement(parser, true);
return new PayloadItem<>(itemNamespace, id, node, new SimplePayload(payloadText.toString()));
}
else {
return new PayloadItem<>(itemNamespace, id, node, extensionProvider.parse(parser));
}
case END_ELEMENT:
return new Item(itemNamespace, id, node);
default:
throw new AssertionError("unknown: " + event);
if (tag == XmlPullParser.Event.END_ELEMENT) {
return new Item(itemNamespace, id, node);
}
else {
String payloadElemName = parser.getName();
String payloadNS = parser.getNamespace();
final ExtensionElementProvider<ExtensionElement> extensionProvider = ProviderManager.getExtensionProvider(payloadElemName, payloadNS);
if (extensionProvider == null) {
// TODO: Should we use StandardExtensionElement in this case? And probably remove SimplePayload all together.
CharSequence payloadText = PacketParserUtils.parseElement(parser, true);
return new PayloadItem<>(itemNamespace, id, node, new SimplePayload(payloadText.toString()));
}
else {
return new PayloadItem<>(itemNamespace, id, node, extensionProvider.parse(parser));
}
}
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Aditya Borikar, 2021 Florian Schmaus
* Copyright 2020 Aditya Borikar
*
* Licensed under the Apache License, Version 2.0 (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.HashCode;
import org.jivesoftware.smackx.formtypes.FormFieldRegistry;
import org.jivesoftware.smackx.mediaelement.element.MediaElement;
import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.FormFieldChildElement;
@ -47,11 +47,6 @@ public final class SoftwareInfoForm extends FilledForm {
public static final String SOFTWARE_VERSION = "software_version";
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) {
super(dataForm);
}

View File

@ -27,26 +27,35 @@ import org.jxmpp.util.XmppDateTime;
public class AbstractMultiFormField extends FormField {
private final List<Value> values;
private final List<String> values;
private final List<String> rawValues;
protected AbstractMultiFormField(Builder<?, ?> builder) {
super(builder);
values = CollectionUtil.cloneAndSeal(builder.values);
rawValues = CollectionUtil.cloneAndSeal(builder.rawValues);
}
@Override
public final List<Value> getRawValues() {
public final List<String> getValues() {
return values;
}
@Override
public final List<String> getRawValues() {
return rawValues;
}
public abstract static class Builder<F extends AbstractMultiFormField, B extends FormField.Builder<F, B>>
extends FormField.Builder<F, B> {
private List<Value> values;
private List<String> values;
private List<String> rawValues;
protected Builder(AbstractMultiFormField formField) {
super(formField);
values = CollectionUtil.newListWith(formField.getRawValues());
values = CollectionUtil.newListWith(formField.getValues());
}
protected Builder(String fieldName, FormField.Type type) {
@ -56,6 +65,7 @@ public class AbstractMultiFormField extends FormField {
private void ensureValuesAreInitialized() {
if (values == null) {
values = new ArrayList<>();
rawValues = new ArrayList<>();
}
}
@ -67,13 +77,11 @@ public class AbstractMultiFormField extends FormField {
public abstract B addValue(CharSequence value);
public B addValueVerbatim(CharSequence value) {
return addValueVerbatim(new Value(value));
}
public B addValueVerbatim(Value value) {
ensureValuesAreInitialized();
values.add(value);
String valueString = value.toString();
values.add(valueString);
rawValues.add(valueString);
return getThis();
}

View File

@ -74,15 +74,8 @@ public class AbstractSingleStringValueFormField extends SingleValueFormField {
return setValue(value);
}
public B setValue(Value value) {
this.value = value.getValue().toString();
this.rawValue = value;
return getThis();
}
public B setValue(CharSequence value) {
this.value = value.toString();
rawValue = new Value(this.value);
this.rawValue = this.value = value.toString();
return getThis();
}

View File

@ -35,31 +35,7 @@ public class BooleanFormField extends SingleValueFormField {
return value.toString();
}
/**
* 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() {
public Boolean getValueAsBoolean() {
return value;
}
@ -95,21 +71,17 @@ public class BooleanFormField extends SingleValueFormField {
@Deprecated
// TODO: Remove in Smack 4.6.
public Builder addValue(CharSequence value) {
return setValue(new Value(value));
return setValue(value);
}
public Builder setValue(CharSequence value) {
return setValue(new Value(value));
}
public Builder setValue(Value value) {
this.value = ParserUtils.parseXmlBoolean(value.getValue().toString());
rawValue = value;
return getThis();
rawValue = value.toString();
boolean valueBoolean = ParserUtils.parseXmlBoolean(rawValue);
return setValue(valueBoolean);
}
public Builder setValue(boolean value) {
rawValue = new Value(Boolean.toString(value));
rawValue = Boolean.toString(value);
this.value = value;
return this;
}

View File

@ -33,7 +33,6 @@ import org.jivesoftware.smack.util.CollectionUtil;
import org.jivesoftware.smack.util.EqualsUtil;
import org.jivesoftware.smack.util.HashCode;
import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
@ -270,25 +269,9 @@ public abstract class FormField implements XmlElement {
*
* @return a List of the default values or answered values of the question.
*/
public List<? extends CharSequence> getValues() {
return getRawValueCharSequences();
}
public abstract List<? extends CharSequence> getValues();
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 abstract List<String> getRawValues();
public boolean hasValueSet() {
List<?> values = getValues();
@ -402,15 +385,12 @@ public abstract class FormField implements XmlElement {
protected transient List<XmlElement> extraXmlChildElements;
/**
* Populate @{link {@link #extraXmlChildElements}}. Note that this method may be overridden by subclasses.
*/
protected void populateExtraXmlChildElements() {
List<Value> values = getRawValues();
// Note that we need to create a new ArrayList here, since subclasses may add to it by overriding
// populateExtraXmlChildElements.
List<? extends CharSequence> values = getValues();
extraXmlChildElements = new ArrayList<>(values.size());
extraXmlChildElements.addAll(values);
for (CharSequence value : values) {
extraXmlChildElements.add(new Value(value));
}
}
@Override
@ -434,8 +414,7 @@ public abstract class FormField implements XmlElement {
populateExtraXmlChildElements();
}
if (formFieldChildElements.isEmpty()
&& (extraXmlChildElements == null || extraXmlChildElements.isEmpty())) {
if (formFieldChildElements.isEmpty() && extraXmlChildElements == null) {
buf.closeEmptyElement();
} else {
buf.rightAngleBracket();
@ -601,7 +580,7 @@ public abstract class FormField implements XmlElement {
* @return a reference to this builder.
*/
public B setLabel(String label) {
this.label = Objects.requireNonNull(label, "label must not be null");
this.label = StringUtils.requireNotNullNorEmpty(label, "label must not be null or empty");
return getThis();
}

View File

@ -23,14 +23,13 @@ import java.util.List;
import org.jivesoftware.smack.util.CollectionUtil;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import org.jxmpp.jid.util.JidUtil;
public final class JidMultiFormField extends FormField {
private final List<Jid> values;
private final List<Value> rawValues;
private final List<String> rawValues;
JidMultiFormField(Builder builder) {
super(builder);
@ -44,7 +43,7 @@ public final class JidMultiFormField extends FormField {
}
@Override
public List<Value> getRawValues() {
public List<String> getRawValues() {
return rawValues;
}
@ -55,7 +54,7 @@ public final class JidMultiFormField extends FormField {
public static final class Builder extends FormField.Builder<JidMultiFormField, JidMultiFormField.Builder> {
private List<Jid> values;
private List<Value> rawValues;
private List<String> rawValues;
private Builder(JidMultiFormField jidMultiFormField) {
super(jidMultiFormField);
@ -80,29 +79,27 @@ public final class JidMultiFormField extends FormField {
}
public Builder addValue(Jid jid) {
Value value = new Value(jid);
ensureValuesAreInitialized();
values.add(jid);
rawValues.add(value);
return getThis();
return addValue(jid, null);
}
public Builder addValue(Value value) throws XmppStringprepException {
Jid jid = JidCreate.from(value.getValue());
public Builder addValue(Jid jid, String rawValue) {
if (rawValue == null) {
rawValue = jid.toString();
}
ensureValuesAreInitialized();
values.add(jid);
rawValues.add(value);
rawValues.add(rawValue);
return this;
}
public Builder addValues(Collection<? extends Jid> jids) {
for (Jid jid : jids) {
addValue(jid);
}
ensureValuesAreInitialized();
values.addAll(jids);
rawValues.addAll(JidUtil.toStringList(jids));
return this;
}

View File

@ -17,8 +17,6 @@
package org.jivesoftware.smackx.xdata;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
public class JidSingleFormField extends SingleValueFormField {
@ -62,13 +60,11 @@ public class JidSingleFormField extends SingleValueFormField {
public Builder setValue(Jid value, String rawValue) {
this.value = value;
this.rawValue = new Value(value);
return getThis();
}
public Builder setValue(Value value) throws XmppStringprepException {
this.value = JidCreate.from(value.getValue());
this.rawValue = value;
if (rawValue != null) {
this.rawValue = rawValue;
} else {
this.rawValue = value.toString();
}
return this;
}

View File

@ -23,7 +23,7 @@ import org.jivesoftware.smack.util.CollectionUtil;
public abstract class SingleValueFormField extends FormField {
private final Value rawValue;
private final String rawValue;
protected SingleValueFormField(Builder<?, ?> builder) {
super(builder);
@ -38,23 +38,24 @@ public abstract class SingleValueFormField extends FormField {
public abstract CharSequence getValue();
public final Value getRawValue() {
public final String getRawValue() {
return rawValue;
}
@Override
public final List<Value> getRawValues() {
Value rawValue = getRawValue();
public final List<String> getRawValues() {
String rawValue = getRawValue();
return CollectionUtil.emptyOrSingletonListFrom(rawValue);
}
@Override
protected void populateExtraXmlChildElements() {
if (rawValue == null) {
CharSequence value = getValue();
if (value == null) {
return;
}
extraXmlChildElements = Collections.singletonList(rawValue);
extraXmlChildElements = Collections.singletonList(new Value(value));
}
public abstract static class Builder<F extends SingleValueFormField, B extends Builder<F, B>>
@ -64,12 +65,11 @@ public abstract class SingleValueFormField extends FormField {
super(fieldName, type);
}
protected Builder(SingleValueFormField formField) {
protected Builder(FormField formField) {
super(formField);
rawValue = formField.getRawValue();
}
protected Value rawValue;
protected String rawValue;
@Override
protected void resetInternal() {

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2020-2021 Florian Schmaus
* Copyright 2020 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,8 +21,6 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.xdata.AbstractMultiFormField;
import org.jivesoftware.smackx.xdata.AbstractSingleStringValueFormField;
import org.jivesoftware.smackx.xdata.BooleanFormField;
@ -56,8 +54,7 @@ public interface FormReader {
return Collections.emptyList();
}
AbstractMultiFormField multiFormField = formField.ifPossibleAs(AbstractMultiFormField.class);
List<? extends CharSequence> charSequences = multiFormField.getValues();
return StringUtils.toStrings(charSequences);
return multiFormField.getValues();
}
default Boolean readBoolean(String fieldName) {

View File

@ -49,6 +49,9 @@ import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jivesoftware.smackx.xdatalayout.packet.DataLayout;
import org.jivesoftware.smackx.xdatalayout.provider.DataLayoutProvider;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
/**
* The DataFormProvider parses DataForm packets.
*
@ -234,7 +237,9 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
case jid_multi:
JidMultiFormField.Builder jidMultiBuilder = FormField.jidMultiBuilder(fieldName);
for (FormField.Value value : values) {
jidMultiBuilder.addValue(value);
String rawValue = value.getValue().toString();
Jid jid = JidCreate.from(rawValue);
jidMultiBuilder.addValue(jid, rawValue);
}
builder = jidMultiBuilder;
break;
@ -242,8 +247,9 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
ensureAtMostSingleValue(type, values);
JidSingleFormField.Builder jidSingleBuilder = FormField.jidSingleBuilder(fieldName);
if (!values.isEmpty()) {
FormField.Value value = values.get(0);
jidSingleBuilder.setValue(value);
String rawValue = values.get(0).getValue().toString();
Jid jid = JidCreate.from(rawValue);
jidSingleBuilder.setValue(jid, rawValue);
}
builder = jidSingleBuilder;
break;
@ -297,7 +303,7 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
BooleanFormField.Builder builder = FormField.booleanBuilder(fieldName);
ensureAtMostSingleValue(builder.getType(), values);
if (values.size() == 1) {
FormField.Value value = values.get(0);
String value = values.get(0).getValue().toString();
builder.setValue(value);
}
return builder;

View File

@ -20,6 +20,5 @@
<className>org.jivesoftware.smackx.receipts.DeliveryReceiptManager</className>
<className>org.jivesoftware.smackx.iqversion.VersionManager</className>
<className>org.jivesoftware.smackx.caps.EntityCapsManager</className>
<className>org.jivesoftware.smackx.softwareinfo.form.SoftwareInfoForm</className>
</startupClasses>
</smack>

View File

@ -67,6 +67,7 @@ public class AMPExtensionTest {
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
XmlPullParser parser = PacketParserUtils.getParserFor(INCORRECT_RECEIVING_STANZA_STREAM);
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
assertEquals(AMPExtension.ELEMENT, parser.getName());
ExtensionElement extension = ampProvider.parse(parser);
@ -84,6 +85,7 @@ public class AMPExtensionTest {
AMPExtensionProvider ampProvider = new AMPExtensionProvider();
XmlPullParser parser = PacketParserUtils.getParserFor(CORRECT_SENDING_STANZA_STREAM);
assertEquals(XmlPullParser.Event.START_ELEMENT, parser.next());
assertEquals(AMPExtension.ELEMENT, parser.getName());
ExtensionElement extension = ampProvider.parse(parser);
assertTrue(extension instanceof AMPExtension);

View File

@ -75,7 +75,8 @@ public class JingleContentTest extends SmackTestSuite {
assertEquals(content1.toXML().toString(), builder.build().toXML().toString());
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());
}
}

View File

@ -38,7 +38,7 @@ public class JingleTest extends SmackTestSuite {
@Test
public void emptyBuilderTest() {
Jingle.Builder builder = Jingle.builder("id");
Jingle.Builder builder = Jingle.getBuilder();
assertThrows(IllegalArgumentException.class, () -> {
builder.build();
});
@ -48,7 +48,7 @@ public class JingleTest extends SmackTestSuite {
public void onlySessionIdBuilderTest() {
String sessionId = "testSessionId";
Jingle.Builder builder = Jingle.builder("id");
Jingle.Builder builder = Jingle.getBuilder();
builder.setSessionId(sessionId);
assertThrows(IllegalArgumentException.class, () -> {
builder.build();
@ -59,7 +59,7 @@ public class JingleTest extends SmackTestSuite {
public void parserTest() throws XmppStringprepException {
String sessionId = "testSessionId";
Jingle.Builder builder = Jingle.builder("id");
Jingle.Builder builder = Jingle.getBuilder();
builder.setSessionId(sessionId);
builder.setAction(JingleAction.session_initiate);

View File

@ -1,48 +0,0 @@
/**
*
* 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);
}
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2017-2022 Florian Schmaus
* Copyright 2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,30 +17,23 @@
package org.jivesoftware.smackx.jingle.provider;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.XmlElement;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.test.util.SmackTestUtil;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.jingle.element.Jingle;
import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
import org.jivesoftware.smackx.jingle.element.JingleReason;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.api.Test;
public class JingleProviderTest {
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testParseUnknownJingleContentDescrption(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
@Test
public void testParseUnknownJingleContentDescrption() throws Exception {
final String unknownJingleContentDescriptionNamespace = "urn:xmpp:jingle:unknown-description:5";
final String unknownJingleContentDescription =
// @formatter:off
@ -57,8 +50,8 @@ public class JingleProviderTest {
"</file>" +
"</description>";
// @formatter:on
CharSequence xml = createTestJingle(unknownJingleContentDescription);
Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind);
XmlPullParser parser = createTestJingle(unknownJingleContentDescription);
Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser);
JingleContentDescription jingleContentDescription = jingle.getSoleContentOrThrow().getDescription();
@ -66,10 +59,8 @@ public class JingleProviderTest {
assertEquals(unknownJingleContentDescriptionNamespace, parsedUnknownJingleContentDescriptionNamespace);
}
@ParameterizedTest
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
public void testParseUnknownJingleContentTransport(SmackTestUtil.XmlPullParserKind parserKind)
throws XmlPullParserException, IOException, SmackParsingException {
@Test
public void testParseUnknownJingleContentTransport() throws Exception {
final String unknownJingleContentTransportNamespace = "urn:xmpp:jingle:unknown-transport:foo:1";
final String unknownJingleContentTransport =
// @formatter:off
@ -90,8 +81,8 @@ public class JingleProviderTest {
" type='direct'/>" +
"</transport>";
// @formatter:on
CharSequence xml = createTestJingle(unknownJingleContentTransport);
Jingle jingle = SmackTestUtil.parse(xml, JingleProvider.class, parserKind);
XmlPullParser parser = createTestJingle(unknownJingleContentTransport);
Jingle jingle = (Jingle) PacketParserUtils.parseIQ(parser);
JingleContentTransport jingleContentTransport = jingle.getSoleContentOrThrow().getTransport();
@ -99,38 +90,7 @@ public class JingleProviderTest {
assertEquals(unknownJingleContentTransportNamespace, parsedUnknownJingleContentTransportNamespace);
}
@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 {
private static XmlPullParser createTestJingle(String... childs) throws XmlPullParserException, IOException {
StringBuilder sb = new StringBuilder();
sb.append(// @formatter:off
"<iq from='romeo@montague.example/dr4hcr0st3lup4c'" +
@ -138,9 +98,9 @@ public class JingleProviderTest {
" to='juliet@capulet.example/yn0cl4bnw0yr3vym'" +
" type='set'>" +
"<jingle xmlns='urn:xmpp:jingle:1' " +
"action='session-initiate' " +
"initiator='romeo@montague.example/dr4hcr0st3lup4c' " +
"sid='851ba2'>" +
" action='session-initiate' " +
" initiator='romeo@montague.example/dr4hcr0st3lup4c' " +
" sid='851ba2'>" +
"<content creator='initiator' name='a-file-offer' senders='initiator'>"
// @formatter:on
);
@ -154,6 +114,9 @@ public class JingleProviderTest {
// @formatter:on
);
return sb;
String jingleStanza = sb.toString();
XmlPullParser parser = PacketParserUtils.getParserFor(jingleStanza);
return parser;
}
}

View File

@ -1,48 +0,0 @@
/**
*
* 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);
}
}

View File

@ -17,8 +17,6 @@
package org.jivesoftware.smackx.xdata;
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.jxmpp.jid.JidTestUtil;
@ -37,18 +35,4 @@ class FormFieldTest {
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));
}
}

View File

@ -115,33 +115,4 @@ public class DataFormProviderTest {
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());
}
}

View File

@ -39,6 +39,7 @@ public class XHTMLExtensionProviderTest {
public void parsesWell() throws IOException, XmlPullParserException {
InputStream inputStream = getClass().getResourceAsStream(XHTML_EXTENSION_SAMPLE_RESOURCE_NAME);
XmlPullParser parser = PacketParserUtils.getParserFor(inputStream);
parser.next();
XHTMLExtensionProvider provider = new XHTMLExtensionProvider();
ExtensionElement extension = provider.parse(parser, parser.getDepth(), null);

View File

@ -469,14 +469,11 @@ public final class Roster extends Manager {
@Override
public void processException(Exception exception) {
rosterState = RosterState.uninitialized;
Level logLevel = Level.SEVERE;
Level logLevel;
if (exception instanceof NotConnectedException) {
logLevel = Level.FINE;
} else if (exception instanceof XMPPErrorException) {
Condition condition = ((XMPPErrorException) exception).getStanzaError().getCondition();
if (condition == Condition.feature_not_implemented || condition == Condition.service_unavailable) {
logLevel = Level.FINE;
}
} else {
logLevel = Level.SEVERE;
}
LOGGER.log(logLevel, "Exception reloading roster", exception);
for (RosterLoadedListener listener : rosterLoadedListeners) {

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2015-2021 Florian Schmaus
* Copyright 2015-2020 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -85,7 +85,6 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
* @param action the action to perform.
* @throws Exception in case of an exception.
*/
@SuppressWarnings("ThreadPriorityCheck")
protected void performActionAndWaitForPresence(XMPPConnection conA, XMPPConnection conB, ThrowingRunnable action)
throws Exception {
final SimpleResultSyncPoint presenceReceivedSyncPoint = new SimpleResultSyncPoint();
@ -110,8 +109,5 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
} finally {
conA.removeAsyncStanzaListener(presenceListener);
}
// TODO: Ugly hack to make tests using this method more reliable. Ideally no test would use this method.
Thread.yield();
}
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2015-2021 Florian Schmaus
* Copyright 2015-2020 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,11 +19,8 @@ package org.igniterealtime.smack.inttest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@ -36,7 +33,6 @@ import javax.net.ssl.SSLContext;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.debugger.ConsoleDebugger;
import org.jivesoftware.smack.util.CollectionUtil;
import org.jivesoftware.smack.util.Function;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.ParserUtils;
@ -105,12 +101,8 @@ public final class Configuration {
public final Set<String> enabledTests;
private final Map<String, Set<String>> enabledTestsMap;
public final Set<String> disabledTests;
private final Map<String, Set<String>> disabledTestsMap;
public final String defaultConnectionNickname;
public final Set<String> enabledConnections;
@ -125,13 +117,6 @@ public final class Configuration {
public final DnsResolver dnsResolver;
public enum CompatibilityMode {
standardsCompliant,
ejabberd,
}
public final CompatibilityMode compatibilityMode;
private Configuration(Configuration.Builder builder) throws KeyManagementException, NoSuchAlgorithmException {
service = Objects.requireNonNull(builder.service,
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
@ -177,10 +162,8 @@ public final class Configuration {
this.accountTwoPassword = builder.accountTwoPassword;
this.accountThreeUsername = builder.accountThreeUsername;
this.accountThreePassword = builder.accountThreePassword;
this.enabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.enabledTests);
this.enabledTestsMap = convertTestsToMap(enabledTests);
this.disabledTests = CollectionUtil.nullSafeUnmodifiableSet(builder.disabledTests);
this.disabledTestsMap = convertTestsToMap(disabledTests);
this.enabledTests = builder.enabledTests;
this.disabledTests = builder.disabledTests;
this.defaultConnectionNickname = builder.defaultConnectionNickname;
this.enabledConnections = builder.enabledConnections;
this.disabledConnections = builder.disabledConnections;
@ -209,7 +192,6 @@ public final class Configuration {
this.verbose = builder.verbose;
this.dnsResolver = builder.dnsResolver;
this.compatibilityMode = builder.compatibilityMode;
}
public boolean isAccountRegistrationPossible() {
@ -264,8 +246,6 @@ public final class Configuration {
private DnsResolver dnsResolver = DnsResolver.minidns;
private CompatibilityMode compatibilityMode = CompatibilityMode.standardsCompliant;
private Builder() {
}
@ -447,20 +427,6 @@ public final class Configuration {
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 {
return new Configuration(this);
}
@ -534,8 +500,6 @@ public final class Configuration {
builder.setDnsResolver(properties.getProperty("dnsResolver"));
builder.setCompatibilityMode(properties.getProperty("compatibilityMode"));
return builder.build();
}
@ -587,112 +551,4 @@ 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);
}
}

View File

@ -282,13 +282,13 @@ public class SmackIntegrationTestFramework {
continue;
}
if (!config.isClassEnabled(testClass)) {
if (config.enabledTests != null && !isInSet(testClass, config.enabledTests)) {
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is not enabled");
testRunResult.disabledTestClasses.add(disabledTestClass);
continue;
}
if (config.isClassDisabled(testClass)) {
if (isInSet(testClass, config.disabledTests)) {
DisabledTestClass disabledTestClass = new DisabledTestClass(testClass, "Skipping test class " + testClassName + " because it is disalbed");
testRunResult.disabledTestClasses.add(disabledTestClass);
continue;
@ -377,13 +377,14 @@ public class SmackIntegrationTestFramework {
while (it.hasNext()) {
final Method method = it.next();
final String methodName = method.getName();
if (!config.isMethodEnabled(method)) {
if (config.enabledTests != null && !(config.enabledTests.contains(methodName)
|| isInSet(testClass, config.enabledTests))) {
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is not enabled");
testRunResult.disabledTests.add(disabledTest);
it.remove();
continue;
}
if (config.isMethodDisabled(method)) {
if (config.disabledTests != null && config.disabledTests.contains(methodName)) {
DisabledTest disabledTest = new DisabledTest(method, "Skipping test method " + methodName + " because it is disabled");
testRunResult.disabledTests.add(disabledTest);
it.remove();
@ -606,6 +607,15 @@ public class SmackIntegrationTestFramework {
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 {
/**

View File

@ -24,7 +24,6 @@ import org.jivesoftware.smack.SmackException.NotLoggedInException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.roster.AbstractPresenceEventListener;
import org.jivesoftware.smack.roster.PresenceEventListener;
import org.jivesoftware.smack.roster.Roster;
@ -100,16 +99,7 @@ public class IntegrationTestRosterUtil {
if (c2Entry == null) {
return;
}
try {
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;
}
roster.removeEntry(c2Entry);
}
}

View File

@ -23,9 +23,8 @@ import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.NotLoggedInException;
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.packet.GeoLocation;
import org.jivesoftware.smackx.pep.PepEventListener;
@ -36,7 +35,7 @@ import org.igniterealtime.smack.inttest.annotations.AfterClass;
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
import org.junit.jupiter.api.Assertions;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.util.XmppDateTime;
public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest {
@ -50,21 +49,10 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest {
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
public void testNotification() throws Exception {
public void test() throws TimeoutException, Exception {
GeoLocation.Builder builder = GeoLocation.builder();
GeoLocation data = builder.setAccuracy(23d)
GeoLocation geoLocation1 = builder.setAccuracy(23d)
.setAlt(1000d)
.setAltAccuracy(10d)
.setArea("Delhi")
@ -89,163 +77,31 @@ public class GeolocationIntegrationTest extends AbstractSmackIntegrationTest {
.build();
IntegrationTestRosterUtil.ensureBothAccountsAreSubscribedToEachOther(conOne, conTwo, timeout);
final SimpleResultSyncPoint geoLocationReceived = new SimpleResultSyncPoint();
final PepEventListener<GeoLocation> geoLocationListener = new PepEventListener<GeoLocation>() {
final PepEventListener<GeoLocation> geoLocationListener = (jid, geoLocation, id, message) -> {
if (geoLocation.equals(data)) {
geoLocationReceived.signal();
@Override
public void onPepEvent(EntityBareJid jid, GeoLocation geoLocation, String id, Message message) {
if (geoLocation.equals(geoLocation1)) {
geoLocationReceived.signal();
} else {
geoLocationReceived.signalFailure("Received non matching GeoLocation");
}
}
};
glm2.addGeoLocationListener(geoLocationListener);
try {
// Register ConTwo's interest in receiving geolocation notifications, and wait for that interest to have been propagated.
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.");
}
glm1.publishGeoLocation(geoLocation1);
geoLocationReceived.waitForResult(timeout);
} finally {
unregisterListener(glm2, geoLocationListener);
glm2.removeGeoLocationListener(geoLocationListener);
}
}
/**
* 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 {
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);
}
@AfterClass
public void unsubscribe() throws NotLoggedInException, NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
IntegrationTestRosterUtil.ensureBothAccountsAreNotInEachOthersRoster(conOne, conTwo);
}
}

View File

@ -16,13 +16,9 @@
*/
package org.jivesoftware.smackx.mood;
import java.util.concurrent.TimeoutException;
import org.jivesoftware.smack.SmackException;
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.pep.PepEventListener;
@ -32,7 +28,6 @@ import org.igniterealtime.smack.inttest.annotations.AfterClass;
import org.igniterealtime.smack.inttest.annotations.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.util.IntegrationTestRosterUtil;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
import org.junit.jupiter.api.Assertions;
public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
@ -45,155 +40,32 @@ public class MoodIntegrationTest extends AbstractSmackIntegrationTest {
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
public void unsubscribe()
throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
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);
}
}
}

View File

@ -22,6 +22,8 @@ import java.util.List;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
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.SmackIntegrationTestEnvironment;
@ -55,8 +57,9 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati
if (services.isEmpty()) {
throw new TestNotPossibleException("No MUC (XEP-45) service found");
}
mucService = services.get(0);
else {
mucService = services.get(0);
}
}
/**
@ -87,57 +90,18 @@ public class AbstractMultiUserChatIntegrationTest extends AbstractSmackIntegrati
muc.destroy("test fixture teardown", null);
}
static void createMuc(MultiUserChat muc, Resourcepart resourceName) throws
SmackException.NoResponseException, XMPPException.XMPPErrorException,
InterruptedException, MultiUserChatException.MucAlreadyJoinedException,
SmackException.NotConnectedException,
MultiUserChatException.MissingMucCreationAcknowledgeException,
MultiUserChatException.NotAMucServiceException {
muc.create(resourceName).makeInstant();
static void createMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException {
MultiUserChat.MucCreateConfigFormHandle handle = muc.create(Resourcepart.from(resourceName));
if (handle != null) {
handle.makeInstant();
}
}
static void createMuc(MultiUserChat muc, String nickname) throws
XmppStringprepException, MultiUserChatException.MucAlreadyJoinedException,
XMPPException.XMPPErrorException, SmackException.NotConnectedException,
MultiUserChatException.MissingMucCreationAcknowledgeException,
SmackException.NoResponseException, InterruptedException,
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();
static void createModeratedMuc(MultiUserChat muc, String resourceName) throws SmackException.NoResponseException, XMPPException.XMPPErrorException, InterruptedException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException, XmppStringprepException {
muc.create(Resourcepart.from(resourceName));
Form configForm = muc.getConfigurationForm();
FillableForm answerForm = configForm.getFillableForm();
answerForm.setAnswer("muc#roomconfig_moderatedroom", true); //TODO Add this to the MucConfigFormManager?
muc.sendConfigurationForm(answerForm);
}
}

Some files were not shown because too many files have changed in this diff Show More