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 org.jivesoftware.smack.packet.IQ;
  20. import org.jivesoftware.smack.packet.ExtensionElement;
  21. import org.jivesoftware.smack.util.StringUtils;
  22. import org.jxmpp.util.XmppDateTime;
  23. import org.jivesoftware.smackx.xdata.packet.DataForm;

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

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

  32.     private String id;

  33.     private String mimeType;

  34.     private File file;

  35.     private Feature featureNegotiation;

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

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

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

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

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

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

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

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

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

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

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

  141.     /**
  142.      * <ul>
  143.      * <li>size: The size, in bytes, of the data to be sent.</li>
  144.      * <li>name: The name of the file that the Sender wishes to send.</li>
  145.      * <li>date: The last modification time of the file. This is specified
  146.      * using the DateTime profile as described in Jabber Date and Time Profiles.</li>
  147.      * <li>hash: The MD5 sum of the file contents.</li>
  148.      * </ul>
  149.      * <p/>
  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 <range> 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.      * <p/>
  168.      * <p/>
  169.      * Both attributes are OPTIONAL on the &lt;range&gt; element. Sending no
  170.      * attributes is synonymous with not sending the &lt;range&gt; element. When
  171.      * no &lt;range&gt; element is sent in the Stream Initiation result, the
  172.      * Sender MUST send the complete file starting at offset 0. More generally,
  173.      * data is sent over the stream byte for byte starting at the offset
  174.      * position for the length specified.
  175.      *
  176.      * @author Alexander Wenckus
  177.      */
  178.     public static class File implements ExtensionElement {

  179.         private final String name;

  180.         private final long size;

  181.         private String hash;

  182.         private Date date;

  183.         private String desc;

  184.         private boolean isRanged;

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

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

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

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

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

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

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

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

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

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

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

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

  281.         public String getElementName() {
  282.             return "file";
  283.         }

  284.         public String getNamespace() {
  285.             return "http://jabber.org/protocol/si/profile/file-transfer";
  286.         }

  287.         public String toXML() {
  288.             StringBuilder buffer = new StringBuilder();

  289.             buffer.append("<").append(getElementName()).append(" xmlns=\"")
  290.                     .append(getNamespace()).append("\" ");

  291.             if (getName() != null) {
  292.                 buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");
  293.             }

  294.             if (getSize() > 0) {
  295.                 buffer.append("size=\"").append(getSize()).append("\" ");
  296.             }

  297.             if (getDate() != null) {
  298.                 buffer.append("date=\"").append(XmppDateTime.formatXEP0082Date(date)).append("\" ");
  299.             }

  300.             if (getHash() != null) {
  301.                 buffer.append("hash=\"").append(getHash()).append("\" ");
  302.             }

  303.             if ((desc != null && desc.length() > 0) || isRanged) {
  304.                 buffer.append(">");
  305.                 if (getDesc() != null && desc.length() > 0) {
  306.                     buffer.append("<desc>").append(StringUtils.escapeForXML(getDesc())).append("</desc>");
  307.                 }
  308.                 if (isRanged()) {
  309.                     buffer.append("<range/>");
  310.                 }
  311.                 buffer.append("</").append(getElementName()).append(">");
  312.             }
  313.             else {
  314.                 buffer.append("/>");
  315.             }
  316.             return buffer.toString();
  317.         }
  318.     }

  319.     /**
  320.      * The feature negotiation portion of the StreamInitiation packet.
  321.      *
  322.      * @author Alexander Wenckus
  323.      *
  324.      */
  325.     public class Feature implements ExtensionElement {

  326.         private final DataForm data;

  327.         /**
  328.          * The dataform can be provided as part of the constructor.
  329.          *
  330.          * @param data The dataform.
  331.          */
  332.         public Feature(final DataForm data) {
  333.             this.data = data;
  334.         }

  335.         /**
  336.          * Returns the dataform associated with the feature negotiation.
  337.          *
  338.          * @return Returns the dataform associated with the feature negotiation.
  339.          */
  340.         public DataForm getData() {
  341.             return data;
  342.         }

  343.         public String getNamespace() {
  344.             return "http://jabber.org/protocol/feature-neg";
  345.         }

  346.         public String getElementName() {
  347.             return "feature";
  348.         }

  349.         public String toXML() {
  350.             StringBuilder buf = new StringBuilder();
  351.             buf
  352.                     .append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">");
  353.             buf.append(data.toXML());
  354.             buf.append("</feature>");
  355.             return buf.toString();
  356.         }
  357.     }
  358. }