Jingle.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software, 2014-2022 Florian Schmaus
  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.jingle.element;

  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;

  21. import org.jivesoftware.smack.XMPPConnection;
  22. import org.jivesoftware.smack.packet.IQ;
  23. import org.jivesoftware.smack.packet.IqBuilder;
  24. import org.jivesoftware.smack.packet.IqData;
  25. import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
  26. import org.jivesoftware.smack.util.Objects;
  27. import org.jivesoftware.smack.util.StringUtils;

  28. import org.jxmpp.jid.FullJid;

  29. /**
  30.  * The Jingle element.
  31.  *
  32.  * <h2>Jingle Element Structure</h2>
  33.  * <pre>{@code
  34.  * jingle
  35.  * │  action (REQUIRED, XEP-0166 § 7.2)
  36.  * |    content-accept
  37.  * |    content-add
  38.  * |    content-modify
  39.  * |    content-reject
  40.  * |    content-remove
  41.  * |    description-info
  42.  * |    security-info
  43.  * |    session-accept
  44.  * |    session-info
  45.  * |    session-initiate
  46.  * |    transport-accept
  47.  * |    transport-info
  48.  * |    transport-reject
  49.  * |    transport-replace
  50.  * │  initiator (RECOMMENDED for session initiate, NOT RECOMMENDED otherwise, full JID, XEP-0166 § 7.1)
  51.  * │  responder (RECOMMENDED for session accept, NOT RECOMMENDED otherwise, full JID. XEP-0166 § 7.1)
  52.  * │  sid (REQUIRED, SHOULD match XML Nmtoken production)
  53.  * │
  54.  * ├── <reason/> (optional, XEP-0166 § 7.4)
  55.  * │    │
  56.  * │    └──(alternative─session│busy│..)
  57.  * │
  58.  * └── <content/> (one or more, XEP-0166 § 7.3)
  59.  *      │  creator (REQUIRED, must be one of)
  60.  *      |    initiator
  61.  *      |    responder
  62.  *      │  disposition (OPTIONAL)
  63.  *      │  name (REQUIRED)
  64.  *      │  senders (OPTIONAL, except when content-modify then REQUIRED)
  65.  *      |    both (default)
  66.  *      |    initiator
  67.  *      |    none
  68.  *      |    responder
  69.  *      │
  70.  *      ├──description
  71.  *      │  │  media
  72.  *      │  │  xmlns
  73.  *      │  │
  74.  *      │  ├──payload─type
  75.  *      │  │
  76.  *      │  └──file (XEP─0234)
  77.  *      │
  78.  *      └──transport
  79.  *         │  xmlns
  80.  *         │  pwd (OPTIONAL, XEP-0176 Jingle ICE)
  81.  *         │  ufrag (OPTIONAL, XEP-0176 Jingle ICE)
  82.  *         │  mode (XEP-0234 Jingle File Transfer)
  83.  *         │  sid (XEP-0234 Jingle File Transfer)
  84.  *         │
  85.  *         └──candidate
  86.  *               component
  87.  *               foundation
  88.  *               generation
  89.  *               id
  90.  *               ip
  91.  *               network
  92.  *               port
  93.  *               priority
  94.  *               protocol
  95.  *               type
  96.  * }</pre>
  97.  *
  98.  * @author Florian Schmaus
  99.  */
  100. public final class Jingle extends IQ {

  101.     public static final String NAMESPACE = "urn:xmpp:jingle:1";

  102.     public static final String ACTION_ATTRIBUTE_NAME = "action";

  103.     public static final String INITIATOR_ATTRIBUTE_NAME = "initiator";

  104.     public static final String RESPONDER_ATTRIBUTE_NAME = "responder";

  105.     public static final String SESSION_ID_ATTRIBUTE_NAME = "sid";

  106.     public static final String ELEMENT = "jingle";

  107.     /**
  108.      * The session ID related to this session. The session ID is a unique identifier generated by the initiator. This
  109.      * should match the XML Nmtoken production so that XML character escaping is not needed for characters such as &.
  110.      */
  111.     private final String sessionId;

  112.     /**
  113.      * The jingle action. This attribute is required.
  114.      */
  115.     private final JingleAction action;

  116.     private final FullJid initiator;

  117.     private final FullJid responder;

  118.     private final JingleReason reason;

  119.     private final List<JingleContent> contents;

  120.     private Jingle(Builder builder, String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
  121.                     List<JingleContent> contents) {
  122.         super(builder, ELEMENT, NAMESPACE);
  123.         this.sessionId = StringUtils.requireNotNullNorEmpty(sessionId, "Jingle session ID must not be null");
  124.         this.action = Objects.requireNonNull(action, "Jingle action must not be null");
  125.         this.initiator = initiator;
  126.         this.responder = responder;
  127.         this.reason = reason;
  128.         if (contents != null) {
  129.             this.contents = Collections.unmodifiableList(contents);
  130.         }
  131.         else {
  132.             this.contents = Collections.emptyList();
  133.         }
  134.         setType(Type.set);
  135.     }

  136.     /**
  137.      * Get the initiator. The initiator will be the full JID of the entity that has initiated the flow (which may be
  138.      * different to the "from" address in the IQ)
  139.      *
  140.      * @return the initiator
  141.      */
  142.     public FullJid getInitiator() {
  143.         return initiator;
  144.     }

  145.     /**
  146.      * Get the responder. The responder is the full JID of the entity that has replied to the initiation (which may be
  147.      * different to the "to" address in the IQ).
  148.      *
  149.      * @return the responder
  150.      */
  151.     public FullJid getResponder() {
  152.         return responder;
  153.     }

  154.     /**
  155.      * Returns the session ID related to the session. The session ID is a unique identifier generated by the initiator.
  156.      * This should match the XML Nmtoken production so that XML character escaping is not needed for characters such as
  157.      * &amp;.
  158.      *
  159.      * @return Returns the session ID related to the session.
  160.      */
  161.     public String getSid() {
  162.         return sessionId;
  163.     }

  164.     /**
  165.      * Get the action specified in the jingle IQ.
  166.      *
  167.      * @return the action.
  168.      */
  169.     public JingleAction getAction() {
  170.         return action;
  171.     }

  172.     public JingleReason getReason() {
  173.         return reason;
  174.     }

  175.     /**
  176.      * Get a List of the contents.
  177.      *
  178.      * @return the contents.
  179.      */
  180.     public List<JingleContent> getContents() {
  181.         return contents;
  182.     }

  183.     /**
  184.      * Get the only jingle content if one exists, or <code>null</code>. This method will throw an
  185.      * {@link IllegalStateException} if there is more than one jingle content.
  186.      *
  187.      * @return a JingleContent instance or <code>null</code>.
  188.      * @throws IllegalStateException if there is more than one jingle content.
  189.      */
  190.     public JingleContent getSoleContentOrThrow() {
  191.         if (contents.isEmpty()) {
  192.             return null;
  193.         }

  194.         if (contents.size() > 1) {
  195.             throw new IllegalStateException();
  196.         }

  197.         return contents.get(0);
  198.     }

  199.     @Override
  200.     protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
  201.         xml.optAttribute(INITIATOR_ATTRIBUTE_NAME, getInitiator());
  202.         xml.optAttribute(RESPONDER_ATTRIBUTE_NAME, getResponder());
  203.         xml.optAttribute(ACTION_ATTRIBUTE_NAME, getAction());
  204.         xml.optAttribute(SESSION_ID_ATTRIBUTE_NAME, getSid());
  205.         xml.rightAngleBracket();

  206.         xml.optElement(reason);

  207.         xml.append(contents);

  208.         return xml;
  209.     }

  210.     /**
  211.      * Deprecated, do not use.
  212.      *
  213.      * @return a builder.
  214.      * @deprecated use {@link #builder(XMPPConnection)} instead.
  215.      */
  216.     @Deprecated
  217.     // TODO: Remove in Smack 4.6.
  218.     public static Builder getBuilder() {
  219.         return builder(StandardStanzaIdSource.DEFAULT.getNewStanzaId());
  220.     }

  221.     public static Builder builder(XMPPConnection connection) {
  222.         return new Builder(connection);
  223.     }

  224.     public static Builder builder(IqData iqData) {
  225.         return new Builder(iqData);
  226.     }

  227.     public static Builder builder(String stanzaId) {
  228.         return new Builder(stanzaId);
  229.     }

  230.     public static final class Builder extends IqBuilder<Builder, Jingle> {
  231.         private String sid;

  232.         private JingleAction action;

  233.         private FullJid initiator;

  234.         private FullJid responder;

  235.         private JingleReason reason;

  236.         private List<JingleContent> contents;

  237.         Builder(IqData iqCommon) {
  238.             super(iqCommon);
  239.         }

  240.         Builder(XMPPConnection connection) {
  241.             super(connection);
  242.         }

  243.         Builder(String stanzaId) {
  244.             super(stanzaId);
  245.         }

  246.         public Builder setSessionId(String sessionId) {
  247.             StringUtils.requireNotNullNorEmpty(sessionId, "Session ID must not be null nor empty");
  248.             this.sid = sessionId;
  249.             return this;
  250.         }

  251.         public Builder setAction(JingleAction action) {
  252.             this.action = action;
  253.             return this;
  254.         }

  255.         public Builder setInitiator(FullJid initator) {
  256.             this.initiator = initator;
  257.             return this;
  258.         }

  259.         public Builder setResponder(FullJid responder) {
  260.             this.responder = responder;
  261.             return this;
  262.         }

  263.         public Builder addJingleContent(JingleContent content) {
  264.             if (contents == null) {
  265.                 contents = new ArrayList<>(1);
  266.             }
  267.             contents.add(content);
  268.             return this;
  269.         }

  270.         public Builder setReason(JingleReason.Reason reason) {
  271.             this.reason = new JingleReason(reason);
  272.             return this;
  273.         }

  274.         public Builder setReason(JingleReason reason) {
  275.             this.reason = reason;
  276.             return this;
  277.         }

  278.         @Override
  279.         public Jingle build() {
  280.             return new Jingle(this, sid, action, initiator, responder, reason, contents);
  281.         }

  282.         @Override
  283.         public Builder getThis() {
  284.             return this;
  285.         }
  286.     }
  287. }