| /**
|
| * $RCSfile$
|
| * $Revision$
|
| * $Date$
|
| *
|
| * Copyright 2003-2006 Jive Software.
|
| *
|
| * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
| * you may not use this file except in compliance with the License.
|
| * You may obtain a copy of the License at
|
| *
|
| * http://www.apache.org/licenses/LICENSE-2.0
|
| *
|
| * Unless required by applicable law or agreed to in writing, software
|
| * distributed under the License is distributed on an "AS IS" BASIS,
|
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| * See the License for the specific language governing permissions and
|
| * limitations under the License.
|
| */
|
| package org.jivesoftware.smackx.packet;
|
|
|
| import java.util.Date;
|
|
|
| import org.jivesoftware.smack.packet.IQ;
|
| import org.jivesoftware.smack.packet.PacketExtension;
|
| import org.jivesoftware.smack.util.StringUtils;
|
|
|
| /**
|
| * The process by which two entities initiate a stream.
|
| *
|
| * @author Alexander Wenckus
|
| */
|
| public class StreamInitiation extends IQ {
|
|
|
| private String id;
|
|
|
| private String mimeType;
|
|
|
| private File file;
|
|
|
| private Feature featureNegotiation;
|
|
|
| /**
|
| * The "id" attribute is an opaque identifier. This attribute MUST be
|
| * present on type='set', and MUST be a valid string. This SHOULD NOT be
|
| * sent back on type='result', since the <iq/> "id" attribute provides the
|
| * only context needed. This value is generated by the Sender, and the same
|
| * value MUST be used throughout a session when talking to the Receiver.
|
| *
|
| * @param id The "id" attribute.
|
| */
|
| public void setSesssionID(final String id) {
|
| this.id = id;
|
| }
|
|
|
| /**
|
| * Uniquely identifies a stream initiation to the recipient.
|
| *
|
| * @return The "id" attribute.
|
| * @see #setSesssionID(String)
|
| */
|
| public String getSessionID() {
|
| return id;
|
| }
|
|
|
| /**
|
| * The "mime-type" attribute identifies the MIME-type for the data across
|
| * the stream. This attribute MUST be a valid MIME-type as registered with
|
| * the Internet Assigned Numbers Authority (IANA) [3] (specifically, as
|
| * listed at <http://www.iana.org/assignments/media-types>). During
|
| * negotiation, this attribute SHOULD be present, and is otherwise not
|
| * required. If not included during negotiation, its value is assumed to be
|
| * "binary/octect-stream".
|
| *
|
| * @param mimeType The valid mime-type.
|
| */
|
| public void setMimeType(final String mimeType) {
|
| this.mimeType = mimeType;
|
| }
|
|
|
| /**
|
| * Identifies the type of file that is desired to be transfered.
|
| *
|
| * @return The mime-type.
|
| * @see #setMimeType(String)
|
| */
|
| public String getMimeType() {
|
| return mimeType;
|
| }
|
|
|
| /**
|
| * Sets the file which contains the information pertaining to the file to be
|
| * transfered.
|
| *
|
| * @param file The file identified by the stream initiator to be sent.
|
| */
|
| public void setFile(final File file) {
|
| this.file = file;
|
| }
|
|
|
| /**
|
| * Returns the file containing the information about the request.
|
| *
|
| * @return Returns the file containing the information about the request.
|
| */
|
| public File getFile() {
|
| return file;
|
| }
|
|
|
| /**
|
| * Sets the data form which contains the valid methods of stream neotiation
|
| * and transfer.
|
| *
|
| * @param form The dataform containing the methods.
|
| */
|
| public void setFeatureNegotiationForm(final DataForm form) {
|
| this.featureNegotiation = new Feature(form);
|
| }
|
|
|
| /**
|
| * Returns the data form which contains the valid methods of stream
|
| * neotiation and transfer.
|
| *
|
| * @return Returns the data form which contains the valid methods of stream
|
| * neotiation and transfer.
|
| */
|
| public DataForm getFeatureNegotiationForm() {
|
| return featureNegotiation.getData();
|
| }
|
|
|
| /*
|
| * (non-Javadoc)
|
| *
|
| * @see org.jivesoftware.smack.packet.IQ#getChildElementXML()
|
| */
|
| public String getChildElementXML() {
|
| StringBuilder buf = new StringBuilder();
|
| if (this.getType().equals(IQ.Type.SET)) {
|
| buf.append("<si xmlns=\"http://jabber.org/protocol/si\" ");
|
| if (getSessionID() != null) {
|
| buf.append("id=\"").append(getSessionID()).append("\" ");
|
| }
|
| if (getMimeType() != null) {
|
| buf.append("mime-type=\"").append(getMimeType()).append("\" ");
|
| }
|
| buf
|
| .append("profile=\"http://jabber.org/protocol/si/profile/file-transfer\">");
|
|
|
| // Add the file section if there is one.
|
| String fileXML = file.toXML();
|
| if (fileXML != null) {
|
| buf.append(fileXML);
|
| }
|
| }
|
| else if (this.getType().equals(IQ.Type.RESULT)) {
|
| buf.append("<si xmlns=\"http://jabber.org/protocol/si\">");
|
| }
|
| else {
|
| throw new IllegalArgumentException("IQ Type not understood");
|
| }
|
| if (featureNegotiation != null) {
|
| buf.append(featureNegotiation.toXML());
|
| }
|
| buf.append("</si>");
|
| return buf.toString();
|
| }
|
|
|
| /**
|
| * <ul>
|
| * <li>size: The size, in bytes, of the data to be sent.</li>
|
| * <li>name: The name of the file that the Sender wishes to send.</li>
|
| * <li>date: The last modification time of the file. This is specified
|
| * using the DateTime profile as described in Jabber Date and Time Profiles.</li>
|
| * <li>hash: The MD5 sum of the file contents.</li>
|
| * </ul>
|
| * <p/>
|
| * <p/>
|
| * <desc> is used to provide a sender-generated description of the
|
| * file so the receiver can better understand what is being sent. It MUST
|
| * NOT be sent in the result.
|
| * <p/>
|
| * <p/>
|
| * When <range> is sent in the offer, it should have no attributes.
|
| * This signifies that the sender can do ranged transfers. When a Stream
|
| * Initiation result is sent with the <range> element, it uses these
|
| * attributes:
|
| * <p/>
|
| * <ul>
|
| * <li>offset: Specifies the position, in bytes, to start transferring the
|
| * file data from. This defaults to zero (0) if not specified.</li>
|
| * <li>length - Specifies the number of bytes to retrieve starting at
|
| * offset. This defaults to the length of the file from offset to the end.</li>
|
| * </ul>
|
| * <p/>
|
| * <p/>
|
| * Both attributes are OPTIONAL on the <range> element. Sending no
|
| * attributes is synonymous with not sending the <range> element. When
|
| * no <range> element is sent in the Stream Initiation result, the
|
| * Sender MUST send the complete file starting at offset 0. More generally,
|
| * data is sent over the stream byte for byte starting at the offset
|
| * position for the length specified.
|
| *
|
| * @author Alexander Wenckus
|
| */
|
| public static class File implements PacketExtension {
|
|
|
| private final String name;
|
|
|
| private final long size;
|
|
|
| private String hash;
|
|
|
| private Date date;
|
|
|
| private String desc;
|
|
|
| private boolean isRanged;
|
|
|
| /**
|
| * Constructor providing the name of the file and its size.
|
| *
|
| * @param name The name of the file.
|
| * @param size The size of the file in bytes.
|
| */
|
| public File(final String name, final long size) {
|
| if (name == null) {
|
| throw new NullPointerException("name cannot be null");
|
| }
|
|
|
| this.name = name;
|
| this.size = size;
|
| }
|
|
|
| /**
|
| * Returns the file's name.
|
| *
|
| * @return Returns the file's name.
|
| */
|
| public String getName() {
|
| return name;
|
| }
|
|
|
| /**
|
| * Returns the file's size.
|
| *
|
| * @return Returns the file's size.
|
| */
|
| public long getSize() {
|
| return size;
|
| }
|
|
|
| /**
|
| * Sets the MD5 sum of the file's contents
|
| *
|
| * @param hash The MD5 sum of the file's contents.
|
| */
|
| public void setHash(final String hash) {
|
| this.hash = hash;
|
| }
|
|
|
| /**
|
| * Returns the MD5 sum of the file's contents
|
| *
|
| * @return Returns the MD5 sum of the file's contents
|
| */
|
| public String getHash() {
|
| return hash;
|
| }
|
|
|
| /**
|
| * Sets the date that the file was last modified.
|
| *
|
| * @param date The date that the file was last modified.
|
| */
|
| public void setDate(Date date) {
|
| this.date = date;
|
| }
|
|
|
| /**
|
| * Returns the date that the file was last modified.
|
| *
|
| * @return Returns the date that the file was last modified.
|
| */
|
| public Date getDate() {
|
| return date;
|
| }
|
|
|
| /**
|
| * Sets the description of the file.
|
| *
|
| * @param desc The description of the file so that the file reciever can
|
| * know what file it is.
|
| */
|
| public void setDesc(final String desc) {
|
| this.desc = desc;
|
| }
|
|
|
| /**
|
| * Returns the description of the file.
|
| *
|
| * @return Returns the description of the file.
|
| */
|
| public String getDesc() {
|
| return desc;
|
| }
|
|
|
| /**
|
| * True if a range can be provided and false if it cannot.
|
| *
|
| * @param isRanged True if a range can be provided and false if it cannot.
|
| */
|
| public void setRanged(final boolean isRanged) {
|
| this.isRanged = isRanged;
|
| }
|
|
|
| /**
|
| * Returns whether or not the initiator can support a range for the file
|
| * tranfer.
|
| *
|
| * @return Returns whether or not the initiator can support a range for
|
| * the file tranfer.
|
| */
|
| public boolean isRanged() {
|
| return isRanged;
|
| }
|
|
|
| public String getElementName() {
|
| return "file";
|
| }
|
|
|
| public String getNamespace() {
|
| return "http://jabber.org/protocol/si/profile/file-transfer";
|
| }
|
|
|
| public String toXML() {
|
| StringBuilder buffer = new StringBuilder();
|
|
|
| buffer.append("<").append(getElementName()).append(" xmlns=\"")
|
| .append(getNamespace()).append("\" ");
|
|
|
| if (getName() != null) {
|
| buffer.append("name=\"").append(StringUtils.escapeForXML(getName())).append("\" ");
|
| }
|
|
|
| if (getSize() > 0) {
|
| buffer.append("size=\"").append(getSize()).append("\" ");
|
| }
|
|
|
| if (getDate() != null) {
|
| buffer.append("date=\"").append(StringUtils.formatXEP0082Date(date)).append("\" ");
|
| } |
| |
| if (getHash() != null) { |
| buffer.append("hash=\"").append(getHash()).append("\" "); |
| } |
| |
| if ((desc != null && desc.length() > 0) || isRanged) { |
| buffer.append(">"); |
| if (getDesc() != null && desc.length() > 0) { |
| buffer.append("<desc>").append(StringUtils.escapeForXML(getDesc())).append("</desc>"); |
| } |
| if (isRanged()) { |
| buffer.append("<range/>"); |
| } |
| buffer.append("</").append(getElementName()).append(">"); |
| } |
| else { |
| buffer.append("/>"); |
| } |
| return buffer.toString(); |
| } |
| } |
| |
| /** |
| * The feature negotiation portion of the StreamInitiation packet. |
| * |
| * @author Alexander Wenckus |
| * |
| */ |
| public class Feature implements PacketExtension { |
| |
| private final DataForm data; |
| |
| /** |
| * The dataform can be provided as part of the constructor. |
| * |
| * @param data The dataform. |
| */ |
| public Feature(final DataForm data) { |
| this.data = data; |
| } |
| |
| /** |
| * Returns the dataform associated with the feature negotiation. |
| * |
| * @return Returns the dataform associated with the feature negotiation. |
| */ |
| public DataForm getData() { |
| return data; |
| } |
| |
| public String getNamespace() { |
| return "http://jabber.org/protocol/feature-neg"; |
| } |
| |
| public String getElementName() { |
| return "feature"; |
| } |
| |
| public String toXML() { |
| StringBuilder buf = new StringBuilder(); |
| buf |
| .append("<feature xmlns=\"http://jabber.org/protocol/feature-neg\">"); |
| buf.append(data.toXML()); |
| buf.append("</feature>"); |
| return buf.toString(); |
| } |
| } |
| } |