1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-26 05:14:49 +02:00
Smack/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/element/OpenPgpContentElement.java
Florian Schmaus 3d4e7938a7 Make ExtensionElement marker interface wrt. QNAME field
ExtensionElement is now a marker interface that requires all
implementation non-abstract classes to carry a static final QNAME
field (of type QName). This is verified by a new unit test.

Also FullyQualifiedElement is renamed to simply XmlElement. XmlElement
is used over ExtensionElement when implementing classes do not
statically know the qualified name of the XML elements they
represent. In general, XmlElement should be used sparingly, and every
XML element should be modeled by its own Java class (implementing
ExtensionElement).
2021-04-18 21:07:19 +02:00

188 lines
6.1 KiB
Java

/**
*
* Copyright 2017-2021 Florian Schmaus, 2018 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.ox.element;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.XmlElement;
import org.jivesoftware.smack.util.MultiMap;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.PacketUtil;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.Jid;
import org.jxmpp.util.XmppDateTime;
/**
* This class describes an OpenPGP content element. It defines the elements and fields that OpenPGP content elements
* do have in common.
*/
public abstract class OpenPgpContentElement implements ExtensionElement {
public static final String ELEM_TO = "to";
public static final String ATTR_JID = "jid";
public static final String ELEM_TIME = "time";
public static final String ATTR_STAMP = "stamp";
public static final String ELEM_PAYLOAD = "payload";
private final Set<? extends Jid> to;
private final Date timestamp;
private final MultiMap<QName, XmlElement> payload;
private String timestampString;
protected OpenPgpContentElement(Set<? extends Jid> to, Date timestamp, List<ExtensionElement> payload) {
this.to = to;
this.timestamp = Objects.requireNonNull(timestamp);
this.payload = new MultiMap<>();
for (ExtensionElement e : payload) {
this.payload.put(e.getQName(), e);
}
}
/**
* Return the set of recipients.
*
* @return recipients.
*/
public final Set<? extends Jid> getTo() {
return to;
}
/**
* Return the timestamp on which the encrypted element has been created.
* This should be checked for sanity by the client.
*
* @return timestamp.
*/
public final Date getTimestamp() {
return timestamp;
}
/**
* Return the payload of the message.
*
* @return payload.
*/
public final List<XmlElement> getExtensions() {
synchronized (payload) {
return payload.values();
}
}
/**
* Return a list of all extensions with the given element name <em>and</em> namespace.
* <p>
* Changes to the returned set will update the stanza extensions, if the returned set is not the empty set.
* </p>
*
* @param elementName the element name, must not be null.
* @param namespace the namespace of the element(s), must not be null.
* @return a set of all matching extensions.
*/
public List<XmlElement> getExtensions(String elementName, String namespace) {
QName key = new QName(namespace, elementName);
return payload.getAll(key);
}
/**
* Returns the first extension of this stanza that has the given namespace.
* <p>
* When possible, use {@link #getExtension(String, String)} instead.
* </p>
*
* @param namespace the namespace of the extension that is desired.
* @return the stanza extension with the given namespace.
*/
public ExtensionElement getExtension(String namespace) {
return PacketUtil.extensionElementFrom(getExtensions(), null, namespace);
}
/**
* Returns the first extension that matches the specified element name and
* namespace, or <code>null</code> if it doesn't exist. If the provided elementName is null,
* only the namespace is matched. Extensions are
* are arbitrary XML elements in standard XMPP stanzas.
*
* @param elementName the XML element name of the extension. (May be null)
* @param namespace the XML element namespace of the extension.
* @param <PE> type of the ExtensionElement.
* @return the extension, or <code>null</code> if it doesn't exist.
*/
@SuppressWarnings("unchecked")
public <PE extends ExtensionElement> PE getExtension(String elementName, String namespace) {
if (namespace == null) {
return null;
}
QName key = new QName(namespace, elementName);
XmlElement packetExtension;
synchronized (payload) {
packetExtension = payload.getFirst(key);
}
if (packetExtension == null) {
return null;
}
return (PE) packetExtension;
}
@Override
public String getNamespace() {
return OpenPgpElement.NAMESPACE;
}
protected void ensureTimestampStringSet() {
if (timestampString != null) return;
timestampString = XmppDateTime.formatXEP0082Date(timestamp);
}
protected void addCommonXml(XmlStringBuilder xml) {
for (Jid toJid : to != null ? to : Collections.<Jid>emptySet()) {
xml.halfOpenElement(ELEM_TO).attribute(ATTR_JID, toJid).closeEmptyElement();
}
ensureTimestampStringSet();
xml.halfOpenElement(ELEM_TIME).attribute(ATTR_STAMP, timestampString).closeEmptyElement();
xml.openElement(ELEM_PAYLOAD);
for (XmlElement element : payload.values()) {
xml.append(element.toXML(getNamespace()));
}
xml.closeElement(ELEM_PAYLOAD);
}
/**
* Return a {@link ByteArrayInputStream} that reads the bytes of the XML representation of this element.
*
* @return InputStream over xml.
*/
public InputStream toInputStream() {
byte[] encoded = toXML().toString().getBytes(Charset.forName("UTF-8"));
return new ByteArrayInputStream(encoded);
}
}