OpenPgpContentElement.java

  1. /**
  2.  *
  3.  * Copyright 2017-2021 Florian Schmaus, 2018 Paul Schaub.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.ox.element;

  18. import java.io.ByteArrayInputStream;
  19. import java.io.InputStream;
  20. import java.nio.charset.Charset;
  21. import java.util.Collections;
  22. import java.util.Date;
  23. import java.util.List;
  24. import java.util.Set;

  25. import javax.xml.namespace.QName;

  26. import org.jivesoftware.smack.packet.ExtensionElement;
  27. import org.jivesoftware.smack.packet.XmlElement;
  28. import org.jivesoftware.smack.util.MultiMap;
  29. import org.jivesoftware.smack.util.Objects;
  30. import org.jivesoftware.smack.util.PacketUtil;
  31. import org.jivesoftware.smack.util.XmlStringBuilder;

  32. import org.jxmpp.jid.Jid;
  33. import org.jxmpp.util.XmppDateTime;

  34. /**
  35.  * This class describes an OpenPGP content element. It defines the elements and fields that OpenPGP content elements
  36.  * do have in common.
  37.  */
  38. public abstract class OpenPgpContentElement implements ExtensionElement {

  39.     public static final String ELEM_TO = "to";
  40.     public static final String ATTR_JID = "jid";
  41.     public static final String ELEM_TIME = "time";
  42.     public static final String ATTR_STAMP = "stamp";
  43.     public static final String ELEM_PAYLOAD = "payload";

  44.     private final Set<? extends Jid> to;
  45.     private final Date timestamp;
  46.     private final MultiMap<QName, XmlElement> payload;

  47.     private String timestampString;

  48.     protected OpenPgpContentElement(Set<? extends Jid> to, Date timestamp, List<ExtensionElement> payload) {
  49.         this.to = to;
  50.         this.timestamp = Objects.requireNonNull(timestamp);
  51.         this.payload = new MultiMap<>();
  52.         for (ExtensionElement e : payload) {
  53.             this.payload.put(e.getQName(), e);
  54.         }
  55.     }

  56.     /**
  57.      * Return the set of recipients.
  58.      *
  59.      * @return recipients.
  60.      */
  61.     public final Set<? extends Jid> getTo() {
  62.         return to;
  63.     }

  64.     /**
  65.      * Return the timestamp on which the encrypted element has been created.
  66.      * This should be checked for sanity by the client.
  67.      *
  68.      * @return timestamp.
  69.      */
  70.     public final Date getTimestamp() {
  71.         return timestamp;
  72.     }

  73.     /**
  74.      * Return the payload of the message.
  75.      *
  76.      * @return payload.
  77.      */
  78.     public final List<XmlElement> getExtensions() {
  79.         synchronized (payload) {
  80.             return payload.values();
  81.         }
  82.     }

  83.     /**
  84.      * Return a list of all extensions with the given element name <em>and</em> namespace.
  85.      * <p>
  86.      * Changes to the returned set will update the stanza extensions, if the returned set is not the empty set.
  87.      * </p>
  88.      *
  89.      * @param elementName the element name, must not be null.
  90.      * @param namespace the namespace of the element(s), must not be null.
  91.      * @return a set of all matching extensions.
  92.      */
  93.     public List<XmlElement> getExtensions(String elementName, String namespace) {
  94.         QName key = new QName(namespace, elementName);
  95.         return payload.getAll(key);
  96.     }

  97.     /**
  98.      * Returns the first extension of this stanza that has the given namespace.
  99.      * <p>
  100.      * When possible, use {@link #getExtension(String, String)} instead.
  101.      * </p>
  102.      *
  103.      * @param namespace the namespace of the extension that is desired.
  104.      * @return the stanza extension with the given namespace.
  105.      */
  106.     public ExtensionElement getExtension(String namespace) {
  107.         return PacketUtil.extensionElementFrom(getExtensions(), null, namespace);
  108.     }

  109.     /**
  110.      * Returns the first extension that matches the specified element name and
  111.      * namespace, or <code>null</code> if it doesn't exist. If the provided elementName is null,
  112.      * only the namespace is matched. Extensions are
  113.      * are arbitrary XML elements in standard XMPP stanzas.
  114.      *
  115.      * @param elementName the XML element name of the extension. (May be null)
  116.      * @param namespace the XML element namespace of the extension.
  117.      * @param <PE> type of the ExtensionElement.
  118.      * @return the extension, or <code>null</code> if it doesn't exist.
  119.      */
  120.     @SuppressWarnings("unchecked")
  121.     public <PE extends ExtensionElement> PE getExtension(String elementName, String namespace) {
  122.         if (namespace == null) {
  123.             return null;
  124.         }
  125.         QName key = new QName(namespace, elementName);
  126.         XmlElement packetExtension;
  127.         synchronized (payload) {
  128.             packetExtension = payload.getFirst(key);
  129.         }
  130.         if (packetExtension == null) {
  131.             return null;
  132.         }
  133.         return (PE) packetExtension;
  134.     }


  135.     @Override
  136.     public String getNamespace() {
  137.         return OpenPgpElement.NAMESPACE;
  138.     }

  139.     protected void ensureTimestampStringSet() {
  140.         if (timestampString != null) return;

  141.         timestampString = XmppDateTime.formatXEP0082Date(timestamp);
  142.     }

  143.     protected void addCommonXml(XmlStringBuilder xml) {
  144.         for (Jid toJid : to != null ? to : Collections.<Jid>emptySet()) {
  145.             xml.halfOpenElement(ELEM_TO).attribute(ATTR_JID, toJid).closeEmptyElement();
  146.         }

  147.         ensureTimestampStringSet();
  148.         xml.halfOpenElement(ELEM_TIME).attribute(ATTR_STAMP, timestampString).closeEmptyElement();

  149.         xml.openElement(ELEM_PAYLOAD);
  150.         for (XmlElement element : payload.values()) {
  151.             xml.append(element.toXML(getNamespace()));
  152.         }
  153.         xml.closeElement(ELEM_PAYLOAD);
  154.     }

  155.     /**
  156.      * Return a {@link ByteArrayInputStream} that reads the bytes of the XML representation of this element.
  157.      *
  158.      * @return InputStream over xml.
  159.      */
  160.     public InputStream toInputStream() {
  161.         byte[] encoded = toXML().toString().getBytes(Charset.forName("UTF-8"));
  162.         return new ByteArrayInputStream(encoded);
  163.     }
  164. }