mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-12 18:49:39 +02:00
Compare commits
57 commits
32ae0d8826
...
498dde2d86
Author | SHA1 | Date | |
---|---|---|---|
|
498dde2d86 | ||
|
77e26fc575 | ||
|
25a5261dc0 | ||
|
577c59484b | ||
|
3270c113c5 | ||
|
2c39dff653 | ||
|
f61ecb65e7 | ||
|
e79710840b | ||
|
5e921e6393 | ||
|
dc443bccd4 | ||
|
cdc5396f6c | ||
|
661b2743d9 | ||
|
e58e6fa75d | ||
|
8e1003723e | ||
|
d0347d1e00 | ||
|
c07f41c119 | ||
|
5c5dfedce2 | ||
|
72b9f79692 | ||
|
22cff274bb | ||
|
c3c14cfdb9 | ||
|
0f7b7df1f0 | ||
|
20aaef2628 | ||
|
26ab832452 | ||
|
ab2822be3e | ||
|
c519dd1213 | ||
|
e2e228fc93 | ||
|
fa643f12d5 | ||
|
162651821e | ||
|
da5f59a996 | ||
|
77d12d4758 | ||
|
cda764d62d | ||
|
4f3d89e666 | ||
|
38c77fd573 | ||
|
cbc2024875 | ||
|
7f2e8b793a | ||
|
ad13effe41 | ||
|
f3e93cef32 | ||
|
dd248adb28 | ||
|
6c3cd53567 | ||
|
7f027bd339 | ||
|
2c6f444bab | ||
|
50da46ffda | ||
|
5114f6dfa4 | ||
|
fb7054bbe7 | ||
|
988954a9db | ||
|
aea95d3401 | ||
|
c49999b933 | ||
|
4f609b855c | ||
|
f5c412a98f | ||
905d5dc102 | |||
e0f7ddf5a8 | |||
72a9cb65a6 | |||
|
9b20e2efd8 | ||
|
6d9936a0a6 | ||
|
340bcb2d12 | ||
|
7c2f9e3603 | ||
|
d0a393118c |
155 changed files with 5449 additions and 3411 deletions
|
@ -4,8 +4,7 @@ android:
|
||||||
components:
|
components:
|
||||||
- android-19
|
- android-19
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- openjdk8
|
||||||
- openjdk9
|
|
||||||
- openjdk11
|
- openjdk11
|
||||||
|
|
||||||
before_cache:
|
before_cache:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Smack
|
Smack
|
||||||
=====
|
=====
|
||||||
|
|
||||||
[](https://travis-ci.org/igniterealtime/Smack) [](https://coveralls.io/r/igniterealtime/Smack) [](https://www.openhub.net/p/smack) [](https://inverse.chat/#converse/room?jid=smack@conference.igniterealtime.org)
|
[](https://travis-ci.com/github/igniterealtime/Smack) [](https://coveralls.io/r/igniterealtime/Smack) [](https://www.openhub.net/p/smack) [](https://inverse.chat/#converse/room?jid=smack@conference.igniterealtime.org)
|
||||||
|
|
||||||
About
|
About
|
||||||
-----
|
-----
|
||||||
|
|
49
build.gradle
49
build.gradle
|
@ -101,20 +101,6 @@ allprojects {
|
||||||
':smack-omemo-signal-integration-test',
|
':smack-omemo-signal-integration-test',
|
||||||
':smack-repl'
|
':smack-repl'
|
||||||
].collect{ project(it) }
|
].collect{ project(it) }
|
||||||
// When this list is empty, then move the according javadoc
|
|
||||||
// tool Werror option into the global configure section.
|
|
||||||
nonStrictJavadocProjects = [
|
|
||||||
':smack-bosh',
|
|
||||||
':smack-core',
|
|
||||||
':smack-experimental',
|
|
||||||
':smack-extensions',
|
|
||||||
':smack-im',
|
|
||||||
':smack-integration-test',
|
|
||||||
':smack-jingle-old',
|
|
||||||
':smack-legacy',
|
|
||||||
':smack-omemo',
|
|
||||||
':smack-tcp',
|
|
||||||
].collect{ project(it) }
|
|
||||||
// Lazily evaluate the Android bootClasspath and offline
|
// Lazily evaluate the Android bootClasspath and offline
|
||||||
// Javadoc using a closure, so that targets which do not
|
// Javadoc using a closure, so that targets which do not
|
||||||
// require it are still able to succeed without an Android
|
// require it are still able to succeed without an Android
|
||||||
|
@ -136,9 +122,11 @@ allprojects {
|
||||||
// Default to true
|
// Default to true
|
||||||
useSonatype = true
|
useSonatype = true
|
||||||
}
|
}
|
||||||
|
javaCompatilibity = JavaVersion.VERSION_1_8
|
||||||
|
javaMajor = javaCompatilibity.getMajorVersion()
|
||||||
}
|
}
|
||||||
group = 'org.igniterealtime.smack'
|
group = 'org.igniterealtime.smack'
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = javaCompatilibity
|
||||||
targetCompatibility = sourceCompatibility
|
targetCompatibility = sourceCompatibility
|
||||||
version = shortVersion
|
version = shortVersion
|
||||||
if (isSnapshot) {
|
if (isSnapshot) {
|
||||||
|
@ -258,7 +246,19 @@ allprojects {
|
||||||
options.addStringOption('Xwerror', '-quiet')
|
options.addStringOption('Xwerror', '-quiet')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tasks.withType(Javadoc) {
|
|
||||||
|
if (JavaVersion.current().isJava9Compatible()) {
|
||||||
|
tasks.withType(Javadoc) {
|
||||||
|
options.addStringOption('-release', javaMajor)
|
||||||
|
}
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs.addAll([
|
||||||
|
'--release', javaMajor,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(Javadoc) {
|
||||||
options.charSet = "UTF-8"
|
options.charSet = "UTF-8"
|
||||||
options.encoding = 'UTF-8'
|
options.encoding = 'UTF-8'
|
||||||
}
|
}
|
||||||
|
@ -304,16 +304,10 @@ task javadocAll(type: Javadoc) {
|
||||||
project.sourceSets.main.compileClasspath})
|
project.sourceSets.main.compileClasspath})
|
||||||
classpath += files(androidBootClasspath)
|
classpath += files(androidBootClasspath)
|
||||||
options {
|
options {
|
||||||
// Add source compatiblitiy statement to work around bug in JDK 11
|
|
||||||
// See
|
|
||||||
// - https://bugs.openjdk.java.net/browse/JDK-8217177
|
|
||||||
// - http://hg.openjdk.java.net/jdk/jdk/rev/8ce4083fc831
|
|
||||||
// - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=920020
|
|
||||||
source = sourceCompatibility
|
|
||||||
linkSource = true
|
linkSource = true
|
||||||
use = true
|
use = true
|
||||||
links = [
|
links = [
|
||||||
"https://docs.oracle.com/javase/${sourceCompatibility.getMajorVersion()}/docs/api/",
|
"https://docs.oracle.com/javase/${javaMajor}/docs/api/",
|
||||||
"https://jxmpp.org/releases/$jxmppVersion/javadoc/",
|
"https://jxmpp.org/releases/$jxmppVersion/javadoc/",
|
||||||
"https://minidns.org/releases/$miniDnsVersion/javadoc/",
|
"https://minidns.org/releases/$miniDnsVersion/javadoc/",
|
||||||
] as String[]
|
] as String[]
|
||||||
|
@ -571,15 +565,6 @@ subprojects*.jar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(subprojects - nonStrictJavadocProjects) {
|
|
||||||
tasks.withType(Javadoc) {
|
|
||||||
// Abort on javadoc warnings.
|
|
||||||
// See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363)
|
|
||||||
// for information about the -Xwerror option.
|
|
||||||
options.addStringOption('Xwerror', '-quiet')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configure(subprojects - gplLicensedProjects) {
|
configure(subprojects - gplLicensedProjects) {
|
||||||
checkstyle {
|
checkstyle {
|
||||||
configProperties.checkstyleLicenseHeader = "header"
|
configProperties.checkstyleLicenseHeader = "header"
|
||||||
|
|
|
@ -102,7 +102,7 @@ The base class that integration tests need to subclass.
|
||||||
|
|
||||||
### `AbstractSmackLowLevelIntegrationTest`
|
### `AbstractSmackLowLevelIntegrationTest`
|
||||||
|
|
||||||
Allows low level integration test, i.e. ever test method will have its on exclusive XMPPTCPConnection instances.
|
Allows low level integration test, i.e. every test method will have its own exclusive XMPPTCPConnection instances.
|
||||||
|
|
||||||
### `AbstractSmackSpecificLowLevelIntegrationTest`
|
### `AbstractSmackSpecificLowLevelIntegrationTest`
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
|
||||||
| Result Set Management | [XEP-0059](https://xmpp.org/extensions/xep-0059.html) | n/a | Page through and otherwise manage the receipt of large result sets |
|
| Result Set Management | [XEP-0059](https://xmpp.org/extensions/xep-0059.html) | n/a | Page through and otherwise manage the receipt of large result sets |
|
||||||
| [PubSub](pubsub.md) | [XEP-0060](https://xmpp.org/extensions/xep-0060.html) | n/a | Generic publish and subscribe functionality. |
|
| [PubSub](pubsub.md) | [XEP-0060](https://xmpp.org/extensions/xep-0060.html) | n/a | Generic publish and subscribe functionality. |
|
||||||
| SOCKS5 Bytestreams | [XEP-0065](https://xmpp.org/extensions/xep-0065.html) | n/a | Out-of-band bytestream between any two XMPP entities. |
|
| SOCKS5 Bytestreams | [XEP-0065](https://xmpp.org/extensions/xep-0065.html) | n/a | Out-of-band bytestream between any two XMPP entities. |
|
||||||
|
| Field Standardization for Data Forms | [XEP-0068](https://xmpp.org/extensions/xep-0068.html) | n/a | Standardized field variables used in the context of jabber:x:data forms. |
|
||||||
| [XHTML-IM](xhtml.md) | [XEP-0071](https://xmpp.org/extensions/xep-0071.html) | n/a | Allows send and receiving formatted messages using XHTML. |
|
| [XHTML-IM](xhtml.md) | [XEP-0071](https://xmpp.org/extensions/xep-0071.html) | n/a | Allows send and receiving formatted messages using XHTML. |
|
||||||
| In-Band Registration | [XEP-0077](https://xmpp.org/extensions/xep-0077.html) | n/a | In-band registration with XMPP services. |
|
| In-Band Registration | [XEP-0077](https://xmpp.org/extensions/xep-0077.html) | n/a | In-band registration with XMPP services. |
|
||||||
| Advanced Message Processing | [XEP-0079](https://xmpp.org/extensions/xep-0079.html) | n/a | Enables entities to request, and servers to perform, advanced processing of XMPP message stanzas. |
|
| Advanced Message Processing | [XEP-0079](https://xmpp.org/extensions/xep-0079.html) | n/a | Enables entities to request, and servers to perform, advanced processing of XMPP message stanzas. |
|
||||||
|
@ -120,6 +121,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
|
||||||
| [Consistent Color Generation](consistent_colors.md) | [XEP-0392](https://xmpp.org/extensions/xep-0392.html) | 0.6.0 | Generate consistent colors for identifiers like usernames to provide a consistent user experience. |
|
| [Consistent Color Generation](consistent_colors.md) | [XEP-0392](https://xmpp.org/extensions/xep-0392.html) | 0.6.0 | Generate consistent colors for identifiers like usernames to provide a consistent user experience. |
|
||||||
| [Message Markup](messagemarkup.md) | [XEP-0394](https://xmpp.org/extensions/xep-0394.html) | 0.1.0 | Style message bodies while keeping body and markup information separated. |
|
| [Message Markup](messagemarkup.md) | [XEP-0394](https://xmpp.org/extensions/xep-0394.html) | 0.1.0 | Style message bodies while keeping body and markup information separated. |
|
||||||
| DNS Queries over XMPP (DoX) | [XEP-0418](https://xmpp.org/extensions/xep-0418.html) | 0.1.0 | Send DNS queries and responses over XMPP. |
|
| DNS Queries over XMPP (DoX) | [XEP-0418](https://xmpp.org/extensions/xep-0418.html) | 0.1.0 | Send DNS queries and responses over XMPP. |
|
||||||
|
| Message Fastening | [XEP-0422](https://xmpp.org/extensions/xep-0422.html) | 0.1.1 | Mark payloads on a message to be logistically fastened to a previous message. |
|
||||||
|
|
||||||
Unofficial XMPP Extensions
|
Unofficial XMPP Extensions
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -527,7 +527,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
saslFeatureReceived.init();
|
saslFeatureReceived.init();
|
||||||
lastFeaturesReceived.init();
|
lastFeaturesReceived.init();
|
||||||
tlsHandled.init();
|
tlsHandled.init();
|
||||||
closingStreamReceived.init();
|
// TODO: We do not init() closingStreamReceived here, as the integration tests use it to check if we waited for
|
||||||
|
// it.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -549,6 +550,7 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
|
||||||
|
|
||||||
// Reset the connection state
|
// Reset the connection state
|
||||||
initState();
|
initState();
|
||||||
|
closingStreamReceived.init();
|
||||||
streamId = null;
|
streamId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smack.filter;
|
||||||
|
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
import org.jivesoftware.smack.util.XmppElementUtil;
|
||||||
|
|
||||||
|
public class ExtensionElementFilter<E extends ExtensionElement> implements StanzaFilter {
|
||||||
|
|
||||||
|
private final Class<E> extensionElementClass;
|
||||||
|
private final QName extensionElementQName;
|
||||||
|
|
||||||
|
protected ExtensionElementFilter(Class<E> extensionElementClass) {
|
||||||
|
this.extensionElementClass = extensionElementClass;
|
||||||
|
extensionElementQName = XmppElementUtil.getQNameFor(extensionElementClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean accept(Stanza stanza) {
|
||||||
|
ExtensionElement extensionElement = stanza.getExtension(extensionElementQName);
|
||||||
|
if (extensionElement == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extensionElementClass.isInstance(extensionElement)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
E specificExtensionElement = extensionElementClass.cast(extensionElement);
|
||||||
|
return accept(specificExtensionElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean accept(E extensionElement) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ public final class MessageTypeFilter extends FlexibleStanzaTypeFilter<Message> {
|
||||||
public static final StanzaFilter HEADLINE = new MessageTypeFilter(Type.headline);
|
public static final StanzaFilter HEADLINE = new MessageTypeFilter(Type.headline);
|
||||||
public static final StanzaFilter ERROR = new MessageTypeFilter(Type.error);
|
public static final StanzaFilter ERROR = new MessageTypeFilter(Type.error);
|
||||||
public static final StanzaFilter NORMAL_OR_CHAT = new OrFilter(NORMAL, CHAT);
|
public static final StanzaFilter NORMAL_OR_CHAT = new OrFilter(NORMAL, CHAT);
|
||||||
|
public static final StanzaFilter NORMAL_OR_HEADLINE = new OrFilter(NORMAL, HEADLINE);
|
||||||
public static final StanzaFilter NORMAL_OR_CHAT_OR_HEADLINE = new OrFilter(NORMAL_OR_CHAT,
|
public static final StanzaFilter NORMAL_OR_CHAT_OR_HEADLINE = new OrFilter(NORMAL_OR_CHAT,
|
||||||
HEADLINE);
|
HEADLINE);
|
||||||
|
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2003-2007 Jive Software.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jivesoftware.smack.filter;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters for packets with a particular type of stanza extension.
|
|
||||||
*
|
|
||||||
* @author Matt Tucker
|
|
||||||
* @deprecated use {@link StanzaExtensionFilter} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class PacketExtensionFilter implements StanzaFilter {
|
|
||||||
|
|
||||||
private final String elementName;
|
|
||||||
private final String namespace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new stanza extension filter. Packets will pass the filter if
|
|
||||||
* they have a stanza extension that matches the specified element name
|
|
||||||
* and namespace.
|
|
||||||
*
|
|
||||||
* @param elementName the XML element name of the stanza extension.
|
|
||||||
* @param namespace the XML namespace of the stanza extension.
|
|
||||||
*/
|
|
||||||
public PacketExtensionFilter(String elementName, String namespace) {
|
|
||||||
StringUtils.requireNotNullNorEmpty(namespace, "namespace must not be null nor empty");
|
|
||||||
|
|
||||||
this.elementName = elementName;
|
|
||||||
this.namespace = namespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new stanza extension filter. Packets will pass the filter if they have a packet
|
|
||||||
* extension that matches the specified namespace.
|
|
||||||
*
|
|
||||||
* @param namespace the XML namespace of the stanza extension.
|
|
||||||
*/
|
|
||||||
public PacketExtensionFilter(String namespace) {
|
|
||||||
this(null, namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new stanza extension filter for the given stanza extension.
|
|
||||||
*
|
|
||||||
* @param packetExtension TODO javadoc me please
|
|
||||||
*/
|
|
||||||
public PacketExtensionFilter(ExtensionElement packetExtension) {
|
|
||||||
this(packetExtension.getElementName(), packetExtension.getNamespace());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accept(Stanza packet) {
|
|
||||||
return packet.hasExtension(elementName, namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + ": element=" + elementName + " namespace=" + namespace;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2003-2007 Jive Software.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jivesoftware.smack.filter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a way to filter packets for particular attributes. Stanza filters are used when
|
|
||||||
* constructing stanza listeners or collectors -- the filter defines what packets match the criteria
|
|
||||||
* of the collector or listener for further stanza processing.
|
|
||||||
* <p>
|
|
||||||
* Several simple filters are pre-defined. These filters can be logically combined for more complex
|
|
||||||
* stanza filtering by using the {@link org.jivesoftware.smack.filter.AndFilter AndFilter} and
|
|
||||||
* {@link org.jivesoftware.smack.filter.OrFilter OrFilter} filters. It's also possible to define
|
|
||||||
* your own filters by implementing this interface. The code example below creates a trivial filter
|
|
||||||
* for packets with a specific ID (real code should use {@link StanzaIdFilter} instead).
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* // Use an anonymous inner class to define a stanza filter that returns
|
|
||||||
* // all packets that have a stanza ID of "RS145".
|
|
||||||
* PacketFilter myFilter = new PacketFilter() {
|
|
||||||
* public boolean accept(Packet packet) {
|
|
||||||
* return "RS145".equals(packet.getStanzaId());
|
|
||||||
* }
|
|
||||||
* };
|
|
||||||
* // Create a new stanza collector using the filter we created.
|
|
||||||
* StanzaCollector myCollector = packetReader.createStanzaCollector(myFilter);
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @see org.jivesoftware.smack.StanzaCollector
|
|
||||||
* @see org.jivesoftware.smack.StanzaListener
|
|
||||||
* @author Matt Tucker
|
|
||||||
* @deprecated use {@link StanzaFilter}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public interface PacketFilter extends StanzaFilter {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2003-2007 Jive Software.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jivesoftware.smack.filter;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters for packets with a particular stanza ID.
|
|
||||||
*
|
|
||||||
* @author Matt Tucker
|
|
||||||
* @deprecated use {@link StanzaIdFilter} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class PacketIDFilter implements StanzaFilter {
|
|
||||||
|
|
||||||
private final String packetID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new stanza ID filter using the specified packet's ID.
|
|
||||||
*
|
|
||||||
* @param packet the stanza which the ID is taken from.
|
|
||||||
* @deprecated use {@link StanzaIdFilter#StanzaIdFilter(Stanza)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public PacketIDFilter(Stanza packet) {
|
|
||||||
this(packet.getStanzaId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new stanza ID filter using the specified stanza ID.
|
|
||||||
*
|
|
||||||
* @param packetID the stanza ID to filter for.
|
|
||||||
* @deprecated use {@link StanzaIdFilter#StanzaIdFilter(Stanza)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public PacketIDFilter(String packetID) {
|
|
||||||
StringUtils.requireNotNullNorEmpty(packetID, "Packet ID must not be null nor empty.");
|
|
||||||
this.packetID = packetID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accept(Stanza packet) {
|
|
||||||
return packetID.equals(packet.getStanzaId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + ": id=" + packetID;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2003-2007 Jive Software.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jivesoftware.smack.filter;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters for packets of a particular type. The type is given as a Class object, so
|
|
||||||
* example types would:
|
|
||||||
* <ul>
|
|
||||||
* <li><code>Message.class</code>
|
|
||||||
* <li><code>IQ.class</code>
|
|
||||||
* <li><code>Presence.class</code>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @author Matt Tucker
|
|
||||||
* @deprecated use {@link StanzaTypeFilter} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class PacketTypeFilter implements StanzaFilter {
|
|
||||||
|
|
||||||
public static final PacketTypeFilter PRESENCE = new PacketTypeFilter(Presence.class);
|
|
||||||
public static final PacketTypeFilter MESSAGE = new PacketTypeFilter(Message.class);
|
|
||||||
|
|
||||||
private final Class<? extends Stanza> packetType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new stanza type filter that will filter for packets that are the
|
|
||||||
* same type as <code>packetType</code>.
|
|
||||||
*
|
|
||||||
* @param packetType the Class type.
|
|
||||||
*/
|
|
||||||
public PacketTypeFilter(Class<? extends Stanza> packetType) {
|
|
||||||
this.packetType = packetType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accept(Stanza packet) {
|
|
||||||
return packetType.isInstance(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + ": " + packetType.getName();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright © 2014 Florian Schmaus
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.jivesoftware.smack.filter;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Match based on the 'to' attribute of a Stanza.
|
|
||||||
*
|
|
||||||
* @deprecated use {@link ToMatchesFilter} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class ToFilter implements StanzaFilter {
|
|
||||||
|
|
||||||
private final Jid to;
|
|
||||||
|
|
||||||
public ToFilter(Jid to) {
|
|
||||||
this.to = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accept(Stanza packet) {
|
|
||||||
Jid packetTo = packet.getTo();
|
|
||||||
if (packetTo == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return packetTo.equals(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + ": to=" + to;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,6 +28,8 @@ import org.jxmpp.jid.Jid;
|
||||||
*/
|
*/
|
||||||
public class FromJidTypeFilter extends AbstractJidTypeFilter {
|
public class FromJidTypeFilter extends AbstractJidTypeFilter {
|
||||||
|
|
||||||
|
public static final FromJidTypeFilter ENTITY_BARE_JID = new FromJidTypeFilter(JidType.EntityBareJid);
|
||||||
|
|
||||||
public FromJidTypeFilter(JidType jidType) {
|
public FromJidTypeFilter(JidType jidType) {
|
||||||
super(jidType);
|
super(jidType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -445,7 +445,7 @@ public abstract class Stanza implements StanzaView, TopLevelStreamElement {
|
||||||
* @param extensions a collection of stanza extensions
|
* @param extensions a collection of stanza extensions
|
||||||
*/
|
*/
|
||||||
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
|
// TODO: Mark this as deprecated once StanzaBuilder is ready and all call sites are gone.
|
||||||
public final void addExtensions(Collection<ExtensionElement> extensions) {
|
public final void addExtensions(Collection<? extends ExtensionElement> extensions) {
|
||||||
if (extensions == null) return;
|
if (extensions == null) return;
|
||||||
for (ExtensionElement packetExtension : extensions) {
|
for (ExtensionElement packetExtension : extensions) {
|
||||||
addExtension(packetExtension);
|
addExtension(packetExtension);
|
||||||
|
|
|
@ -106,5 +106,12 @@ public interface StanzaView extends XmlLangElement {
|
||||||
|
|
||||||
List<ExtensionElement> getExtensions(QName qname);
|
List<ExtensionElement> getExtensions(QName qname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all extension elements of the given type. Returns the empty list if there a none.
|
||||||
|
*
|
||||||
|
* @param <E> the type of extension elements.
|
||||||
|
* @param extensionElementClass the class of the type of extension elements.
|
||||||
|
* @return a list of extension elements of that type, which may be empty.
|
||||||
|
*/
|
||||||
<E extends ExtensionElement> List<E> getExtensions(Class<E> extensionElementClass);
|
<E extends ExtensionElement> List<E> getExtensions(Class<E> extensionElementClass);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2019 Florian Schmaus
|
* Copyright 2019-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -30,6 +30,10 @@ public class SmackParsingException extends Exception {
|
||||||
super(exception);
|
super(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SmackParsingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
public static class SmackTextParseException extends SmackParsingException {
|
public static class SmackTextParseException extends SmackParsingException {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,9 +18,12 @@ package org.jivesoftware.smack.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class CollectionUtil {
|
public class CollectionUtil {
|
||||||
|
@ -59,6 +62,20 @@ public class CollectionUtil {
|
||||||
return new ArrayList<>(collection);
|
return new ArrayList<>(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> cloneAndSeal(Collection<? extends T> collection) {
|
||||||
|
if (collection == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<T> clone = newListWith(collection);
|
||||||
|
return Collections.unmodifiableList(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> cloneAndSeal(Map<K, V> map) {
|
||||||
|
Map<K, V> clone = new HashMap<>(map);
|
||||||
|
return Collections.unmodifiableMap(clone);
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> Set<T> newSetWith(Collection<? extends T> collection) {
|
public static <T> Set<T> newSetWith(Collection<? extends T> collection) {
|
||||||
if (collection == null) {
|
if (collection == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2019 Florian Schmaus.
|
* Copyright 2019-2020 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -34,6 +34,12 @@ public final class EqualsUtil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int thisHashCode = thisObject.hashCode();
|
||||||
|
int otherHashCode = other.hashCode();
|
||||||
|
if (thisHashCode != otherHashCode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
EqualsUtil.Builder equalsBuilder = new EqualsUtil.Builder();
|
EqualsUtil.Builder equalsBuilder = new EqualsUtil.Builder();
|
||||||
|
|
||||||
equalsComperator.compare(equalsBuilder, thisObjectClass.cast(other));
|
equalsComperator.compare(equalsBuilder, thisObjectClass.cast(other));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014 Florian Schmaus
|
* Copyright © 2014-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -22,40 +22,6 @@ import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
|
||||||
public class PacketUtil {
|
public class PacketUtil {
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a extension element from a collection.
|
|
||||||
* @param collection TODO javadoc me please
|
|
||||||
* @param element TODO javadoc me please
|
|
||||||
* @param namespace TODO javadoc me please
|
|
||||||
* @param <PE> the type of the extension element.
|
|
||||||
* @return the extension element
|
|
||||||
* @deprecated use {@link #extensionElementFrom(Collection, String, String)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static <PE extends ExtensionElement> PE packetExtensionfromCollection(
|
|
||||||
Collection<ExtensionElement> collection, String element,
|
|
||||||
String namespace) {
|
|
||||||
return extensionElementFrom(collection, element, namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a extension element from a collection.
|
|
||||||
*
|
|
||||||
* @param collection Collection of ExtensionElements.
|
|
||||||
* @param element name of the targeted ExtensionElement.
|
|
||||||
* @param namespace namespace of the targeted ExtensionElement.
|
|
||||||
* @param <PE> Type of the ExtensionElement
|
|
||||||
*
|
|
||||||
* @return the extension element
|
|
||||||
* @deprecated use {@link #extensionElementFrom(Collection, String, String)} instead
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static <PE extends ExtensionElement> PE packetExtensionFromCollection(
|
|
||||||
Collection<ExtensionElement> collection, String element,
|
|
||||||
String namespace) {
|
|
||||||
return extensionElementFrom(collection, element, namespace);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a extension element from a collection.
|
* Get a extension element from a collection.
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,8 +20,10 @@ package org.jivesoftware.smack.util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -591,4 +593,11 @@ public class StringUtils {
|
||||||
}
|
}
|
||||||
return appendable.append('\n');
|
return appendable.append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final String PORTABLE_NEWLINE_REGEX = "\\r?\\n";
|
||||||
|
|
||||||
|
public static List<String> splitLinesPortable(String input) {
|
||||||
|
String[] lines = input.split(PORTABLE_NEWLINE_REGEX);
|
||||||
|
return Arrays.asList(lines);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,10 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlEnvironment getXmlEnvironment() {
|
||||||
|
return effectiveXmlEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
public XmlStringBuilder escapedElement(String name, String escapedContent) {
|
public XmlStringBuilder escapedElement(String name, String escapedContent) {
|
||||||
assert escapedContent != null;
|
assert escapedContent != null;
|
||||||
openElement(name);
|
openElement(name);
|
||||||
|
@ -493,6 +497,13 @@ public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder optAppend(Collection<? extends Element> elements) {
|
||||||
|
if (elements != null) {
|
||||||
|
append(elements);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public XmlStringBuilder optTextChild(CharSequence sqc, NamedElement parentElement) {
|
public XmlStringBuilder optTextChild(CharSequence sqc, NamedElement parentElement) {
|
||||||
if (sqc == null) {
|
if (sqc == null) {
|
||||||
return closeEmptyElement();
|
return closeEmptyElement();
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class XmppElementUtil {
|
||||||
namespace = (String) fullyQualifiedElement.getField("NAMESPACE").get(null);
|
namespace = (String) fullyQualifiedElement.getField("NAMESPACE").get(null);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
|
catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
|
||||||
throw new IllegalArgumentException("The class" + fullyQualifiedElement + " has no ELEMENT, NAMSEPACE or QNAME member. Consider adding QNAME", e);
|
throw new IllegalArgumentException("The class" + fullyQualifiedElement + " has no ELEMENT, NAMESPACE or QNAME member. Consider adding QNAME", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new QName(namespace, element);
|
return new QName(namespace, element);
|
||||||
|
|
|
@ -143,6 +143,7 @@ public final class EnhancedDebuggerWindow {
|
||||||
debugger.tabbedPane.setName("XMPPConnection_" + tabbedPane.getComponentCount());
|
debugger.tabbedPane.setName("XMPPConnection_" + tabbedPane.getComponentCount());
|
||||||
tabbedPane.add(debugger.tabbedPane, tabbedPane.getComponentCount() - 1);
|
tabbedPane.add(debugger.tabbedPane, tabbedPane.getComponentCount() - 1);
|
||||||
tabbedPane.setIconAt(tabbedPane.indexOfComponent(debugger.tabbedPane), connectionCreatedIcon);
|
tabbedPane.setIconAt(tabbedPane.indexOfComponent(debugger.tabbedPane), connectionCreatedIcon);
|
||||||
|
tabbedPane.setSelectedIndex(tabbedPane.indexOfComponent(debugger.tabbedPane));
|
||||||
frame.setTitle(
|
frame.setTitle(
|
||||||
"Smack Debug Window -- Total connections: " + (tabbedPane.getComponentCount() - 1));
|
"Smack Debug Window -- Total connections: " + (tabbedPane.getComponentCount() - 1));
|
||||||
// Keep the added debugger for later access
|
// Keep the added debugger for later access
|
||||||
|
|
|
@ -278,8 +278,9 @@ public final class MamManager extends Manager {
|
||||||
if (dataForm != null) {
|
if (dataForm != null) {
|
||||||
return dataForm;
|
return dataForm;
|
||||||
}
|
}
|
||||||
dataForm = getNewMamForm();
|
DataForm.Builder dataFormBuilder = getNewMamForm();
|
||||||
dataForm.addFields(formFields.values());
|
dataFormBuilder.addFields(formFields.values());
|
||||||
|
dataForm = dataFormBuilder.build();
|
||||||
return dataForm;
|
return dataForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +331,7 @@ public final class MamManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
FormField formField = getWithFormField(withJid);
|
FormField formField = getWithFormField(withJid);
|
||||||
formFields.put(formField.getVariable(), formField);
|
formFields.put(formField.getFieldName(), formField);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -341,9 +342,9 @@ public final class MamManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
FormField formField = FormField.builder(FORM_FIELD_START)
|
FormField formField = FormField.builder(FORM_FIELD_START)
|
||||||
.addValue(start)
|
.setValue(start)
|
||||||
.build();
|
.build();
|
||||||
formFields.put(formField.getVariable(), formField);
|
formFields.put(formField.getFieldName(), formField);
|
||||||
|
|
||||||
FormField endFormField = formFields.get(FORM_FIELD_END);
|
FormField endFormField = formFields.get(FORM_FIELD_END);
|
||||||
if (endFormField != null) {
|
if (endFormField != null) {
|
||||||
|
@ -369,9 +370,9 @@ public final class MamManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
FormField formField = FormField.builder(FORM_FIELD_END)
|
FormField formField = FormField.builder(FORM_FIELD_END)
|
||||||
.addValue(end)
|
.setValue(end)
|
||||||
.build();
|
.build();
|
||||||
formFields.put(formField.getVariable(), formField);
|
formFields.put(formField.getFieldName(), formField);
|
||||||
|
|
||||||
FormField startFormField = formFields.get(FORM_FIELD_START);
|
FormField startFormField = formFields.get(FORM_FIELD_START);
|
||||||
if (startFormField != null) {
|
if (startFormField != null) {
|
||||||
|
@ -418,7 +419,7 @@ public final class MamManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder withAdditionalFormField(FormField formField) {
|
public Builder withAdditionalFormField(FormField formField) {
|
||||||
formFields.put(formField.getVariable(), formField);
|
formFields.put(formField.getFieldName(), formField);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,7 +484,7 @@ public final class MamManager extends Manager {
|
||||||
|
|
||||||
private static FormField getWithFormField(Jid withJid) {
|
private static FormField getWithFormField(Jid withJid) {
|
||||||
return FormField.builder(FORM_FIELD_WITH)
|
return FormField.builder(FORM_FIELD_WITH)
|
||||||
.addValue(withJid.toString())
|
.setValue(withJid.toString())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,9 +719,9 @@ public final class MamManager extends Manager {
|
||||||
throw new SmackException.FeatureNotSupportedException(ADVANCED_CONFIG_NODE, archiveAddress);
|
throw new SmackException.FeatureNotSupportedException(ADVANCED_CONFIG_NODE, archiveAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataForm getNewMamForm() {
|
private static DataForm.Builder getNewMamForm() {
|
||||||
FormField field = FormField.hiddenFormType(MamElements.NAMESPACE);
|
FormField field = FormField.buildHiddenFormType(MamElements.NAMESPACE);
|
||||||
DataForm form = new DataForm(DataForm.Type.submit);
|
DataForm.Builder form = DataForm.builder();
|
||||||
form.addField(field);
|
form.addField(field);
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2016 Florian Schmaus and Fernando Ramirez
|
* Copyright © 2016-2020 Florian Schmaus and Fernando Ramirez
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,7 +18,6 @@ package org.jivesoftware.smackx.mam.element;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,11 +87,11 @@ public class MamQueryIQ extends IQ {
|
||||||
this.dataForm = dataForm;
|
this.dataForm = dataForm;
|
||||||
|
|
||||||
if (dataForm != null) {
|
if (dataForm != null) {
|
||||||
FormField field = dataForm.getHiddenFormTypeField();
|
String formType = dataForm.getFormType();
|
||||||
if (field == null) {
|
if (formType == null) {
|
||||||
throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field");
|
throw new IllegalArgumentException("If a data form is given it must posses a hidden form type field");
|
||||||
}
|
}
|
||||||
if (!field.getValues().get(0).toString().equals(MamElements.NAMESPACE)) {
|
if (!formType.equals(MamElements.NAMESPACE)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'");
|
"Value of the hidden form type field must be '" + MamElements.NAMESPACE + "'");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* 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.message_fastening;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
|
import org.jivesoftware.smack.Manager;
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||||
|
import org.jivesoftware.smack.packet.MessageBuilder;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.message_fastening.element.FasteningElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smacks API for XEP-0422: Message Fastening.
|
||||||
|
* The API is still very bare bones, as the XEP intends Message Fastening to be used as a tool by other protocols.
|
||||||
|
*
|
||||||
|
* To enable / disable auto-announcing support for this feature, call {@link #setEnabledByDefault(boolean)} (default true).
|
||||||
|
*
|
||||||
|
* To fasten a payload to a previous message, create an {@link FasteningElement} using the builder provided by
|
||||||
|
* {@link FasteningElement#builder()}.
|
||||||
|
*
|
||||||
|
* You need to provide the {@link org.jivesoftware.smackx.sid.element.OriginIdElement} of the message you want to reference.
|
||||||
|
* Then add wrapped payloads using {@link FasteningElement.Builder#addWrappedPayloads(List)}
|
||||||
|
* and external payloads using {@link FasteningElement.Builder#addExternalPayloads(List)}.
|
||||||
|
*
|
||||||
|
* If you fastened some payloads onto the message previously and now want to replace the previous fastening, call
|
||||||
|
* {@link FasteningElement.Builder#isRemovingElement()}.
|
||||||
|
* Once you are finished, build the {@link FasteningElement} using {@link FasteningElement.Builder#build()} and add it to
|
||||||
|
* a stanza by calling {@link FasteningElement#applyTo(MessageBuilder)}.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html">XEP-0422: Message Fastening</a>
|
||||||
|
*/
|
||||||
|
public final class MessageFasteningManager extends Manager {
|
||||||
|
|
||||||
|
public static final String NAMESPACE = "urn:xmpp:fasten:0";
|
||||||
|
|
||||||
|
private static boolean ENABLED_BY_DEFAULT = false;
|
||||||
|
|
||||||
|
private static final WeakHashMap<XMPPConnection, MessageFasteningManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
|
||||||
|
@Override
|
||||||
|
public void connectionCreated(XMPPConnection connection) {
|
||||||
|
if (ENABLED_BY_DEFAULT) {
|
||||||
|
MessageFasteningManager.getInstanceFor(connection).announceSupport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageFasteningManager(XMPPConnection connection) {
|
||||||
|
super(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized MessageFasteningManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
MessageFasteningManager manager = INSTANCES.get(connection);
|
||||||
|
if (manager == null) {
|
||||||
|
manager = new MessageFasteningManager(connection);
|
||||||
|
INSTANCES.put(connection, manager);
|
||||||
|
}
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable auto-announcing support for Message Fastening.
|
||||||
|
* Default is enabled.
|
||||||
|
*
|
||||||
|
* @param enabled enabled
|
||||||
|
*/
|
||||||
|
public static synchronized void setEnabledByDefault(boolean enabled) {
|
||||||
|
ENABLED_BY_DEFAULT = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Announce support for Message Fastening via Service Discovery.
|
||||||
|
*/
|
||||||
|
public void announceSupport() {
|
||||||
|
ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection());
|
||||||
|
discoveryManager.addFeature(NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop announcing support for Message Fastening.
|
||||||
|
*/
|
||||||
|
public void stopAnnouncingSupport() {
|
||||||
|
ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection());
|
||||||
|
discoveryManager.removeFeature(NAMESPACE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* 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.message_fastening.element;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child element of {@link FasteningElement}.
|
||||||
|
* Reference to a top level element in the stanza that contains the {@link FasteningElement}.
|
||||||
|
*/
|
||||||
|
public class ExternalElement implements NamedElement {
|
||||||
|
|
||||||
|
public static final String ELEMENT = "external";
|
||||||
|
public static final String ATTR_NAME = "name";
|
||||||
|
public static final String ATTR_ELEMENT_NAMESPACE = "element-namespace";
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String elementNamespace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link ExternalElement} that references a top level element with the given name.
|
||||||
|
*
|
||||||
|
* @param name name of the top level element
|
||||||
|
*/
|
||||||
|
public ExternalElement(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link ExternalElement} that references a top level element with the given name and namespace.
|
||||||
|
*
|
||||||
|
* @param name name of the top level element
|
||||||
|
* @param elementNamespace namespace of the top level element
|
||||||
|
*/
|
||||||
|
public ExternalElement(String name, String elementNamespace) {
|
||||||
|
this.name = name;
|
||||||
|
this.elementNamespace = elementNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
|
XmlStringBuilder xml = new XmlStringBuilder(this);
|
||||||
|
xml.attribute(ATTR_NAME, getName());
|
||||||
|
xml.optAttribute(ATTR_ELEMENT_NAMESPACE, getElementNamespace());
|
||||||
|
return xml.closeEmptyElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the referenced top level element, eg. 'body'.
|
||||||
|
* @return element name
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the referenced top level element, eg. 'urn:example:lik'.
|
||||||
|
* @return element namespace
|
||||||
|
*/
|
||||||
|
public String getElementNamespace() {
|
||||||
|
return elementNamespace;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,325 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* 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.message_fastening.element;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smack.packet.MessageBuilder;
|
||||||
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
import org.jivesoftware.smackx.message_fastening.MessageFasteningManager;
|
||||||
|
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message Fastening container element.
|
||||||
|
*/
|
||||||
|
public final class FasteningElement implements ExtensionElement {
|
||||||
|
|
||||||
|
public static final String ELEMENT = "apply-to";
|
||||||
|
public static final String NAMESPACE = MessageFasteningManager.NAMESPACE;
|
||||||
|
public static final String ATTR_ID = "id";
|
||||||
|
public static final String ATTR_CLEAR = "clear";
|
||||||
|
public static final String ATTR_SHELL = "shell";
|
||||||
|
|
||||||
|
private final OriginIdElement referencedStanzasOriginId;
|
||||||
|
private final List<ExternalElement> externalPayloads = new ArrayList<>();
|
||||||
|
private final List<ExtensionElement> wrappedPayloads = new ArrayList<>();
|
||||||
|
private final boolean clear;
|
||||||
|
private final boolean shell;
|
||||||
|
|
||||||
|
private FasteningElement(OriginIdElement originId,
|
||||||
|
List<ExtensionElement> wrappedPayloads,
|
||||||
|
List<ExternalElement> externalPayloads,
|
||||||
|
boolean clear,
|
||||||
|
boolean shell) {
|
||||||
|
this.referencedStanzasOriginId = Objects.requireNonNull(originId, "Fastening element MUST contain an origin-id.");
|
||||||
|
this.wrappedPayloads.addAll(wrappedPayloads);
|
||||||
|
this.externalPayloads.addAll(externalPayloads);
|
||||||
|
this.clear = clear;
|
||||||
|
this.shell = shell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link OriginIdElement origin-id} of the {@link Stanza} that the message fastenings are to be
|
||||||
|
* applied to.
|
||||||
|
*
|
||||||
|
* @return origin id of the referenced stanza
|
||||||
|
*/
|
||||||
|
public OriginIdElement getReferencedStanzasOriginId() {
|
||||||
|
return referencedStanzasOriginId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all wrapped payloads of this element.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#wrapped-payloads">XEP-0422: §3.1. Wrapped Payloads</a>
|
||||||
|
*
|
||||||
|
* @return wrapped payloads.
|
||||||
|
*/
|
||||||
|
public List<ExtensionElement> getWrappedPayloads() {
|
||||||
|
return Collections.unmodifiableList(wrappedPayloads);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all external payloads of this element.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#external-payloads">XEP-0422: §3.2. External Payloads</a>
|
||||||
|
*
|
||||||
|
* @return external payloads.
|
||||||
|
*/
|
||||||
|
public List<ExternalElement> getExternalPayloads() {
|
||||||
|
return Collections.unmodifiableList(externalPayloads);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this element remove a previously sent {@link FasteningElement}?
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#remove">
|
||||||
|
* XEP-0422: Message Fastening §3.4 Removing fastenings</a>
|
||||||
|
*
|
||||||
|
* @return true if the clear attribute is set.
|
||||||
|
*/
|
||||||
|
public boolean isRemovingElement() {
|
||||||
|
return clear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a shell element?
|
||||||
|
* Shell elements are otherwise empty elements that indicate that an encrypted payload of a message
|
||||||
|
* encrypted using XEP-420: Stanza Content Encryption contains a sensitive {@link FasteningElement}.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#encryption">
|
||||||
|
* XEP-0422: Message Fastening §3.5 Interaction with stanza encryption</a>
|
||||||
|
*
|
||||||
|
* @return true if this is a shell element.
|
||||||
|
*/
|
||||||
|
public boolean isShellElement() {
|
||||||
|
return shell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the provided {@link Message} contains a {@link FasteningElement}.
|
||||||
|
*
|
||||||
|
* @param message message
|
||||||
|
* @return true if the stanza has an {@link FasteningElement}.
|
||||||
|
*/
|
||||||
|
public static boolean hasFasteningElement(Message message) {
|
||||||
|
return message.hasExtension(ELEMENT, MessageFasteningManager.NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the provided {@link MessageBuilder} contains a {@link FasteningElement}.
|
||||||
|
*
|
||||||
|
* @param builder message builder
|
||||||
|
* @return true if the stanza has an {@link FasteningElement}.
|
||||||
|
*/
|
||||||
|
public static boolean hasFasteningElement(MessageBuilder builder) {
|
||||||
|
return builder.hasExtension(FasteningElement.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return MessageFasteningManager.NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
|
XmlStringBuilder xml = new XmlStringBuilder(this)
|
||||||
|
.attribute(ATTR_ID, referencedStanzasOriginId.getId())
|
||||||
|
.optBooleanAttribute(ATTR_CLEAR, isRemovingElement())
|
||||||
|
.optBooleanAttribute(ATTR_SHELL, isShellElement())
|
||||||
|
.rightAngleBracket();
|
||||||
|
addPayloads(xml);
|
||||||
|
return xml.closeElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPayloads(XmlStringBuilder xml) {
|
||||||
|
for (ExternalElement external : externalPayloads) {
|
||||||
|
xml.append(external);
|
||||||
|
}
|
||||||
|
for (ExtensionElement wrapped : wrappedPayloads) {
|
||||||
|
xml.append(wrapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FasteningElement createShellElementForSensitiveElement(FasteningElement sensitiveElement) {
|
||||||
|
return createShellElementForSensitiveElement(sensitiveElement.getReferencedStanzasOriginId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FasteningElement createShellElementForSensitiveElement(String originIdOfSensitiveElement) {
|
||||||
|
return createShellElementForSensitiveElement(new OriginIdElement(originIdOfSensitiveElement));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FasteningElement createShellElementForSensitiveElement(OriginIdElement originIdOfSensitiveElement) {
|
||||||
|
return FasteningElement.builder()
|
||||||
|
.setOriginId(originIdOfSensitiveElement)
|
||||||
|
.setShell()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add this element to the provided message builder.
|
||||||
|
* Note: The stanza MUST NOT contain more than one apply-to elements at the same time.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#rules">XEP-0422 §4: Business Rules</a>
|
||||||
|
*
|
||||||
|
* @param messageBuilder message builder
|
||||||
|
*/
|
||||||
|
public void applyTo(MessageBuilder messageBuilder) {
|
||||||
|
if (FasteningElement.hasFasteningElement(messageBuilder)) {
|
||||||
|
throw new IllegalArgumentException("Stanza cannot contain more than one apply-to elements.");
|
||||||
|
} else {
|
||||||
|
messageBuilder.addExtension(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private OriginIdElement originId;
|
||||||
|
private final List<ExtensionElement> wrappedPayloads = new ArrayList<>();
|
||||||
|
private final List<ExternalElement> externalPayloads = new ArrayList<>();
|
||||||
|
private boolean isClear = false;
|
||||||
|
private boolean isShell = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the origin-id of the referenced message.
|
||||||
|
*
|
||||||
|
* @param originIdString origin id as String
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder setOriginId(String originIdString) {
|
||||||
|
return setOriginId(new OriginIdElement(originIdString));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link OriginIdElement} of the referenced message.
|
||||||
|
*
|
||||||
|
* @param originId origin-id as element
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder setOriginId(OriginIdElement originId) {
|
||||||
|
this.originId = originId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a wrapped payload.
|
||||||
|
*
|
||||||
|
* @param wrappedPayload wrapped payload
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder addWrappedPayload(ExtensionElement wrappedPayload) {
|
||||||
|
return addWrappedPayloads(Collections.singletonList(wrappedPayload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add multiple wrapped payloads at once.
|
||||||
|
*
|
||||||
|
* @param wrappedPayloads list of wrapped payloads
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder addWrappedPayloads(List<ExtensionElement> wrappedPayloads) {
|
||||||
|
this.wrappedPayloads.addAll(wrappedPayloads);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an external payload.
|
||||||
|
*
|
||||||
|
* @param externalPayload external payload
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder addExternalPayload(ExternalElement externalPayload) {
|
||||||
|
return addExternalPayloads(Collections.singletonList(externalPayload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add multiple external payloads at once.
|
||||||
|
*
|
||||||
|
* @param externalPayloads external payloads
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder addExternalPayloads(List<ExternalElement> externalPayloads) {
|
||||||
|
this.externalPayloads.addAll(externalPayloads);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare this {@link FasteningElement} to remove previous fastenings.
|
||||||
|
* Semantically the wrapped payloads of this element declares all wrapped payloads from the referenced
|
||||||
|
* fastening element that share qualified names as removed.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#remove">
|
||||||
|
* XEP-0422: Message Fastening §3.4 Removing fastenings</a>
|
||||||
|
*
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder setClear() {
|
||||||
|
isClear = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare this {@link FasteningElement} to be a shell element.
|
||||||
|
* Shell elements are used as hints that a Stanza Content Encryption payload contains another sensitive
|
||||||
|
* {@link FasteningElement}. The outer "shell" {@link FasteningElement} is used to do fastening collation.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#encryption">XEP-0422: Message Fastening §3.5 Interaction with stanza encryption</a>
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0420.html">XEP-0420: Stanza Content Encryption</a>
|
||||||
|
*
|
||||||
|
* @return builder instance
|
||||||
|
*/
|
||||||
|
public Builder setShell() {
|
||||||
|
isShell = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the element.
|
||||||
|
* @return built element.
|
||||||
|
*/
|
||||||
|
public FasteningElement build() {
|
||||||
|
validateThatIfIsShellThenOtherwiseEmpty();
|
||||||
|
return new FasteningElement(originId, wrappedPayloads, externalPayloads, isClear, isShell);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateThatIfIsShellThenOtherwiseEmpty() {
|
||||||
|
if (!isShell) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isClear || !wrappedPayloads.isEmpty() || !externalPayloads.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("A fastening that is a shell element must be otherwise empty " +
|
||||||
|
"and cannot have a 'clear' attribute.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XEP-0422: Message Fastening.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html">XEP-0422: Message
|
||||||
|
* Fastening</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.message_fastening.element;
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XEP-0422: Message Fastening.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html">XEP-0422: Message
|
||||||
|
* Fastening</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.message_fastening;
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* 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.message_fastening.provider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
||||||
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
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.smackx.message_fastening.MessageFasteningManager;
|
||||||
|
import org.jivesoftware.smackx.message_fastening.element.ExternalElement;
|
||||||
|
import org.jivesoftware.smackx.message_fastening.element.FasteningElement;
|
||||||
|
|
||||||
|
public class FasteningElementProvider extends ExtensionElementProvider<FasteningElement> {
|
||||||
|
|
||||||
|
public static final FasteningElementProvider TEST_INSTANCE = new FasteningElementProvider();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FasteningElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
FasteningElement.Builder builder = FasteningElement.builder();
|
||||||
|
builder.setOriginId(parser.getAttributeValue("", FasteningElement.ATTR_ID));
|
||||||
|
if (ParserUtils.getBooleanAttribute(parser, FasteningElement.ATTR_CLEAR, false)) {
|
||||||
|
builder.setClear();
|
||||||
|
}
|
||||||
|
if (ParserUtils.getBooleanAttribute(parser, FasteningElement.ATTR_SHELL, false)) {
|
||||||
|
builder.setShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
outerloop: while (true) {
|
||||||
|
XmlPullParser.Event tag = parser.next();
|
||||||
|
switch (tag) {
|
||||||
|
case START_ELEMENT:
|
||||||
|
String name = parser.getName();
|
||||||
|
String namespace = parser.getNamespace();
|
||||||
|
|
||||||
|
// Parse external payload
|
||||||
|
if (MessageFasteningManager.NAMESPACE.equals(namespace) && ExternalElement.ELEMENT.equals(name)) {
|
||||||
|
ExternalElement external = new ExternalElement(
|
||||||
|
parser.getAttributeValue("", ExternalElement.ATTR_NAME),
|
||||||
|
parser.getAttributeValue("", ExternalElement.ATTR_ELEMENT_NAMESPACE));
|
||||||
|
builder.addExternalPayload(external);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse wrapped payload
|
||||||
|
ExtensionElement wrappedPayload = PacketParserUtils.parseExtensionElement(name, namespace, parser, xmlEnvironment);
|
||||||
|
builder.addWrappedPayload(wrappedPayload);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case END_ELEMENT:
|
||||||
|
if (parser.getDepth() == initialDepth) {
|
||||||
|
break outerloop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XEP-0422: Message Fastening.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html">XEP-0422: Message
|
||||||
|
* Fastening</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.message_fastening.provider;
|
|
@ -24,6 +24,7 @@ import org.jivesoftware.smack.packet.IQ;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.TextSingleFormField;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
@ -98,24 +99,23 @@ public class EnablePushNotificationsIQ extends IQ {
|
||||||
xml.rightAngleBracket();
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
if (publishOptions != null) {
|
if (publishOptions != null) {
|
||||||
DataForm dataForm = new DataForm(DataForm.Type.submit);
|
DataForm.Builder dataForm = DataForm.builder();
|
||||||
|
|
||||||
// TODO: Shouldn't this use some potentially existing PubSub API? Also FORM_TYPE fields are usually of type
|
// TODO: Shouldn't this use some potentially existing PubSub API? Also FORM_TYPE fields are usually of type
|
||||||
// 'hidden', but the examples in XEP-0357 do also not set the value to hidden and FORM_TYPE itself appears
|
// 'hidden', but the examples in XEP-0357 do also not set the value to hidden and FORM_TYPE itself appears
|
||||||
// to be more convention than specification.
|
// to be more convention than specification.
|
||||||
FormField.Builder formTypeField = FormField.builder("FORM_TYPE");
|
FormField formTypeField = FormField.buildHiddenFormType(PubSub.NAMESPACE + "#publish-options");
|
||||||
formTypeField.addValue(PubSub.NAMESPACE + "#publish-options");
|
dataForm.addField(formTypeField);
|
||||||
dataForm.addField(formTypeField.build());
|
|
||||||
|
|
||||||
Iterator<Map.Entry<String, String>> publishOptionsIterator = publishOptions.entrySet().iterator();
|
Iterator<Map.Entry<String, String>> publishOptionsIterator = publishOptions.entrySet().iterator();
|
||||||
while (publishOptionsIterator.hasNext()) {
|
while (publishOptionsIterator.hasNext()) {
|
||||||
Map.Entry<String, String> pairVariableValue = publishOptionsIterator.next();
|
Map.Entry<String, String> pairVariableValue = publishOptionsIterator.next();
|
||||||
FormField.Builder field = FormField.builder(pairVariableValue.getKey());
|
TextSingleFormField.Builder field = FormField.builder(pairVariableValue.getKey());
|
||||||
field.addValue(pairVariableValue.getValue());
|
field.setValue(pairVariableValue.getValue());
|
||||||
dataForm.addField(field.build());
|
dataForm.addField(field.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
xml.append(dataForm);
|
xml.append(dataForm.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
return xml;
|
return xml;
|
||||||
|
|
|
@ -101,4 +101,25 @@ public class OriginIdElement extends StableAndUniqueIdElement {
|
||||||
.attribute(ATTR_ID, getId())
|
.attribute(ATTR_ID, getId())
|
||||||
.closeEmptyElement();
|
.closeEmptyElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (other == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(other instanceof OriginIdElement)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OriginIdElement otherId = (OriginIdElement) other;
|
||||||
|
return getId().equals(otherId.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getId().hashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,6 +292,12 @@
|
||||||
<className>org.jivesoftware.smackx.dox.provider.DnsIqProvider</className>
|
<className>org.jivesoftware.smackx.dox.provider.DnsIqProvider</className>
|
||||||
</iqProvider>
|
</iqProvider>
|
||||||
|
|
||||||
|
<!-- XEP-0422: Message Fastening -->
|
||||||
|
<extensionProvider>
|
||||||
|
<elementName>apply-to</elementName>
|
||||||
|
<namespace>urn:xmpp:fasten:0</namespace>
|
||||||
|
<className>org.jivesoftware.smackx.message_fastening.provider.FasteningElementProvider</className>
|
||||||
|
</extensionProvider>
|
||||||
|
|
||||||
<!-- XEP-xxxx: Multi-User Chat Light -->
|
<!-- XEP-xxxx: Multi-User Chat Light -->
|
||||||
<iqProvider>
|
<iqProvider>
|
||||||
|
|
|
@ -8,5 +8,6 @@
|
||||||
<className>org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager</className>
|
<className>org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager</className>
|
||||||
<className>org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager</className>
|
<className>org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager</className>
|
||||||
<className>org.jivesoftware.smackx.xmlelement.DataFormsXmlElementManager</className>
|
<className>org.jivesoftware.smackx.xmlelement.DataFormsXmlElementManager</className>
|
||||||
|
<className>org.jivesoftware.smackx.message_fastening.MessageFasteningManager</className>
|
||||||
</startupClasses>
|
</startupClasses>
|
||||||
</smack>
|
</smack>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2016 Fernando Ramirez, 2018 Florian Schmaus
|
* Copyright 2016 Fernando Ramirez, 2018-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -34,7 +34,7 @@ import org.jxmpp.util.XmppDateTime;
|
||||||
public class FiltersTest extends MamTest {
|
public class FiltersTest extends MamTest {
|
||||||
|
|
||||||
private static String getMamXMemberWith(List<String> fieldsNames, List<? extends CharSequence> fieldsValues) {
|
private static String getMamXMemberWith(List<String> fieldsNames, List<? extends CharSequence> fieldsValues) {
|
||||||
String xml = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE' type='hidden'>" + "<value>"
|
String xml = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
||||||
+ MamElements.NAMESPACE + "</value>" + "</field>";
|
+ MamElements.NAMESPACE + "</value>" + "</field>";
|
||||||
|
|
||||||
for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) {
|
for (int i = 0; i < fieldsNames.size() && i < fieldsValues.size(); i++) {
|
||||||
|
|
|
@ -85,7 +85,7 @@ public class MamQueryIQProviderTest {
|
||||||
assertEquals(fields2.get(1).getType(), FormField.Type.jid_single);
|
assertEquals(fields2.get(1).getType(), FormField.Type.jid_single);
|
||||||
assertEquals(fields2.get(2).getType(), FormField.Type.text_single);
|
assertEquals(fields2.get(2).getType(), FormField.Type.text_single);
|
||||||
assertEquals(fields2.get(2).getValues(), new ArrayList<>());
|
assertEquals(fields2.get(2).getValues(), new ArrayList<>());
|
||||||
assertEquals(fields2.get(4).getVariable(), "urn:example:xmpp:free-text-search");
|
assertEquals(fields2.get(4).getFieldName(), "urn:example:xmpp:free-text-search");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,8 @@ public class MamTest extends SmackTestSuite {
|
||||||
IllegalArgumentException, InvocationTargetException {
|
IllegalArgumentException, InvocationTargetException {
|
||||||
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm");
|
Method methodGetNewMamForm = MamManager.class.getDeclaredMethod("getNewMamForm");
|
||||||
methodGetNewMamForm.setAccessible(true);
|
methodGetNewMamForm.setAccessible(true);
|
||||||
return (DataForm) methodGetNewMamForm.invoke(mamManager);
|
DataForm.Builder dataFormBuilder = (DataForm.Builder) methodGetNewMamForm.invoke(mamManager);
|
||||||
|
return dataFormBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.junit.jupiter.api.Test;
|
||||||
public class PagingTest extends MamTest {
|
public class PagingTest extends MamTest {
|
||||||
|
|
||||||
private static final String pagingStanza = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:1' queryid='testid'>"
|
private static final String pagingStanza = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:1' queryid='testid'>"
|
||||||
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE' type='hidden'>"
|
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>"
|
||||||
+ "<value>urn:xmpp:mam:1</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
|
+ "<value>urn:xmpp:mam:1</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
|
||||||
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
|
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ import org.jxmpp.jid.impl.JidCreate;
|
||||||
public class QueryArchiveTest extends MamTest {
|
public class QueryArchiveTest extends MamTest {
|
||||||
|
|
||||||
private static final String mamSimpleQueryIQ = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:1' queryid='testid'>"
|
private static final String mamSimpleQueryIQ = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:1' queryid='testid'>"
|
||||||
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE' type='hidden'>" + "<value>"
|
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
||||||
+ MamElements.NAMESPACE + "</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'>"
|
private static final String mamQueryResultExample = "<message to='hag66@shakespeare.lit/pda' from='coven@chat.shakespeare.lit' id='iasd207'>"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2016 Fernando Ramirez, 2018-2019 Florian Schmaus
|
* Copyright 2016 Fernando Ramirez, 2018-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test;
|
||||||
public class ResultsLimitTest extends MamTest {
|
public class ResultsLimitTest extends MamTest {
|
||||||
|
|
||||||
private static final String resultsLimitStanza = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:1' queryid='testid'>"
|
private static final String resultsLimitStanza = "<iq id='sarasa' type='set'>" + "<query xmlns='urn:xmpp:mam:1' queryid='testid'>"
|
||||||
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE' type='hidden'>" + "<value>"
|
+ "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>" + "<value>"
|
||||||
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
|
+ MamElements.NAMESPACE + "</value>" + "</field>" + "</x>" + "<set xmlns='http://jabber.org/protocol/rsm'>"
|
||||||
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
|
+ "<max>10</max>" + "</set>" + "</query>" + "</iq>";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2016 Fernando Ramirez, 2018-2019 Florian Schmaus
|
* Copyright 2016 Fernando Ramirez, 2018-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -28,16 +28,17 @@ import org.jivesoftware.smackx.xdata.FormField;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.jxmpp.jid.JidTestUtil;
|
||||||
|
|
||||||
public class RetrieveFormFieldsTest extends MamTest {
|
public class RetrieveFormFieldsTest extends MamTest {
|
||||||
|
|
||||||
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamElements.NAMESPACE
|
private static final String retrieveFormFieldStanza = "<iq id='sarasa' type='get'>" + "<query xmlns='" + MamElements.NAMESPACE
|
||||||
+ "' queryid='testid'></query>" + "</iq>";
|
+ "' queryid='testid'></query>" + "</iq>";
|
||||||
|
|
||||||
private static final String additionalFieldsStanza = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE' type='hidden'>"
|
private static final String additionalFieldsStanza = "<x xmlns='jabber:x:data' type='submit'>" + "<field var='FORM_TYPE'>"
|
||||||
+ "<value>" + MamElements.NAMESPACE + "</value>" + "</field>"
|
+ "<value>" + MamElements.NAMESPACE + "</value>" + "</field>"
|
||||||
+ "<field var='urn:example:xmpp:free-text-search'>" + "<value>Hi</value>" + "</field>"
|
+ "<field var='urn:example:xmpp:free-text-search'>" + "<value>Hi</value>" + "</field>"
|
||||||
+ "<field var='urn:example:xmpp:stanza-content' type='jid-single'>" + "<value>Hi2</value>" + "</field>"
|
+ "<field var='urn:example:xmpp:stanza-content'>" + "<value>one@exampleone.org</value>" + "</field>"
|
||||||
+ "</x>";
|
+ "</x>";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -51,13 +52,11 @@ public class RetrieveFormFieldsTest extends MamTest {
|
||||||
@Test
|
@Test
|
||||||
public void checkAddAdditionalFieldsStanza() throws Exception {
|
public void checkAddAdditionalFieldsStanza() throws Exception {
|
||||||
FormField field1 = FormField.builder("urn:example:xmpp:free-text-search")
|
FormField field1 = FormField.builder("urn:example:xmpp:free-text-search")
|
||||||
.setType(FormField.Type.text_single)
|
.setValue("Hi")
|
||||||
.addValue("Hi")
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
FormField field2 = FormField.builder("urn:example:xmpp:stanza-content")
|
FormField field2 = FormField.jidSingleBuilder("urn:example:xmpp:stanza-content")
|
||||||
.setType(FormField.Type.jid_single)
|
.setValue(JidTestUtil.BARE_JID_1)
|
||||||
.addValue("Hi2")
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
MamQueryArgs mamQueryArgs = MamQueryArgs.builder()
|
MamQueryArgs mamQueryArgs = MamQueryArgs.builder()
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2019 Paul Schaub
|
||||||
|
*
|
||||||
|
* 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.message_fastening;
|
||||||
|
|
||||||
|
import static org.jivesoftware.smack.test.util.XmlUnitUtils.assertXmlSimilar;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.MessageBuilder;
|
||||||
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.StanzaFactory;
|
||||||
|
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
|
||||||
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
|
import org.jivesoftware.smack.test.util.SmackTestUtil;
|
||||||
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
import org.jivesoftware.smackx.message_fastening.element.ExternalElement;
|
||||||
|
import org.jivesoftware.smackx.message_fastening.element.FasteningElement;
|
||||||
|
import org.jivesoftware.smackx.message_fastening.provider.FasteningElementProvider;
|
||||||
|
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.EnumSource;
|
||||||
|
|
||||||
|
public class MessageFasteningElementsTest {
|
||||||
|
|
||||||
|
private final StanzaFactory stanzaFactory = new StanzaFactory(new StandardStanzaIdSource());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test XML serialization of the {@link FasteningElement} using the example provided by
|
||||||
|
* the XEP.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#wrapped-payloads">XEP-0422 §3.1 Wrapped Payloads</a>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void fasteningElementSerializationTest() {
|
||||||
|
String xml =
|
||||||
|
"<apply-to xmlns='urn:xmpp:fasten:0' id='origin-id-1'>" +
|
||||||
|
" <i-like-this xmlns='urn:example:like'/>" +
|
||||||
|
"</apply-to>";
|
||||||
|
|
||||||
|
FasteningElement applyTo = FasteningElement.builder()
|
||||||
|
.setOriginId("origin-id-1")
|
||||||
|
.addWrappedPayload(new StandardExtensionElement("i-like-this", "urn:example:like"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertXmlSimilar(xml, applyTo.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@EnumSource(SmackTestUtil.XmlPullParserKind.class)
|
||||||
|
public void fasteningDeserializationTest(SmackTestUtil.XmlPullParserKind parserKind) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
String xml =
|
||||||
|
"<apply-to xmlns='urn:xmpp:fasten:0' id='origin-id-1'>" +
|
||||||
|
" <i-like-this xmlns='urn:example:like'/>" +
|
||||||
|
" <external name='custom' element-namespace='urn:example:custom'/>" +
|
||||||
|
" <external name='body'/>" +
|
||||||
|
"</apply-to>";
|
||||||
|
|
||||||
|
FasteningElement parsed = SmackTestUtil.parse(xml, FasteningElementProvider.class, parserKind);
|
||||||
|
|
||||||
|
assertNotNull(parsed);
|
||||||
|
assertEquals(new OriginIdElement("origin-id-1"), parsed.getReferencedStanzasOriginId());
|
||||||
|
assertFalse(parsed.isRemovingElement());
|
||||||
|
assertFalse(parsed.isShellElement());
|
||||||
|
|
||||||
|
assertEquals(1, parsed.getWrappedPayloads().size());
|
||||||
|
assertEquals("i-like-this", parsed.getWrappedPayloads().get(0).getElementName());
|
||||||
|
assertEquals("urn:example:like", parsed.getWrappedPayloads().get(0).getNamespace());
|
||||||
|
|
||||||
|
assertEquals(2, parsed.getExternalPayloads().size());
|
||||||
|
ExternalElement custom = parsed.getExternalPayloads().get(0);
|
||||||
|
assertEquals("custom", custom.getName());
|
||||||
|
assertEquals("urn:example:custom", custom.getElementNamespace());
|
||||||
|
ExternalElement body = parsed.getExternalPayloads().get(1);
|
||||||
|
assertEquals("body", body.getName());
|
||||||
|
assertNull(body.getElementNamespace());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fasteningDeserializationClearTest() throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
String xml =
|
||||||
|
"<apply-to xmlns='urn:xmpp:fasten:0' id='origin-id-1' clear='true'>" +
|
||||||
|
" <i-like-this xmlns='urn:example:like'/>" +
|
||||||
|
"</apply-to>";
|
||||||
|
|
||||||
|
FasteningElement parsed = FasteningElementProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml));
|
||||||
|
|
||||||
|
assertTrue(parsed.isRemovingElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fasteningElementWithExternalElementsTest() {
|
||||||
|
String xml =
|
||||||
|
"<apply-to xmlns='urn:xmpp:fasten:0' id='origin-id-2'>" +
|
||||||
|
" <external name='body'/>" +
|
||||||
|
" <external name='custom' element-namespace='urn:example:custom'/>" +
|
||||||
|
" <edit xmlns='urn:example.edit'/>" +
|
||||||
|
"</apply-to>";
|
||||||
|
|
||||||
|
FasteningElement element = FasteningElement.builder()
|
||||||
|
.setOriginId("origin-id-2")
|
||||||
|
.addExternalPayloads(Arrays.asList(
|
||||||
|
new ExternalElement("body"),
|
||||||
|
new ExternalElement("custom", "urn:example:custom")
|
||||||
|
))
|
||||||
|
.addWrappedPayload(
|
||||||
|
new StandardExtensionElement("edit", "urn:example.edit"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertXmlSimilar(xml, element.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createShellElementSharesOriginIdTest() {
|
||||||
|
OriginIdElement originIdElement = new OriginIdElement("sensitive-stanza-1");
|
||||||
|
FasteningElement sensitiveFastening = FasteningElement.builder()
|
||||||
|
.setOriginId(originIdElement)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
FasteningElement shellElement = FasteningElement.createShellElementForSensitiveElement(sensitiveFastening);
|
||||||
|
|
||||||
|
assertEquals(originIdElement, shellElement.getReferencedStanzasOriginId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fasteningRemoveSerializationTest() {
|
||||||
|
String xml =
|
||||||
|
"<apply-to xmlns='urn:xmpp:fasten:0' id='origin-id-1' clear='true'>" +
|
||||||
|
" <i-like-this xmlns='urn:example:like'>Very much</i-like-this>" +
|
||||||
|
"</apply-to>";
|
||||||
|
|
||||||
|
FasteningElement element = FasteningElement.builder()
|
||||||
|
.setOriginId("origin-id-1")
|
||||||
|
.setClear()
|
||||||
|
.addWrappedPayload(StandardExtensionElement.builder("i-like-this", "urn:example:like")
|
||||||
|
.setText("Very much")
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertXmlSimilar(xml, element.toXML().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasFasteningElementTest() {
|
||||||
|
MessageBuilder messageBuilderWithFasteningElement = MessageBuilder.buildMessage()
|
||||||
|
.setBody("Hi!")
|
||||||
|
.addExtension(FasteningElement.builder().setOriginId("origin-id-1").build());
|
||||||
|
MessageBuilder messageBuilderWithoutFasteningElement = MessageBuilder.buildMessage()
|
||||||
|
.setBody("Ho!");
|
||||||
|
|
||||||
|
assertTrue(FasteningElement.hasFasteningElement(messageBuilderWithFasteningElement));
|
||||||
|
assertFalse(FasteningElement.hasFasteningElement(messageBuilderWithoutFasteningElement));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shellElementMustNotHaveClearAttributeTest() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
FasteningElement.builder()
|
||||||
|
.setShell()
|
||||||
|
.setClear()
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shellElementMustNotContainAnyPayloads() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
FasteningElement.builder()
|
||||||
|
.setShell()
|
||||||
|
.addWrappedPayload(new StandardExtensionElement("edit", "urn:example.edit"))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
FasteningElement.builder()
|
||||||
|
.setShell()
|
||||||
|
.addExternalPayload(new ExternalElement("body"))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureAddFasteningElementToStanzaWorks() {
|
||||||
|
MessageBuilder message = stanzaFactory.buildMessageStanza();
|
||||||
|
FasteningElement fasteningElement = FasteningElement.builder().setOriginId("another-apply-to").build();
|
||||||
|
|
||||||
|
// Adding only one element is allowed
|
||||||
|
fasteningElement.applyTo(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure, that {@link FasteningElement#applyTo(MessageBuilder)}
|
||||||
|
* throws when trying to add an {@link FasteningElement} to a {@link MessageBuilder} that already contains one
|
||||||
|
* such element.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0422.html#rules">XEP-0422: §4. Business Rules</a>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void ensureStanzaCanOnlyContainOneFasteningElement() {
|
||||||
|
MessageBuilder messageWithFastening = stanzaFactory.buildMessageStanza();
|
||||||
|
FasteningElement.builder().setOriginId("origin-id").build().applyTo(messageWithFastening);
|
||||||
|
|
||||||
|
// Adding a second fastening MUST result in exception
|
||||||
|
Assertions.assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
FasteningElement.builder().setOriginId("another-apply-to").build()
|
||||||
|
.applyTo(messageWithFastening));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2016-2019 Florian Schmaus
|
* Copyright 2016-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -29,7 +29,7 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.commands.AdHocCommandManager;
|
import org.jivesoftware.smackx.commands.AdHocCommandManager;
|
||||||
import org.jivesoftware.smackx.commands.RemoteCommand;
|
import org.jivesoftware.smackx.commands.RemoteCommand;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
@ -72,7 +72,7 @@ public class ServiceAdministrationManager extends Manager {
|
||||||
RemoteCommand command = addUser();
|
RemoteCommand command = addUser();
|
||||||
command.execute();
|
command.execute();
|
||||||
|
|
||||||
Form answerForm = command.getForm().createAnswerForm();
|
FillableForm answerForm = new FillableForm(command.getForm());
|
||||||
|
|
||||||
answerForm.setAnswer("accountjid", userJid);
|
answerForm.setAnswer("accountjid", userJid);
|
||||||
answerForm.setAnswer("password", password);
|
answerForm.setAnswer("password", password);
|
||||||
|
@ -101,7 +101,7 @@ public class ServiceAdministrationManager extends Manager {
|
||||||
RemoteCommand command = deleteUser();
|
RemoteCommand command = deleteUser();
|
||||||
command.execute();
|
command.execute();
|
||||||
|
|
||||||
Form answerForm = command.getForm().createAnswerForm();
|
FillableForm answerForm = new FillableForm(command.getForm());
|
||||||
|
|
||||||
answerForm.setAnswer("accountjids", jidsToDelete);
|
answerForm.setAnswer("accountjids", jidsToDelete);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2009 Jonas Ådahl, 2011-2019 Florian Schmaus
|
* Copyright © 2009 Jonas Ådahl, 2011-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,13 +19,16 @@ package org.jivesoftware.smackx.caps;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
@ -48,7 +51,6 @@ import org.jivesoftware.smack.filter.PresenceTypeFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
|
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaTypeFilter;
|
import org.jivesoftware.smack.filter.StanzaTypeFilter;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
import org.jivesoftware.smack.packet.PresenceBuilder;
|
import org.jivesoftware.smack.packet.PresenceBuilder;
|
||||||
|
@ -548,7 +550,7 @@ public final class EntityCapsManager extends Manager {
|
||||||
final List<Identity> identities = new LinkedList<>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities());
|
final List<Identity> identities = new LinkedList<>(ServiceDiscoveryManager.getInstanceFor(connection).getIdentities());
|
||||||
sdm.setNodeInformationProvider(localNodeVer, new AbstractNodeInformationProvider() {
|
sdm.setNodeInformationProvider(localNodeVer, new AbstractNodeInformationProvider() {
|
||||||
List<String> features = sdm.getFeatures();
|
List<String> features = sdm.getFeatures();
|
||||||
List<ExtensionElement> packetExtensions = sdm.getExtendedInfoAsList();
|
List<DataForm> packetExtensions = sdm.getExtendedInfo();
|
||||||
@Override
|
@Override
|
||||||
public List<String> getNodeFeatures() {
|
public List<String> getNodeFeatures() {
|
||||||
return features;
|
return features;
|
||||||
|
@ -558,7 +560,7 @@ public final class EntityCapsManager extends Manager {
|
||||||
return identities;
|
return identities;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public List<ExtensionElement> getNodePacketExtensions() {
|
public List<DataForm> getNodePacketExtensions() {
|
||||||
return packetExtensions;
|
return packetExtensions;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -600,7 +602,7 @@ public final class EntityCapsManager extends Manager {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// step 3.5 check for well-formed packet extensions
|
// step 3.5 check for well-formed packet extensions
|
||||||
if (verifyPacketExtensions(info))
|
if (!verifyPacketExtensions(info))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
String calculatedVer = generateVerificationString(info, hash).version;
|
String calculatedVer = generateVerificationString(info, hash).version;
|
||||||
|
@ -612,27 +614,29 @@ public final class EntityCapsManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Verify that the given discovery info is not ill-formed.
|
||||||
*
|
*
|
||||||
* @param info TODO javadoc me please
|
* @param info the discovery info to verify.
|
||||||
* @return true if the stanza extensions is ill-formed
|
* @return true if the stanza extensions is not ill-formed
|
||||||
*/
|
*/
|
||||||
protected static boolean verifyPacketExtensions(DiscoverInfo info) {
|
private static boolean verifyPacketExtensions(DiscoverInfo info) {
|
||||||
List<FormField> foundFormTypes = new LinkedList<>();
|
Set<String> foundFormTypes = new HashSet<>();
|
||||||
for (ExtensionElement pe : info.getExtensions()) {
|
List<DataForm> dataForms = info.getExtensions(DataForm.class);
|
||||||
if (pe.getNamespace().equals(DataForm.NAMESPACE)) {
|
for (DataForm dataForm : dataForms) {
|
||||||
DataForm df = (DataForm) pe;
|
FormField formFieldTypeField = dataForm.getHiddenFormTypeField();
|
||||||
for (FormField f : df.getFields()) {
|
if (formFieldTypeField == null) {
|
||||||
if (f.getVariable().equals("FORM_TYPE")) {
|
continue;
|
||||||
for (FormField fft : foundFormTypes) {
|
}
|
||||||
if (f.equals(fft))
|
|
||||||
return true;
|
String type = formFieldTypeField.getFirstValue();
|
||||||
}
|
boolean noDuplicate = foundFormTypes.add(type);
|
||||||
foundFormTypes.add(f);
|
if (!noDuplicate) {
|
||||||
}
|
// Ill-formed extension: duplicate forms (by form field type string).
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static CapsVersionAndHash generateVerificationString(DiscoverInfoView discoverInfo) {
|
protected static CapsVersionAndHash generateVerificationString(DiscoverInfoView discoverInfo) {
|
||||||
|
@ -664,8 +668,6 @@ public final class EntityCapsManager extends Manager {
|
||||||
// be "broken" implementation in the wild, so we *always* transform to lowercase.
|
// be "broken" implementation in the wild, so we *always* transform to lowercase.
|
||||||
hash = hash.toLowerCase(Locale.US);
|
hash = hash.toLowerCase(Locale.US);
|
||||||
|
|
||||||
DataForm extendedInfo = DataForm.from(discoverInfo);
|
|
||||||
|
|
||||||
// 1. Initialize an empty string S ('sb' in this method).
|
// 1. Initialize an empty string S ('sb' in this method).
|
||||||
StringBuilder sb = new StringBuilder(); // Use StringBuilder as we don't
|
StringBuilder sb = new StringBuilder(); // Use StringBuilder as we don't
|
||||||
// need thread-safe StringBuffer
|
// need thread-safe StringBuffer
|
||||||
|
@ -705,50 +707,47 @@ public final class EntityCapsManager extends Manager {
|
||||||
sb.append('<');
|
sb.append('<');
|
||||||
}
|
}
|
||||||
|
|
||||||
// only use the data form for calculation is it has a hidden FORM_TYPE
|
List<DataForm> extendedInfos = discoverInfo.getExtensions(DataForm.class);
|
||||||
// field
|
for (DataForm extendedInfo : extendedInfos) {
|
||||||
// see XEP-0115 5.4 step 3.6
|
if (!extendedInfo.hasHiddenFormTypeField()) {
|
||||||
if (extendedInfo != null && extendedInfo.hasHiddenFormTypeField()) {
|
// Only use the data form for calculation is it has a hidden FORM_TYPE field.
|
||||||
synchronized (extendedInfo) {
|
// See XEP-0115 5.4 step 3.f
|
||||||
// 6. If the service discovery information response includes
|
continue;
|
||||||
// XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e.,
|
}
|
||||||
// by the XML character data of the <value/> element).
|
|
||||||
SortedSet<FormField> fs = new TreeSet<>(new Comparator<FormField>() {
|
|
||||||
@Override
|
|
||||||
public int compare(FormField f1, FormField f2) {
|
|
||||||
return f1.getVariable().compareTo(f2.getVariable());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
FormField ft = null;
|
// 6. If the service discovery information response includes
|
||||||
|
// XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e.,
|
||||||
for (FormField f : extendedInfo.getFields()) {
|
// by the XML character data of the <value/> element).
|
||||||
if (!f.getVariable().equals("FORM_TYPE")) {
|
SortedSet<FormField> fs = new TreeSet<>(new Comparator<FormField>() {
|
||||||
fs.add(f);
|
@Override
|
||||||
} else {
|
public int compare(FormField f1, FormField f2) {
|
||||||
ft = f;
|
return f1.getFieldName().compareTo(f2.getFieldName());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Add FORM_TYPE values
|
for (FormField f : extendedInfo.getFields()) {
|
||||||
if (ft != null) {
|
if (!f.getFieldName().equals("FORM_TYPE")) {
|
||||||
formFieldValuesToCaps(ft.getValues(), sb);
|
fs.add(f);
|
||||||
}
|
|
||||||
|
|
||||||
// 7. 3. For each field other than FORM_TYPE:
|
|
||||||
// 1. Append the value of the "var" attribute, followed by the
|
|
||||||
// '<' character.
|
|
||||||
// 2. Sort values by the XML character data of the <value/>
|
|
||||||
// element.
|
|
||||||
// 3. For each <value/> element, append the XML character data,
|
|
||||||
// followed by the '<' character.
|
|
||||||
for (FormField f : fs) {
|
|
||||||
sb.append(f.getVariable());
|
|
||||||
sb.append('<');
|
|
||||||
formFieldValuesToCaps(f.getValues(), sb);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add FORM_TYPE values
|
||||||
|
formFieldValuesToCaps(Collections.singletonList(extendedInfo.getFormType()), sb);
|
||||||
|
|
||||||
|
// 7. 3. For each field other than FORM_TYPE:
|
||||||
|
// 1. Append the value of the "var" attribute, followed by the
|
||||||
|
// '<' character.
|
||||||
|
// 2. Sort values by the XML character data of the <value/>
|
||||||
|
// element.
|
||||||
|
// 3. For each <value/> element, append the XML character data,
|
||||||
|
// followed by the '<' character.
|
||||||
|
for (FormField f : fs) {
|
||||||
|
sb.append(f.getFieldName());
|
||||||
|
sb.append('<');
|
||||||
|
formFieldValuesToCaps(f.getValues(), sb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Ensure that S is encoded according to the UTF-8 encoding (RFC
|
// 8. Ensure that S is encoded according to the UTF-8 encoding (RFC
|
||||||
// 3269).
|
// 3269).
|
||||||
// 9. Compute the verification string by hashing S using the algorithm
|
// 9. Compute the verification string by hashing S using the algorithm
|
||||||
|
@ -767,7 +766,7 @@ public final class EntityCapsManager extends Manager {
|
||||||
return new CapsVersionAndHash(version, hash);
|
return new CapsVersionAndHash(version, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void formFieldValuesToCaps(List<CharSequence> i, StringBuilder sb) {
|
private static void formFieldValuesToCaps(List<? extends CharSequence> i, StringBuilder sb) {
|
||||||
SortedSet<CharSequence> fvs = new TreeSet<>();
|
SortedSet<CharSequence> fvs = new TreeSet<>();
|
||||||
fvs.addAll(i);
|
fvs.addAll(i);
|
||||||
for (CharSequence fv : fvs) {
|
for (CharSequence fv : fvs) {
|
||||||
|
|
|
@ -24,7 +24,8 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.StanzaError;
|
import org.jivesoftware.smack.packet.StanzaError;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.commands.packet.AdHocCommandData;
|
import org.jivesoftware.smackx.commands.packet.AdHocCommandData;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
@ -188,13 +189,8 @@ public abstract class AdHocCommand {
|
||||||
* @return the form of the current stage to fill out or the result of the
|
* @return the form of the current stage to fill out or the result of the
|
||||||
* execution.
|
* execution.
|
||||||
*/
|
*/
|
||||||
public Form getForm() {
|
public DataForm getForm() {
|
||||||
if (data.getForm() == null) {
|
return data.getForm();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new Form(data.getForm());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,8 +201,8 @@ public abstract class AdHocCommand {
|
||||||
* @param form the form of the current stage to fill out or the result of the
|
* @param form the form of the current stage to fill out or the result of the
|
||||||
* execution.
|
* execution.
|
||||||
*/
|
*/
|
||||||
protected void setForm(Form form) {
|
protected void setForm(DataForm form) {
|
||||||
data.setForm(form.getDataFormToSend());
|
data.setForm(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -234,7 +230,7 @@ public abstract class AdHocCommand {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public abstract void next(Form response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
|
public abstract void next(FillableForm response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completes the command execution with the information provided in the
|
* Completes the command execution with the information provided in the
|
||||||
|
@ -250,7 +246,7 @@ public abstract class AdHocCommand {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public abstract void complete(Form response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
|
public abstract void complete(FillableForm response) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Goes to the previous stage. The requester is asking to re-send the
|
* Goes to the previous stage. The requester is asking to re-send the
|
||||||
|
|
|
@ -52,7 +52,8 @@ import org.jivesoftware.smackx.disco.AbstractNodeInformationProvider;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
@ -462,7 +463,8 @@ public final class AdHocCommandManager extends Manager {
|
||||||
|
|
||||||
if (Action.next.equals(action)) {
|
if (Action.next.equals(action)) {
|
||||||
command.incrementStage();
|
command.incrementStage();
|
||||||
command.next(new Form(requestData.getForm()));
|
DataForm dataForm = requestData.getForm();
|
||||||
|
command.next(new FillableForm(dataForm));
|
||||||
if (command.isLastStage()) {
|
if (command.isLastStage()) {
|
||||||
// If it is the last stage then the command is
|
// If it is the last stage then the command is
|
||||||
// completed
|
// completed
|
||||||
|
@ -475,7 +477,8 @@ public final class AdHocCommandManager extends Manager {
|
||||||
}
|
}
|
||||||
else if (Action.complete.equals(action)) {
|
else if (Action.complete.equals(action)) {
|
||||||
command.incrementStage();
|
command.incrementStage();
|
||||||
command.complete(new Form(requestData.getForm()));
|
DataForm dataForm = requestData.getForm();
|
||||||
|
command.complete(new FillableForm(dataForm));
|
||||||
response.setStatus(Status.completed);
|
response.setStatus(Status.completed);
|
||||||
// Remove the completed session
|
// Remove the completed session
|
||||||
executingCommands.remove(sessionId);
|
executingCommands.remove(sessionId);
|
||||||
|
|
|
@ -23,7 +23,8 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.commands.packet.AdHocCommandData;
|
import org.jivesoftware.smackx.commands.packet.AdHocCommandData;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
@ -80,8 +81,8 @@ public class RemoteCommand extends AdHocCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void complete(Form form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public void complete(FillableForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
executeAction(Action.complete, form);
|
executeAction(Action.complete, form.getDataFormToSubmit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -100,13 +101,13 @@ public class RemoteCommand extends AdHocCommand {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public void execute(Form form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public void execute(FillableForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
executeAction(Action.execute, form);
|
executeAction(Action.execute, form.getDataFormToSubmit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void next(Form form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public void next(FillableForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
executeAction(Action.next, form);
|
executeAction(Action.next, form.getDataFormToSubmit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -130,7 +131,7 @@ public class RemoteCommand extends AdHocCommand {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
private void executeAction(Action action, Form form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
private void executeAction(Action action, DataForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
// TODO: Check that all the required fields of the form were filled, if
|
// TODO: Check that all the required fields of the form were filled, if
|
||||||
// TODO: not throw the corresponding exception. This will make a faster response,
|
// TODO: not throw the corresponding exception. This will make a faster response,
|
||||||
// TODO: since the request is stopped before it's sent.
|
// TODO: since the request is stopped before it's sent.
|
||||||
|
@ -140,10 +141,7 @@ public class RemoteCommand extends AdHocCommand {
|
||||||
data.setNode(getNode());
|
data.setNode(getNode());
|
||||||
data.setSessionID(sessionID);
|
data.setSessionID(sessionID);
|
||||||
data.setAction(action);
|
data.setAction(action);
|
||||||
|
data.setForm(form);
|
||||||
if (form != null) {
|
|
||||||
data.setForm(form.getDataFormToSend());
|
|
||||||
}
|
|
||||||
|
|
||||||
AdHocCommandData responseData = null;
|
AdHocCommandData responseData = null;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright © 2014 Florian Schmaus
|
* Copyright © 2014-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -42,7 +42,7 @@ public abstract class AbstractNodeInformationProvider implements NodeInformation
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ExtensionElement> getNodePacketExtensions() {
|
public List<? extends ExtensionElement> getNodePacketExtensions() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,5 +68,5 @@ public interface NodeInformationProvider {
|
||||||
*
|
*
|
||||||
* @return a list of the stanza extensions defined in the node.
|
* @return a list of the stanza extensions defined in the node.
|
||||||
*/
|
*/
|
||||||
List<ExtensionElement> getNodePacketExtensions();
|
List<? extends ExtensionElement> getNodePacketExtensions();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2018-2019 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2018-2020 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -38,10 +38,10 @@ import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
||||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.StanzaError;
|
import org.jivesoftware.smack.packet.StanzaError;
|
||||||
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
private static final Map<XMPPConnection, ServiceDiscoveryManager> instances = new WeakHashMap<>();
|
private static final Map<XMPPConnection, ServiceDiscoveryManager> instances = new WeakHashMap<>();
|
||||||
|
|
||||||
private final Set<String> features = new HashSet<>();
|
private final Set<String> features = new HashSet<>();
|
||||||
private DataForm extendedInfo = null;
|
private List<DataForm> extendedInfos = new ArrayList<>(2);
|
||||||
private final Map<String, NodeInformationProvider> nodeInformationProviders = new ConcurrentHashMap<>();
|
private final Map<String, NodeInformationProvider> nodeInformationProviders = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// Create a new ServiceDiscoveryManager on every established connection
|
// Create a new ServiceDiscoveryManager on every established connection
|
||||||
|
@ -307,9 +307,8 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
for (String feature : getFeatures()) {
|
for (String feature : getFeatures()) {
|
||||||
response.addFeature(feature);
|
response.addFeature(feature);
|
||||||
}
|
}
|
||||||
if (extendedInfo != null) {
|
|
||||||
response.addExtension(extendedInfo);
|
response.addExtensions(extendedInfos);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -427,25 +426,59 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
* configure the extended info before logging to the server so that the
|
* configure the extended info before logging to the server so that the
|
||||||
* information is already available if it is required upon login.
|
* information is already available if it is required upon login.
|
||||||
*
|
*
|
||||||
* @param info TODO javadoc me please
|
* @param info the data form that contains the extend service discovery
|
||||||
* the data form that contains the extend service discovery
|
|
||||||
* information.
|
* information.
|
||||||
|
* @deprecated use {@link #addExtendedInfo(DataForm)} instead.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Remove in Smack 4.5
|
||||||
|
@Deprecated
|
||||||
public synchronized void setExtendedInfo(DataForm info) {
|
public synchronized void setExtendedInfo(DataForm info) {
|
||||||
extendedInfo = info;
|
addExtendedInfo(info);
|
||||||
// Notify others of a state change of SDM. In order to keep the state consistent, this
|
|
||||||
// method is synchronized
|
|
||||||
renewEntityCapsVersion();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data form that is set as extended information for this Service Discovery instance (XEP-0128).
|
* Registers extended discovery information of this XMPP entity. When this
|
||||||
|
* client is queried for its information this data form will be returned as
|
||||||
|
* specified by XEP-0128.
|
||||||
|
* <p>
|
||||||
*
|
*
|
||||||
* @see <a href="http://xmpp.org/extensions/xep-0128.html">XEP-128: Service Discovery Extensions</a>
|
* Since no stanza is actually sent to the server it is safe to perform this
|
||||||
* @return the data form
|
* operation before logging to the server. In fact, you may want to
|
||||||
|
* configure the extended info before logging to the server so that the
|
||||||
|
* information is already available if it is required upon login.
|
||||||
|
*
|
||||||
|
* @param extendedInfo the data form that contains the extend service discovery information.
|
||||||
|
* @return the old data form which got replaced (if any)
|
||||||
|
* @since 4.4.0
|
||||||
*/
|
*/
|
||||||
public DataForm getExtendedInfo() {
|
public DataForm addExtendedInfo(DataForm extendedInfo) {
|
||||||
return extendedInfo;
|
String formType = extendedInfo.getFormType();
|
||||||
|
StringUtils.requireNotNullNorEmpty(formType, "The data form must have a form type set");
|
||||||
|
|
||||||
|
DataForm removedDataForm;
|
||||||
|
synchronized (this) {
|
||||||
|
removedDataForm = DataForm.remove(extendedInfos, formType);
|
||||||
|
|
||||||
|
extendedInfos.add(extendedInfo);
|
||||||
|
|
||||||
|
// Notify others of a state change of SDM. In order to keep the state consistent, this
|
||||||
|
// method is synchronized
|
||||||
|
renewEntityCapsVersion();
|
||||||
|
}
|
||||||
|
return removedDataForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the extended discovery information of the given form type.
|
||||||
|
*
|
||||||
|
* @param formType the type of the data form with the extended discovery information to remove.
|
||||||
|
* @since 4.4.0
|
||||||
|
*/
|
||||||
|
public synchronized void removeExtendedInfo(String formType) {
|
||||||
|
DataForm removedForm = DataForm.remove(extendedInfos, formType);
|
||||||
|
if (removedForm != null) {
|
||||||
|
renewEntityCapsVersion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -454,13 +487,21 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
*
|
*
|
||||||
* @return the data form as List of PacketExtensions
|
* @return the data form as List of PacketExtensions
|
||||||
*/
|
*/
|
||||||
public List<ExtensionElement> getExtendedInfoAsList() {
|
public synchronized List<DataForm> getExtendedInfo() {
|
||||||
List<ExtensionElement> res = null;
|
return CollectionUtil.newListWith(extendedInfos);
|
||||||
if (extendedInfo != null) {
|
}
|
||||||
res = new ArrayList<>(1);
|
|
||||||
res.add(extendedInfo);
|
/**
|
||||||
}
|
* Returns the data form as List of PacketExtensions, or null if no data form is set.
|
||||||
return res;
|
* This representation is needed by some classes (e.g. EntityCapsManager, NodeInformationProvider)
|
||||||
|
*
|
||||||
|
* @return the data form as List of PacketExtensions
|
||||||
|
* @deprecated use {@link #getExtendedInfo()} instead.
|
||||||
|
*/
|
||||||
|
// TODO: Remove in Smack 4.5
|
||||||
|
@Deprecated
|
||||||
|
public List<DataForm> getExtendedInfoAsList() {
|
||||||
|
return getExtendedInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -471,10 +512,13 @@ public final class ServiceDiscoveryManager extends Manager {
|
||||||
* operation before logging to the server.
|
* operation before logging to the server.
|
||||||
*/
|
*/
|
||||||
public synchronized void removeExtendedInfo() {
|
public synchronized void removeExtendedInfo() {
|
||||||
extendedInfo = null;
|
int extendedInfosCount = extendedInfos.size();
|
||||||
// Notify others of a state change of SDM. In order to keep the state consistent, this
|
extendedInfos.clear();
|
||||||
// method is synchronized
|
if (extendedInfosCount > 0) {
|
||||||
renewEntityCapsVersion();
|
// Notify others of a state change of SDM. In order to keep the state consistent, this
|
||||||
|
// method is synchronized
|
||||||
|
renewEntityCapsVersion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2003-2006 Jive Software.
|
|
||||||
*
|
|
||||||
* 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.filetransfer;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
|
|
||||||
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
|
||||||
import org.jivesoftware.smackx.si.packet.StreamInitiation;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The fault tolerant negotiator takes two stream negotiators, the primary and the secondary
|
|
||||||
* negotiator. If the primary negotiator fails during the stream negotiation process, the second
|
|
||||||
* negotiator is used.
|
|
||||||
*/
|
|
||||||
public class FaultTolerantNegotiator extends StreamNegotiator {
|
|
||||||
|
|
||||||
private final StreamNegotiator primaryNegotiator;
|
|
||||||
private final StreamNegotiator secondaryNegotiator;
|
|
||||||
|
|
||||||
public FaultTolerantNegotiator(XMPPConnection connection, StreamNegotiator primary,
|
|
||||||
StreamNegotiator secondary) {
|
|
||||||
super(connection);
|
|
||||||
this.primaryNegotiator = primary;
|
|
||||||
this.secondaryNegotiator = secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void newStreamInitiation(Jid from, String streamID) {
|
|
||||||
primaryNegotiator.newStreamInitiation(from, streamID);
|
|
||||||
secondaryNegotiator.newStreamInitiation(from, streamID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
InputStream negotiateIncomingStream(Stanza streamInitiation) {
|
|
||||||
throw new UnsupportedOperationException("Negotiation only handled by create incoming " +
|
|
||||||
"stream method.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream createIncomingStream(final StreamInitiation initiation) throws SmackException, XMPPErrorException, InterruptedException {
|
|
||||||
// This could be either an xep47 ibb 'open' iq or an xep65 streamhost iq
|
|
||||||
IQ initiationSet = initiateIncomingStream(connection(), initiation);
|
|
||||||
|
|
||||||
StreamNegotiator streamNegotiator = determineNegotiator(initiationSet);
|
|
||||||
|
|
||||||
return streamNegotiator.negotiateIncomingStream(initiationSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private StreamNegotiator determineNegotiator(Stanza streamInitiation) {
|
|
||||||
if (streamInitiation instanceof Bytestream) {
|
|
||||||
return primaryNegotiator;
|
|
||||||
} else if (streamInitiation instanceof Open) {
|
|
||||||
return secondaryNegotiator;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Unknown stream initiation type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OutputStream createOutgoingStream(String streamID, Jid initiator, Jid target)
|
|
||||||
throws SmackException, XMPPException, InterruptedException {
|
|
||||||
OutputStream stream;
|
|
||||||
try {
|
|
||||||
stream = primaryNegotiator.createOutgoingStream(streamID, initiator, target);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
stream = secondaryNegotiator.createOutgoingStream(streamID, initiator, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getNamespaces() {
|
|
||||||
String[] primary = primaryNegotiator.getNamespaces();
|
|
||||||
String[] secondary = secondaryNegotiator.getNamespaces();
|
|
||||||
|
|
||||||
String[] namespaces = new String[primary.length + secondary.length];
|
|
||||||
System.arraycopy(primary, 0, namespaces, 0, primary.length);
|
|
||||||
System.arraycopy(secondary, 0, namespaces, primary.length, secondary.length);
|
|
||||||
|
|
||||||
return namespaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -42,6 +42,7 @@ import org.jivesoftware.smackx.filetransfer.FileTransferException.NoAcceptableTr
|
||||||
import org.jivesoftware.smackx.filetransfer.FileTransferException.NoStreamMethodsOfferedException;
|
import org.jivesoftware.smackx.filetransfer.FileTransferException.NoStreamMethodsOfferedException;
|
||||||
import org.jivesoftware.smackx.si.packet.StreamInitiation;
|
import org.jivesoftware.smackx.si.packet.StreamInitiation;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.ListSingleFormField;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
@ -189,7 +190,7 @@ public final class FileTransferNegotiator extends Manager {
|
||||||
public StreamNegotiator selectStreamNegotiator(
|
public StreamNegotiator selectStreamNegotiator(
|
||||||
FileTransferRequest request) throws NotConnectedException, NoStreamMethodsOfferedException, NoAcceptableTransferMechanisms, InterruptedException {
|
FileTransferRequest request) throws NotConnectedException, NoStreamMethodsOfferedException, NoAcceptableTransferMechanisms, InterruptedException {
|
||||||
StreamInitiation si = request.getStreamInitiation();
|
StreamInitiation si = request.getStreamInitiation();
|
||||||
FormField streamMethodField = getStreamMethodField(si
|
ListSingleFormField streamMethodField = getStreamMethodField(si
|
||||||
.getFeatureNegotiationForm());
|
.getFeatureNegotiationForm());
|
||||||
|
|
||||||
if (streamMethodField == null) {
|
if (streamMethodField == null) {
|
||||||
|
@ -216,11 +217,11 @@ public final class FileTransferNegotiator extends Manager {
|
||||||
return selectedStreamNegotiator;
|
return selectedStreamNegotiator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FormField getStreamMethodField(DataForm form) {
|
private static ListSingleFormField getStreamMethodField(DataForm form) {
|
||||||
return form.getField(STREAM_DATA_FIELD_NAME);
|
return (ListSingleFormField) form.getField(STREAM_DATA_FIELD_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StreamNegotiator getNegotiator(final FormField field)
|
private StreamNegotiator getNegotiator(final ListSingleFormField field)
|
||||||
throws NoAcceptableTransferMechanisms {
|
throws NoAcceptableTransferMechanisms {
|
||||||
String variable;
|
String variable;
|
||||||
boolean isByteStream = false;
|
boolean isByteStream = false;
|
||||||
|
@ -239,12 +240,7 @@ public final class FileTransferNegotiator extends Manager {
|
||||||
throw new FileTransferException.NoAcceptableTransferMechanisms();
|
throw new FileTransferException.NoAcceptableTransferMechanisms();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isByteStream && isIBB) {
|
if (isByteStream) {
|
||||||
return new FaultTolerantNegotiator(connection(),
|
|
||||||
byteStreamTransferManager,
|
|
||||||
inbandTransferManager);
|
|
||||||
}
|
|
||||||
else if (isByteStream) {
|
|
||||||
return byteStreamTransferManager;
|
return byteStreamTransferManager;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -355,11 +351,7 @@ public final class FileTransferNegotiator extends Manager {
|
||||||
throw new FileTransferException.NoAcceptableTransferMechanisms();
|
throw new FileTransferException.NoAcceptableTransferMechanisms();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isByteStream && isIBB) {
|
if (isByteStream) {
|
||||||
return new FaultTolerantNegotiator(connection(),
|
|
||||||
byteStreamTransferManager, inbandTransferManager);
|
|
||||||
}
|
|
||||||
else if (isByteStream) {
|
|
||||||
return byteStreamTransferManager;
|
return byteStreamTransferManager;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -368,16 +360,15 @@ public final class FileTransferNegotiator extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataForm createDefaultInitiationForm() {
|
private static DataForm createDefaultInitiationForm() {
|
||||||
DataForm form = new DataForm(DataForm.Type.form);
|
DataForm.Builder form = DataForm.builder()
|
||||||
FormField.Builder fieldBuilder = FormField.builder();
|
.setType(DataForm.Type.form);
|
||||||
fieldBuilder.setFieldName(STREAM_DATA_FIELD_NAME)
|
ListSingleFormField.Builder fieldBuilder = FormField.listSingleBuilder(STREAM_DATA_FIELD_NAME);
|
||||||
.setType(FormField.Type.list_single);
|
|
||||||
|
|
||||||
if (!IBB_ONLY) {
|
if (!IBB_ONLY) {
|
||||||
fieldBuilder.addOption(Bytestream.NAMESPACE);
|
fieldBuilder.addOption(Bytestream.NAMESPACE);
|
||||||
}
|
}
|
||||||
fieldBuilder.addOption(DataPacketExtension.NAMESPACE);
|
fieldBuilder.addOption(DataPacketExtension.NAMESPACE);
|
||||||
form.addField(fieldBuilder.build());
|
form.addField(fieldBuilder.build());
|
||||||
return form;
|
return form.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,8 +90,8 @@ public class IBBTransferNegotiator extends StreamNegotiator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getNamespaces() {
|
public String getNamespace() {
|
||||||
return new String[] { DataPacketExtension.NAMESPACE };
|
return DataPacketExtension.NAMESPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -88,8 +88,8 @@ public class Socks5TransferNegotiator extends StreamNegotiator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getNamespaces() {
|
public String getNamespace() {
|
||||||
return new String[] { Bytestream.NAMESPACE };
|
return Bytestream.NAMESPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.jivesoftware.smack.util.EventManger.Callback;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.si.packet.StreamInitiation;
|
import org.jivesoftware.smackx.si.packet.StreamInitiation;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.ListSingleFormField;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
@ -69,33 +70,31 @@ public abstract class StreamNegotiator extends Manager {
|
||||||
* initiator.
|
* initiator.
|
||||||
*
|
*
|
||||||
* @param streamInitiationOffer The offer from the stream initiator to connect for a stream.
|
* @param streamInitiationOffer The offer from the stream initiator to connect for a stream.
|
||||||
* @param namespaces The namespace that relates to the accepted means of transfer.
|
* @param namespace The namespace that relates to the accepted means of transfer.
|
||||||
* @return The response to be forwarded to the initiator.
|
* @return The response to be forwarded to the initiator.
|
||||||
*/
|
*/
|
||||||
protected static StreamInitiation createInitiationAccept(
|
protected static StreamInitiation createInitiationAccept(
|
||||||
StreamInitiation streamInitiationOffer, String[] namespaces) {
|
StreamInitiation streamInitiationOffer, String namespace) {
|
||||||
StreamInitiation response = new StreamInitiation();
|
StreamInitiation response = new StreamInitiation();
|
||||||
response.setTo(streamInitiationOffer.getFrom());
|
response.setTo(streamInitiationOffer.getFrom());
|
||||||
response.setFrom(streamInitiationOffer.getTo());
|
response.setFrom(streamInitiationOffer.getTo());
|
||||||
response.setType(IQ.Type.result);
|
response.setType(IQ.Type.result);
|
||||||
response.setStanzaId(streamInitiationOffer.getStanzaId());
|
response.setStanzaId(streamInitiationOffer.getStanzaId());
|
||||||
|
|
||||||
DataForm form = new DataForm(DataForm.Type.submit);
|
DataForm.Builder form = DataForm.builder();
|
||||||
FormField.Builder field = FormField.builder(
|
ListSingleFormField.Builder field = FormField.listSingleBuilder(
|
||||||
FileTransferNegotiator.STREAM_DATA_FIELD_NAME);
|
FileTransferNegotiator.STREAM_DATA_FIELD_NAME);
|
||||||
for (String namespace : namespaces) {
|
field.setValue(namespace);
|
||||||
field.addValue(namespace);
|
|
||||||
}
|
|
||||||
form.addField(field.build());
|
form.addField(field.build());
|
||||||
|
|
||||||
response.setFeatureNegotiationForm(form);
|
response.setFeatureNegotiationForm(form.build());
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final IQ initiateIncomingStream(final XMPPConnection connection, StreamInitiation initiation)
|
protected final IQ initiateIncomingStream(final XMPPConnection connection, StreamInitiation initiation)
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException {
|
throws NoResponseException, XMPPErrorException, NotConnectedException {
|
||||||
final StreamInitiation response = createInitiationAccept(initiation,
|
final StreamInitiation response = createInitiationAccept(initiation,
|
||||||
getNamespaces());
|
getNamespace());
|
||||||
|
|
||||||
newStreamInitiation(initiation.getFrom(), initiation.getSessionID());
|
newStreamInitiation(initiation.getFrom(), initiation.getSessionID());
|
||||||
|
|
||||||
|
@ -182,7 +181,7 @@ public abstract class StreamNegotiator extends Manager {
|
||||||
* @return Returns the XMPP namespace reserved for this particular type of
|
* @return Returns the XMPP namespace reserved for this particular type of
|
||||||
* file transfer.
|
* file transfer.
|
||||||
*/
|
*/
|
||||||
public abstract String[] getNamespaces();
|
public abstract String getNamespace();
|
||||||
|
|
||||||
public static void signal(String eventKey, IQ eventValue) {
|
public static void signal(String eventKey, IQ eventValue) {
|
||||||
initationSetEvents.signalEvent(eventKey, eventValue);
|
initationSetEvents.signalEvent(eventKey, eventValue);
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.formtypes;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.TextSingleFormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
public class FormFieldRegistry {
|
||||||
|
|
||||||
|
private static final Map<String, Map<String, FormField.Type>> REGISTRY = new HashMap<>();
|
||||||
|
|
||||||
|
private static final Map<String, FormField.Type> LOOKASIDE_REGISTRY = new HashMap<>();
|
||||||
|
|
||||||
|
private static final Map<String, String> FIELD_NAME_TO_FORM_TYPE = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
register(FormField.FORM_TYPE, FormField.Type.hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReferenceEquality")
|
||||||
|
public static synchronized void register(DataForm dataForm) {
|
||||||
|
// TODO: Also allow forms of type 'result'?
|
||||||
|
if (dataForm.getType() != DataForm.Type.form) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
String formType = null;
|
||||||
|
TextSingleFormField hiddenFormTypeField = dataForm.getHiddenFormTypeField();
|
||||||
|
if (hiddenFormTypeField != null) {
|
||||||
|
formType = hiddenFormTypeField.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (FormField formField : dataForm.getFields()) {
|
||||||
|
// Note that we can compare here by reference equality to skip the hidden form type field.
|
||||||
|
if (formField == hiddenFormTypeField) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = formField.getFieldName();
|
||||||
|
FormField.Type type = formField.getType();
|
||||||
|
register(formType, fieldName, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void register(String formType, String fieldName, FormField.Type type) {
|
||||||
|
if (formType == null) {
|
||||||
|
FormFieldInformation formFieldInformation = lookup(fieldName);
|
||||||
|
if (formFieldInformation != null) {
|
||||||
|
if (Objects.equals(formType, formFieldInformation.formType)
|
||||||
|
&& type.equals(formFieldInformation.formFieldType)) {
|
||||||
|
// The field is already registered, nothing to do here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = "There is already a field with the name'" + fieldName
|
||||||
|
+ "' registered with the field type '" + formFieldInformation.formFieldType
|
||||||
|
+ "', while this tries to register the field with the type '" + type + '\'';
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOOKASIDE_REGISTRY.put(fieldName, type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, FormField.Type> fieldNameToType = REGISTRY.get(formType);
|
||||||
|
if (fieldNameToType == null) {
|
||||||
|
fieldNameToType = new HashMap<>();
|
||||||
|
REGISTRY.put(formType, fieldNameToType);
|
||||||
|
} else {
|
||||||
|
FormField.Type previousType = fieldNameToType.get(fieldName);
|
||||||
|
if (previousType != null && previousType != type) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNameToType.put(fieldName, type);
|
||||||
|
|
||||||
|
FIELD_NAME_TO_FORM_TYPE.put(fieldName, formType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void register(String fieldName, FormField.Type type) {
|
||||||
|
FormField.Type previousType = LOOKASIDE_REGISTRY.get(fieldName);
|
||||||
|
if (previousType != null) {
|
||||||
|
if (previousType == type) {
|
||||||
|
// Nothing to do here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("There is already a field with the name '" + fieldName
|
||||||
|
+ "' registered with type " + previousType
|
||||||
|
+ ", while trying to register this field with type '" + type + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOOKASIDE_REGISTRY.put(fieldName, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized FormField.Type lookup(String formType, String fieldName) {
|
||||||
|
if (formType != null) {
|
||||||
|
Map<String, FormField.Type> fieldNameToTypeMap = REGISTRY.get(formType);
|
||||||
|
if (fieldNameToTypeMap != null) {
|
||||||
|
FormField.Type type = fieldNameToTypeMap.get(fieldName);
|
||||||
|
if (type != null) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
formType = FIELD_NAME_TO_FORM_TYPE.get(fieldName);
|
||||||
|
if (formType != null) {
|
||||||
|
FormField.Type type = lookup(formType, fieldName);
|
||||||
|
if (type != null) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to lookaside registry.
|
||||||
|
return LOOKASIDE_REGISTRY.get(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized FormFieldInformation lookup(String fieldName) {
|
||||||
|
String formType = FIELD_NAME_TO_FORM_TYPE.get(fieldName);
|
||||||
|
FormField.Type type = lookup(formType, fieldName);
|
||||||
|
if (type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FormFieldInformation(type, formType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class FormFieldInformation {
|
||||||
|
public final FormField.Type formFieldType;
|
||||||
|
public final String formType;
|
||||||
|
|
||||||
|
|
||||||
|
private FormFieldInformation(FormField.Type formFieldType, String formType) {
|
||||||
|
this.formFieldType = formFieldType;
|
||||||
|
this.formType = formType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smack's implementation of XEP-0068: Field Standardization for Data Forms.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.formtypes;
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015-2017 Ishan Khanna, Fernando Ramirez 2019 Florian Schmaus
|
* Copyright 2015-2017 Ishan Khanna, Fernando Ramirez 2019-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,13 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.geoloc;
|
package org.jivesoftware.smackx.geoloc;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.AsyncButOrdered;
|
|
||||||
import org.jivesoftware.smack.ConnectionCreationListener;
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
|
@ -30,31 +26,26 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
|
||||||
import org.jivesoftware.smackx.geoloc.packet.GeoLocation;
|
import org.jivesoftware.smackx.geoloc.packet.GeoLocation;
|
||||||
import org.jivesoftware.smackx.geoloc.provider.GeoLocationProvider;
|
import org.jivesoftware.smackx.geoloc.provider.GeoLocationProvider;
|
||||||
import org.jivesoftware.smackx.pep.PepListener;
|
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||||
import org.jivesoftware.smackx.pep.PepManager;
|
import org.jivesoftware.smackx.pep.PepManager;
|
||||||
import org.jivesoftware.smackx.pubsub.EventElement;
|
|
||||||
import org.jivesoftware.smackx.pubsub.ItemsExtension;
|
|
||||||
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
|
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
|
||||||
import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProviderManager;
|
import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProviderManager;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for Smacks API for XEP-0080: User Location.
|
* Entry point for Smacks API for XEP-0080: User Location.
|
||||||
* <br>
|
* <br>
|
||||||
* To publish a UserLocation, please use {@link #sendGeolocation(GeoLocation)} method. This will publish the node.
|
* To publish a UserLocation, please use {@link #publishGeoLocation(GeoLocation)} method. This will publish the node.
|
||||||
* <br>
|
* <br>
|
||||||
* To stop publishing a UserLocation, please use {@link #stopPublishingGeolocation()} method. This will send a disble publishing signal.
|
* To stop publishing a UserLocation, please use {@link #stopPublishingGeolocation()} method. This will send a disble publishing signal.
|
||||||
* <br>
|
* <br>
|
||||||
* To add a {@link GeoLocationListener} in order to remain updated with other users GeoLocation, use {@link #addGeoLocationListener(GeoLocationListener)} method.
|
* To add a {@link PepEventListener} in order to remain updated with other users GeoLocation, use {@link #addGeoLocationListener(PepEventListener)} method.
|
||||||
* <br>
|
* <br>
|
||||||
* To link a GeoLocation with {@link Message}, use `message.addExtension(geoLocation)`.
|
* To link a GeoLocation with {@link Message}, use `message.addExtension(geoLocation)`.
|
||||||
* <br>
|
* <br>
|
||||||
|
@ -65,16 +56,10 @@ import org.jxmpp.jid.Jid;
|
||||||
*/
|
*/
|
||||||
public final class GeoLocationManager extends Manager {
|
public final class GeoLocationManager extends Manager {
|
||||||
|
|
||||||
public static final String GEOLOCATION_NODE = "http://jabber.org/protocol/geoloc";
|
public static final String GEOLOCATION_NODE = GeoLocation.NAMESPACE;
|
||||||
public static final String GEOLOCATION_NOTIFY = GEOLOCATION_NODE + "+notify";
|
|
||||||
|
|
||||||
private static final Map<XMPPConnection, GeoLocationManager> INSTANCES = new WeakHashMap<>();
|
private static final Map<XMPPConnection, GeoLocationManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
private static boolean ENABLE_USER_LOCATION_NOTIFICATIONS_BY_DEFAULT = true;
|
|
||||||
|
|
||||||
private final Set<GeoLocationListener> geoLocationListeners = new CopyOnWriteArraySet<>();
|
|
||||||
private final AsyncButOrdered<BareJid> asyncButOrdered = new AsyncButOrdered<BareJid>();
|
|
||||||
private final ServiceDiscoveryManager serviceDiscoveryManager;
|
|
||||||
private final PepManager pepManager;
|
private final PepManager pepManager;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -108,31 +93,6 @@ public final class GeoLocationManager extends Manager {
|
||||||
private GeoLocationManager(XMPPConnection connection) {
|
private GeoLocationManager(XMPPConnection connection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
pepManager = PepManager.getInstanceFor(connection);
|
pepManager = PepManager.getInstanceFor(connection);
|
||||||
pepManager.addPepListener(new PepListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventReceived(EntityBareJid from, EventElement event, Message message) {
|
|
||||||
if (!GEOLOCATION_NODE.equals(event.getEvent().getNode())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final BareJid contact = from.asBareJid();
|
|
||||||
asyncButOrdered.performAsyncButOrdered(contact, () -> {
|
|
||||||
ItemsExtension itemsExtension = (ItemsExtension) event.getEvent();
|
|
||||||
List<ExtensionElement> items = itemsExtension.getExtensions();
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
PayloadItem<GeoLocation> payload = (PayloadItem<GeoLocation>) items.get(0);
|
|
||||||
GeoLocation geoLocation = payload.getPayload();
|
|
||||||
for (GeoLocationListener listener : geoLocationListeners) {
|
|
||||||
listener.onGeoLocationUpdated(contact, geoLocation, message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
|
||||||
if (ENABLE_USER_LOCATION_NOTIFICATIONS_BY_DEFAULT) {
|
|
||||||
enableUserLocationNotifications();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendGeoLocationToJid(GeoLocation geoLocation, Jid jid) throws InterruptedException,
|
public void sendGeoLocationToJid(GeoLocation geoLocation, Jid jid) throws InterruptedException,
|
||||||
|
@ -160,18 +120,18 @@ public final class GeoLocationManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send geolocation through the PubSub node.
|
* Publish the user's geographic location through the Personal Eventing Protocol (PEP).
|
||||||
*
|
*
|
||||||
* @param geoLocation TODO javadoc me please
|
* @param geoLocation the geographic location to publish.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws XMPPErrorException if there was an XMPP error returned.
|
* @throws XMPPErrorException if there was an XMPP error returned.
|
||||||
* @throws NoResponseException if there was no response from the remote entity.
|
* @throws NoResponseException if there was no response from the remote entity.
|
||||||
* @throws NotALeafNodeException if a PubSub leaf node operation was attempted on a non-leaf node.
|
* @throws NotALeafNodeException if a PubSub leaf node operation was attempted on a non-leaf node.
|
||||||
*/
|
*/
|
||||||
public void sendGeolocation(GeoLocation geoLocation)
|
public void publishGeoLocation(GeoLocation geoLocation)
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotALeafNodeException {
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotALeafNodeException {
|
||||||
pepManager.publish(GeoLocation.NAMESPACE, new PayloadItem<GeoLocation>(geoLocation));
|
pepManager.publish(GEOLOCATION_NODE, new PayloadItem<GeoLocation>(geoLocation));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,25 +145,14 @@ public final class GeoLocationManager extends Manager {
|
||||||
*/
|
*/
|
||||||
public void stopPublishingGeolocation()
|
public void stopPublishingGeolocation()
|
||||||
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotALeafNodeException {
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotALeafNodeException {
|
||||||
pepManager.publish(GeoLocation.NAMESPACE, new PayloadItem<GeoLocation>(GeoLocation.EMPTY_GEO_LOCATION));
|
pepManager.publish(GEOLOCATION_NODE, new PayloadItem<GeoLocation>(GeoLocation.EMPTY_GEO_LOCATION));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setGeoLocationNotificationsEnabledByDefault(boolean bool) {
|
public boolean addGeoLocationListener(PepEventListener<GeoLocation> listener) {
|
||||||
ENABLE_USER_LOCATION_NOTIFICATIONS_BY_DEFAULT = bool;
|
return pepManager.addPepEventListener(GEOLOCATION_NODE, GeoLocation.class, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableUserLocationNotifications() {
|
public boolean removeGeoLocationListener(PepEventListener<GeoLocation> listener) {
|
||||||
serviceDiscoveryManager.addFeature(GEOLOCATION_NOTIFY);
|
return pepManager.removePepEventListener(listener);
|
||||||
}
|
|
||||||
|
|
||||||
public void disableGeoLocationNotifications() {
|
|
||||||
serviceDiscoveryManager.removeFeature(GEOLOCATION_NOTIFY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addGeoLocationListener(GeoLocationListener geoLocationListener) {
|
|
||||||
return geoLocationListeners.add(geoLocationListener);
|
|
||||||
}
|
|
||||||
public boolean removeGeoLocationListener(GeoLocationListener geoLocationListener) {
|
|
||||||
return geoLocationListeners.remove(geoLocationListener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015-2017 Ishan Khanna, Fernando Ramirez, 2019 Florian Schmaus
|
* Copyright 2015-2017 Ishan Khanna, Fernando Ramirez, 2019-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,14 +19,13 @@ package org.jivesoftware.smackx.geoloc.packet;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.xml.namespace.QName;
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.EqualsUtil;
|
||||||
|
import org.jivesoftware.smack.util.HashCode;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
@ -50,8 +49,6 @@ public final class GeoLocation implements Serializable, ExtensionElement, FormFi
|
||||||
|
|
||||||
public static final GeoLocation EMPTY_GEO_LOCATION = GeoLocation.builder().build();
|
public static final GeoLocation EMPTY_GEO_LOCATION = GeoLocation.builder().build();
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(GeoLocation.class.getName());
|
|
||||||
|
|
||||||
private final Double accuracy;
|
private final Double accuracy;
|
||||||
private final Double alt;
|
private final Double alt;
|
||||||
private final Double altAccuracy;
|
private final Double altAccuracy;
|
||||||
|
@ -77,50 +74,31 @@ public final class GeoLocation implements Serializable, ExtensionElement, FormFi
|
||||||
private final String tzo;
|
private final String tzo;
|
||||||
private final URI uri;
|
private final URI uri;
|
||||||
|
|
||||||
private GeoLocation(Double accuracy, Double alt, Double altAccuracy, String area, Double bearing, String building, String country,
|
private GeoLocation(Builder builder) {
|
||||||
String countryCode, String datum, String description, Double error, String floor, Double lat,
|
accuracy = builder.accuracy;
|
||||||
String locality, Double lon, String postalcode, String region, String room, Double speed,
|
alt = builder.alt;
|
||||||
String street, String text, Date timestamp, String tzo, URI uri) {
|
altAccuracy = builder.altAccuracy;
|
||||||
this.accuracy = accuracy;
|
area = builder.area;
|
||||||
this.alt = alt;
|
bearing = builder.bearing;
|
||||||
this.altAccuracy = altAccuracy;
|
building = builder.building;
|
||||||
this.area = area;
|
country = builder.country;
|
||||||
this.bearing = bearing;
|
countryCode = builder.countryCode;
|
||||||
this.building = building;
|
datum = builder.datum;
|
||||||
this.country = country;
|
description = builder.description;
|
||||||
this.countryCode = countryCode;
|
error = builder.error;
|
||||||
|
floor = builder.floor;
|
||||||
// If datum is not included, receiver MUST assume WGS84; receivers MUST implement WGS84; senders MAY use another
|
lat = builder.lat;
|
||||||
// datum, but it is not recommended.
|
locality = builder.locality;
|
||||||
|
lon = builder.lon;
|
||||||
if (StringUtils.isNullOrEmpty(datum)) {
|
postalcode = builder.postalcode;
|
||||||
datum = "WGS84";
|
region = builder.region;
|
||||||
}
|
room = builder.room;
|
||||||
|
speed = builder.speed;
|
||||||
this.datum = datum;
|
street = builder.street;
|
||||||
this.description = description;
|
text = builder.text;
|
||||||
|
timestamp = builder.timestamp;
|
||||||
// error element is deprecated in favor of accuracy
|
tzo = builder.tzo;
|
||||||
if (accuracy != null) {
|
uri = builder.uri;
|
||||||
error = null;
|
|
||||||
LOGGER.log(Level.WARNING,
|
|
||||||
"Error and accuracy set. Ignoring error as it is deprecated in favor of accuracy");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.error = error;
|
|
||||||
this.floor = floor;
|
|
||||||
this.lat = lat;
|
|
||||||
this.locality = locality;
|
|
||||||
this.lon = lon;
|
|
||||||
this.postalcode = postalcode;
|
|
||||||
this.region = region;
|
|
||||||
this.room = room;
|
|
||||||
this.speed = speed;
|
|
||||||
this.street = street;
|
|
||||||
this.text = text;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.tzo = tzo;
|
|
||||||
this.uri = uri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getAccuracy() {
|
public Double getAccuracy() {
|
||||||
|
@ -163,6 +141,13 @@ public final class GeoLocation implements Serializable, ExtensionElement, FormFi
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the error.
|
||||||
|
*
|
||||||
|
* @return the error.
|
||||||
|
* @deprecated use {@link #getAccuracy()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public Double getError() {
|
public Double getError() {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -266,6 +251,70 @@ public final class GeoLocation implements Serializable, ExtensionElement, FormFi
|
||||||
return NAMESPACE;
|
return NAMESPACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final HashCode.Cache hashCodeCache = new HashCode.Cache();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return hashCodeCache.getHashCode(c ->
|
||||||
|
c
|
||||||
|
.append(accuracy)
|
||||||
|
.append(alt)
|
||||||
|
.append(altAccuracy)
|
||||||
|
.append(area)
|
||||||
|
.append(bearing)
|
||||||
|
.append(building)
|
||||||
|
.append(country)
|
||||||
|
.append(countryCode)
|
||||||
|
.append(datum)
|
||||||
|
.append(description)
|
||||||
|
.append(error)
|
||||||
|
.append(floor)
|
||||||
|
.append(lat)
|
||||||
|
.append(locality)
|
||||||
|
.append(lon)
|
||||||
|
.append(postalcode)
|
||||||
|
.append(region)
|
||||||
|
.append(room)
|
||||||
|
.append(speed)
|
||||||
|
.append(street)
|
||||||
|
.append(text)
|
||||||
|
.append(timestamp)
|
||||||
|
.append(tzo)
|
||||||
|
.append(uri)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return EqualsUtil.equals(this, obj, (e, o) -> {
|
||||||
|
e
|
||||||
|
.append(accuracy, o.accuracy)
|
||||||
|
.append(altAccuracy, o.altAccuracy)
|
||||||
|
.append(area, o.area)
|
||||||
|
.append(bearing, o.bearing)
|
||||||
|
.append(building, o.building)
|
||||||
|
.append(country, o.country)
|
||||||
|
.append(countryCode, o.countryCode)
|
||||||
|
.append(datum, o.datum)
|
||||||
|
.append(description, o.description)
|
||||||
|
.append(error, o.error)
|
||||||
|
.append(floor, o.floor)
|
||||||
|
.append(lat, o.lat)
|
||||||
|
.append(locality, o.locality)
|
||||||
|
.append(lon, o.lon)
|
||||||
|
.append(postalcode, o.postalcode)
|
||||||
|
.append(region, o.region)
|
||||||
|
.append(room, o.room)
|
||||||
|
.append(speed, o.speed)
|
||||||
|
.append(street, o.street)
|
||||||
|
.append(text, o.text)
|
||||||
|
.append(timestamp, o.timestamp)
|
||||||
|
.append(tzo, o.tzo)
|
||||||
|
.append(uri, o.uri)
|
||||||
|
;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new instance of {@link Builder}.
|
* Returns a new instance of {@link Builder}.
|
||||||
* @return Builder
|
* @return Builder
|
||||||
|
@ -318,7 +367,11 @@ public final class GeoLocation implements Serializable, ExtensionElement, FormFi
|
||||||
private String building;
|
private String building;
|
||||||
private String country;
|
private String country;
|
||||||
private String countryCode;
|
private String countryCode;
|
||||||
private String datum;
|
|
||||||
|
// If datum is not included, receiver MUST assume WGS84; receivers MUST implement WGS84; senders MAY use another
|
||||||
|
// datum, but it is not recommended.
|
||||||
|
private String datum = "WGS84";
|
||||||
|
|
||||||
private String description;
|
private String description;
|
||||||
private Double error;
|
private Double error;
|
||||||
private String floor;
|
private String floor;
|
||||||
|
@ -453,7 +506,9 @@ public final class GeoLocation implements Serializable, ExtensionElement, FormFi
|
||||||
*
|
*
|
||||||
* @param error error in arc minutes
|
* @param error error in arc minutes
|
||||||
* @return Builder
|
* @return Builder
|
||||||
|
* @deprecated use {@link #setAccuracy(Double)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Builder setError(Double error) {
|
public Builder setError(Double error) {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
return this;
|
return this;
|
||||||
|
@ -610,10 +665,7 @@ public final class GeoLocation implements Serializable, ExtensionElement, FormFi
|
||||||
* @return GeoLocation
|
* @return GeoLocation
|
||||||
*/
|
*/
|
||||||
public GeoLocation build() {
|
public GeoLocation build() {
|
||||||
|
return new GeoLocation(this);
|
||||||
return new GeoLocation(accuracy, alt, altAccuracy, area, bearing, building, country, countryCode, datum, description,
|
|
||||||
error, floor, lat, locality, lon, postalcode, region, room, speed, street, text, timestamp,
|
|
||||||
tzo, uri);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015-2017 Ishan Khanna, Fernando Ramirez, 2019 Florian Schmaus
|
* Copyright 2015-2017 Ishan Khanna, Fernando Ramirez, 2019-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -79,7 +79,7 @@ public class GeoLocationProvider extends ExtensionElementProvider<GeoLocation> {
|
||||||
builder.setDescription(parser.nextText());
|
builder.setDescription(parser.nextText());
|
||||||
break;
|
break;
|
||||||
case "error":
|
case "error":
|
||||||
builder.setError(ParserUtils.getDoubleFromNextText(parser));
|
parseError(builder, parser);
|
||||||
break;
|
break;
|
||||||
case "floor":
|
case "floor":
|
||||||
builder.setFloor(parser.nextText());
|
builder.setFloor(parser.nextText());
|
||||||
|
@ -136,6 +136,12 @@ public class GeoLocationProvider extends ExtensionElementProvider<GeoLocation> {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static void parseError(GeoLocation.Builder builder, XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
double error = ParserUtils.getDoubleFromNextText(parser);
|
||||||
|
builder.setError(error);
|
||||||
|
}
|
||||||
|
|
||||||
public static class GeoLocationFormFieldChildElementProvider extends FormFieldChildElementProvider<GeoLocation> {
|
public static class GeoLocationFormFieldChildElementProvider extends FormFieldChildElementProvider<GeoLocation> {
|
||||||
|
|
||||||
public static final GeoLocationFormFieldChildElementProvider INSTANCE = new GeoLocationFormFieldChildElementProvider();
|
public static final GeoLocationFormFieldChildElementProvider INSTANCE = new GeoLocationFormFieldChildElementProvider();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2018 Paul Schaub.
|
* Copyright 2018 Paul Schaub, 2020 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -20,9 +20,10 @@ import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.mood.element.MoodElement;
|
import org.jivesoftware.smackx.mood.element.MoodElement;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
public interface MoodListener {
|
public interface MoodListener {
|
||||||
|
|
||||||
void onMoodUpdated(BareJid jid, Message message, MoodElement moodElement);
|
void onMoodUpdated(EntityBareJid from, MoodElement moodElement, String id, Message message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2018 Paul Schaub.
|
* Copyright 2018 Paul Schaub, 2020 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,12 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.mood;
|
package org.jivesoftware.smackx.mood;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
import org.jivesoftware.smack.AsyncButOrdered;
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
@ -29,21 +26,13 @@ import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.provider.ProviderManager;
|
import org.jivesoftware.smack.provider.ProviderManager;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
|
||||||
import org.jivesoftware.smackx.mood.element.MoodConcretisation;
|
import org.jivesoftware.smackx.mood.element.MoodConcretisation;
|
||||||
import org.jivesoftware.smackx.mood.element.MoodElement;
|
import org.jivesoftware.smackx.mood.element.MoodElement;
|
||||||
import org.jivesoftware.smackx.mood.provider.MoodConcretisationProvider;
|
import org.jivesoftware.smackx.mood.provider.MoodConcretisationProvider;
|
||||||
import org.jivesoftware.smackx.pep.PepListener;
|
import org.jivesoftware.smackx.pep.PepEventListener;
|
||||||
import org.jivesoftware.smackx.pep.PepManager;
|
import org.jivesoftware.smackx.pep.PepManager;
|
||||||
import org.jivesoftware.smackx.pubsub.EventElement;
|
|
||||||
import org.jivesoftware.smackx.pubsub.ItemsExtension;
|
|
||||||
import org.jivesoftware.smackx.pubsub.LeafNode;
|
|
||||||
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for Smacks API for XEP-0107: User Mood.
|
* Entry point for Smacks API for XEP-0107: User Mood.
|
||||||
|
@ -51,8 +40,8 @@ import org.jxmpp.jid.EntityBareJid;
|
||||||
* To set a mood, please use one of the {@link #setMood(Mood)} methods. This will publish the users mood to a pubsub
|
* To set a mood, please use one of the {@link #setMood(Mood)} methods. This will publish the users mood to a pubsub
|
||||||
* node.<br>
|
* node.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* In order to get updated about other users moods, register a {@link MoodListener} at
|
* In order to get updated about other users moods, register a {@link PepEventListener} at
|
||||||
* {@link #addMoodListener(MoodListener)}. That listener will get updated about any incoming mood updates of contacts.<br>
|
* {@link #addMoodListener(PepEventListener)}. That listener will get updated about any incoming mood updates of contacts.<br>
|
||||||
* <br>
|
* <br>
|
||||||
* To stop publishing the users mood, refer to {@link #clearMood()}.<br>
|
* To stop publishing the users mood, refer to {@link #clearMood()}.<br>
|
||||||
* <br>
|
* <br>
|
||||||
|
@ -68,39 +57,15 @@ import org.jxmpp.jid.EntityBareJid;
|
||||||
public final class MoodManager extends Manager {
|
public final class MoodManager extends Manager {
|
||||||
|
|
||||||
public static final String MOOD_NODE = "http://jabber.org/protocol/mood";
|
public static final String MOOD_NODE = "http://jabber.org/protocol/mood";
|
||||||
public static final String MOOD_NOTIFY = MOOD_NODE + "+notify";
|
|
||||||
|
|
||||||
private static final Map<XMPPConnection, MoodManager> INSTANCES = new WeakHashMap<>();
|
private static final Map<XMPPConnection, MoodManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
private final Set<MoodListener> moodListeners = new HashSet<>();
|
private final PepManager pepManager;
|
||||||
private final AsyncButOrdered<BareJid> asyncButOrdered = new AsyncButOrdered<>();
|
|
||||||
private PubSubManager pubSubManager;
|
|
||||||
|
|
||||||
private MoodManager(XMPPConnection connection) {
|
private MoodManager(XMPPConnection connection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(MOOD_NOTIFY);
|
|
||||||
PepManager.getInstanceFor(connection).addPepListener(new PepListener() {
|
|
||||||
@Override
|
|
||||||
public void eventReceived(final EntityBareJid from, final EventElement event, final Message message) {
|
|
||||||
if (!MOOD_NODE.equals(event.getEvent().getNode())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final BareJid contact = from.asBareJid();
|
pepManager = PepManager.getInstanceFor(connection);
|
||||||
asyncButOrdered.performAsyncButOrdered(contact, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ItemsExtension items = (ItemsExtension) event.getExtensions().get(0);
|
|
||||||
PayloadItem<?> payload = (PayloadItem<?>) items.getItems().get(0);
|
|
||||||
MoodElement mood = (MoodElement) payload.getPayload();
|
|
||||||
|
|
||||||
for (MoodListener listener : moodListeners) {
|
|
||||||
listener.onMoodUpdated(contact, message, mood);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized MoodManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized MoodManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
@ -147,12 +112,7 @@ public final class MoodManager extends Manager {
|
||||||
private void publishMood(MoodElement moodElement)
|
private void publishMood(MoodElement moodElement)
|
||||||
throws SmackException.NotLoggedInException, InterruptedException, PubSubException.NotALeafNodeException,
|
throws SmackException.NotLoggedInException, InterruptedException, PubSubException.NotALeafNodeException,
|
||||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
if (pubSubManager == null) {
|
pepManager.publish(MOOD_NODE, new PayloadItem<>(moodElement));
|
||||||
pubSubManager = PubSubManager.getInstanceFor(getAuthenticatedConnectionOrThrow(), connection().getUser().asBareJid());
|
|
||||||
}
|
|
||||||
|
|
||||||
LeafNode node = pubSubManager.getOrCreateLeafNode(MOOD_NODE);
|
|
||||||
node.publish(new PayloadItem<>(moodElement));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MoodElement buildMood(Mood mood, MoodConcretisation concretisation, String text) {
|
private static MoodElement buildMood(Mood mood, MoodConcretisation concretisation, String text) {
|
||||||
|
@ -170,11 +130,11 @@ public final class MoodManager extends Manager {
|
||||||
message.addExtension(element);
|
message.addExtension(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void addMoodListener(MoodListener listener) {
|
public boolean addMoodListener(PepEventListener<MoodElement> listener) {
|
||||||
moodListeners.add(listener);
|
return pepManager.addPepEventListener(MOOD_NODE, MoodElement.class, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void removeMoodListener(MoodListener listener) {
|
public boolean removeMoodListener(PepEventListener<MoodElement> listener) {
|
||||||
moodListeners.remove(listener);
|
return pepManager.removePepEventListener(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015 Florian Schmaus
|
* Copyright 2015-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -23,17 +23,18 @@ import java.util.List;
|
||||||
import org.jivesoftware.smack.SmackException.NoResponseException;
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
||||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.muc.MultiUserChatException.MucConfigurationNotSupportedException;
|
import org.jivesoftware.smackx.muc.MultiUserChatException.MucConfigurationNotSupportedException;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FilledForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.Form;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
import org.jxmpp.jid.util.JidUtil;
|
import org.jxmpp.jid.util.JidUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multi-User Chat configuration form manager is used to fill out and submit a {@link Form} used to
|
* Multi-User Chat configuration form manager is used to fill out and submit a {@link FilledForm} used to
|
||||||
* configure rooms.
|
* configure rooms.
|
||||||
* <p>
|
* <p>
|
||||||
* Room configuration needs either be done right after the room is created and still locked. Or at
|
* Room configuration needs either be done right after the room is created and still locked. Or at
|
||||||
|
@ -43,12 +44,17 @@ import org.jxmpp.jid.util.JidUtil;
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* The manager may not provide all possible configuration options. If you want direct access to the
|
* The manager may not provide all possible configuration options. If you want direct access to the
|
||||||
* configuraiton form, use {@link MultiUserChat#getConfigurationForm()} and
|
* configuration form, use {@link MultiUserChat#getConfigurationForm()} and
|
||||||
* {@link MultiUserChat#sendConfigurationForm(Form)}.
|
* {@link MultiUserChat#sendConfigurationForm(FillableForm)}.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class MucConfigFormManager {
|
public class MucConfigFormManager {
|
||||||
/**
|
|
||||||
|
private static final String HASH_ROOMCONFIG = "#roomconfig";
|
||||||
|
|
||||||
|
public static final String FORM_TYPE = MultiUserChatConstants.NAMESPACE + HASH_ROOMCONFIG;
|
||||||
|
|
||||||
|
/**
|
||||||
* The constant String {@value}.
|
* The constant String {@value}.
|
||||||
*
|
*
|
||||||
* @see <a href="http://xmpp.org/extensions/xep-0045.html#owner">XEP-0045 § 10. Owner Use Cases</a>
|
* @see <a href="http://xmpp.org/extensions/xep-0045.html#owner">XEP-0045 § 10. Owner Use Cases</a>
|
||||||
|
@ -73,7 +79,7 @@ public class MucConfigFormManager {
|
||||||
public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret";
|
public static final String MUC_ROOMCONFIG_ROOMSECRET = "muc#roomconfig_roomsecret";
|
||||||
|
|
||||||
private final MultiUserChat multiUserChat;
|
private final MultiUserChat multiUserChat;
|
||||||
private final Form answerForm;
|
private final FillableForm answerForm;
|
||||||
private final List<Jid> owners;
|
private final List<Jid> owners;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,20 +100,13 @@ public class MucConfigFormManager {
|
||||||
|
|
||||||
// Set the answer form
|
// Set the answer form
|
||||||
Form configForm = multiUserChat.getConfigurationForm();
|
Form configForm = multiUserChat.getConfigurationForm();
|
||||||
this.answerForm = configForm.createAnswerForm();
|
this.answerForm = configForm.getFillableForm();
|
||||||
// Add the default answers to the form to submit
|
|
||||||
for (FormField field : configForm.getFields()) {
|
|
||||||
if (field.getType() == FormField.Type.hidden
|
|
||||||
|| StringUtils.isNullOrEmpty(field.getVariable())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
answerForm.setDefaultAnswer(field.getVariable());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the local variables according to the fields found in the answer form
|
// Set the local variables according to the fields found in the answer form
|
||||||
if (answerForm.hasField(MUC_ROOMCONFIG_ROOMOWNERS)) {
|
FormField roomOwnersFormField = answerForm.getDataForm().getField(MUC_ROOMCONFIG_ROOMOWNERS);
|
||||||
|
if (roomOwnersFormField != null) {
|
||||||
// Set 'owners' to the currently configured owners
|
// Set 'owners' to the currently configured owners
|
||||||
List<CharSequence> ownerStrings = answerForm.getField(MUC_ROOMCONFIG_ROOMOWNERS).getValues();
|
List<? extends CharSequence> ownerStrings = roomOwnersFormField.getValues();
|
||||||
owners = new ArrayList<>(ownerStrings.size());
|
owners = new ArrayList<>(ownerStrings.size());
|
||||||
JidUtil.jidsFrom(ownerStrings, owners, null);
|
JidUtil.jidsFrom(ownerStrings, owners, null);
|
||||||
}
|
}
|
||||||
|
@ -244,7 +243,7 @@ public class MucConfigFormManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit the configuration as {@link Form} to the room.
|
* Submit the configuration as {@link FilledForm} to the room.
|
||||||
*
|
*
|
||||||
* @throws NoResponseException if there was no response from the room.
|
* @throws NoResponseException if there was no response from the room.
|
||||||
* @throws XMPPErrorException if there was an XMPP error returned.
|
* @throws XMPPErrorException if there was an XMPP error returned.
|
||||||
|
|
|
@ -133,7 +133,7 @@ public final class MucEnterConfiguration {
|
||||||
*
|
*
|
||||||
* @param presenceBuilderConsumer a consumer which will be passed the presence build.
|
* @param presenceBuilderConsumer a consumer which will be passed the presence build.
|
||||||
* @return a reference to this builder.
|
* @return a reference to this builder.
|
||||||
* @since 4.5
|
* @since 4.4.0
|
||||||
*/
|
*/
|
||||||
public Builder withPresence(Consumer<? super PresenceBuilder> presenceBuilderConsumer) {
|
public Builder withPresence(Consumer<? super PresenceBuilder> presenceBuilderConsumer) {
|
||||||
presenceBuilderConsumer.accept(joinPresenceBuilder);
|
presenceBuilderConsumer.accept(joinPresenceBuilder);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software.
|
* Copyright 2003-2007 Jive Software. 2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -75,8 +75,10 @@ import org.jivesoftware.smackx.muc.packet.MUCItem;
|
||||||
import org.jivesoftware.smackx.muc.packet.MUCOwner;
|
import org.jivesoftware.smackx.muc.packet.MUCOwner;
|
||||||
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
||||||
import org.jivesoftware.smackx.muc.packet.MUCUser.Status;
|
import org.jivesoftware.smackx.muc.packet.MUCUser.Status;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.TextSingleFormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.Form;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.DomainBareJid;
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
|
@ -149,7 +151,6 @@ public class MultiUserChat {
|
||||||
|
|
||||||
private String subject;
|
private String subject;
|
||||||
private EntityFullJid myRoomJid;
|
private EntityFullJid myRoomJid;
|
||||||
private boolean joined = false;
|
|
||||||
private StanzaCollector messageCollector;
|
private StanzaCollector messageCollector;
|
||||||
|
|
||||||
MultiUserChat(XMPPConnection connection, EntityBareJid room, MultiUserChatManager multiUserChatManager) {
|
MultiUserChat(XMPPConnection connection, EntityBareJid room, MultiUserChatManager multiUserChatManager) {
|
||||||
|
@ -233,10 +234,13 @@ public class MultiUserChat {
|
||||||
occupantsMap.remove(from);
|
occupantsMap.remove(from);
|
||||||
MUCUser mucUser = MUCUser.from(packet);
|
MUCUser mucUser = MUCUser.from(packet);
|
||||||
if (mucUser != null && mucUser.hasStatus()) {
|
if (mucUser != null && mucUser.hasStatus()) {
|
||||||
|
if (isUserStatusModification) {
|
||||||
|
userHasLeft();
|
||||||
|
}
|
||||||
// Fire events according to the received presence code
|
// Fire events according to the received presence code
|
||||||
checkPresenceCode(
|
checkPresenceCode(
|
||||||
mucUser.getStatus(),
|
mucUser.getStatus(),
|
||||||
presence.getFrom().equals(myRoomJID),
|
isUserStatusModification,
|
||||||
mucUser,
|
mucUser,
|
||||||
from);
|
from);
|
||||||
} else {
|
} else {
|
||||||
|
@ -246,6 +250,27 @@ public class MultiUserChat {
|
||||||
listener.left(from);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UserStatusListener listener : userStatusListeners) {
|
||||||
|
listener.roomDestroyed(alternateMuc, destroy.getReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isUserStatusModification) {
|
||||||
|
for (UserStatusListener listener : userStatusListeners) {
|
||||||
|
listener.removed(mucUser, presence);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -383,8 +408,6 @@ public class MultiUserChat {
|
||||||
Resourcepart receivedNickname = presence.getFrom().getResourceOrThrow();
|
Resourcepart receivedNickname = presence.getFrom().getResourceOrThrow();
|
||||||
setNickname(receivedNickname);
|
setNickname(receivedNickname);
|
||||||
|
|
||||||
joined = true;
|
|
||||||
|
|
||||||
// Update the list of joined rooms
|
// Update the list of joined rooms
|
||||||
multiUserChatManager.addJoinedRoom(room);
|
multiUserChatManager.addJoinedRoom(room);
|
||||||
return presence;
|
return presence;
|
||||||
|
@ -436,7 +459,7 @@ public class MultiUserChat {
|
||||||
public synchronized MucCreateConfigFormHandle create(Resourcepart nickname) throws NoResponseException,
|
public synchronized MucCreateConfigFormHandle create(Resourcepart nickname) throws NoResponseException,
|
||||||
XMPPErrorException, InterruptedException, MucAlreadyJoinedException,
|
XMPPErrorException, InterruptedException, MucAlreadyJoinedException,
|
||||||
NotConnectedException, MissingMucCreationAcknowledgeException, NotAMucServiceException {
|
NotConnectedException, MissingMucCreationAcknowledgeException, NotAMucServiceException {
|
||||||
if (joined) {
|
if (isJoined()) {
|
||||||
throw new MucAlreadyJoinedException();
|
throw new MucAlreadyJoinedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,7 +514,7 @@ public class MultiUserChat {
|
||||||
*/
|
*/
|
||||||
public synchronized MucCreateConfigFormHandle createOrJoin(MucEnterConfiguration mucEnterConfiguration)
|
public synchronized MucCreateConfigFormHandle createOrJoin(MucEnterConfiguration mucEnterConfiguration)
|
||||||
throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException {
|
throws NoResponseException, XMPPErrorException, InterruptedException, MucAlreadyJoinedException, NotConnectedException, NotAMucServiceException {
|
||||||
if (joined) {
|
if (isJoined()) {
|
||||||
throw new MucAlreadyJoinedException();
|
throw new MucAlreadyJoinedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,8 +535,8 @@ public class MultiUserChat {
|
||||||
* instant room, use {@link #makeInstant()}.
|
* instant room, use {@link #makeInstant()}.
|
||||||
* <p>
|
* <p>
|
||||||
* For advanced configuration options, use {@link MultiUserChat#getConfigurationForm()}, get the answer form with
|
* For advanced configuration options, use {@link MultiUserChat#getConfigurationForm()}, get the answer form with
|
||||||
* {@link Form#createAnswerForm()}, fill it out and send it back to the room with
|
* {@link Form#getFillableForm()}, fill it out and send it back to the room with
|
||||||
* {@link MultiUserChat#sendConfigurationForm(Form)}.
|
* {@link MultiUserChat#sendConfigurationForm(FillableForm)}.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class MucCreateConfigFormHandle {
|
public class MucCreateConfigFormHandle {
|
||||||
|
@ -531,7 +554,7 @@ public class MultiUserChat {
|
||||||
*/
|
*/
|
||||||
public void makeInstant() throws NoResponseException, XMPPErrorException, NotConnectedException,
|
public void makeInstant() throws NoResponseException, XMPPErrorException, NotConnectedException,
|
||||||
InterruptedException {
|
InterruptedException {
|
||||||
sendConfigurationForm(new Form(DataForm.Type.submit));
|
sendConfigurationForm(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -662,7 +685,7 @@ public class MultiUserChat {
|
||||||
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotAMucServiceException {
|
throws XMPPErrorException, NoResponseException, NotConnectedException, InterruptedException, NotAMucServiceException {
|
||||||
// If we've already joined the room, leave it before joining under a new
|
// If we've already joined the room, leave it before joining under a new
|
||||||
// nickname.
|
// nickname.
|
||||||
if (joined) {
|
if (isJoined()) {
|
||||||
try {
|
try {
|
||||||
leaveSync();
|
leaveSync();
|
||||||
}
|
}
|
||||||
|
@ -680,7 +703,7 @@ public class MultiUserChat {
|
||||||
* @return true if currently in the multi user chat room.
|
* @return true if currently in the multi user chat room.
|
||||||
*/
|
*/
|
||||||
public boolean isJoined() {
|
public boolean isJoined() {
|
||||||
return joined;
|
return myRoomJid != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -716,10 +739,6 @@ public class MultiUserChat {
|
||||||
// "if (!joined) return" because it should be always be possible to leave the room in case the instance's
|
// "if (!joined) return" because it should be always be possible to leave the room in case the instance's
|
||||||
// state does not reflect the actual state.
|
// state does not reflect the actual state.
|
||||||
|
|
||||||
// Reset occupant information first so that we are assume that we left the room even if sendStanza() would
|
|
||||||
// throw.
|
|
||||||
userHasLeft();
|
|
||||||
|
|
||||||
final EntityFullJid myRoomJid = this.myRoomJid;
|
final EntityFullJid myRoomJid = this.myRoomJid;
|
||||||
if (myRoomJid == null) {
|
if (myRoomJid == null) {
|
||||||
throw new MucNotJoinedException(this);
|
throw new MucNotJoinedException(this);
|
||||||
|
@ -741,6 +760,10 @@ public class MultiUserChat {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 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();
|
Presence reflectedLeavePresence = connection.createStanzaCollectorAndSend(reflectedLeavePresenceFilter, leavePresence).nextResultOrThrow();
|
||||||
|
|
||||||
return reflectedLeavePresence;
|
return reflectedLeavePresence;
|
||||||
|
@ -783,7 +806,8 @@ public class MultiUserChat {
|
||||||
iq.setType(IQ.Type.get);
|
iq.setType(IQ.Type.get);
|
||||||
|
|
||||||
IQ answer = connection.createStanzaCollectorAndSend(iq).nextResultOrThrow();
|
IQ answer = connection.createStanzaCollectorAndSend(iq).nextResultOrThrow();
|
||||||
return Form.getFormFrom(answer);
|
DataForm dataForm = DataForm.from(answer, MucConfigFormManager.FORM_TYPE);
|
||||||
|
return new Form(dataForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -796,11 +820,19 @@ public class MultiUserChat {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public void sendConfigurationForm(Form form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public void sendConfigurationForm(FillableForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
final DataForm dataForm;
|
||||||
|
if (form != null) {
|
||||||
|
dataForm = form.getDataFormToSubmit();
|
||||||
|
} else {
|
||||||
|
// Instant room, cf. XEP-0045 § 10.1.2
|
||||||
|
dataForm = DataForm.builder().build();
|
||||||
|
}
|
||||||
|
|
||||||
MUCOwner iq = new MUCOwner();
|
MUCOwner iq = new MUCOwner();
|
||||||
iq.setTo(room);
|
iq.setTo(room);
|
||||||
iq.setType(IQ.Type.set);
|
iq.setType(IQ.Type.set);
|
||||||
iq.addExtension(form.getDataFormToSend());
|
iq.addExtension(dataForm);
|
||||||
|
|
||||||
connection.createStanzaCollectorAndSend(iq).nextResultOrThrow();
|
connection.createStanzaCollectorAndSend(iq).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
@ -828,7 +860,8 @@ public class MultiUserChat {
|
||||||
reg.setTo(room);
|
reg.setTo(room);
|
||||||
|
|
||||||
IQ result = connection.createStanzaCollectorAndSend(reg).nextResultOrThrow();
|
IQ result = connection.createStanzaCollectorAndSend(reg).nextResultOrThrow();
|
||||||
return Form.getFormFrom(result);
|
DataForm dataForm = DataForm.from(result);
|
||||||
|
return new Form(dataForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -848,11 +881,11 @@ public class MultiUserChat {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public void sendRegistrationForm(Form form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public void sendRegistrationForm(FillableForm form) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
Registration reg = new Registration();
|
Registration reg = new Registration();
|
||||||
reg.setType(IQ.Type.set);
|
reg.setType(IQ.Type.set);
|
||||||
reg.setTo(room);
|
reg.setTo(room);
|
||||||
reg.addExtension(form.getDataFormToSend());
|
reg.addExtension(form.getDataFormToSubmit());
|
||||||
|
|
||||||
connection.createStanzaCollectorAndSend(reg).nextResultOrThrow();
|
connection.createStanzaCollectorAndSend(reg).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1168,7 @@ public class MultiUserChat {
|
||||||
Objects.requireNonNull(nickname, "Nickname must not be null or blank.");
|
Objects.requireNonNull(nickname, "Nickname must not be null or blank.");
|
||||||
// Check that we already have joined the room before attempting to change the
|
// Check that we already have joined the room before attempting to change the
|
||||||
// nickname.
|
// nickname.
|
||||||
if (!joined) {
|
if (!isJoined()) {
|
||||||
throw new MucNotJoinedException(this);
|
throw new MucNotJoinedException(this);
|
||||||
}
|
}
|
||||||
final EntityFullJid jid = JidCreate.entityFullFrom(room, nickname);
|
final EntityFullJid jid = JidCreate.entityFullFrom(room, nickname);
|
||||||
|
@ -1178,11 +1211,6 @@ public class MultiUserChat {
|
||||||
throw new MucNotJoinedException(this);
|
throw new MucNotJoinedException(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that we already have joined the room before attempting to change the
|
|
||||||
// availability status.
|
|
||||||
if (!joined) {
|
|
||||||
throw new MucNotJoinedException(this);
|
|
||||||
}
|
|
||||||
// We change the availability status by sending a presence packet to the room with the
|
// We change the availability status by sending a presence packet to the room with the
|
||||||
// new presence status and mode
|
// new presence status and mode
|
||||||
Presence joinPresence = connection.getStanzaFactory().buildPresenceStanza()
|
Presence joinPresence = connection.getStanzaFactory().buildPresenceStanza()
|
||||||
|
@ -1231,19 +1259,17 @@ public class MultiUserChat {
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public void requestVoice() throws NotConnectedException, InterruptedException {
|
public void requestVoice() throws NotConnectedException, InterruptedException {
|
||||||
DataForm form = new DataForm(DataForm.Type.submit);
|
DataForm.Builder form = DataForm.builder()
|
||||||
FormField.Builder formTypeField = FormField.builder(FormField.FORM_TYPE);
|
.setFormType(MUCInitialPresence.NAMESPACE + "#request");
|
||||||
formTypeField.addValue(MUCInitialPresence.NAMESPACE + "#request");
|
|
||||||
form.addField(formTypeField.build());
|
TextSingleFormField.Builder requestVoiceField = FormField.textSingleBuilder("muc#role");
|
||||||
FormField.Builder requestVoiceField = FormField.builder("muc#role");
|
|
||||||
requestVoiceField.setType(FormField.Type.text_single);
|
|
||||||
requestVoiceField.setLabel("Requested role");
|
requestVoiceField.setLabel("Requested role");
|
||||||
requestVoiceField.addValue("participant");
|
requestVoiceField.setValue("participant");
|
||||||
form.addField(requestVoiceField.build());
|
form.addField(requestVoiceField.build());
|
||||||
|
|
||||||
Message message = connection.getStanzaFactory().buildMessageStanza()
|
Message message = connection.getStanzaFactory().buildMessageStanza()
|
||||||
.to(room)
|
.to(room)
|
||||||
.addExtension(form)
|
.addExtension(form.build())
|
||||||
.build();
|
.build();
|
||||||
connection.sendStanza(message);
|
connection.sendStanza(message);
|
||||||
}
|
}
|
||||||
|
@ -2119,7 +2145,7 @@ public class MultiUserChat {
|
||||||
// to call leave() in order to resync the state. And leave() requires the nickname to send the unsubscribe
|
// to call leave() in order to resync the state. And leave() requires the nickname to send the unsubscribe
|
||||||
// presence.
|
// presence.
|
||||||
occupantsMap.clear();
|
occupantsMap.clear();
|
||||||
joined = false;
|
myRoomJid = null;
|
||||||
// Update the list of joined rooms
|
// Update the list of joined rooms
|
||||||
multiUserChatManager.removeJoinedRoom(room);
|
multiUserChatManager.removeJoinedRoom(room);
|
||||||
removeConnectionCallbacks();
|
removeConnectionCallbacks();
|
||||||
|
@ -2437,9 +2463,6 @@ public class MultiUserChat {
|
||||||
if (statusCodes.contains(Status.KICKED_307)) {
|
if (statusCodes.contains(Status.KICKED_307)) {
|
||||||
// Check if this occupant was kicked
|
// Check if this occupant was kicked
|
||||||
if (isUserModification) {
|
if (isUserModification) {
|
||||||
// Reset occupant information.
|
|
||||||
userHasLeft();
|
|
||||||
|
|
||||||
for (UserStatusListener listener : userStatusListeners) {
|
for (UserStatusListener listener : userStatusListeners) {
|
||||||
listener.kicked(mucUser.getItem().getActor(), mucUser.getItem().getReason());
|
listener.kicked(mucUser.getItem().getActor(), mucUser.getItem().getReason());
|
||||||
}
|
}
|
||||||
|
@ -2454,15 +2477,9 @@ public class MultiUserChat {
|
||||||
if (statusCodes.contains(Status.BANNED_301)) {
|
if (statusCodes.contains(Status.BANNED_301)) {
|
||||||
// Check if this occupant was banned
|
// Check if this occupant was banned
|
||||||
if (isUserModification) {
|
if (isUserModification) {
|
||||||
joined = false;
|
|
||||||
for (UserStatusListener listener : userStatusListeners) {
|
for (UserStatusListener listener : userStatusListeners) {
|
||||||
listener.banned(mucUser.getItem().getActor(), mucUser.getItem().getReason());
|
listener.banned(mucUser.getItem().getActor(), mucUser.getItem().getReason());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset occupant information.
|
|
||||||
occupantsMap.clear();
|
|
||||||
myRoomJid = null;
|
|
||||||
userHasLeft();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (ParticipantStatusListener listener : participantStatusListeners) {
|
for (ParticipantStatusListener listener : participantStatusListeners) {
|
||||||
|
@ -2474,15 +2491,9 @@ public class MultiUserChat {
|
||||||
if (statusCodes.contains(Status.REMOVED_AFFIL_CHANGE_321)) {
|
if (statusCodes.contains(Status.REMOVED_AFFIL_CHANGE_321)) {
|
||||||
// Check if this occupant's membership was revoked
|
// Check if this occupant's membership was revoked
|
||||||
if (isUserModification) {
|
if (isUserModification) {
|
||||||
joined = false;
|
|
||||||
for (UserStatusListener listener : userStatusListeners) {
|
for (UserStatusListener listener : userStatusListeners) {
|
||||||
listener.membershipRevoked();
|
listener.membershipRevoked();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset occupant information.
|
|
||||||
occupantsMap.clear();
|
|
||||||
myRoomJid = null;
|
|
||||||
userHasLeft();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A occupant has changed his nickname in the room
|
// A occupant has changed his nickname in the room
|
||||||
|
@ -2491,18 +2502,6 @@ public class MultiUserChat {
|
||||||
listener.nicknameChanged(from, mucUser.getItem().getNick());
|
listener.nicknameChanged(from, mucUser.getItem().getNick());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The room has been destroyed.
|
|
||||||
if (mucUser.getDestroy() != null) {
|
|
||||||
MultiUserChat alternateMUC = multiUserChatManager.getMultiUserChat(mucUser.getDestroy().getJid());
|
|
||||||
for (UserStatusListener listener : userStatusListeners) {
|
|
||||||
listener.roomDestroyed(alternateMUC, mucUser.getDestroy().getReason());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset occupant information.
|
|
||||||
occupantsMap.clear();
|
|
||||||
myRoomJid = null;
|
|
||||||
userHasLeft();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Aditya Borikar.
|
* Copyright 2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,13 +14,10 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.geoloc;
|
package org.jivesoftware.smackx.muc;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
public class MultiUserChatConstants {
|
||||||
import org.jivesoftware.smackx.geoloc.packet.GeoLocation;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
public static final String NAMESPACE = "http://jabber.org/protocol/muc";
|
||||||
|
|
||||||
public interface GeoLocationListener {
|
|
||||||
void onGeoLocationUpdated(BareJid jid, GeoLocation geoLocation, Message message);
|
|
||||||
}
|
}
|
|
@ -25,8 +25,8 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
@ -130,7 +130,7 @@ public class RoomInfo {
|
||||||
/**
|
/**
|
||||||
* The rooms extended configuration form;
|
* The rooms extended configuration form;
|
||||||
*/
|
*/
|
||||||
private final Form form;
|
private final DataForm form;
|
||||||
|
|
||||||
RoomInfo(DiscoverInfo info) {
|
RoomInfo(DiscoverInfo info) {
|
||||||
final Jid from = info.getFrom();
|
final Jid from = info.getFrom();
|
||||||
|
@ -166,7 +166,7 @@ public class RoomInfo {
|
||||||
URL logs = null;
|
URL logs = null;
|
||||||
String pubsub = null;
|
String pubsub = null;
|
||||||
// Get the information based on the discovered extended information
|
// Get the information based on the discovered extended information
|
||||||
form = Form.getFormFrom(info);
|
form = DataForm.from(info);
|
||||||
if (form != null) {
|
if (form != null) {
|
||||||
FormField descField = form.getField("muc#roominfo_description");
|
FormField descField = form.getField("muc#roominfo_description");
|
||||||
if (descField != null && !descField.getValues().isEmpty()) {
|
if (descField != null && !descField.getValues().isEmpty()) {
|
||||||
|
@ -191,7 +191,7 @@ public class RoomInfo {
|
||||||
|
|
||||||
FormField contactJidField = form.getField("muc#roominfo_contactjid");
|
FormField contactJidField = form.getField("muc#roominfo_contactjid");
|
||||||
if (contactJidField != null && !contactJidField.getValues().isEmpty()) {
|
if (contactJidField != null && !contactJidField.getValues().isEmpty()) {
|
||||||
List<CharSequence> contactJidValues = contactJidField.getValues();
|
List<? extends CharSequence> contactJidValues = contactJidField.getValues();
|
||||||
contactJid = JidUtil.filterEntityBareJidList(JidUtil.jidSetFrom(contactJidValues));
|
contactJid = JidUtil.filterEntityBareJidList(JidUtil.jidSetFrom(contactJidValues));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +420,7 @@ public class RoomInfo {
|
||||||
* href="http://xmpp.org/extensions/xep-0045.html#disco-roominfo">XEP-45:
|
* href="http://xmpp.org/extensions/xep-0045.html#disco-roominfo">XEP-45:
|
||||||
* Multi User Chat - 6.5 Querying for Room Information</a>
|
* Multi User Chat - 6.5 Querying for Room Information</a>
|
||||||
*/
|
*/
|
||||||
public Form getForm() {
|
public DataForm getForm() {
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,21 @@
|
||||||
|
|
||||||
package org.jivesoftware.smackx.muc;
|
package org.jivesoftware.smackx.muc;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener that is fired anytime your participant's status in a room is changed, such as the
|
* A listener that is fired anytime your participant's status in a room is changed, such as the user being kicked,
|
||||||
* user being kicked, banned, or granted admin permissions or the room is destroyed.
|
* banned, or granted admin permissions or the room is destroyed.
|
||||||
|
* <p>
|
||||||
|
* Note that the methods {@link #kicked(Jid, String)}, {@link #banned(Jid, String)} and
|
||||||
|
* {@link #roomDestroyed(MultiUserChat, String)} will be called before the generic {@link #removed(MUCUser, Presence)}
|
||||||
|
* callback will be invoked. The generic {@link #removed(MUCUser, Presence)} callback will be invoked every time the user
|
||||||
|
* was removed from the MUC involuntarily. It is hence the recommended callback to listen for and act upon.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Gaston Dombiak
|
* @author Gaston Dombiak
|
||||||
*/
|
*/
|
||||||
|
@ -33,6 +43,7 @@ public interface UserStatusListener {
|
||||||
*
|
*
|
||||||
* @param actor the moderator that kicked your user from the room (e.g. user@host.org).
|
* @param actor the moderator that kicked your user from the room (e.g. user@host.org).
|
||||||
* @param reason the reason provided by the actor to kick you from the room.
|
* @param reason the reason provided by the actor to kick you from the room.
|
||||||
|
* @see #removed(MUCUser, Presence)
|
||||||
*/
|
*/
|
||||||
void kicked(Jid actor, String reason);
|
void kicked(Jid actor, String reason);
|
||||||
|
|
||||||
|
@ -58,10 +69,21 @@ public interface UserStatusListener {
|
||||||
*
|
*
|
||||||
* @param actor the administrator that banned your user (e.g. user@host.org).
|
* @param actor the administrator that banned your user (e.g. user@host.org).
|
||||||
* @param reason the reason provided by the administrator to banned you.
|
* @param reason the reason provided by the administrator to banned you.
|
||||||
|
* @see #removed(MUCUser, Presence)
|
||||||
*/
|
*/
|
||||||
void banned(Jid actor, String reason);
|
void banned(Jid actor, String reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Called when a user is involuntarily removed from the room.
|
||||||
|
*
|
||||||
|
* @param mucUser the optional muc#user extension element
|
||||||
|
* @param presence the carrier presence
|
||||||
|
* @since 4.4.0
|
||||||
|
*/
|
||||||
|
default void removed(MUCUser mucUser, Presence presence) {
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
* Called when an administrator grants your user membership to the room. This means that you
|
* Called when an administrator grants your user membership to the room. This means that you
|
||||||
* will be able to join the members-only room.
|
* will be able to join the members-only room.
|
||||||
*
|
*
|
||||||
|
@ -128,6 +150,7 @@ public interface UserStatusListener {
|
||||||
*
|
*
|
||||||
* @param alternateMUC an alternate MultiUserChat, may be null.
|
* @param alternateMUC an alternate MultiUserChat, may be null.
|
||||||
* @param reason the reason why the room was closed, may be null.
|
* @param reason the reason why the room was closed, may be null.
|
||||||
|
* @see #removed(MUCUser, Presence)
|
||||||
*/
|
*/
|
||||||
void roomDestroyed(MultiUserChat alternateMUC, String reason);
|
void roomDestroyed(MultiUserChat alternateMUC, String reason);
|
||||||
|
|
||||||
|
|
|
@ -410,6 +410,7 @@ public class MUCUser implements ExtensionElement {
|
||||||
public static final Status NEW_NICKNAME_303 = Status.create(303);
|
public static final Status NEW_NICKNAME_303 = Status.create(303);
|
||||||
public static final Status KICKED_307 = Status.create(307);
|
public static final Status KICKED_307 = Status.create(307);
|
||||||
public static final Status REMOVED_AFFIL_CHANGE_321 = Status.create(321);
|
public static final Status REMOVED_AFFIL_CHANGE_321 = Status.create(321);
|
||||||
|
public static final Status REMOVED_FOR_TECHNICAL_REASONS_333 = Status.create(333);
|
||||||
|
|
||||||
private final Integer code;
|
private final Integer code;
|
||||||
|
|
||||||
|
@ -419,10 +420,14 @@ public class MUCUser implements ExtensionElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Status create(Integer i) {
|
public static Status create(Integer i) {
|
||||||
Status status = statusMap.get(i);
|
Status status;
|
||||||
if (status == null) {
|
// TODO: Use computeIfAbsent once Smack's minimum required Android SDK level is 24 or higher.
|
||||||
status = new Status(i);
|
synchronized (statusMap) {
|
||||||
statusMap.put(i, status);
|
status = statusMap.get(i);
|
||||||
|
if (status == null) {
|
||||||
|
status = new Status(i);
|
||||||
|
statusMap.put(i, status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software.
|
* Copyright 2003-2007 Jive Software, 2020 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -43,7 +43,7 @@ import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
import org.jivesoftware.smackx.offline.packet.OfflineMessageInfo;
|
import org.jivesoftware.smackx.offline.packet.OfflineMessageInfo;
|
||||||
import org.jivesoftware.smackx.offline.packet.OfflineMessageRequest;
|
import org.jivesoftware.smackx.offline.packet.OfflineMessageRequest;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The OfflineMessageManager helps manage offline messages even before the user has sent an
|
* The OfflineMessageManager helps manage offline messages even before the user has sent an
|
||||||
|
@ -115,12 +115,12 @@ public final class OfflineMessageManager extends Manager {
|
||||||
*/
|
*/
|
||||||
public int getMessageCount() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public int getMessageCount() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
DiscoverInfo info = serviceDiscoveryManager.discoverInfo(null, namespace);
|
DiscoverInfo info = serviceDiscoveryManager.discoverInfo(null, namespace);
|
||||||
Form extendedInfo = Form.getFormFrom(info);
|
DataForm dataForm = DataForm.from(info, namespace);
|
||||||
if (extendedInfo != null) {
|
if (dataForm == null) {
|
||||||
String value = extendedInfo.getField("number_of_messages").getFirstValue();
|
return 0;
|
||||||
return Integer.parseInt(value);
|
|
||||||
}
|
}
|
||||||
return 0;
|
String numberOfMessagesString = dataForm.getField("number_of_messages").getFirstValue();
|
||||||
|
return Integer.parseInt(numberOfMessagesString);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2019 Aditya Borikar.
|
* Copyright 2020 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,15 +14,15 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.usertune;
|
package org.jivesoftware.smackx.pep;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.usertune.element.UserTuneElement;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
public interface PepEventListener<E extends ExtensionElement> {
|
||||||
|
|
||||||
public interface UserTuneListener {
|
void onPepEvent(EntityBareJid from, E event, String id, Message carrierMessage);
|
||||||
|
|
||||||
void onUserTuneUpdated(BareJid jid, Message message, UserTuneElement userTuneElement);
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2015-2019 Florian Schmaus
|
* Copyright 2003-2007 Jive Software, 2015-2020 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,10 +17,13 @@
|
||||||
|
|
||||||
package org.jivesoftware.smackx.pep;
|
package org.jivesoftware.smackx.pep;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.AsyncButOrdered;
|
import org.jivesoftware.smack.AsyncButOrdered;
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
|
@ -30,20 +33,26 @@ import org.jivesoftware.smack.StanzaListener;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
||||||
import org.jivesoftware.smack.filter.AndFilter;
|
import org.jivesoftware.smack.filter.AndFilter;
|
||||||
|
import org.jivesoftware.smack.filter.MessageTypeFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
import org.jivesoftware.smack.filter.jidtype.AbstractJidTypeFilter.JidType;
|
|
||||||
import org.jivesoftware.smack.filter.jidtype.FromJidTypeFilter;
|
import org.jivesoftware.smack.filter.jidtype.FromJidTypeFilter;
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
import org.jivesoftware.smack.util.CollectionUtil;
|
||||||
|
import org.jivesoftware.smack.util.MultiMap;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.pubsub.EventElement;
|
import org.jivesoftware.smackx.pubsub.EventElement;
|
||||||
import org.jivesoftware.smackx.pubsub.Item;
|
import org.jivesoftware.smackx.pubsub.Item;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ItemsExtension;
|
||||||
import org.jivesoftware.smackx.pubsub.LeafNode;
|
import org.jivesoftware.smackx.pubsub.LeafNode;
|
||||||
|
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
|
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubFeature;
|
import org.jivesoftware.smackx.pubsub.PubSubFeature;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
||||||
import org.jivesoftware.smackx.pubsub.filter.EventExtensionFilter;
|
import org.jivesoftware.smackx.pubsub.filter.EventItemsExtensionFilter;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
@ -70,6 +79,8 @@ import org.jxmpp.jid.EntityBareJid;
|
||||||
*/
|
*/
|
||||||
public final class PepManager extends Manager {
|
public final class PepManager extends Manager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(PepManager.class.getName());
|
||||||
|
|
||||||
private static final Map<XMPPConnection, PepManager> INSTANCES = new WeakHashMap<>();
|
private static final Map<XMPPConnection, PepManager> INSTANCES = new WeakHashMap<>();
|
||||||
|
|
||||||
public static synchronized PepManager getInstanceFor(XMPPConnection connection) {
|
public static synchronized PepManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
@ -81,16 +92,25 @@ public final class PepManager extends Manager {
|
||||||
return pepManager;
|
return pepManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final StanzaFilter FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER = new AndFilter(
|
// TODO: Ideally PepManager would re-use PubSubManager for this. But the functionality in PubSubManager does not yet
|
||||||
new FromJidTypeFilter(JidType.BareJid),
|
// exist.
|
||||||
EventExtensionFilter.INSTANCE);
|
private static final StanzaFilter PEP_EVENTS_FILTER = new AndFilter(
|
||||||
|
MessageTypeFilter.NORMAL_OR_HEADLINE,
|
||||||
|
FromJidTypeFilter.ENTITY_BARE_JID,
|
||||||
|
EventItemsExtensionFilter.INSTANCE);
|
||||||
|
|
||||||
private final Set<PepListener> pepListeners = new CopyOnWriteArraySet<>();
|
private final Set<PepListener> pepListeners = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
private final AsyncButOrdered<EntityBareJid> asyncButOrdered = new AsyncButOrdered<>();
|
private final AsyncButOrdered<EntityBareJid> asyncButOrdered = new AsyncButOrdered<>();
|
||||||
|
|
||||||
|
private final ServiceDiscoveryManager serviceDiscoveryManager;
|
||||||
|
|
||||||
private final PubSubManager pepPubSubManager;
|
private final PubSubManager pepPubSubManager;
|
||||||
|
|
||||||
|
private final MultiMap<String, PepEventListenerCoupling<? extends ExtensionElement>> pepEventListeners = new MultiMap<>();
|
||||||
|
|
||||||
|
private final Map<PepEventListener<?>, PepEventListenerCoupling<?>> listenerToCouplingMap = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new PEP exchange manager.
|
* Creates a new PEP exchange manager.
|
||||||
*
|
*
|
||||||
|
@ -98,6 +118,10 @@ public final class PepManager extends Manager {
|
||||||
*/
|
*/
|
||||||
private PepManager(XMPPConnection connection) {
|
private PepManager(XMPPConnection connection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
|
|
||||||
|
serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
|
pepPubSubManager = PubSubManager.getInstanceFor(connection, null);
|
||||||
|
|
||||||
StanzaListener packetListener = new StanzaListener() {
|
StanzaListener packetListener = new StanzaListener() {
|
||||||
@Override
|
@Override
|
||||||
public void processStanza(Stanza stanza) {
|
public void processStanza(Stanza stanza) {
|
||||||
|
@ -106,20 +130,118 @@ public final class PepManager extends Manager {
|
||||||
assert event != null;
|
assert event != null;
|
||||||
final EntityBareJid from = message.getFrom().asEntityBareJidIfPossible();
|
final EntityBareJid from = message.getFrom().asEntityBareJidIfPossible();
|
||||||
assert from != null;
|
assert from != null;
|
||||||
|
|
||||||
asyncButOrdered.performAsyncButOrdered(from, new Runnable() {
|
asyncButOrdered.performAsyncButOrdered(from, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
ItemsExtension itemsExtension = (ItemsExtension) event.getEvent();
|
||||||
|
String node = itemsExtension.getNode();
|
||||||
|
|
||||||
for (PepListener listener : pepListeners) {
|
for (PepListener listener : pepListeners) {
|
||||||
listener.eventReceived(from, event, message);
|
listener.eventReceived(from, event, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<PepEventListenerCoupling<? extends ExtensionElement>> nodeListeners;
|
||||||
|
synchronized (pepEventListeners) {
|
||||||
|
nodeListeners = pepEventListeners.getAll(node);
|
||||||
|
if (nodeListeners.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a copy of the list. Note that it is important to do this within the synchronized
|
||||||
|
// block.
|
||||||
|
nodeListeners = CollectionUtil.newListWith(nodeListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PepEventListenerCoupling<? extends ExtensionElement> listener : nodeListeners) {
|
||||||
|
// TODO: Can there be more than one item?
|
||||||
|
List<? extends NamedElement> items = itemsExtension.getItems();
|
||||||
|
for (NamedElement namedElementItem : items) {
|
||||||
|
Item item = (Item) namedElementItem;
|
||||||
|
String id = item.getId();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
PayloadItem<ExtensionElement> payloadItem = (PayloadItem<ExtensionElement>) item;
|
||||||
|
ExtensionElement payload = payloadItem.getPayload();
|
||||||
|
|
||||||
|
listener.invoke(from, payload, id, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// TODO Add filter to check if from supports PubSub as per xep163 2 2.4
|
// TODO Add filter to check if from supports PubSub as per xep163 2 2.4
|
||||||
connection.addSyncStanzaListener(packetListener, FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER);
|
connection.addSyncStanzaListener(packetListener, PEP_EVENTS_FILTER);
|
||||||
|
}
|
||||||
|
|
||||||
pepPubSubManager = PubSubManager.getInstanceFor(connection, null);
|
private static final class PepEventListenerCoupling<E extends ExtensionElement> {
|
||||||
|
private final String node;
|
||||||
|
private final Class<E> extensionElementType;
|
||||||
|
private final PepEventListener<E> pepEventListener;
|
||||||
|
|
||||||
|
private PepEventListenerCoupling(String node, Class<E> extensionElementType,
|
||||||
|
PepEventListener<E> pepEventListener) {
|
||||||
|
this.node = node;
|
||||||
|
this.extensionElementType = extensionElementType;
|
||||||
|
this.pepEventListener = pepEventListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invoke(EntityBareJid from, ExtensionElement payload, String id, Message carrierMessage) {
|
||||||
|
if (!extensionElementType.isInstance(payload)) {
|
||||||
|
LOGGER.warning("Ignoring " + payload + " from " + carrierMessage + " as it is not of type "
|
||||||
|
+ extensionElementType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
E extensionElementPayload = extensionElementType.cast(payload);
|
||||||
|
pepEventListener.onPepEvent(from, extensionElementPayload, id, carrierMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends ExtensionElement> boolean addPepEventListener(String node, Class<E> extensionElementType,
|
||||||
|
PepEventListener<E> pepEventListener) {
|
||||||
|
PepEventListenerCoupling<E> pepEventListenerCoupling = new PepEventListenerCoupling<>(node,
|
||||||
|
extensionElementType, pepEventListener);
|
||||||
|
|
||||||
|
synchronized (pepEventListeners) {
|
||||||
|
if (listenerToCouplingMap.containsKey(pepEventListener)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
listenerToCouplingMap.put(pepEventListener, pepEventListenerCoupling);
|
||||||
|
/*
|
||||||
|
* TODO: Replace the above with the below using putIfAbsent() if Smack's minimum required Android SDK level
|
||||||
|
* is 24 or higher. PepEventListenerCoupling<?> currentPepEventListenerCoupling =
|
||||||
|
* listenerToCouplingMap.putIfAbsent(pepEventListener, pepEventListenerCoupling); if
|
||||||
|
* (currentPepEventListenerCoupling != null) { return false; }
|
||||||
|
*/
|
||||||
|
|
||||||
|
boolean listenerForNodeExisted = pepEventListeners.put(node, pepEventListenerCoupling);
|
||||||
|
if (!listenerForNodeExisted) {
|
||||||
|
serviceDiscoveryManager.addFeature(node + PubSubManager.PLUS_NOTIFY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removePepEventListener(PepEventListener<?> pepEventListener) {
|
||||||
|
synchronized (pepEventListeners) {
|
||||||
|
PepEventListenerCoupling<?> pepEventListenerCoupling = listenerToCouplingMap.remove(pepEventListener);
|
||||||
|
if (pepEventListenerCoupling == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String node = pepEventListenerCoupling.node;
|
||||||
|
|
||||||
|
boolean mappingExisted = pepEventListeners.removeOne(node, pepEventListenerCoupling);
|
||||||
|
assert mappingExisted;
|
||||||
|
|
||||||
|
if (!pepEventListeners.containsKey(pepEventListenerCoupling.node)) {
|
||||||
|
// This was the last listener for the node. Remove the +notify feature.
|
||||||
|
serviceDiscoveryManager.removeFeature(node + PubSubManager.PLUS_NOTIFY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PubSubManager getPepPubSubManager() {
|
public PubSubManager getPepPubSubManager() {
|
||||||
|
@ -127,8 +249,7 @@ public final class PepManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a listener to PEPs. The listener will be fired anytime PEP events
|
* Adds a listener to PEPs. The listener will be fired anytime PEP events are received from remote XMPP clients.
|
||||||
* are received from remote XMPP clients.
|
|
||||||
*
|
*
|
||||||
* @param pepListener a roster exchange listener.
|
* @param pepListener a roster exchange listener.
|
||||||
* @return true if pepListener was added.
|
* @return true if pepListener was added.
|
||||||
|
@ -176,8 +297,8 @@ public final class PepManager extends Manager {
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
};
|
};
|
||||||
|
|
||||||
public boolean isSupported() throws NoResponseException, XMPPErrorException,
|
public boolean isSupported()
|
||||||
NotConnectedException, InterruptedException {
|
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
XMPPConnection connection = connection();
|
XMPPConnection connection = connection();
|
||||||
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
||||||
BareJid localBareJid = connection.getUser().asBareJid();
|
BareJid localBareJid = connection.getUser().asBareJid();
|
||||||
|
|
|
@ -22,6 +22,8 @@ import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.FilledConfigureForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the <b>configuration</b> element of a PubSub message event which
|
* Represents the <b>configuration</b> element of a PubSub message event which
|
||||||
* associates a configuration form to the node which was configured. The form
|
* associates a configuration form to the node which was configured. The form
|
||||||
|
@ -30,18 +32,18 @@ import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
* @author Robin Collier
|
* @author Robin Collier
|
||||||
*/
|
*/
|
||||||
public class ConfigurationEvent extends NodeExtension implements EmbeddedPacketExtension {
|
public class ConfigurationEvent extends NodeExtension implements EmbeddedPacketExtension {
|
||||||
private ConfigureForm form;
|
private final FilledConfigureForm form;
|
||||||
|
|
||||||
public ConfigurationEvent(String nodeId) {
|
public ConfigurationEvent(String nodeId) {
|
||||||
super(PubSubElementType.CONFIGURATION, nodeId);
|
this(nodeId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationEvent(String nodeId, ConfigureForm configForm) {
|
public ConfigurationEvent(String nodeId, FilledConfigureForm configForm) {
|
||||||
super(PubSubElementType.CONFIGURATION, nodeId);
|
super(PubSubElementType.CONFIGURATION, nodeId);
|
||||||
form = configForm;
|
form = configForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigureForm getConfiguration() {
|
public FilledConfigureForm getConfiguration() {
|
||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +52,6 @@ public class ConfigurationEvent extends NodeExtension implements EmbeddedPacketE
|
||||||
if (getConfiguration() == null)
|
if (getConfiguration() == null)
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
else
|
else
|
||||||
return Arrays.asList((ExtensionElement) getConfiguration().getDataFormToSend());
|
return Arrays.asList((ExtensionElement) getConfiguration().getDataForm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,681 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright the original author or authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.jivesoftware.smackx.pubsub;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A decorator for a {@link Form} to easily enable reading and updating
|
|
||||||
* of node configuration. All operations read or update the underlying {@link DataForm}.
|
|
||||||
*
|
|
||||||
* <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
|
|
||||||
* exist, all <b>ConfigureForm.setXXX</b> methods will create the field in the wrapped form
|
|
||||||
* if it does not already exist.
|
|
||||||
*
|
|
||||||
* @author Robin Collier
|
|
||||||
*/
|
|
||||||
public class ConfigureForm extends Form {
|
|
||||||
/**
|
|
||||||
* Create a decorator from an existing {@link DataForm} that has been
|
|
||||||
* retrieved from parsing a node configuration request.
|
|
||||||
*
|
|
||||||
* @param configDataForm TODO javadoc me please
|
|
||||||
*/
|
|
||||||
public ConfigureForm(DataForm configDataForm) {
|
|
||||||
super(configDataForm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a decorator from an existing {@link Form} for node configuration.
|
|
||||||
* Typically, this can be used to create a decorator for an answer form
|
|
||||||
* by using the result of {@link #createAnswerForm()} as the input parameter.
|
|
||||||
*
|
|
||||||
* @param nodeConfigForm TODO javadoc me please
|
|
||||||
*/
|
|
||||||
public ConfigureForm(Form nodeConfigForm) {
|
|
||||||
super(nodeConfigForm.getDataFormToSend());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new form for configuring a node. This would typically only be used
|
|
||||||
* when creating and configuring a node at the same time via {@link PubSubManager#createNode(String, Form)}, since
|
|
||||||
* configuration of an existing node is typically accomplished by calling {@link LeafNode#getNodeConfiguration()} and
|
|
||||||
* using the resulting form to create a answer form. See {@link #ConfigureForm(Form)}.
|
|
||||||
* @param formType TODO javadoc me please
|
|
||||||
*/
|
|
||||||
public ConfigureForm(DataForm.Type formType) {
|
|
||||||
super(formType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the currently configured {@link AccessModel}, null if it is not set.
|
|
||||||
*
|
|
||||||
* @return The current {@link AccessModel}
|
|
||||||
*/
|
|
||||||
public AccessModel getAccessModel() {
|
|
||||||
String value = getFieldValue(ConfigureNodeFields.access_model);
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
return null;
|
|
||||||
else
|
|
||||||
return AccessModel.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of access model.
|
|
||||||
*
|
|
||||||
* @param accessModel TODO javadoc me please
|
|
||||||
*/
|
|
||||||
public void setAccessModel(AccessModel accessModel) {
|
|
||||||
addField(ConfigureNodeFields.access_model, FormField.Type.list_single);
|
|
||||||
setAnswer(ConfigureNodeFields.access_model.getFieldName(), getListSingle(accessModel.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the URL of an XSL transformation which can be applied to payloads in order to
|
|
||||||
* generate an appropriate message body element.
|
|
||||||
*
|
|
||||||
* @return URL to an XSL
|
|
||||||
*/
|
|
||||||
public String getBodyXSLT() {
|
|
||||||
return getFieldValue(ConfigureNodeFields.body_xslt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the URL of an XSL transformation which can be applied to payloads in order to
|
|
||||||
* generate an appropriate message body element.
|
|
||||||
*
|
|
||||||
* @param bodyXslt The URL of an XSL
|
|
||||||
*/
|
|
||||||
public void setBodyXSLT(String bodyXslt) {
|
|
||||||
addField(ConfigureNodeFields.body_xslt, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.body_xslt.getFieldName(), bodyXslt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The id's of the child nodes associated with a collection node (both leaf and collection).
|
|
||||||
*
|
|
||||||
* @return list of child nodes.
|
|
||||||
*/
|
|
||||||
public List<String> getChildren() {
|
|
||||||
return getFieldValues(ConfigureNodeFields.children);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the list of child node ids that are associated with a collection node.
|
|
||||||
*
|
|
||||||
* @param children TODO javadoc me please
|
|
||||||
*/
|
|
||||||
public void setChildren(List<String> children) {
|
|
||||||
addField(ConfigureNodeFields.children, FormField.Type.text_multi);
|
|
||||||
setAnswer(ConfigureNodeFields.children.getFieldName(), children);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the policy that determines who may associate children with the node.
|
|
||||||
*
|
|
||||||
* @return The current policy
|
|
||||||
*/
|
|
||||||
public ChildrenAssociationPolicy getChildrenAssociationPolicy() {
|
|
||||||
String value = getFieldValue(ConfigureNodeFields.children_association_policy);
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
return null;
|
|
||||||
else
|
|
||||||
return ChildrenAssociationPolicy.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the policy that determines who may associate children with the node.
|
|
||||||
*
|
|
||||||
* @param policy The policy being set
|
|
||||||
*/
|
|
||||||
public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy) {
|
|
||||||
addField(ConfigureNodeFields.children_association_policy, FormField.Type.list_single);
|
|
||||||
List<String> values = new ArrayList<>(1);
|
|
||||||
values.add(policy.toString());
|
|
||||||
setAnswer(ConfigureNodeFields.children_association_policy.getFieldName(), values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of JID's that are on the whitelist that determines who can associate child nodes
|
|
||||||
* with the collection node. This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
|
|
||||||
* {@link ChildrenAssociationPolicy#whitelist}.
|
|
||||||
*
|
|
||||||
* @return List of the whitelist
|
|
||||||
*/
|
|
||||||
public List<String> getChildrenAssociationWhitelist() {
|
|
||||||
return getFieldValues(ConfigureNodeFields.children_association_whitelist);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the JID's in the whitelist of users that can associate child nodes with the collection
|
|
||||||
* node. This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
|
|
||||||
* {@link ChildrenAssociationPolicy#whitelist}.
|
|
||||||
*
|
|
||||||
* @param whitelist The list of JID's
|
|
||||||
*/
|
|
||||||
public void setChildrenAssociationWhitelist(List<String> whitelist) {
|
|
||||||
addField(ConfigureNodeFields.children_association_whitelist, FormField.Type.jid_multi);
|
|
||||||
setAnswer(ConfigureNodeFields.children_association_whitelist.getFieldName(), whitelist);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum number of child nodes that can be associated with the collection node.
|
|
||||||
*
|
|
||||||
* @return The maximum number of child nodes
|
|
||||||
*/
|
|
||||||
public int getChildrenMax() {
|
|
||||||
return Integer.parseInt(getFieldValue(ConfigureNodeFields.children_max));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the maximum number of child nodes that can be associated with a collection node.
|
|
||||||
*
|
|
||||||
* @param max The maximum number of child nodes.
|
|
||||||
*/
|
|
||||||
public void setChildrenMax(int max) {
|
|
||||||
addField(ConfigureNodeFields.children_max, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.children_max.getFieldName(), max);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the collection node which the node is affiliated with.
|
|
||||||
*
|
|
||||||
* @return The collection node id
|
|
||||||
*/
|
|
||||||
public String getCollection() {
|
|
||||||
return getFieldValue(ConfigureNodeFields.collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the collection node which the node is affiliated with.
|
|
||||||
*
|
|
||||||
* @param collection The node id of the collection node
|
|
||||||
*/
|
|
||||||
public void setCollection(String collection) {
|
|
||||||
addField(ConfigureNodeFields.collection, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.collection.getFieldName(), collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the URL of an XSL transformation which can be applied to the payload
|
|
||||||
* format in order to generate a valid Data Forms result that the client could
|
|
||||||
* display using a generic Data Forms rendering engine.
|
|
||||||
*
|
|
||||||
* @return The URL of an XSL transformation
|
|
||||||
*/
|
|
||||||
public String getDataformXSLT() {
|
|
||||||
return getFieldValue(ConfigureNodeFields.dataform_xslt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the URL of an XSL transformation which can be applied to the payload
|
|
||||||
* format in order to generate a valid Data Forms result that the client could
|
|
||||||
* display using a generic Data Forms rendering engine.
|
|
||||||
*
|
|
||||||
* @param url The URL of an XSL transformation
|
|
||||||
*/
|
|
||||||
public void setDataformXSLT(String url) {
|
|
||||||
addField(ConfigureNodeFields.dataform_xslt, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.dataform_xslt.getFieldName(), url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the node deliver payloads with event notifications.
|
|
||||||
*
|
|
||||||
* @return true if it does, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isDeliverPayloads() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(ConfigureNodeFields.deliver_payloads));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether the node will deliver payloads with event notifications.
|
|
||||||
*
|
|
||||||
* @param deliver true if the payload will be delivered, false otherwise
|
|
||||||
*/
|
|
||||||
public void setDeliverPayloads(boolean deliver) {
|
|
||||||
addField(ConfigureNodeFields.deliver_payloads, FormField.Type.bool);
|
|
||||||
setAnswer(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines who should get replies to items.
|
|
||||||
*
|
|
||||||
* @return Who should get the reply
|
|
||||||
*/
|
|
||||||
public ItemReply getItemReply() {
|
|
||||||
String value = getFieldValue(ConfigureNodeFields.itemreply);
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
return null;
|
|
||||||
else
|
|
||||||
return ItemReply.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets who should get the replies to items.
|
|
||||||
*
|
|
||||||
* @param reply Defines who should get the reply
|
|
||||||
*/
|
|
||||||
public void setItemReply(ItemReply reply) {
|
|
||||||
addField(ConfigureNodeFields.itemreply, FormField.Type.list_single);
|
|
||||||
setAnswer(ConfigureNodeFields.itemreply.getFieldName(), getListSingle(reply.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is
|
|
||||||
* true.
|
|
||||||
*
|
|
||||||
* @return The maximum number of items to persist
|
|
||||||
*/
|
|
||||||
public int getMaxItems() {
|
|
||||||
return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_items));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is
|
|
||||||
* true.
|
|
||||||
*
|
|
||||||
* @param max The maximum number of items to persist
|
|
||||||
*/
|
|
||||||
public void setMaxItems(int max) {
|
|
||||||
addField(ConfigureNodeFields.max_items, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.max_items.getFieldName(), max);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum payload size in bytes.
|
|
||||||
*
|
|
||||||
* @return The maximum payload size
|
|
||||||
*/
|
|
||||||
public int getMaxPayloadSize() {
|
|
||||||
return Integer.parseInt(getFieldValue(ConfigureNodeFields.max_payload_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum payload size in bytes.
|
|
||||||
*
|
|
||||||
* @param max The maximum payload size
|
|
||||||
*/
|
|
||||||
public void setMaxPayloadSize(int max) {
|
|
||||||
addField(ConfigureNodeFields.max_payload_size, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.max_payload_size.getFieldName(), max);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the node type.
|
|
||||||
*
|
|
||||||
* @return The node type
|
|
||||||
*/
|
|
||||||
public NodeType getNodeType() {
|
|
||||||
String value = getFieldValue(ConfigureNodeFields.node_type);
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
return null;
|
|
||||||
else
|
|
||||||
return NodeType.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the node type.
|
|
||||||
*
|
|
||||||
* @param type The node type
|
|
||||||
*/
|
|
||||||
public void setNodeType(NodeType type) {
|
|
||||||
addField(ConfigureNodeFields.node_type, FormField.Type.list_single);
|
|
||||||
setAnswer(ConfigureNodeFields.node_type.getFieldName(), getListSingle(type.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if subscribers should be notified when the configuration changes.
|
|
||||||
*
|
|
||||||
* @return true if they should be notified, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isNotifyConfig() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(ConfigureNodeFields.notify_config));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether subscribers should be notified when the configuration changes.
|
|
||||||
*
|
|
||||||
* @param notify true if subscribers should be notified, false otherwise
|
|
||||||
*/
|
|
||||||
public void setNotifyConfig(boolean notify) {
|
|
||||||
addField(ConfigureNodeFields.notify_config, FormField.Type.bool);
|
|
||||||
setAnswer(ConfigureNodeFields.notify_config.getFieldName(), notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether subscribers should be notified when the node is deleted.
|
|
||||||
*
|
|
||||||
* @return true if subscribers should be notified, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isNotifyDelete() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(ConfigureNodeFields.notify_delete));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether subscribers should be notified when the node is deleted.
|
|
||||||
*
|
|
||||||
* @param notify true if subscribers should be notified, false otherwise
|
|
||||||
*/
|
|
||||||
public void setNotifyDelete(boolean notify) {
|
|
||||||
addField(ConfigureNodeFields.notify_delete, FormField.Type.bool);
|
|
||||||
setAnswer(ConfigureNodeFields.notify_delete.getFieldName(), notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether subscribers should be notified when items are deleted
|
|
||||||
* from the node.
|
|
||||||
*
|
|
||||||
* @return true if subscribers should be notified, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isNotifyRetract() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(ConfigureNodeFields.notify_retract));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether subscribers should be notified when items are deleted
|
|
||||||
* from the node.
|
|
||||||
*
|
|
||||||
* @param notify true if subscribers should be notified, false otherwise
|
|
||||||
*/
|
|
||||||
public void setNotifyRetract(boolean notify) {
|
|
||||||
addField(ConfigureNodeFields.notify_retract, FormField.Type.bool);
|
|
||||||
setAnswer(ConfigureNodeFields.notify_retract.getFieldName(), notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the type of notifications which are sent.
|
|
||||||
*
|
|
||||||
* @return NotificationType for the node configuration
|
|
||||||
* @since 4.3
|
|
||||||
*/
|
|
||||||
public NotificationType getNotificationType() {
|
|
||||||
String value = getFieldValue(ConfigureNodeFields.notification_type);
|
|
||||||
if (value == null)
|
|
||||||
return null;
|
|
||||||
return NotificationType.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the NotificationType for the node.
|
|
||||||
*
|
|
||||||
* @param notificationType The enum representing the possible options
|
|
||||||
* @since 4.3
|
|
||||||
*/
|
|
||||||
public void setNotificationType(NotificationType notificationType) {
|
|
||||||
addField(ConfigureNodeFields.notification_type, FormField.Type.list_single);
|
|
||||||
setAnswer(ConfigureNodeFields.notification_type.getFieldName(), getListSingle(notificationType.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether items should be persisted in the node.
|
|
||||||
*
|
|
||||||
* @return true if items are persisted
|
|
||||||
*/
|
|
||||||
public boolean isPersistItems() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(ConfigureNodeFields.persist_items));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether items should be persisted in the node.
|
|
||||||
*
|
|
||||||
* @param persist true if items should be persisted, false otherwise
|
|
||||||
*/
|
|
||||||
public void setPersistentItems(boolean persist) {
|
|
||||||
addField(ConfigureNodeFields.persist_items, FormField.Type.bool);
|
|
||||||
setAnswer(ConfigureNodeFields.persist_items.getFieldName(), persist);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether to deliver notifications to available users only.
|
|
||||||
*
|
|
||||||
* @return true if users must be available
|
|
||||||
*/
|
|
||||||
public boolean isPresenceBasedDelivery() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(ConfigureNodeFields.presence_based_delivery));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether to deliver notifications to available users only.
|
|
||||||
*
|
|
||||||
* @param presenceBased true if user must be available, false otherwise
|
|
||||||
*/
|
|
||||||
public void setPresenceBasedDelivery(boolean presenceBased) {
|
|
||||||
addField(ConfigureNodeFields.presence_based_delivery, FormField.Type.bool);
|
|
||||||
setAnswer(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the publishing model for the node, which determines who may publish to it.
|
|
||||||
*
|
|
||||||
* @return The publishing model
|
|
||||||
*/
|
|
||||||
public PublishModel getPublishModel() {
|
|
||||||
String value = getFieldValue(ConfigureNodeFields.publish_model);
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
return null;
|
|
||||||
else
|
|
||||||
return PublishModel.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the publishing model for the node, which determines who may publish to it.
|
|
||||||
*
|
|
||||||
* @param publish The enum representing the possible options for the publishing model
|
|
||||||
*/
|
|
||||||
public void setPublishModel(PublishModel publish) {
|
|
||||||
addField(ConfigureNodeFields.publish_model, FormField.Type.list_single);
|
|
||||||
setAnswer(ConfigureNodeFields.publish_model.getFieldName(), getListSingle(publish.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of the multi user chat rooms that are specified as reply rooms.
|
|
||||||
*
|
|
||||||
* @return The reply room JID's
|
|
||||||
*/
|
|
||||||
public List<String> getReplyRoom() {
|
|
||||||
return getFieldValues(ConfigureNodeFields.replyroom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the multi user chat rooms that are specified as reply rooms.
|
|
||||||
*
|
|
||||||
* @param replyRooms The multi user chat room to use as reply rooms
|
|
||||||
*/
|
|
||||||
public void setReplyRoom(List<String> replyRooms) {
|
|
||||||
addField(ConfigureNodeFields.replyroom, FormField.Type.list_multi);
|
|
||||||
setAnswer(ConfigureNodeFields.replyroom.getFieldName(), replyRooms);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the specific JID's for reply to.
|
|
||||||
*
|
|
||||||
* @return The JID's
|
|
||||||
*/
|
|
||||||
public List<String> getReplyTo() {
|
|
||||||
return getFieldValues(ConfigureNodeFields.replyto);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the specific JID's for reply to.
|
|
||||||
*
|
|
||||||
* @param replyTos The JID's to reply to
|
|
||||||
*/
|
|
||||||
public void setReplyTo(List<String> replyTos) {
|
|
||||||
addField(ConfigureNodeFields.replyto, FormField.Type.list_multi);
|
|
||||||
setAnswer(ConfigureNodeFields.replyto.getFieldName(), replyTos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the roster groups that are allowed to subscribe and retrieve items.
|
|
||||||
*
|
|
||||||
* @return The roster groups
|
|
||||||
*/
|
|
||||||
public List<String> getRosterGroupsAllowed() {
|
|
||||||
return getFieldValues(ConfigureNodeFields.roster_groups_allowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the roster groups that are allowed to subscribe and retrieve items.
|
|
||||||
*
|
|
||||||
* @param groups The roster groups
|
|
||||||
*/
|
|
||||||
public void setRosterGroupsAllowed(List<String> groups) {
|
|
||||||
addField(ConfigureNodeFields.roster_groups_allowed, FormField.Type.list_multi);
|
|
||||||
setAnswer(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if subscriptions are allowed.
|
|
||||||
*
|
|
||||||
* @return true if subscriptions are allowed, false otherwise
|
|
||||||
* @deprecated use {@link #isSubscribe()} instead
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
// TODO: Remove in Smack 4.5.
|
|
||||||
public boolean isSubscibe() {
|
|
||||||
return isSubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if subscriptions are allowed.
|
|
||||||
*
|
|
||||||
* @return true if subscriptions are allowed, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isSubscribe() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(ConfigureNodeFields.subscribe));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether subscriptions are allowed.
|
|
||||||
*
|
|
||||||
* @param subscribe true if they are, false otherwise
|
|
||||||
*/
|
|
||||||
public void setSubscribe(boolean subscribe) {
|
|
||||||
addField(ConfigureNodeFields.subscribe, FormField.Type.bool);
|
|
||||||
setAnswer(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the human readable node title.
|
|
||||||
*
|
|
||||||
* @return The node title
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return getFieldValue(ConfigureNodeFields.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a human readable title for the node.
|
|
||||||
*
|
|
||||||
* @param title The node title
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setTitle(String title) {
|
|
||||||
addField(ConfigureNodeFields.title, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.title.getFieldName(), title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of node data, usually specified by the namespace of the payload (if any).
|
|
||||||
*
|
|
||||||
* @return The type of node data
|
|
||||||
*/
|
|
||||||
public String getDataType() {
|
|
||||||
return getFieldValue(ConfigureNodeFields.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the type of node data, usually specified by the namespace of the payload (if any).
|
|
||||||
*
|
|
||||||
* @param type The type of node data
|
|
||||||
*/
|
|
||||||
public void setDataType(String type) {
|
|
||||||
addField(ConfigureNodeFields.type, FormField.Type.text_single);
|
|
||||||
setAnswer(ConfigureNodeFields.type.getFieldName(), type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder result = new StringBuilder(getClass().getName() + " Content [");
|
|
||||||
|
|
||||||
for (FormField formField : getFields()) {
|
|
||||||
result.append('(');
|
|
||||||
result.append(formField.getVariable());
|
|
||||||
result.append(':');
|
|
||||||
|
|
||||||
StringBuilder valuesBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
for (CharSequence value : formField.getValues()) {
|
|
||||||
if (valuesBuilder.length() > 0)
|
|
||||||
result.append(',');
|
|
||||||
valuesBuilder.append(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valuesBuilder.length() == 0)
|
|
||||||
valuesBuilder.append("NOT SET");
|
|
||||||
result.append(valuesBuilder);
|
|
||||||
result.append(')');
|
|
||||||
}
|
|
||||||
result.append(']');
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getFieldValue(ConfigureNodeFields field) {
|
|
||||||
FormField formField = getField(field.getFieldName());
|
|
||||||
|
|
||||||
return formField.getFirstValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getFieldValues(ConfigureNodeFields field) {
|
|
||||||
FormField formField = getField(field.getFieldName());
|
|
||||||
|
|
||||||
return formField.getValuesAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addField(ConfigureNodeFields nodeField, FormField.Type type) {
|
|
||||||
String fieldName = nodeField.getFieldName();
|
|
||||||
|
|
||||||
if (getField(fieldName) == null) {
|
|
||||||
FormField field = FormField.builder()
|
|
||||||
.setVariable(fieldName)
|
|
||||||
.setType(type)
|
|
||||||
.build();
|
|
||||||
addField(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<String> getListSingle(String value) {
|
|
||||||
List<String> list = new ArrayList<>(1);
|
|
||||||
list.add(value);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,12 +18,13 @@ package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.pubsub.form.ConfigureForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FilledForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This enumeration represents all the fields of a node configuration form. This enumeration
|
* This enumeration represents all the fields of a node configuration form. This enumeration
|
||||||
* is not required when using the {@link ConfigureForm} to configure nodes, but may be helpful
|
* is not required when using the {@link ConfigureForm} to configure nodes, but may be helpful
|
||||||
* for generic UI's using only a {@link Form} for configuration.
|
* for generic UI's using only a {@link FilledForm} for configuration.
|
||||||
*
|
*
|
||||||
* @author Robin Collier
|
* @author Robin Collier
|
||||||
*/
|
*/
|
||||||
|
@ -176,20 +177,6 @@ public enum ConfigureNodeFields {
|
||||||
*/
|
*/
|
||||||
publish_model,
|
publish_model,
|
||||||
|
|
||||||
/**
|
|
||||||
* The specific multi-user chat rooms to specify for replyroom.
|
|
||||||
*
|
|
||||||
* <p><b>Value: List of JIDs as Strings</b></p>
|
|
||||||
*/
|
|
||||||
replyroom,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The specific JID(s) to specify for replyto.
|
|
||||||
*
|
|
||||||
* <p><b>Value: List of JIDs as Strings</b></p>
|
|
||||||
*/
|
|
||||||
replyto,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The roster group(s) allowed to subscribe and retrieve items.
|
* The roster group(s) allowed to subscribe and retrieve items.
|
||||||
*
|
*
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic stanza extension which represents any PubSub form that is
|
* Generic stanza extension which represents any PubSub form that is
|
||||||
|
@ -27,7 +27,7 @@ import org.jivesoftware.smackx.xdata.Form;
|
||||||
* @author Robin Collier
|
* @author Robin Collier
|
||||||
*/
|
*/
|
||||||
public class FormNode extends NodeExtension {
|
public class FormNode extends NodeExtension {
|
||||||
private final Form configForm;
|
private final DataForm configForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link FormNode} which contains the specified form.
|
* Create a {@link FormNode} which contains the specified form.
|
||||||
|
@ -35,7 +35,7 @@ public class FormNode extends NodeExtension {
|
||||||
* @param formType The type of form being sent
|
* @param formType The type of form being sent
|
||||||
* @param submitForm The form
|
* @param submitForm The form
|
||||||
*/
|
*/
|
||||||
public FormNode(FormNodeType formType, Form submitForm) {
|
public FormNode(FormNodeType formType, DataForm submitForm) {
|
||||||
super(formType.getNodeElement());
|
super(formType.getNodeElement());
|
||||||
|
|
||||||
if (submitForm == null)
|
if (submitForm == null)
|
||||||
|
@ -51,7 +51,7 @@ public class FormNode extends NodeExtension {
|
||||||
* @param nodeId The node the form is associated with
|
* @param nodeId The node the form is associated with
|
||||||
* @param submitForm The form
|
* @param submitForm The form
|
||||||
*/
|
*/
|
||||||
public FormNode(FormNodeType formType, String nodeId, Form submitForm) {
|
public FormNode(FormNodeType formType, String nodeId, DataForm submitForm) {
|
||||||
super(formType.getNodeElement(), nodeId);
|
super(formType.getNodeElement(), nodeId);
|
||||||
|
|
||||||
if (submitForm == null)
|
if (submitForm == null)
|
||||||
|
@ -64,7 +64,7 @@ public class FormNode extends NodeExtension {
|
||||||
*
|
*
|
||||||
* @return The form
|
* @return The form
|
||||||
*/
|
*/
|
||||||
public Form getForm() {
|
public DataForm getForm() {
|
||||||
return configForm;
|
return configForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ public class FormNode extends NodeExtension {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
builder.append('>');
|
builder.append('>');
|
||||||
builder.append(configForm.getDataFormToSend().toXML());
|
builder.append(configForm.toXML());
|
||||||
builder.append("</");
|
builder.append("</");
|
||||||
builder.append(getElementName() + '>');
|
builder.append(getElementName() + '>');
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.jivesoftware.smackx.pubsub;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.ConfigureForm;
|
||||||
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
|
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.FillableConfigureForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are the options for the node configuration setting {@link ConfigureForm#setItemReply(ItemReply)},
|
* These are the options for the node configuration setting {@link FillableConfigureForm#setItemReply(ItemReply)},
|
||||||
* which defines who should receive replies to items.
|
* which defines who should receive replies to items.
|
||||||
*
|
*
|
||||||
* @author Robin Collier
|
* @author Robin Collier
|
||||||
|
|
|
@ -135,6 +135,7 @@ public class ItemsExtension extends NodeExtension implements EmbeddedPacketExten
|
||||||
*
|
*
|
||||||
* @return List of {@link Item}, {@link RetractItem}, or null
|
* @return List of {@link Item}, {@link RetractItem}, or null
|
||||||
*/
|
*/
|
||||||
|
// TODO: Shouldn't this return List<Item>? Why is RetractItem not a subtype of item?
|
||||||
public List<? extends NamedElement> getItems() {
|
public List<? extends NamedElement> getItems() {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.IQ.Type;
|
import org.jivesoftware.smack.packet.IQ.Type;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.ConfigureForm;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -37,6 +37,10 @@ import org.jivesoftware.smackx.delay.DelayInformationManager;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.pubsub.Affiliation.AffiliationNamespace;
|
import org.jivesoftware.smackx.pubsub.Affiliation.AffiliationNamespace;
|
||||||
import org.jivesoftware.smackx.pubsub.SubscriptionsExtension.SubscriptionsNamespace;
|
import org.jivesoftware.smackx.pubsub.SubscriptionsExtension.SubscriptionsNamespace;
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.ConfigureForm;
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.FillableConfigureForm;
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.FillableSubscribeForm;
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.SubscribeForm;
|
||||||
import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener;
|
import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener;
|
||||||
import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;
|
import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;
|
||||||
import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener;
|
import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener;
|
||||||
|
@ -45,7 +49,7 @@ import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
|
||||||
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
||||||
import org.jivesoftware.smackx.shim.packet.Header;
|
import org.jivesoftware.smackx.shim.packet.Header;
|
||||||
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
@ -81,7 +85,7 @@ public abstract class Node {
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns a configuration form, from which you can create an answer form to be submitted
|
* Returns a configuration form, from which you can create an answer form to be submitted
|
||||||
* via the {@link #sendConfigurationForm(Form)}.
|
* via the {@link #sendConfigurationForm(FillableConfigureForm)}.
|
||||||
*
|
*
|
||||||
* @return the configuration form
|
* @return the configuration form
|
||||||
* @throws XMPPErrorException if there was an XMPP error returned.
|
* @throws XMPPErrorException if there was an XMPP error returned.
|
||||||
|
@ -97,17 +101,17 @@ public abstract class Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the configuration with the contents of the new {@link Form}.
|
* Update the configuration with the contents of the new {@link FillableConfigureForm}.
|
||||||
*
|
*
|
||||||
* @param submitForm TODO javadoc me please
|
* @param configureForm the filled node configuration form with the nodes new configuration.
|
||||||
* @throws XMPPErrorException if there was an XMPP error returned.
|
* @throws XMPPErrorException if there was an XMPP error returned.
|
||||||
* @throws NoResponseException if there was no response from the remote entity.
|
* @throws NoResponseException if there was no response from the remote entity.
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public void sendConfigurationForm(Form submitForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public void sendConfigurationForm(FillableConfigureForm configureForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
PubSub packet = createPubsubPacket(Type.set, new FormNode(FormNodeType.CONFIGURE_OWNER,
|
PubSub packet = createPubsubPacket(Type.set, new FormNode(FormNodeType.CONFIGURE_OWNER,
|
||||||
getId(), submitForm));
|
getId(), configureForm.getDataFormToSubmit()));
|
||||||
pubSubManager.getConnection().createStanzaCollectorAndSend(packet).nextResultOrThrow();
|
pubSubManager.getConnection().createStanzaCollectorAndSend(packet).nextResultOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,9 +458,10 @@ public abstract class Node {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public Subscription subscribe(Jid jid, SubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public Subscription subscribe(Jid jid, FillableSubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
|
DataForm submitForm = subForm.getDataFormToSubmit();
|
||||||
PubSub request = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
|
PubSub request = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
|
||||||
request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
|
request.addExtension(new FormNode(FormNodeType.OPTIONS, submitForm));
|
||||||
PubSub reply = sendPubsubPacket(request);
|
PubSub reply = sendPubsubPacket(request);
|
||||||
return reply.getExtension(PubSubElementType.SUBSCRIPTION);
|
return reply.getExtension(PubSubElementType.SUBSCRIPTION);
|
||||||
}
|
}
|
||||||
|
@ -483,11 +488,11 @@ public abstract class Node {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
* @throws IllegalArgumentException if the provided string is not a valid JID.
|
* @throws IllegalArgumentException if the provided string is not a valid JID.
|
||||||
* @deprecated use {@link #subscribe(Jid, SubscribeForm)} instead.
|
* @deprecated use {@link #subscribe(Jid, FillableSubscribeForm)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
// TODO: Remove in Smack 4.5.
|
// TODO: Remove in Smack 4.5.
|
||||||
public Subscription subscribe(String jidString, SubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public Subscription subscribe(String jidString, FillableSubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
Jid jid;
|
Jid jid;
|
||||||
try {
|
try {
|
||||||
jid = JidCreate.from(jidString);
|
jid = JidCreate.from(jidString);
|
||||||
|
@ -529,7 +534,7 @@ public abstract class Node {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a SubscribeForm for subscriptions, from which you can create an answer form to be submitted
|
* Returns a SubscribeForm for subscriptions, from which you can create an answer form to be submitted
|
||||||
* via the {@link #sendConfigurationForm(Form)}.
|
* via the {@link #sendConfigurationForm(FillableConfigureForm)}.
|
||||||
*
|
*
|
||||||
* @param jid TODO javadoc me please
|
* @param jid TODO javadoc me please
|
||||||
*
|
*
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.FillableConfigureForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify the delivery style for event notifications. Denotes possible values
|
* Specify the delivery style for event notifications. Denotes possible values
|
||||||
* for {@link ConfigureForm#setNotificationType(NotificationType)}.
|
* for {@link FillableConfigureForm#setNotificationType(NotificationType)}.
|
||||||
*
|
*
|
||||||
* @author Timothy Pitt
|
* @author Timothy Pitt
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.ConfigureForm;
|
||||||
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
|
import org.jivesoftware.smackx.pubsub.provider.ItemProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.SubscribeForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the possible valid presence states for node subscription via
|
* Defines the possible valid presence states for node subscription via
|
||||||
* {@link SubscribeForm#getShowValues()}.
|
* {@link SubscribeForm#getShowValues()}.
|
||||||
|
|
|
@ -46,11 +46,12 @@ import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
|
import org.jivesoftware.smackx.pubsub.PubSubException.NotALeafNodeException;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException.NotAPubSubNodeException;
|
import org.jivesoftware.smackx.pubsub.PubSubException.NotAPubSubNodeException;
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.ConfigureForm;
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.FillableConfigureForm;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
|
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
|
||||||
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
import org.jivesoftware.smackx.pubsub.util.NodeUtils;
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.jid.DomainBareJid;
|
import org.jxmpp.jid.DomainBareJid;
|
||||||
|
@ -69,6 +70,8 @@ import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
*/
|
*/
|
||||||
public final class PubSubManager extends Manager {
|
public final class PubSubManager extends Manager {
|
||||||
|
|
||||||
|
public static final String PLUS_NOTIFY = "+notify";
|
||||||
|
|
||||||
public static final String AUTO_CREATE_FEATURE = "http://jabber.org/protocol/pubsub#auto-create";
|
public static final String AUTO_CREATE_FEATURE = "http://jabber.org/protocol/pubsub#auto-create";
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName());
|
||||||
|
@ -253,16 +256,18 @@ public final class PubSubManager extends Manager {
|
||||||
* @throws NotConnectedException if the XMPP connection is not connected.
|
* @throws NotConnectedException if the XMPP connection is not connected.
|
||||||
* @throws InterruptedException if the calling thread was interrupted.
|
* @throws InterruptedException if the calling thread was interrupted.
|
||||||
*/
|
*/
|
||||||
public Node createNode(String nodeId, Form config) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
public Node createNode(String nodeId, FillableConfigureForm config) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
||||||
PubSub request = PubSub.createPubsubPacket(pubSubService, Type.set, new NodeExtension(PubSubElementType.CREATE, nodeId));
|
PubSub request = PubSub.createPubsubPacket(pubSubService, Type.set, new NodeExtension(PubSubElementType.CREATE, nodeId));
|
||||||
boolean isLeafNode = true;
|
boolean isLeafNode = true;
|
||||||
|
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
request.addExtension(new FormNode(FormNodeType.CONFIGURE, config));
|
DataForm submitForm = config.getDataFormToSubmit();
|
||||||
FormField nodeTypeField = config.getField(ConfigureNodeFields.node_type.getFieldName());
|
request.addExtension(new FormNode(FormNodeType.CONFIGURE, submitForm));
|
||||||
|
NodeType nodeType = config.getNodeType();
|
||||||
if (nodeTypeField != null)
|
// Note that some implementations do to have the pubsub#node_type field in their defauilt configuration,
|
||||||
isLeafNode = nodeTypeField.getValues().get(0).toString().equals(NodeType.leaf.toString());
|
// which I believe to be a bug. However, since PubSub specifies the default node type to be 'leaf' we assume
|
||||||
|
// leaf if the field does not exist.
|
||||||
|
isLeafNode = nodeType == null || nodeType == NodeType.leaf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errors will cause exceptions in getReply, so it only returns
|
// Errors will cause exceptions in getReply, so it only returns
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.pubsub;
|
package org.jivesoftware.smackx.pubsub;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.form.FillableConfigureForm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines who may publish to a node. Denotes possible values
|
* Determines who may publish to a node. Denotes possible values
|
||||||
* for {@link ConfigureForm#setPublishModel(PublishModel)}.
|
* for {@link FillableConfigureForm#setPublishModel(PublishModel)}.
|
||||||
*
|
*
|
||||||
* @author Robin Collier
|
* @author Robin Collier
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright the original author or authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.jivesoftware.smackx.pubsub;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UnknownFormatConversionException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.ParserUtils;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.xdata.Form;
|
|
||||||
import org.jivesoftware.smackx.xdata.FormField;
|
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
|
||||||
|
|
||||||
import org.jxmpp.util.XmppDateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A decorator for a {@link Form} to easily enable reading and updating
|
|
||||||
* of subscription options. All operations read or update the underlying {@link DataForm}.
|
|
||||||
*
|
|
||||||
* <p>Unlike the {@link Form}.setAnswer(XXX)} methods, which throw an exception if the field does not
|
|
||||||
* exist, all <b>SubscribeForm.setXXX</b> methods will create the field in the wrapped form
|
|
||||||
* if it does not already exist.
|
|
||||||
*
|
|
||||||
* @author Robin Collier
|
|
||||||
*/
|
|
||||||
public class SubscribeForm extends Form {
|
|
||||||
public SubscribeForm(DataForm configDataForm) {
|
|
||||||
super(configDataForm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubscribeForm(Form subscribeOptionsForm) {
|
|
||||||
super(subscribeOptionsForm.getDataFormToSend());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubscribeForm(DataForm.Type formType) {
|
|
||||||
super(formType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if an entity wants to receive notifications.
|
|
||||||
*
|
|
||||||
* @return true if want to receive, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isDeliverOn() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(SubscribeOptionFields.deliver));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether an entity wants to receive notifications.
|
|
||||||
*
|
|
||||||
* @param deliverNotifications TODO javadoc me please
|
|
||||||
*/
|
|
||||||
public void setDeliverOn(boolean deliverNotifications) {
|
|
||||||
addField(SubscribeOptionFields.deliver, FormField.Type.bool);
|
|
||||||
setAnswer(SubscribeOptionFields.deliver.getFieldName(), deliverNotifications);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if notifications should be delivered as aggregations or not.
|
|
||||||
*
|
|
||||||
* @return true to aggregate, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isDigestOn() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(SubscribeOptionFields.digest));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether notifications should be delivered as aggregations or not.
|
|
||||||
*
|
|
||||||
* @param digestOn true to aggregate, false otherwise
|
|
||||||
*/
|
|
||||||
public void setDigestOn(boolean digestOn) {
|
|
||||||
addField(SubscribeOptionFields.deliver, FormField.Type.bool);
|
|
||||||
setAnswer(SubscribeOptionFields.deliver.getFieldName(), digestOn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the minimum number of milliseconds between sending notification digests.
|
|
||||||
*
|
|
||||||
* @return The frequency in milliseconds
|
|
||||||
*/
|
|
||||||
public int getDigestFrequency() {
|
|
||||||
return Integer.parseInt(getFieldValue(SubscribeOptionFields.digest_frequency));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the minimum number of milliseconds between sending notification digests.
|
|
||||||
*
|
|
||||||
* @param frequency The frequency in milliseconds
|
|
||||||
*/
|
|
||||||
public void setDigestFrequency(int frequency) {
|
|
||||||
addField(SubscribeOptionFields.digest_frequency, FormField.Type.text_single);
|
|
||||||
setAnswer(SubscribeOptionFields.digest_frequency.getFieldName(), frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the time at which the leased subscription will expire, or has expired.
|
|
||||||
*
|
|
||||||
* @return The expiry date
|
|
||||||
*/
|
|
||||||
public Date getExpiry() {
|
|
||||||
String dateTime = getFieldValue(SubscribeOptionFields.expire);
|
|
||||||
try {
|
|
||||||
return XmppDateTime.parseDate(dateTime);
|
|
||||||
}
|
|
||||||
catch (ParseException e) {
|
|
||||||
UnknownFormatConversionException exc = new UnknownFormatConversionException(dateTime);
|
|
||||||
exc.initCause(e);
|
|
||||||
throw exc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the time at which the leased subscription will expire, or has expired.
|
|
||||||
*
|
|
||||||
* @param expire The expiry date
|
|
||||||
*/
|
|
||||||
public void setExpiry(Date expire) {
|
|
||||||
addField(SubscribeOptionFields.expire, FormField.Type.text_single);
|
|
||||||
setAnswer(SubscribeOptionFields.expire.getFieldName(), XmppDateTime.formatXEP0082Date(expire));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether the entity wants to receive an XMPP message body in
|
|
||||||
* addition to the payload format.
|
|
||||||
*
|
|
||||||
* @return true to receive the message body, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean isIncludeBody() {
|
|
||||||
return ParserUtils.parseXmlBoolean(getFieldValue(SubscribeOptionFields.include_body));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether the entity wants to receive an XMPP message body in
|
|
||||||
* addition to the payload format.
|
|
||||||
*
|
|
||||||
* @param include true to receive the message body, false otherwise
|
|
||||||
*/
|
|
||||||
public void setIncludeBody(boolean include) {
|
|
||||||
addField(SubscribeOptionFields.include_body, FormField.Type.bool);
|
|
||||||
setAnswer(SubscribeOptionFields.include_body.getFieldName(), include);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the {@link PresenceState} for which an entity wants to receive
|
|
||||||
* notifications.
|
|
||||||
*
|
|
||||||
* @return the list of states
|
|
||||||
*/
|
|
||||||
public List<PresenceState> getShowValues() {
|
|
||||||
ArrayList<PresenceState> result = new ArrayList<>(5);
|
|
||||||
|
|
||||||
for (String state : getFieldValues(SubscribeOptionFields.show_values)) {
|
|
||||||
result.add(PresenceState.valueOf(state));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the list of {@link PresenceState} for which an entity wants
|
|
||||||
* to receive notifications.
|
|
||||||
*
|
|
||||||
* @param stateValues The list of states
|
|
||||||
*/
|
|
||||||
public void setShowValues(Collection<PresenceState> stateValues) {
|
|
||||||
ArrayList<String> values = new ArrayList<>(stateValues.size());
|
|
||||||
|
|
||||||
for (PresenceState state : stateValues) {
|
|
||||||
values.add(state.toString());
|
|
||||||
}
|
|
||||||
addField(SubscribeOptionFields.show_values, FormField.Type.list_multi);
|
|
||||||
setAnswer(SubscribeOptionFields.show_values.getFieldName(), values);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getFieldValue(SubscribeOptionFields field) {
|
|
||||||
FormField formField = getField(field.getFieldName());
|
|
||||||
|
|
||||||
return formField.getFirstValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getFieldValues(SubscribeOptionFields field) {
|
|
||||||
FormField formField = getField(field.getFieldName());
|
|
||||||
|
|
||||||
return formField.getValuesAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addField(SubscribeOptionFields nodeField, FormField.Type type) {
|
|
||||||
String fieldName = nodeField.getFieldName();
|
|
||||||
|
|
||||||
if (getField(fieldName) == null) {
|
|
||||||
FormField.Builder field = FormField.builder(fieldName);
|
|
||||||
field.setType(type);
|
|
||||||
addField(field.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.filter;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.filter.ExtensionElementFilter;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.EventElement;
|
||||||
|
import org.jivesoftware.smackx.pubsub.EventElementType;
|
||||||
|
|
||||||
|
public final class EventItemsExtensionFilter extends ExtensionElementFilter<EventElement> {
|
||||||
|
|
||||||
|
public static final EventItemsExtensionFilter INSTANCE = new EventItemsExtensionFilter();
|
||||||
|
|
||||||
|
private EventItemsExtensionFilter() {
|
||||||
|
super(EventElement.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(EventElement eventElement) {
|
||||||
|
EventElementType eventElementType = eventElement.getEventType();
|
||||||
|
return eventElementType == EventElementType.items;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.xdata.form.Form;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
public class ConfigureForm extends Form implements ConfigureFormReader {
|
||||||
|
|
||||||
|
public ConfigureForm(DataForm dataForm) {
|
||||||
|
super(dataForm);
|
||||||
|
ensureFormType(dataForm, FORM_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FillableConfigureForm getFillableForm() {
|
||||||
|
return new FillableConfigureForm(getDataForm());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,292 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright the original author or authors, 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.AccessModel;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ChildrenAssociationPolicy;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ConfigureNodeFields;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ItemReply;
|
||||||
|
import org.jivesoftware.smackx.pubsub.NodeType;
|
||||||
|
import org.jivesoftware.smackx.pubsub.NotificationType;
|
||||||
|
import org.jivesoftware.smackx.pubsub.PublishModel;
|
||||||
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.JidMultiFormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FormReader;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
public interface ConfigureFormReader extends FormReader {
|
||||||
|
|
||||||
|
String FORM_TYPE = PubSub.NAMESPACE + "#node_config";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently configured {@link AccessModel}, null if it is not set.
|
||||||
|
*
|
||||||
|
* @return The current {@link AccessModel}
|
||||||
|
*/
|
||||||
|
default AccessModel getAccessModel() {
|
||||||
|
String value = readFirstValue(ConfigureNodeFields.access_model.getFieldName());
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return AccessModel.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL of an XSL transformation which can be applied to payloads in order to
|
||||||
|
* generate an appropriate message body element.
|
||||||
|
*
|
||||||
|
* @return URL to an XSL
|
||||||
|
*/
|
||||||
|
default String getBodyXSLT() {
|
||||||
|
return readFirstValue(ConfigureNodeFields.body_xslt.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id's of the child nodes associated with a collection node (both leaf and collection).
|
||||||
|
*
|
||||||
|
* @return list of child nodes.
|
||||||
|
*/
|
||||||
|
default List<String> getChildren() {
|
||||||
|
return readStringValues(ConfigureNodeFields.children.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the policy that determines who may associate children with the node.
|
||||||
|
*
|
||||||
|
* @return The current policy
|
||||||
|
*/
|
||||||
|
default ChildrenAssociationPolicy getChildrenAssociationPolicy() {
|
||||||
|
String value = readFirstValue(ConfigureNodeFields.children_association_policy.getFieldName());
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ChildrenAssociationPolicy.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of JID's that are on the whitelist that determines who can associate child nodes
|
||||||
|
* with the collection node. This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
|
||||||
|
* {@link ChildrenAssociationPolicy#whitelist}.
|
||||||
|
*
|
||||||
|
* @return List of the whitelist
|
||||||
|
*/
|
||||||
|
default List<Jid> getChildrenAssociationWhitelist() {
|
||||||
|
FormField formField = read(ConfigureNodeFields.children_association_whitelist.getFieldName());
|
||||||
|
if (formField == null) {
|
||||||
|
Collections.emptyList();
|
||||||
|
}
|
||||||
|
JidMultiFormField jidMultiFormField = formField.ifPossibleAs(JidMultiFormField.class);
|
||||||
|
return jidMultiFormField.getValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum number of child nodes that can be associated with the collection node.
|
||||||
|
*
|
||||||
|
* @return The maximum number of child nodes
|
||||||
|
*/
|
||||||
|
default Integer getChildrenMax() {
|
||||||
|
return readInteger(ConfigureNodeFields.children_max.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the collection node which the node is affiliated with.
|
||||||
|
*
|
||||||
|
* @return The collection node id
|
||||||
|
*/
|
||||||
|
default List<? extends CharSequence> getCollection() {
|
||||||
|
return readValues(ConfigureNodeFields.collection.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the URL of an XSL transformation which can be applied to the payload
|
||||||
|
* format in order to generate a valid Data Forms result that the client could
|
||||||
|
* display using a generic Data Forms rendering engine.
|
||||||
|
*
|
||||||
|
* @return The URL of an XSL transformation
|
||||||
|
*/
|
||||||
|
default String getDataformXSLT() {
|
||||||
|
return readFirstValue(ConfigureNodeFields.dataform_xslt.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the node deliver payloads with event notifications.
|
||||||
|
*
|
||||||
|
* @return true if it does, false otherwise
|
||||||
|
*/
|
||||||
|
default Boolean isDeliverPayloads() {
|
||||||
|
return readBoolean(ConfigureNodeFields.deliver_payloads.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines who should get replies to items.
|
||||||
|
*
|
||||||
|
* @return Who should get the reply
|
||||||
|
*/
|
||||||
|
default ItemReply getItemReply() {
|
||||||
|
String value = readFirstValue(ConfigureNodeFields.itemreply.getFieldName());
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ItemReply.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum number of items to persisted to this node if {@link #isPersistItems()} is
|
||||||
|
* true.
|
||||||
|
*
|
||||||
|
* @return The maximum number of items to persist
|
||||||
|
*/
|
||||||
|
default Integer getMaxItems() {
|
||||||
|
return readInteger(ConfigureNodeFields.max_items.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum payload size in bytes.
|
||||||
|
*
|
||||||
|
* @return The maximum payload size
|
||||||
|
*/
|
||||||
|
default Integer getMaxPayloadSize() {
|
||||||
|
return readInteger(ConfigureNodeFields.max_payload_size.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the node type.
|
||||||
|
*
|
||||||
|
* @return The node type
|
||||||
|
*/
|
||||||
|
default NodeType getNodeType() {
|
||||||
|
String value = readFirstValue(ConfigureNodeFields.node_type.getFieldName());
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return NodeType.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if subscribers should be notified when the configuration changes.
|
||||||
|
*
|
||||||
|
* @return true if they should be notified, false otherwise
|
||||||
|
*/
|
||||||
|
default Boolean isNotifyConfig() {
|
||||||
|
return readBoolean(ConfigureNodeFields.notify_config.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether subscribers should be notified when the node is deleted.
|
||||||
|
*
|
||||||
|
* @return true if subscribers should be notified, false otherwise
|
||||||
|
*/
|
||||||
|
default Boolean isNotifyDelete() {
|
||||||
|
return readBoolean(ConfigureNodeFields.notify_delete.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether subscribers should be notified when items are deleted
|
||||||
|
* from the node.
|
||||||
|
*
|
||||||
|
* @return true if subscribers should be notified, false otherwise
|
||||||
|
*/
|
||||||
|
default Boolean isNotifyRetract() {
|
||||||
|
return readBoolean(ConfigureNodeFields.notify_retract.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the type of notifications which are sent.
|
||||||
|
*
|
||||||
|
* @return NotificationType for the node configuration
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
default NotificationType getNotificationType() {
|
||||||
|
String value = readFirstValue(ConfigureNodeFields.notification_type.getFieldName());
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return NotificationType.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether items should be persisted in the node.
|
||||||
|
*
|
||||||
|
* @return true if items are persisted
|
||||||
|
*/
|
||||||
|
default boolean isPersistItems() {
|
||||||
|
return readBoolean(ConfigureNodeFields.persist_items.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether to deliver notifications to available users only.
|
||||||
|
*
|
||||||
|
* @return true if users must be available
|
||||||
|
*/
|
||||||
|
default boolean isPresenceBasedDelivery() {
|
||||||
|
return readBoolean(ConfigureNodeFields.presence_based_delivery.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the publishing model for the node, which determines who may publish to it.
|
||||||
|
*
|
||||||
|
* @return The publishing model
|
||||||
|
*/
|
||||||
|
default PublishModel getPublishModel() {
|
||||||
|
String value = readFirstValue(ConfigureNodeFields.publish_model.getFieldName());
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return PublishModel.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the roster groups that are allowed to subscribe and retrieve items.
|
||||||
|
*
|
||||||
|
* @return The roster groups
|
||||||
|
*/
|
||||||
|
default List<String> getRosterGroupsAllowed() {
|
||||||
|
return readStringValues(ConfigureNodeFields.roster_groups_allowed.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if subscriptions are allowed.
|
||||||
|
*
|
||||||
|
* @return true if subscriptions are allowed, false otherwise
|
||||||
|
*/
|
||||||
|
default boolean isSubscribe() {
|
||||||
|
return readBoolean(ConfigureNodeFields.subscribe.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the human readable node title.
|
||||||
|
*
|
||||||
|
* @return The node title
|
||||||
|
*/
|
||||||
|
default String getTitle() {
|
||||||
|
return readFirstValue(ConfigureNodeFields.title.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of node data, usually specified by the namespace of the payload (if any).
|
||||||
|
*
|
||||||
|
* @return The type of node data
|
||||||
|
*/
|
||||||
|
default String getDataType() {
|
||||||
|
return readFirstValue(ConfigureNodeFields.type.getFieldName());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,314 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright the original author or authors, 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.AccessModel;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ChildrenAssociationPolicy;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ConfigureNodeFields;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ItemReply;
|
||||||
|
import org.jivesoftware.smackx.pubsub.NodeType;
|
||||||
|
import org.jivesoftware.smackx.pubsub.NotificationType;
|
||||||
|
import org.jivesoftware.smackx.pubsub.PublishModel;
|
||||||
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
public class FillableConfigureForm extends FillableForm implements ConfigureFormReader {
|
||||||
|
|
||||||
|
public FillableConfigureForm(DataForm dataForm) {
|
||||||
|
super(dataForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of access model.
|
||||||
|
*
|
||||||
|
* @param accessModel TODO javadoc me please
|
||||||
|
*/
|
||||||
|
public void setAccessModel(AccessModel accessModel) {
|
||||||
|
FormField formField = FormField.listSingleBuilder(ConfigureNodeFields.access_model.getFieldName())
|
||||||
|
.setValue(accessModel)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the URL of an XSL transformation which can be applied to payloads in order to
|
||||||
|
* generate an appropriate message body element.
|
||||||
|
*
|
||||||
|
* @param bodyXslt The URL of an XSL
|
||||||
|
*/
|
||||||
|
public void setBodyXSLT(String bodyXslt) {
|
||||||
|
FormField formField = FormField.listSingleBuilder(ConfigureNodeFields.body_xslt.getFieldName())
|
||||||
|
.setValue(bodyXslt)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the list of child node ids that are associated with a collection node.
|
||||||
|
*
|
||||||
|
* @param children TODO javadoc me please
|
||||||
|
*/
|
||||||
|
public void setChildren(List<String> children) {
|
||||||
|
FormField formField = FormField.textMultiBuilder(ConfigureNodeFields.children.getFieldName())
|
||||||
|
.addValues(children)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the policy that determines who may associate children with the node.
|
||||||
|
*
|
||||||
|
* @param policy The policy being set
|
||||||
|
*/
|
||||||
|
public void setChildrenAssociationPolicy(ChildrenAssociationPolicy policy) {
|
||||||
|
FormField formField = FormField.listSingleBuilder(ConfigureNodeFields.children_association_policy.getFieldName())
|
||||||
|
.setValue(policy)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the JID's in the whitelist of users that can associate child nodes with the collection
|
||||||
|
* node. This is only relevant if {@link #getChildrenAssociationPolicy()} is set to
|
||||||
|
* {@link ChildrenAssociationPolicy#whitelist}.
|
||||||
|
*
|
||||||
|
* @param whitelist The list of JID's
|
||||||
|
*/
|
||||||
|
public void setChildrenAssociationWhitelist(List<? extends Jid> whitelist) {
|
||||||
|
FormField formField = FormField.jidMultiBuilder(ConfigureNodeFields.children_association_whitelist.getFieldName())
|
||||||
|
.addValues(whitelist)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum number of child nodes that can be associated with a collection node.
|
||||||
|
*
|
||||||
|
* @param max The maximum number of child nodes.
|
||||||
|
*/
|
||||||
|
public void setChildrenMax(int max) {
|
||||||
|
FormField formField = FormField.textSingleBuilder(ConfigureNodeFields.children_max.getFieldName())
|
||||||
|
.setValue(max)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the collection node which the node is affiliated with.
|
||||||
|
*
|
||||||
|
* @param collection The node id of the collection node
|
||||||
|
*/
|
||||||
|
public void setCollection(String collection) {
|
||||||
|
setCollections(Collections.singletonList(collection));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCollections(Collection<String> collections) {
|
||||||
|
FormField formField = FormField.textMultiBuilder(ConfigureNodeFields.collection.getFieldName())
|
||||||
|
.addValues(collections)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the URL of an XSL transformation which can be applied to the payload
|
||||||
|
* format in order to generate a valid Data Forms result that the client could
|
||||||
|
* display using a generic Data Forms rendering engine.
|
||||||
|
*
|
||||||
|
* @param url The URL of an XSL transformation
|
||||||
|
*/
|
||||||
|
public void setDataformXSLT(URL url) {
|
||||||
|
FormField formField = FormField.textSingleBuilder(ConfigureNodeFields.dataform_xslt.getFieldName())
|
||||||
|
.setValue(url)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the node will deliver payloads with event notifications.
|
||||||
|
*
|
||||||
|
* @param deliver true if the payload will be delivered, false otherwise
|
||||||
|
*/
|
||||||
|
public void setDeliverPayloads(boolean deliver) {
|
||||||
|
writeBoolean(ConfigureNodeFields.deliver_payloads.getFieldName(), deliver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets who should get the replies to items.
|
||||||
|
*
|
||||||
|
* @param reply Defines who should get the reply
|
||||||
|
*/
|
||||||
|
public void setItemReply(ItemReply reply) {
|
||||||
|
FormField formField = FormField.listSingleBuilder(ConfigureNodeFields.itemreply.getFieldName())
|
||||||
|
.setValue(reply)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum number of items to persisted to this node if {@link #isPersistItems()} is
|
||||||
|
* true.
|
||||||
|
*
|
||||||
|
* @param max The maximum number of items to persist
|
||||||
|
*/
|
||||||
|
public void setMaxItems(int max) {
|
||||||
|
FormField formField = FormField.textSingleBuilder(ConfigureNodeFields.max_items.getFieldName())
|
||||||
|
.setValue(max)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum payload size in bytes.
|
||||||
|
*
|
||||||
|
* @param max The maximum payload size
|
||||||
|
*/
|
||||||
|
public void setMaxPayloadSize(int max) {
|
||||||
|
FormField formField = FormField.textSingleBuilder(ConfigureNodeFields.max_payload_size.getFieldName())
|
||||||
|
.setValue(max)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the node type.
|
||||||
|
*
|
||||||
|
* @param type The node type
|
||||||
|
*/
|
||||||
|
public void setNodeType(NodeType type) {
|
||||||
|
FormField formField = FormField.listSingleBuilder(ConfigureNodeFields.node_type.getFieldName())
|
||||||
|
.setValue(type)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether subscribers should be notified when the configuration changes.
|
||||||
|
*
|
||||||
|
* @param notify true if subscribers should be notified, false otherwise
|
||||||
|
*/
|
||||||
|
public void setNotifyConfig(boolean notify) {
|
||||||
|
writeBoolean(ConfigureNodeFields.notify_config.getFieldName(), notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether subscribers should be notified when the node is deleted.
|
||||||
|
*
|
||||||
|
* @param notify true if subscribers should be notified, false otherwise
|
||||||
|
*/
|
||||||
|
public void setNotifyDelete(boolean notify) {
|
||||||
|
writeBoolean(ConfigureNodeFields.notify_delete.getFieldName(), notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether subscribers should be notified when items are deleted
|
||||||
|
* from the node.
|
||||||
|
*
|
||||||
|
* @param notify true if subscribers should be notified, false otherwise
|
||||||
|
*/
|
||||||
|
public void setNotifyRetract(boolean notify) {
|
||||||
|
writeBoolean(ConfigureNodeFields.notify_retract.getFieldName(), notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the NotificationType for the node.
|
||||||
|
*
|
||||||
|
* @param notificationType The enum representing the possible options
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public void setNotificationType(NotificationType notificationType) {
|
||||||
|
FormField formField = FormField.listSingleBuilder(ConfigureNodeFields.notification_type.getFieldName())
|
||||||
|
.setValue(notificationType)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether items should be persisted in the node.
|
||||||
|
*
|
||||||
|
* @param persist true if items should be persisted, false otherwise
|
||||||
|
*/
|
||||||
|
public void setPersistentItems(boolean persist) {
|
||||||
|
writeBoolean(ConfigureNodeFields.persist_items.getFieldName(), persist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to deliver notifications to available users only.
|
||||||
|
*
|
||||||
|
* @param presenceBased true if user must be available, false otherwise
|
||||||
|
*/
|
||||||
|
public void setPresenceBasedDelivery(boolean presenceBased) {
|
||||||
|
writeBoolean(ConfigureNodeFields.presence_based_delivery.getFieldName(), presenceBased);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the publishing model for the node, which determines who may publish to it.
|
||||||
|
*
|
||||||
|
* @param publish The enum representing the possible options for the publishing model
|
||||||
|
*/
|
||||||
|
public void setPublishModel(PublishModel publish) {
|
||||||
|
FormField formField = FormField.listSingleBuilder(ConfigureNodeFields.publish_model.getFieldName())
|
||||||
|
.setValue(publish)
|
||||||
|
.build();
|
||||||
|
write(formField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the roster groups that are allowed to subscribe and retrieve items.
|
||||||
|
*
|
||||||
|
* @param groups The roster groups
|
||||||
|
*/
|
||||||
|
public void setRosterGroupsAllowed(List<? extends CharSequence> groups) {
|
||||||
|
writeListMulti(ConfigureNodeFields.roster_groups_allowed.getFieldName(), groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether subscriptions are allowed.
|
||||||
|
*
|
||||||
|
* @param subscribe true if they are, false otherwise
|
||||||
|
*/
|
||||||
|
public void setSubscribe(boolean subscribe) {
|
||||||
|
writeBoolean(ConfigureNodeFields.subscribe.getFieldName(), subscribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a human readable title for the node.
|
||||||
|
*
|
||||||
|
* @param title The node title
|
||||||
|
*/
|
||||||
|
public void setTitle(CharSequence title) {
|
||||||
|
writeTextSingle(ConfigureNodeFields.title.getFieldName(), title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the type of node data, usually specified by the namespace of the payload (if any).
|
||||||
|
*
|
||||||
|
* @param type The type of node data
|
||||||
|
*/
|
||||||
|
public void setDataType(CharSequence type) {
|
||||||
|
writeTextSingle(ConfigureNodeFields.type.getFieldName(), type);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.PresenceState;
|
||||||
|
import org.jivesoftware.smackx.pubsub.SubscribeOptionFields;
|
||||||
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.ListMultiFormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FillableForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
public class FillableSubscribeForm extends FillableForm {
|
||||||
|
|
||||||
|
public FillableSubscribeForm(DataForm dataForm) {
|
||||||
|
super(dataForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether an entity wants to receive notifications.
|
||||||
|
*
|
||||||
|
* @param deliverNotifications TODO javadoc me please
|
||||||
|
*/
|
||||||
|
public void setDeliverOn(boolean deliverNotifications) {
|
||||||
|
writeBoolean(SubscribeOptionFields.deliver.getFieldName(), deliverNotifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether notifications should be delivered as aggregations or not.
|
||||||
|
*
|
||||||
|
* @param digestOn true to aggregate, false otherwise
|
||||||
|
*/
|
||||||
|
public void setDigestOn(boolean digestOn) {
|
||||||
|
writeBoolean(SubscribeOptionFields.digest.getFieldName(), digestOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum number of milliseconds between sending notification digests.
|
||||||
|
*
|
||||||
|
* @param frequency The frequency in milliseconds
|
||||||
|
*/
|
||||||
|
public void setDigestFrequency(int frequency) {
|
||||||
|
write(SubscribeOptionFields.digest_frequency.getFieldName(), frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the time at which the leased subscription will expire, or has expired.
|
||||||
|
*
|
||||||
|
* @param expire The expiry date
|
||||||
|
*/
|
||||||
|
public void setExpiry(Date expire) {
|
||||||
|
write(SubscribeOptionFields.expire.getFieldName(), expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the entity wants to receive an XMPP message body in
|
||||||
|
* addition to the payload format.
|
||||||
|
*
|
||||||
|
* @param include true to receive the message body, false otherwise
|
||||||
|
*/
|
||||||
|
public void setIncludeBody(boolean include) {
|
||||||
|
writeBoolean(SubscribeOptionFields.include_body.getFieldName(), include);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of {@link PresenceState} for which an entity wants
|
||||||
|
* to receive notifications.
|
||||||
|
*
|
||||||
|
* @param stateValues The list of states
|
||||||
|
*/
|
||||||
|
public void setShowValues(Collection<PresenceState> stateValues) {
|
||||||
|
ListMultiFormField.Builder builder = FormField.listMultiBuilder(SubscribeOptionFields.show_values.getFieldName());
|
||||||
|
for (PresenceState state : stateValues) {
|
||||||
|
builder.addValue(state.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
write(builder.build());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FilledForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
public class FilledConfigureForm extends FilledForm implements ConfigureFormReader {
|
||||||
|
|
||||||
|
public FilledConfigureForm(DataForm dataForm) {
|
||||||
|
super(dataForm);
|
||||||
|
ensureFormType(dataForm, FORM_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FilledForm;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
public class FilledSubscribeForm extends FilledForm implements SubscribeFormReader {
|
||||||
|
|
||||||
|
public FilledSubscribeForm(DataForm dataForm) {
|
||||||
|
super(dataForm);
|
||||||
|
ensureFormType(dataForm, FORM_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.xdata.form.Form;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
public class SubscribeForm extends Form implements SubscribeFormReader {
|
||||||
|
|
||||||
|
public SubscribeForm(DataForm dataForm) {
|
||||||
|
super(dataForm);
|
||||||
|
ensureFormType(dataForm, FORM_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* 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.form;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.pubsub.PresenceState;
|
||||||
|
import org.jivesoftware.smackx.pubsub.SubscribeOptionFields;
|
||||||
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
|
import org.jivesoftware.smackx.xdata.form.FormReader;
|
||||||
|
|
||||||
|
public interface SubscribeFormReader extends FormReader {
|
||||||
|
|
||||||
|
String FORM_TYPE = PubSub.NAMESPACE + "#subscribe_options";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if an entity wants to receive notifications.
|
||||||
|
*
|
||||||
|
* @return true if want to receive, false otherwise
|
||||||
|
*/
|
||||||
|
default boolean isDeliverOn() {
|
||||||
|
return readBoolean(SubscribeOptionFields.deliver.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if notifications should be delivered as aggregations or not.
|
||||||
|
*
|
||||||
|
* @return true to aggregate, false otherwise
|
||||||
|
*/
|
||||||
|
default Boolean isDigestOn() {
|
||||||
|
return readBoolean(SubscribeOptionFields.digest.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum number of milliseconds between sending notification digests.
|
||||||
|
*
|
||||||
|
* @return The frequency in milliseconds
|
||||||
|
*/
|
||||||
|
default Integer getDigestFrequency() {
|
||||||
|
return readInteger(SubscribeOptionFields.digest_frequency.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the time at which the leased subscription will expire, or has expired.
|
||||||
|
*
|
||||||
|
* @return The expiry date
|
||||||
|
* @throws ParseException in case the date could not be parsed.
|
||||||
|
*/
|
||||||
|
default Date getExpiry() throws ParseException {
|
||||||
|
return readDate(SubscribeOptionFields.expire.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the entity wants to receive an XMPP message body in
|
||||||
|
* addition to the payload format.
|
||||||
|
*
|
||||||
|
* @return true to receive the message body, false otherwise
|
||||||
|
*/
|
||||||
|
default Boolean isIncludeBody() {
|
||||||
|
return readBoolean(SubscribeOptionFields.include_body.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link PresenceState} for which an entity wants to receive
|
||||||
|
* notifications.
|
||||||
|
*
|
||||||
|
* @return the list of states
|
||||||
|
*/
|
||||||
|
default List<PresenceState> getShowValues() {
|
||||||
|
List<String> values = readStringValues(SubscribeOptionFields.show_values.getFieldName());
|
||||||
|
List<PresenceState> result = new ArrayList<>(values.size());
|
||||||
|
|
||||||
|
for (String state : values) {
|
||||||
|
result.add(PresenceState.valueOf(state));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smack's implementation of Data Forms (XEP-0004) for PubSub.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.pubsub.form;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue