StreamInitiation.java

  1. /**
  2.  *
  3.  * Copyright 2003-2006 Jive Software.
  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.si.packet;

  18. import java.util.Date;

  19. import javax.xml.namespace.QName;

  20. import org.jivesoftware.smack.packet.ExtensionElement;
  21. import org.jivesoftware.smack.packet.IQ;
  22. import org.jivesoftware.smack.util.StringUtils;

  23. import org.jivesoftware.smackx.xdata.packet.DataForm;

  24. import org.jxmpp.util.XmppDateTime;

  25. /**
  26.  * The process by which two entities initiate a stream.
  27.  *
  28.  * @author Alexander Wenckus
  29.  */
  30. public class StreamInitiation extends IQ {

  31.     public static final String ELEMENT = "si";
  32.     public static final String NAMESPACE = "http://jabber.org/protocol/si";

  33.     private String id;

  34.     private String mimeType;

  35.     private File file;

  36.     private Feature featureNegotiation;

  37.     public StreamInitiation() {
  38.         super(ELEMENT, NAMESPACE);
  39.     }

  40.     /**
  41.      * The "id" attribute is an opaque identifier. This attribute MUST be
  42.      * present on type='set', and MUST be a valid string. This SHOULD NOT be
  43.      * sent back on type='result', since the <iq/> "id" attribute provides the
  44.      * only context needed. This value is generated by the Sender, and the same
  45.      * value MUST be used throughout a session when talking to the Receiver.
  46.      *
  47.      * @param id The "id" attribute.
  48.      */
  49.     public void setSessionID(final String id) {
  50.         this.id = id;
  51.     }

  52.     /**
  53.      * Uniquely identifies a stream initiation to the recipient.
  54.      *
  55.      * @return The "id" attribute.
  56.      * @see #setSessionID(String)
  57.      */
  58.     public String getSessionID() {
  59.         return id;
  60.     }

  61.     /**
  62.      * The "mime-type" attribute identifies the MIME-type for the data across
  63.      * the stream. This attribute MUST be a valid MIME-type as registered with
  64.      * the Internet Assigned Numbers Authority (IANA) [3] (specifically, as
  65.      * listed at <http://www.iana.org/assignments/media-types>). During
  66.      * negotiation, this attribute SHOULD be present, and is otherwise not
  67.      * required. If not included during negotiation, its value is assumed to be
  68.      * "binary/octet-stream".
  69.      *
  70.      * @param mimeType The valid mime-type.
  71.      */
  72.     public void setMimeType(final String mimeType) {
  73.         this.mimeType = mimeType;
  74.     }

  75.     /**
  76.      * Identifies the type of file that is desired to be transferred.
  77.      *
  78.      * @return The mime-type.
  79.      * @see #setMimeType(String)
  80.      */
  81.     public String getMimeType() {
  82.         return mimeType;
  83.     }

  84.     /**
  85.      * Sets the file which contains the information pertaining to the file to be
  86.      * transferred.
  87.      *
  88.      * @param file The file identified by the stream initiator to be sent.
  89.      */
  90.     public void setFile(final File file) {
  91.         this.file = file;
  92.     }

  93.     /**
  94.      * Returns the file containing the information about the request.
  95.      *
  96.      * @return Returns the file containing the information about the request.
  97.      */
  98.     public File getFile() {
  99.         return file;
  100.     }

  101.     /**
  102.      * Sets the data form which contains the valid methods of stream negotiation
  103.      * and transfer.
  104.      *
  105.      * @param form The dataform containing the methods.
  106.      */
  107.     public void setFeatureNegotiationForm(final DataForm form) {
  108.         this.featureNegotiation = new Feature(form);
  109.     }

  110.     /**
  111.      * Returns the data form which contains the valid methods of stream
  112.      * negotiation and transfer.
  113.      *
  114.      * @return Returns the data form which contains the valid methods of stream
  115.      *         negotiation and transfer.
  116.      */
  117.     public DataForm getFeatureNegotiationForm() {
  118.         return featureNegotiation.getData();
  119.     }

  120.     @Override
  121.     protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder buf) {
  122.         switch (getType()) {
  123.         case set:
  124.             buf.optAttribute("id", getSessionID());
  125.             buf.optAttribute("mime-type", getMimeType());
  126.             buf.attribute("profile", NAMESPACE + "/profile/file-transfer");
  127.             buf.rightAngleBracket();

  128.             // Add the file section if there is one.
  129.             buf.optElement(file);
  130.             break;
  131.         case result:
  132.             buf.rightAngleBracket();
  133.             break;
  134.         default:
  135.             throw new IllegalArgumentException("IQ Type not understood");
  136.         }
  137.         if (featureNegotiation != null) {
  138.             buf.append(featureNegotiation.toXML());
  139.         }
  140.         return buf;
  141.     }

  142.     /**
  143.      * <ul>
  144.      * <li>size: The size, in bytes, of the data to be sent.</li>
  145.      * <li>name: The name of the file that the Sender wishes to send.</li>
  146.      * <li>date: The last modification time of the file. This is specified
  147.      * using the DateTime profile as described in Jabber Date and Time Profiles.</li>
  148.      * <li>hash: The MD5 sum of the file contents.</li>
  149.      * </ul>
  150.      * <p>
  151.      * &lt;desc&gt; is used to provide a sender-generated description of the
  152.      * file so the receiver can better understand what is being sent. It MUST
  153.      * NOT be sent in the result.
  154.      * </p>
  155.      * <p>
  156.      * When &lt;range&gt; is sent in the offer, it should have no attributes.
  157.      * This signifies that the sender can do ranged transfers. When a Stream
  158.      * Initiation result is sent with the &lt;range&gt; element, it uses these
  159.      * attributes:
  160.      * </p>
  161.      * <ul>
  162.      * <li>offset: Specifies the position, in bytes, to start transferring the
  163.      * file data from. This defaults to zero (0) if not specified.</li>
  164.      * <li>length - Specifies the number of bytes to retrieve starting at
  165.      * offset. This defaults to the length of the file from offset to the end.</li>
  166.      * </ul>
  167.      * Both attributes are OPTIONAL on the &lt;range&gt; element. Sending no
  168.      * attributes is synonymous with not sending the &lt;range&gt; element. When
  169.      * no &lt;range&gt; element is sent in the Stream Initiation result, the
  170.      * Sender MUST send the complete file starting at offset 0. More generally,
  171.      * data is sent over the stream byte for byte starting at the offset
  172.      * position for the length specified.
  173.      *
  174.      * @author Alexander Wenckus
  175.      */
  176.     public static class File implements ExtensionElement {

  177.         public static final String ELEMENT = "file";
  178.         public static final String NAMESPACE = "http://jabber.org/protocol/si/profile/file-transfer";
  179.         public static final QName QNAME = new QName(NAMESPACE, ELEMENT);

  180.         private final String name;

  181.         private final long size;

  182.         private String hash;

  183.         private Date date;

  184.         private String desc;

  185.         private boolean isRanged;

  186.         /**
  187.          * Constructor providing the name of the file and its size.
  188.          *
  189.          * @param name The name of the file.
  190.          * @param size The size of the file in bytes.
  191.          */
  192.         public File(final String name, final long size) {
  193.             if (name == null) {
  194.                 throw new NullPointerException("name cannot be null");
  195.             }

  196.             this.name = name;
  197.             this.size = size;
  198.         }

  199.         /**
  200.          * Returns the file's name.
  201.          *
  202.          * @return Returns the file's name.
  203.          */
  204.         public String getName() {
  205.             return name;
  206.         }

  207.         /**
  208.          * Returns the file's size.
  209.          *
  210.          * @return Returns the file's size.
  211.          */
  212.         public long getSize() {
  213.             return size;
  214.         }

  215.         /**
  216.          * Sets the MD5 sum of the file's contents.
  217.          *
  218.          * @param hash The MD5 sum of the file's contents.
  219.          */
  220.         public void setHash(final String hash) {
  221.             this.hash = hash;
  222.         }

  223.         /**
  224.          * Returns the MD5 sum of the file's contents.
  225.          *
  226.          * @return Returns the MD5 sum of the file's contents
  227.          */
  228.         public String getHash() {
  229.             return hash;
  230.         }

  231.         /**
  232.          * Sets the date that the file was last modified.
  233.          *
  234.          * @param date The date that the file was last modified.
  235.          */
  236.         public void setDate(Date date) {
  237.             this.date = date;
  238.         }

  239.         /**
  240.          * Returns the date that the file was last modified.
  241.          *
  242.          * @return Returns the date that the file was last modified.
  243.          */
  244.         public Date getDate() {
  245.             return date;
  246.         }

  247.         /**
  248.          * Sets the description of the file.
  249.          *
  250.          * @param desc The description of the file so that the file receiver can
  251.          *             know what file it is.
  252.          */
  253.         public void setDesc(final String desc) {
  254.             this.desc = desc;
  255.         }

  256.         /**
  257.          * Returns the description of the file.
  258.          *
  259.          * @return Returns the description of the file.
  260.          */
  261.         public String getDesc() {
  262.             return desc;
  263.         }

  264.         /**
  265.          * True if a range can be provided and false if it cannot.
  266.          *
  267.          * @param isRanged True if a range can be provided and false if it cannot.
  268.          */
  269.         public void setRanged(final boolean isRanged) {
  270.             this.isRanged = isRanged;
  271.         }

  272.         /**
  273.          * Returns whether or not the initiator can support a range for the file
  274.          * transfer.
  275.          *
  276.          * @return Returns whether or not the initiator can support a range for
  277.          *         the file transfer.
  278.          */
  279.         public boolean isRanged() {
  280.             return isRanged;
  281.         }

  282.         @Override
  283.         public String getElementName() {
  284.             return QNAME.getLocalPart();
  285.         }

  286.         @Override
  287.         public String getNamespace() {
  288.             return QNAME.getNamespaceURI();
  289.         }

  290.         @Override
  291.         public String toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
  292.             StringBuilder buffer = new StringBuilder();

  293.             buffer.append('<').append(getElementName()).append(" xmlns=\"")
  294.                     .append(getNamespace()).append("\" ");

  295.             if (getName() != null) {
  296.                 buffer.append("name=\"").append(StringUtils.escapeForXmlAttribute(getName())).append("\" ");
  297.             }

  298.             if (getSize() > 0) {
  299.                 buffer.append("size=\"").append(getSize()).append("\" ");
  300.             }

  301.             if (getDate() != null) {
  302.                 buffer.append("date=\"").append(XmppDateTime.formatXEP0082Date(date)).append("\" ");
  303.             }

  304.             if (getHash() != null) {
  305.                 buffer.append("hash=\"").append(getHash()).append("\" ");
  306.             }

  307.             if ((desc != null && desc.length() > 0) || isRanged) {
  308.                 buffer.append('>');
  309.                 if (getDesc() != null && desc.length() > 0) {
  310.                     buffer.append("<desc>").append(StringUtils.escapeForXmlText(getDesc())).append("</desc>");
  311.                 }
  312.                 if (isRanged()) {
  313.                     buffer.append("<range/>");
  314.                 }
  315.                 buffer.append("</").append(getElementName()).append('>');
  316.             }
  317.             else {
  318.                 buffer.append("/>");
  319.             }
  320.             return buffer.toString();
  321.         }
  322.     }

  323.     /**
  324.      * The feature negotiation portion of the StreamInitiation packet.
  325.      *
  326.      * @author Alexander Wenckus
  327.      *
  328.      */
  329.     public static class Feature implements ExtensionElement {

  330.         public static final QName QNAME = new QName("http://jabber.org/protocol/feature-neg", "feature");

  331.         private final DataForm data;

  332.         /**
  333.          * The dataform can be provided as part of the constructor.
  334.          *
  335.          * @param data The dataform.
  336.          */
  337.         public Feature(final DataForm data) {
  338.             this.data = data;
  339.         }

  340.         /**
  341.          * Returns the dataform associated with the feature negotiation.
  342.          *
  343.          * @return Returns the dataform associated with the feature negotiation.
  344.          */
  345.         public DataForm getData() {
  346.             return data;
  347.         }

  348.         @Override
  349.         public String getElementName() {
  350.             return QNAME.getLocalPart();
  351.         }

  352.         @Override
  353.         public String getNamespace() {
  354.             return QNAME.getNamespaceURI();
  355.         }

  356.         @Override
  357.         public String toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
  358.             StringBuilder buf = new StringBuilder();
  359.             buf
  360.                     .append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">");
  361.             buf.append(data.toXML());
  362.             buf.append("</feature>");
  363.             return buf.toString();
  364.         }
  365.     }
  366. }