mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 04:22:05 +01:00
Merge pull request #355 from vanitasvitae/messageFastening
Message fastening
This commit is contained in:
commit
4f3d89e666
12 changed files with 929 additions and 0 deletions
|
@ -120,6 +120,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
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
|
@ -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 = true;
|
||||||
|
|
||||||
|
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;
|
|
@ -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>
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue