• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * $RCSfile$
3  * $Revision$
4  * $Date$
5  *
6  * Copyright 2003-2006 Jive Software.
7  *
8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package org.jivesoftware.smackx.packet;
21 
22 import java.util.Date;
23 
24 import org.jivesoftware.smack.packet.IQ;
25 import org.jivesoftware.smack.packet.PacketExtension;
26 import org.jivesoftware.smack.util.StringUtils;
27 
28 /**
29  * The process by which two entities initiate a stream.
30  *
31  * @author Alexander Wenckus
32  */
33 public class StreamInitiation extends IQ {
34 
35     private String id;
36 
37     private String mimeType;
38 
39     private File file;
40 
41     private Feature featureNegotiation;
42 
43     /**
44      * The "id" attribute is an opaque identifier. This attribute MUST be
45      * present on type='set', and MUST be a valid string. This SHOULD NOT be
46      * sent back on type='result', since the <iq/> "id" attribute provides the
47      * only context needed. This value is generated by the Sender, and the same
48      * value MUST be used throughout a session when talking to the Receiver.
49      *
50      * @param id The "id" attribute.
51      */
setSesssionID(final String id)52     public void setSesssionID(final String id) {
53         this.id = id;
54     }
55 
56     /**
57      * Uniquely identifies a stream initiation to the recipient.
58      *
59      * @return The "id" attribute.
60      * @see #setSesssionID(String)
61      */
getSessionID()62     public String getSessionID() {
63         return id;
64     }
65 
66     /**
67      * The "mime-type" attribute identifies the MIME-type for the data across
68      * the stream. This attribute MUST be a valid MIME-type as registered with
69      * the Internet Assigned Numbers Authority (IANA) [3] (specifically, as
70      * listed at <http://www.iana.org/assignments/media-types>). During
71      * negotiation, this attribute SHOULD be present, and is otherwise not
72      * required. If not included during negotiation, its value is assumed to be
73      * "binary/octect-stream".
74      *
75      * @param mimeType The valid mime-type.
76      */
setMimeType(final String mimeType)77     public void setMimeType(final String mimeType) {
78         this.mimeType = mimeType;
79     }
80 
81     /**
82      * Identifies the type of file that is desired to be transfered.
83      *
84      * @return The mime-type.
85      * @see #setMimeType(String)
86      */
getMimeType()87     public String getMimeType() {
88         return mimeType;
89     }
90 
91     /**
92      * Sets the file which contains the information pertaining to the file to be
93      * transfered.
94      *
95      * @param file The file identified by the stream initiator to be sent.
96      */
setFile(final File file)97     public void setFile(final File file) {
98         this.file = file;
99     }
100 
101     /**
102      * Returns the file containing the information about the request.
103      *
104      * @return Returns the file containing the information about the request.
105      */
getFile()106     public File getFile() {
107         return file;
108     }
109 
110     /**
111      * Sets the data form which contains the valid methods of stream neotiation
112      * and transfer.
113      *
114      * @param form The dataform containing the methods.
115      */
setFeatureNegotiationForm(final DataForm form)116     public void setFeatureNegotiationForm(final DataForm form) {
117         this.featureNegotiation = new Feature(form);
118     }
119 
120     /**
121      * Returns the data form which contains the valid methods of stream
122      * neotiation and transfer.
123      *
124      * @return Returns the data form which contains the valid methods of stream
125      *         neotiation and transfer.
126      */
getFeatureNegotiationForm()127     public DataForm getFeatureNegotiationForm() {
128         return featureNegotiation.getData();
129     }
130 
131     /*
132       * (non-Javadoc)
133       *
134       * @see org.jivesoftware.smack.packet.IQ#getChildElementXML()
135       */
getChildElementXML()136     public String getChildElementXML() {
137         StringBuilder buf = new StringBuilder();
138         if (this.getType().equals(IQ.Type.SET)) {
139             buf.append("<si xmlns=\"http://jabber.org/protocol/si\" ");
140             if (getSessionID() != null) {
141                 buf.append("id=\"").append(getSessionID()).append("\" ");
142             }
143             if (getMimeType() != null) {
144                 buf.append("mime-type=\"").append(getMimeType()).append("\" ");
145             }
146             buf
147                     .append("profile=\"http://jabber.org/protocol/si/profile/file-transfer\">");
148 
149             // Add the file section if there is one.
150             String fileXML = file.toXML();
151             if (fileXML != null) {
152                 buf.append(fileXML);
153             }
154         }
155         else if (this.getType().equals(IQ.Type.RESULT)) {
156             buf.append("<si xmlns=\"http://jabber.org/protocol/si\">");
157         }
158         else {
159             throw new IllegalArgumentException("IQ Type not understood");
160         }
161         if (featureNegotiation != null) {
162             buf.append(featureNegotiation.toXML());
163         }
164         buf.append("</si>");
165         return buf.toString();
166     }
167 
168     /**
169      * <ul>
170      * <li>size: The size, in bytes, of the data to be sent.</li>
171      * <li>name: The name of the file that the Sender wishes to send.</li>
172      * <li>date: The last modification time of the file. This is specified
173      * using the DateTime profile as described in Jabber Date and Time Profiles.</li>
174      * <li>hash: The MD5 sum of the file contents.</li>
175      * </ul>
176      * <p/>
177      * <p/>
178      * &lt;desc&gt; is used to provide a sender-generated description of the
179      * file so the receiver can better understand what is being sent. It MUST
180      * NOT be sent in the result.
181      * <p/>
182      * <p/>
183      * When &lt;range&gt; is sent in the offer, it should have no attributes.
184      * This signifies that the sender can do ranged transfers. When a Stream
185      * Initiation result is sent with the <range> element, it uses these
186      * attributes:
187      * <p/>
188      * <ul>
189      * <li>offset: Specifies the position, in bytes, to start transferring the
190      * file data from. This defaults to zero (0) if not specified.</li>
191      * <li>length - Specifies the number of bytes to retrieve starting at
192      * offset. This defaults to the length of the file from offset to the end.</li>
193      * </ul>
194      * <p/>
195      * <p/>
196      * Both attributes are OPTIONAL on the &lt;range&gt; element. Sending no
197      * attributes is synonymous with not sending the &lt;range&gt; element. When
198      * no &lt;range&gt; element is sent in the Stream Initiation result, the
199      * Sender MUST send the complete file starting at offset 0. More generally,
200      * data is sent over the stream byte for byte starting at the offset
201      * position for the length specified.
202      *
203      * @author Alexander Wenckus
204      */
205     public static class File implements PacketExtension {
206 
207         private final String name;
208 
209         private final long size;
210 
211         private String hash;
212 
213         private Date date;
214 
215         private String desc;
216 
217         private boolean isRanged;
218 
219         /**
220          * Constructor providing the name of the file and its size.
221          *
222          * @param name The name of the file.
223          * @param size The size of the file in bytes.
224          */
File(final String name, final long size)225         public File(final String name, final long size) {
226             if (name == null) {
227                 throw new NullPointerException("name cannot be null");
228             }
229 
230             this.name = name;
231             this.size = size;
232         }
233 
234         /**
235          * Returns the file's name.
236          *
237          * @return Returns the file's name.
238          */
getName()239         public String getName() {
240             return name;
241         }
242 
243         /**
244          * Returns the file's size.
245          *
246          * @return Returns the file's size.
247          */
getSize()248         public long getSize() {
249             return size;
250         }
251 
252         /**
253          * Sets the MD5 sum of the file's contents
254          *
255          * @param hash The MD5 sum of the file's contents.
256          */
setHash(final String hash)257         public void setHash(final String hash) {
258             this.hash = hash;
259         }
260 
261         /**
262          * Returns the MD5 sum of the file's contents
263          *
264          * @return Returns the MD5 sum of the file's contents
265          */
getHash()266         public String getHash() {
267             return hash;
268         }
269 
270         /**
271          * Sets the date that the file was last modified.
272          *
273          * @param date The date that the file was last modified.
274          */
setDate(Date date)275         public void setDate(Date date) {
276             this.date = date;
277         }
278 
279         /**
280          * Returns the date that the file was last modified.
281          *
282          * @return Returns the date that the file was last modified.
283          */
getDate()284         public Date getDate() {
285             return date;
286         }
287 
288         /**
289          * Sets the description of the file.
290          *
291          * @param desc The description of the file so that the file reciever can
292          *             know what file it is.
293          */
setDesc(final String desc)294         public void setDesc(final String desc) {
295             this.desc = desc;
296         }
297 
298         /**
299          * Returns the description of the file.
300          *
301          * @return Returns the description of the file.
302          */
getDesc()303         public String getDesc() {
304             return desc;
305         }
306 
307         /**
308          * True if a range can be provided and false if it cannot.
309          *
310          * @param isRanged True if a range can be provided and false if it cannot.
311          */
setRanged(final boolean isRanged)312         public void setRanged(final boolean isRanged) {
313             this.isRanged = isRanged;
314         }
315 
316         /**
317          * Returns whether or not the initiator can support a range for the file
318          * tranfer.
319          *
320          * @return Returns whether or not the initiator can support a range for
321          *         the file tranfer.
322          */
isRanged()323         public boolean isRanged() {
324             return isRanged;
325         }
326 
getElementName()327         public String getElementName() {
328             return "file";
329         }
330 
getNamespace()331         public String getNamespace() {
332             return "http://jabber.org/protocol/si/profile/file-transfer";
333         }
334 
toXML()335         public String toXML() {
336             StringBuilder buffer = new StringBuilder();
337 
338             buffer.append("<").append(getElementName()).append(" xmlns=\"")
339                     .append(getNamespace()).append("\" ");
340 
341             if (getName() != null) {
342                 buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");
343             }
344 
345             if (getSize() > 0) {
346                 buffer.append("size=\"").append(getSize()).append("\" ");
347             }
348 
349             if (getDate() != null) {
350                 buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");
351             }
352 
353             if (getHash() != null) {
354                 buffer.append("hash=\"").append(getHash()).append("\" ");
355             }
356 
357             if ((desc != null && desc.length() > 0) || isRanged) {
358                 buffer.append(">");
359                 if (getDesc() != null && desc.length() > 0) {
360                     buffer.append("<desc>").append(StringUtils.escapeForXML(getDesc())).append("</desc>");
361                 }
362                 if (isRanged()) {
363                     buffer.append("<range/>");
364                 }
365                 buffer.append("</").append(getElementName()).append(">");
366             }
367             else {
368                 buffer.append("/>");
369             }
370             return buffer.toString();
371         }
372     }
373 
374     /**
375      * The feature negotiation portion of the StreamInitiation packet.
376      *
377      * @author Alexander Wenckus
378      *
379      */
380     public class Feature implements PacketExtension {
381 
382         private final DataForm data;
383 
384         /**
385          * The dataform can be provided as part of the constructor.
386          *
387          * @param data The dataform.
388          */
Feature(final DataForm data)389         public Feature(final DataForm data) {
390             this.data = data;
391         }
392 
393         /**
394          * Returns the dataform associated with the feature negotiation.
395          *
396          * @return Returns the dataform associated with the feature negotiation.
397          */
getData()398         public DataForm getData() {
399             return data;
400         }
401 
getNamespace()402         public String getNamespace() {
403             return "http://jabber.org/protocol/feature-neg";
404         }
405 
getElementName()406         public String getElementName() {
407             return "feature";
408         }
409 
toXML()410         public String toXML() {
411             StringBuilder buf = new StringBuilder();
412             buf
413                     .append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">");
414 			buf.append(data.toXML());
415 			buf.append("</feature>");
416 			return buf.toString();
417 		}
418 	}
419 }
420